diff --git a/work/meshai/transport/meshcore_transport.py b/work/meshai/transport/meshcore_transport.py index 8d556f1..0ea7027 100644 --- a/work/meshai/transport/meshcore_transport.py +++ b/work/meshai/transport/meshcore_transport.py @@ -206,14 +206,20 @@ class MeshCoreTransport(MeshTransport): logger.error("MeshCoreTransport: cannot send, not connected") return False - chan_idx = channel or getattr(self.config, "meshcore_channel_index", 0) - try: if destination: result = self._run_coro( self._mc.commands.send_msg(destination, text) ) else: + # Channel-index semantics do NOT cross transports: the passed + # `channel` carries Meshtastic channel-index semantics (e.g. + # index 8) that have no relationship to MeshCore's separate + # channel table. The configured MeshCore channel is therefore + # authoritative for broadcasts, so we ignore `channel` here + # (this also avoids an explicit channel=0 being treated as + # falsy). + chan_idx = getattr(self.config, "meshcore_channel_index", 0) result = self._run_coro( self._mc.commands.send_chan_msg(chan_idx, text) ) diff --git a/work/tests/test_meshcore_transport.py b/work/tests/test_meshcore_transport.py index 5a2b301..0f3eb48 100644 --- a/work/tests/test_meshcore_transport.py +++ b/work/tests/test_meshcore_transport.py @@ -202,8 +202,9 @@ class TestSendMessageChannel: # _mc is None, no loop started assert t.send_message("test") is False - def test_uses_config_channel_index_when_channel_zero(self): - cfg = _mc_config(meshcore_channel_index=3) + def _transport_with_configured_index(self, index): + """Build a MeshCoreTransport whose config sets meshcore_channel_index.""" + cfg = _mc_config(meshcore_channel_index=index) t = MeshCoreTransport(cfg) ok = MagicMock() ok.is_error.return_value = False @@ -217,9 +218,32 @@ class TestSendMessageChannel: thread = threading.Thread(target=loop.run_forever, daemon=True) thread.start() t._loop_thread = thread + return t, mc + + def test_uses_config_channel_index_when_channel_zero(self): + t, mc = self._transport_with_configured_index(3) try: t.send_message("hi", channel=0) - # channel=0 is falsy → falls back to meshcore_channel_index=3 + # channel=0 must not be treated as falsy-fallthrough: broadcasts + # always use the configured meshcore_channel_index=3. + mc.commands.send_chan_msg.assert_awaited_once_with(3, "hi") + finally: + _cleanup(t) + + def test_uses_config_channel_index_when_channel_default(self): + t, mc = self._transport_with_configured_index(3) + try: + t.send_message("hi") # default channel param + mc.commands.send_chan_msg.assert_awaited_once_with(3, "hi") + finally: + _cleanup(t) + + def test_ignores_meshtastic_channel_index(self): + # channel=8 carries Meshtastic channel-index semantics that do NOT map + # to MeshCore's channel table; the configured index (3) is authoritative. + t, mc = self._transport_with_configured_index(3) + try: + t.send_message("hi", channel=8) mc.commands.send_chan_msg.assert_awaited_once_with(3, "hi") finally: _cleanup(t)