mmrelay 1.1.0__py3-none-any.whl → 1.1.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of mmrelay might be problematic. Click here for more details.
- mmrelay/__init__.py +1 -1
- mmrelay/meshtastic_utils.py +81 -45
- mmrelay/tools/sample-docker-compose.yaml +8 -1
- {mmrelay-1.1.0.dist-info → mmrelay-1.1.1.dist-info}/METADATA +3 -2
- {mmrelay-1.1.0.dist-info → mmrelay-1.1.1.dist-info}/RECORD +9 -9
- {mmrelay-1.1.0.dist-info → mmrelay-1.1.1.dist-info}/WHEEL +0 -0
- {mmrelay-1.1.0.dist-info → mmrelay-1.1.1.dist-info}/entry_points.txt +0 -0
- {mmrelay-1.1.0.dist-info → mmrelay-1.1.1.dist-info}/licenses/LICENSE +0 -0
- {mmrelay-1.1.0.dist-info → mmrelay-1.1.1.dist-info}/top_level.txt +0 -0
mmrelay/__init__.py
CHANGED
mmrelay/meshtastic_utils.py
CHANGED
|
@@ -50,14 +50,15 @@ reconnect_task = None # To keep track of the reconnect task
|
|
|
50
50
|
# Track pubsub subscription state to prevent duplicate subscriptions during reconnections
|
|
51
51
|
subscribed_to_messages = False
|
|
52
52
|
subscribed_to_connection_lost = False
|
|
53
|
+
subscribed_to_connection_established = False
|
|
53
54
|
|
|
54
55
|
|
|
55
56
|
def is_running_as_service():
|
|
56
57
|
"""
|
|
57
|
-
|
|
58
|
-
|
|
58
|
+
Check if the application is running as a systemd service.
|
|
59
|
+
|
|
59
60
|
Returns:
|
|
60
|
-
|
|
61
|
+
True if the process is running under systemd, either by detecting the INVOCATION_ID environment variable or by verifying that the parent process is systemd; otherwise, False.
|
|
61
62
|
"""
|
|
62
63
|
# Check for INVOCATION_ID environment variable (set by systemd)
|
|
63
64
|
if os.environ.get("INVOCATION_ID"):
|
|
@@ -88,14 +89,14 @@ def serial_port_exists(port_name):
|
|
|
88
89
|
|
|
89
90
|
def connect_meshtastic(passed_config=None, force_connect=False):
|
|
90
91
|
"""
|
|
91
|
-
Establishes and manages a connection to a Meshtastic device using the
|
|
92
|
-
|
|
93
|
-
If a configuration is provided, updates the global configuration and Matrix room mappings. Handles reconnection logic, including closing any existing connection if `force_connect` is True. Retries connection attempts with exponential backoff until successful or shutdown is initiated. Subscribes to message and connection
|
|
94
|
-
|
|
92
|
+
Establishes and manages a connection to a Meshtastic device using the specified connection type (serial, BLE, or TCP).
|
|
93
|
+
|
|
94
|
+
If a configuration is provided, updates the global configuration and Matrix room mappings. Handles reconnection logic, including closing any existing connection if `force_connect` is True. Retries connection attempts with exponential backoff until successful or shutdown is initiated. Subscribes to message and connection events only once per process to avoid duplicate subscriptions.
|
|
95
|
+
|
|
95
96
|
Parameters:
|
|
96
97
|
passed_config (dict, optional): Configuration dictionary to use for the connection. If provided, updates the global configuration.
|
|
97
98
|
force_connect (bool, optional): If True, forces a new connection even if one already exists.
|
|
98
|
-
|
|
99
|
+
|
|
99
100
|
Returns:
|
|
100
101
|
Meshtastic interface client if the connection is successful, or None if the connection fails or shutdown is in progress.
|
|
101
102
|
"""
|
|
@@ -202,8 +203,8 @@ def connect_meshtastic(passed_config=None, force_connect=False):
|
|
|
202
203
|
f"Connected to {nodeInfo['user']['shortName']} / {nodeInfo['user']['hwModel']}"
|
|
203
204
|
)
|
|
204
205
|
|
|
205
|
-
# Subscribe to message and connection
|
|
206
|
-
global subscribed_to_messages, subscribed_to_connection_lost
|
|
206
|
+
# Subscribe to message and connection events (only if not already subscribed)
|
|
207
|
+
global subscribed_to_messages, subscribed_to_connection_lost, subscribed_to_connection_established
|
|
207
208
|
if not subscribed_to_messages:
|
|
208
209
|
pub.subscribe(on_meshtastic_message, "meshtastic.receive")
|
|
209
210
|
subscribed_to_messages = True
|
|
@@ -216,6 +217,13 @@ def connect_meshtastic(passed_config=None, force_connect=False):
|
|
|
216
217
|
subscribed_to_connection_lost = True
|
|
217
218
|
logger.debug("Subscribed to meshtastic.connection.lost")
|
|
218
219
|
|
|
220
|
+
if not subscribed_to_connection_established:
|
|
221
|
+
pub.subscribe(
|
|
222
|
+
on_established_meshtastic_connection, "meshtastic.connection.established"
|
|
223
|
+
)
|
|
224
|
+
subscribed_to_connection_established = True
|
|
225
|
+
logger.debug("Subscribed to meshtastic.connection.established")
|
|
226
|
+
|
|
219
227
|
except (
|
|
220
228
|
serial.SerialException,
|
|
221
229
|
BleakDBusError,
|
|
@@ -241,10 +249,13 @@ def connect_meshtastic(passed_config=None, force_connect=False):
|
|
|
241
249
|
return meshtastic_client
|
|
242
250
|
|
|
243
251
|
|
|
244
|
-
def on_lost_meshtastic_connection(interface=None):
|
|
252
|
+
def on_lost_meshtastic_connection(interface=None, detection_source="detected by library"):
|
|
245
253
|
"""
|
|
246
|
-
|
|
247
|
-
|
|
254
|
+
Handles Meshtastic connection loss by initiating a reconnection sequence unless a shutdown is in progress or a reconnection is already underway.
|
|
255
|
+
|
|
256
|
+
Parameters:
|
|
257
|
+
interface: The interface that lost connection (unused; present for compatibility).
|
|
258
|
+
detection_source (str): Description of how the disconnection was detected.
|
|
248
259
|
"""
|
|
249
260
|
global meshtastic_client, reconnecting, shutting_down, event_loop, reconnect_task
|
|
250
261
|
with meshtastic_lock:
|
|
@@ -257,7 +268,7 @@ def on_lost_meshtastic_connection(interface=None):
|
|
|
257
268
|
)
|
|
258
269
|
return
|
|
259
270
|
reconnecting = True
|
|
260
|
-
logger.error("Lost connection. Reconnecting...")
|
|
271
|
+
logger.error(f"Lost connection ({detection_source}). Reconnecting...")
|
|
261
272
|
|
|
262
273
|
if meshtastic_client:
|
|
263
274
|
try:
|
|
@@ -278,8 +289,9 @@ def on_lost_meshtastic_connection(interface=None):
|
|
|
278
289
|
|
|
279
290
|
async def reconnect():
|
|
280
291
|
"""
|
|
281
|
-
Asynchronously attempts to reconnect
|
|
282
|
-
|
|
292
|
+
Asynchronously attempts to reconnect to the Meshtastic device using exponential backoff, stopping if a shutdown is initiated.
|
|
293
|
+
|
|
294
|
+
The function increases the wait time between attempts up to a maximum of 5 minutes and provides a progress bar if not running as a service. The reconnection process halts immediately if the shutdown flag is set or if reconnection succeeds.
|
|
283
295
|
"""
|
|
284
296
|
global meshtastic_client, reconnecting, shutting_down
|
|
285
297
|
backoff_time = 10
|
|
@@ -334,11 +346,23 @@ async def reconnect():
|
|
|
334
346
|
reconnecting = False
|
|
335
347
|
|
|
336
348
|
|
|
337
|
-
def
|
|
349
|
+
def on_established_meshtastic_connection(interface=None):
|
|
350
|
+
"""
|
|
351
|
+
Callback triggered when a Meshtastic connection is successfully established.
|
|
352
|
+
|
|
353
|
+
Clears the reconnecting flag and logs the connection event.
|
|
338
354
|
"""
|
|
339
|
-
|
|
355
|
+
global reconnecting
|
|
356
|
+
with meshtastic_lock:
|
|
357
|
+
logger.info("Connection established (detected by library)")
|
|
358
|
+
reconnecting = False # Clear reconnecting flag when connection is confirmed
|
|
359
|
+
|
|
340
360
|
|
|
341
|
-
|
|
361
|
+
def on_meshtastic_message(packet, interface):
|
|
362
|
+
"""
|
|
363
|
+
Process incoming Meshtastic messages and relay them to Matrix rooms or plugins according to message type and interaction settings.
|
|
364
|
+
|
|
365
|
+
Handles reactions and replies by relaying them to Matrix if enabled. Normal text messages are relayed to all mapped Matrix rooms unless handled by a plugin or directed to the relay node. Non-text messages are passed to plugins for processing. Messages from unmapped channels or disabled detection sensors are filtered out, and sender information is retrieved or stored as needed.
|
|
342
366
|
"""
|
|
343
367
|
global config, matrix_rooms
|
|
344
368
|
|
|
@@ -643,10 +667,11 @@ def on_meshtastic_message(packet, interface):
|
|
|
643
667
|
|
|
644
668
|
async def check_connection():
|
|
645
669
|
"""
|
|
646
|
-
Periodically
|
|
647
|
-
|
|
648
|
-
If
|
|
649
|
-
|
|
670
|
+
Periodically verifies the health of the Meshtastic connection and triggers reconnection if needed.
|
|
671
|
+
|
|
672
|
+
For non-BLE connections, this coroutine calls `localNode.getMetadata()` at a configurable interval to confirm the connection is alive. If the health check fails or does not return expected metadata, it invokes the connection lost handler to initiate reconnection. BLE connections are excluded from periodic checks, as their library provides real-time disconnection detection.
|
|
673
|
+
|
|
674
|
+
This coroutine runs continuously until shutdown is requested.
|
|
650
675
|
"""
|
|
651
676
|
global meshtastic_client, shutting_down, config, reconnecting
|
|
652
677
|
|
|
@@ -663,33 +688,44 @@ async def check_connection():
|
|
|
663
688
|
f"Starting connection heartbeat monitor (interval: {heartbeat_interval}s)"
|
|
664
689
|
)
|
|
665
690
|
|
|
691
|
+
# Track if we've logged the BLE skip message to avoid spam
|
|
692
|
+
ble_skip_logged = False
|
|
693
|
+
|
|
666
694
|
while not shutting_down:
|
|
667
695
|
if meshtastic_client and not reconnecting:
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
696
|
+
# BLE has real-time disconnection detection in the library
|
|
697
|
+
# Skip periodic health checks to avoid duplicate reconnection attempts
|
|
698
|
+
if connection_type == "ble":
|
|
699
|
+
if not ble_skip_logged:
|
|
700
|
+
logger.info("BLE connection uses real-time disconnection detection - health checks disabled")
|
|
701
|
+
ble_skip_logged = True
|
|
702
|
+
else:
|
|
703
|
+
try:
|
|
704
|
+
logger.debug(
|
|
705
|
+
f"Checking {connection_type} connection health using getMetadata()"
|
|
706
|
+
)
|
|
707
|
+
output_capture = io.StringIO()
|
|
708
|
+
with contextlib.redirect_stdout(
|
|
709
|
+
output_capture
|
|
710
|
+
), contextlib.redirect_stderr(output_capture):
|
|
711
|
+
meshtastic_client.localNode.getMetadata()
|
|
677
712
|
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
713
|
+
console_output = output_capture.getvalue()
|
|
714
|
+
if "firmware_version" not in console_output:
|
|
715
|
+
raise Exception("No firmware_version in getMetadata output.")
|
|
681
716
|
|
|
682
|
-
|
|
717
|
+
logger.debug(f"{connection_type.capitalize()} connection healthy")
|
|
683
718
|
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
719
|
+
except Exception as e:
|
|
720
|
+
# Only trigger reconnection if we're not already reconnecting
|
|
721
|
+
if not reconnecting:
|
|
722
|
+
logger.warning(
|
|
723
|
+
f"{connection_type.capitalize()} connection health check failed: {e}"
|
|
724
|
+
)
|
|
725
|
+
# Use existing handler with health check reason
|
|
726
|
+
on_lost_meshtastic_connection(detection_source=f"health check failed: {str(e)}")
|
|
727
|
+
else:
|
|
728
|
+
logger.debug("Skipping reconnection trigger - already reconnecting")
|
|
693
729
|
elif reconnecting:
|
|
694
730
|
logger.debug("Skipping connection check - reconnection in progress")
|
|
695
731
|
elif not meshtastic_client:
|
|
@@ -3,6 +3,7 @@ services:
|
|
|
3
3
|
build: .
|
|
4
4
|
container_name: meshtastic-matrix-relay
|
|
5
5
|
restart: unless-stopped
|
|
6
|
+
user: "${UID:-1000}:${GID:-1000}"
|
|
6
7
|
|
|
7
8
|
environment:
|
|
8
9
|
- TZ=UTC
|
|
@@ -28,5 +29,11 @@ services:
|
|
|
28
29
|
# - /dev/ttyUSB0:/dev/ttyUSB0
|
|
29
30
|
# - /dev/ttyACM0:/dev/ttyACM0
|
|
30
31
|
|
|
31
|
-
# For BLE connections, uncomment
|
|
32
|
+
# For BLE connections, uncomment these:
|
|
32
33
|
# privileged: true
|
|
34
|
+
# network_mode: host
|
|
35
|
+
# Additional volumes for BLE (add to existing volumes section above):
|
|
36
|
+
# - /var/run/dbus:/var/run/dbus:ro
|
|
37
|
+
# - /sys/bus/usb:/sys/bus/usb:ro
|
|
38
|
+
# - /sys/class/bluetooth:/sys/class/bluetooth:ro
|
|
39
|
+
# - /sys/devices:/sys/devices:ro
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mmrelay
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.1
|
|
4
4
|
Summary: Bridge between Meshtastic mesh networks and Matrix chat rooms
|
|
5
5
|
Home-page: https://github.com/geoffwhittington/meshtastic-matrix-relay
|
|
6
6
|
Author: Geoff Whittington, Jeremiah K., and contributors
|
|
@@ -56,6 +56,7 @@ A powerful and easy-to-use relay between Meshtastic devices and Matrix chat room
|
|
|
56
56
|
- Supports mapping multiple rooms and channels 1:1
|
|
57
57
|
- Relays messages to/from an MQTT broker, if configured in the Meshtastic firmware
|
|
58
58
|
- ✨️ _Bidirectional replies and reactions support_ ✨️ **NEW!!**
|
|
59
|
+
- ✨️ _Native Docker support_ ✨️ **NEW!!**
|
|
59
60
|
|
|
60
61
|
_We would love to support [Matrix E2EE rooms](https://github.com/geoffwhittington/meshtastic-matrix-relay/issues/33), but this is currently not implemented._
|
|
61
62
|
|
|
@@ -98,7 +99,7 @@ make logs # View logs
|
|
|
98
99
|
|
|
99
100
|
Docker provides isolated environment, easy deployment, automatic restarts, and volume persistence.
|
|
100
101
|
|
|
101
|
-
For detailed Docker setup instructions, see the [Docker Guide](DOCKER.md).
|
|
102
|
+
For detailed Docker setup instructions, see the [Docker Guide](docs/DOCKER.md).
|
|
102
103
|
|
|
103
104
|
> **Note**: Docker builds currently use a temporary fork of the meshtastic library with BLE hanging fixes. PyPI releases use the upstream library. This will be resolved when the fixes are merged upstream.
|
|
104
105
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
mmrelay/__init__.py,sha256=
|
|
1
|
+
mmrelay/__init__.py,sha256=v-xcK3JoA0m2eBmIudme82RPgp5PQInzRSs5mitHB28,594
|
|
2
2
|
mmrelay/cli.py,sha256=hdPTlcGsXTJC9GEUiScG7b3IFp02B3lwhqgwFpU3NsM,13835
|
|
3
3
|
mmrelay/config.py,sha256=5VZag8iSc5yLQgvwI76bbpizbtqag74cHnfXCrWHNyA,7910
|
|
4
4
|
mmrelay/config_checker.py,sha256=UnoHVTXzfdTfFkbmXv9r_Si76v-sxXLb5FOaQSOM45E,4909
|
|
@@ -6,7 +6,7 @@ mmrelay/db_utils.py,sha256=eTMMkYVWsmO_DkrBfnZMw4ohg_xa0S9TXJoBjRFTwzo,13590
|
|
|
6
6
|
mmrelay/log_utils.py,sha256=ot0GpYppyNuPOWY8d3EXdLPojoJYEqfmUjVlcYm8neY,7532
|
|
7
7
|
mmrelay/main.py,sha256=TL5xWFXIGwAQKau-hN4sRB0wxtNWzc629ry_qPbovv0,11585
|
|
8
8
|
mmrelay/matrix_utils.py,sha256=XSOHztRrdIjFGyYt8QhGBL6nMv_6iodeYL288pG9voA,45813
|
|
9
|
-
mmrelay/meshtastic_utils.py,sha256=
|
|
9
|
+
mmrelay/meshtastic_utils.py,sha256=MgCNGSufQDddncVKJVgmp_6DDZbcHSvOvW72vuNDLeg,31596
|
|
10
10
|
mmrelay/plugin_loader.py,sha256=NRiXF6Ty1WD9jNXXKvzJh7kE0ba5oICXNVAfMaTPqH4,39247
|
|
11
11
|
mmrelay/setup_utils.py,sha256=N6qdScHKHEMFKDmT1l7dcLDPNTusZXPkyxrLXjFLhRI,19910
|
|
12
12
|
mmrelay/plugins/__init__.py,sha256=KVMQIXRhe0wlGj4O3IZ0vOIQRKFkfPYejHXhJL17qrc,51
|
|
@@ -23,12 +23,12 @@ mmrelay/plugins/telemetry_plugin.py,sha256=8SxWv4BLXMUTbiVaD3MjlMMdQyS7S_1OfLlVN
|
|
|
23
23
|
mmrelay/plugins/weather_plugin.py,sha256=1bQhmiX-enNphzGoFVprU0LcZQX9BvGxWAJAG8Wekg0,8596
|
|
24
24
|
mmrelay/tools/__init__.py,sha256=WFjDQjdevgg19_zT6iEoL29rvb1JPqYSd8708Jn5D7A,838
|
|
25
25
|
mmrelay/tools/mmrelay.service,sha256=3vqK1VbfXvVftkTrTEOan77aTHeOT36hIAL7HqJsmTg,567
|
|
26
|
-
mmrelay/tools/sample-docker-compose.yaml,sha256=
|
|
26
|
+
mmrelay/tools/sample-docker-compose.yaml,sha256=vVgJrh-6l48hkj5F-52JA5tpDWPBjiPQ36CE9Pkqn44,1251
|
|
27
27
|
mmrelay/tools/sample.env,sha256=RP-o3rX3jnEIrVG2rqCZq31O1yRXou4HcGrXWLVbKKw,311
|
|
28
28
|
mmrelay/tools/sample_config.yaml,sha256=0BKND0qbke8z9X9J9iHleu567dZt3RmHUxhZlQEEdFk,3290
|
|
29
|
-
mmrelay-1.1.
|
|
30
|
-
mmrelay-1.1.
|
|
31
|
-
mmrelay-1.1.
|
|
32
|
-
mmrelay-1.1.
|
|
33
|
-
mmrelay-1.1.
|
|
34
|
-
mmrelay-1.1.
|
|
29
|
+
mmrelay-1.1.1.dist-info/licenses/LICENSE,sha256=yceWauM1c0-FHxVplsD7W1-AbSeRaUNlmqT4UO1msBU,1073
|
|
30
|
+
mmrelay-1.1.1.dist-info/METADATA,sha256=ltSYRsnKQvdwsKd0CqAqMoSMWLsqZq8Pcpmfy_OjqI8,6713
|
|
31
|
+
mmrelay-1.1.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
32
|
+
mmrelay-1.1.1.dist-info/entry_points.txt,sha256=SJZwGUOEpQ-qx4H8UL4xKFnKeInGUaZNW1I0ddjK7Ws,45
|
|
33
|
+
mmrelay-1.1.1.dist-info/top_level.txt,sha256=B_ZLCRm7NYAmI3PipRUyHGymP-C-q16LSeMGzmqJfo4,8
|
|
34
|
+
mmrelay-1.1.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|