* Moving to socket.sendall() is safer, as sendall will send the entire
buffer, while send() would return the number of bytes sent and
require being called multiple times if the buffer was full.
* On exceptions: reconnect to the server.
* On reconnection: make sure using a lock that there isn't a race
between the readers and the writers triggering a reconnect.
Handle MeshInterface.MeshInterfaceError when device is rebooting
or connection times out, with user-friendly error message.
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
Replace raw tracebacks with helpful error messages that explain:
- What went wrong
- Possible causes
- How to fix it
Covers all BLEError cases:
- Device not found (BLE disabled, sleep mode, out of range)
- Multiple devices found (need to specify which one)
- Write errors (pairing PIN, Linux bluetooth group)
- Read errors (device disconnected)
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
This fixes two issues that caused BLE connections to hang on macOS
when not using the --debug flag:
1. Race condition in BLEClient event loop initialization
- The event loop thread was started but asyncio operations were
submitted before the loop was actually running
- Added threading.Event synchronization to ensure the event loop
is running before any operations are submitted
- The ready signal is sent from within the loop via call_soon()
to guarantee the loop is truly active
2. CoreBluetooth callback delivery on macOS
- On macOS, CoreBluetooth requires occasional I/O operations for
callbacks to be properly delivered to the main thread
- Without --debug, no I/O was happening, causing callbacks to
never be processed and operations to hang indefinitely
- Added sys.stdout.flush() call before waiting for async results
to trigger the necessary I/O
The --debug flag masked these issues because:
- Debug logging introduces timing delays that let the event loop start
- Logger I/O triggers the necessary callback delivery mechanism
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
Ensures that automatic replies are sent back on the same channel index the message was received on. Previously, all replies defaulted to the primary channel (0), even if the incoming message arrived on a secondary channel. Additionally it ensures incoming messages match the specified channel index.
E.g: meshtastic --ch-index 1 --reply .
Modified the `onReceive` handler to extract the `channel` index from received packets. This ensures `interface.sendText` targets the originating channel rather than always defaulting to the primary channel. Added a filter to ensure that only the specified channel index is being replied to.
this function, xor_hash, and a variable for the default key (as bytes, I think, rather than the base64 version) really all belong in meshtastic.util rather than here.
There's multiple forms of hashing in firmware so this should be named to denote that, perhaps channel_hash. If we later want to add the frequency-slot-style hash, better if it's distinguished better from the start.
### Summary
- Added a new method `get_channels_with_hash()` to the `Node` class.
- This method returns a list of dictionaries, each containing the channel index, role, name, and a hash value derived from the channel name and PSK.
- The hash is calculated using the existing `generate_hash` utility, ensuring consistency with other parts of the codebase.
- This utility makes it easier to programmatically access channel metadata, including a unique hash, for scripting, debugging, or integration purposes.
### Motivation
- The protobuf `Channel` objects do not include a hash value by default.
- This addition provides a Pythonic, easy-to-use way to access channel info and a unique hash for each channel, which can be useful for diagnostics, scripting, or UI display.
### Example Usage
```python
channels = node.get_channels_with_hash()
for ch in channels:
print(f"Index {ch['index']}: {ch['role']} name='{ch['name']}' hash={ch['hash']}")
```
### Impact
- No breaking changes; existing APIs and CLI output remain unchanged.
- The new method is additive and can be used where needed for enhanced channel introspection.
The double flush() is not the root cause; the real issue is that code is trying to use the serial port after it has been closed.
The error occurs both in close() (during flush()) and later in _writeBytes() (during write()), indicating the port is closed or invalid at those times.
This ensures flush() is only called if the stream is open, and logs (but ignores) any exceptions during flush. This should prevent the "Bad file descriptor" error.
I see this error a lot on a rak unit, I dont know this is the way but .. you be the judge.