diff --git a/meshtastic/__init__.py b/meshtastic/__init__.py index 81d9fa1..68ba4e7 100644 --- a/meshtastic/__init__.py +++ b/meshtastic/__init__.py @@ -68,7 +68,7 @@ import base64 import platform import socket from . import mesh_pb2, portnums_pb2, apponly_pb2, admin_pb2, environmental_measurement_pb2, remote_hardware_pb2, channel_pb2, radioconfig_pb2, util -from .util import fixme, catchAndIgnore, stripnl +from .util import fixme, catchAndIgnore, stripnl, DeferredExecution from pubsub import pub from dotmap import DotMap from typing import * @@ -95,6 +95,7 @@ format is Mmmss (where M is 1+the numeric major number. i.e. 20120 means 1.1.20 """ OUR_APP_VERSION = 20200 +publishingThread = DeferredExecution("publishing") class ResponseHandler(NamedTuple): """A pending response callback, waiting for a response to one of our messages""" @@ -555,7 +556,7 @@ class MeshInterface: def _disconnected(self): """Called by subclasses to tell clients this interface has disconnected""" self.isConnected.clear() - catchAndIgnore("disconnection publish", lambda: pub.sendMessage( + publishingThread.queueWork(lambda: pub.sendMessage( "meshtastic.connection.lost", interface=self)) def _connected(self): @@ -566,7 +567,7 @@ class MeshInterface: # for the local interface if not self.isConnected.is_set(): self.isConnected.set() - catchAndIgnore("connection publish", lambda: pub.sendMessage( + publishingThread.queueWork(lambda: pub.sendMessage( "meshtastic.connection.established", interface=self)) def _startConfig(self): @@ -779,7 +780,7 @@ class MeshInterface: handler.callback(asDict) logging.debug(f"Publishing {topic}: packet={stripnl(asDict)} ") - catchAndIgnore(f"publishing {topic}", lambda: pub.sendMessage( + publishingThread.queueWork(lambda: pub.sendMessage( topic, packet=asDict, interface=self)) @@ -826,7 +827,6 @@ class BLEInterface(MeshInterface): if not wasEmpty: self._handleFromRadio(b) - class StreamInterface(MeshInterface): """Interface class for meshtastic devices over a stream link (serial, TCP, etc)""" diff --git a/meshtastic/admin_pb2.py b/meshtastic/admin_pb2.py index fe6ca5c..556ed8d 100644 --- a/meshtastic/admin_pb2.py +++ b/meshtastic/admin_pb2.py @@ -22,7 +22,7 @@ DESCRIPTOR = _descriptor.FileDescriptor( syntax='proto3', serialized_options=b'\n\023com.geeksville.meshB\013AdminProtosH\003', create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\x0b\x61\x64min.proto\x1a\nmesh.proto\x1a\x11radioconfig.proto\x1a\rchannel.proto\"\x8b\x02\n\x0c\x41\x64minMessage\x12!\n\tset_radio\x18\x01 \x01(\x0b\x32\x0c.RadioConfigH\x00\x12\x1a\n\tset_owner\x18\x02 \x01(\x0b\x32\x05.UserH\x00\x12\x1f\n\x0bset_channel\x18\x03 \x01(\x0b\x32\x08.ChannelH\x00\x12\x1b\n\x11get_radio_request\x18\x04 \x01(\x08H\x00\x12*\n\x12get_radio_response\x18\x05 \x01(\x0b\x32\x0c.RadioConfigH\x00\x12\x1d\n\x13get_channel_request\x18\x06 \x01(\rH\x00\x12(\n\x14get_channel_response\x18\x07 \x01(\x0b\x32\x08.ChannelH\x00\x42\t\n\x07variantB$\n\x13\x63om.geeksville.meshB\x0b\x41\x64minProtosH\x03\x62\x06proto3' + serialized_pb=b'\n\x0b\x61\x64min.proto\x1a\nmesh.proto\x1a\x11radioconfig.proto\x1a\rchannel.proto\"\xc7\x02\n\x0c\x41\x64minMessage\x12!\n\tset_radio\x18\x01 \x01(\x0b\x32\x0c.RadioConfigH\x00\x12\x1a\n\tset_owner\x18\x02 \x01(\x0b\x32\x05.UserH\x00\x12\x1f\n\x0bset_channel\x18\x03 \x01(\x0b\x32\x08.ChannelH\x00\x12\x1b\n\x11get_radio_request\x18\x04 \x01(\x08H\x00\x12*\n\x12get_radio_response\x18\x05 \x01(\x0b\x32\x0c.RadioConfigH\x00\x12\x1d\n\x13get_channel_request\x18\x06 \x01(\rH\x00\x12(\n\x14get_channel_response\x18\x07 \x01(\x0b\x32\x08.ChannelH\x00\x12\x1d\n\x13\x63onfirm_set_channel\x18 \x01(\x08H\x00\x12\x1b\n\x11\x63onfirm_set_radio\x18! \x01(\x08H\x00\x42\t\n\x07variantB$\n\x13\x63om.geeksville.meshB\x0b\x41\x64minProtosH\x03\x62\x06proto3' , dependencies=[mesh__pb2.DESCRIPTOR,radioconfig__pb2.DESCRIPTOR,channel__pb2.DESCRIPTOR,]) @@ -86,6 +86,20 @@ _ADMINMESSAGE = _descriptor.Descriptor( message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='confirm_set_channel', full_name='AdminMessage.confirm_set_channel', index=7, + number=32, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='confirm_set_radio', full_name='AdminMessage.confirm_set_radio', index=8, + number=33, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), ], extensions=[ ], @@ -104,7 +118,7 @@ _ADMINMESSAGE = _descriptor.Descriptor( fields=[]), ], serialized_start=62, - serialized_end=329, + serialized_end=389, ) _ADMINMESSAGE.fields_by_name['set_radio'].message_type = radioconfig__pb2._RADIOCONFIG @@ -133,6 +147,12 @@ _ADMINMESSAGE.fields_by_name['get_channel_request'].containing_oneof = _ADMINMES _ADMINMESSAGE.oneofs_by_name['variant'].fields.append( _ADMINMESSAGE.fields_by_name['get_channel_response']) _ADMINMESSAGE.fields_by_name['get_channel_response'].containing_oneof = _ADMINMESSAGE.oneofs_by_name['variant'] +_ADMINMESSAGE.oneofs_by_name['variant'].fields.append( + _ADMINMESSAGE.fields_by_name['confirm_set_channel']) +_ADMINMESSAGE.fields_by_name['confirm_set_channel'].containing_oneof = _ADMINMESSAGE.oneofs_by_name['variant'] +_ADMINMESSAGE.oneofs_by_name['variant'].fields.append( + _ADMINMESSAGE.fields_by_name['confirm_set_radio']) +_ADMINMESSAGE.fields_by_name['confirm_set_radio'].containing_oneof = _ADMINMESSAGE.oneofs_by_name['variant'] DESCRIPTOR.message_types_by_name['AdminMessage'] = _ADMINMESSAGE _sym_db.RegisterFileDescriptor(DESCRIPTOR) diff --git a/meshtastic/deviceonly_pb2.py b/meshtastic/deviceonly_pb2.py index d5c34ec..8667b17 100644 --- a/meshtastic/deviceonly_pb2.py +++ b/meshtastic/deviceonly_pb2.py @@ -12,8 +12,8 @@ _sym_db = _symbol_database.Default() from . import mesh_pb2 as mesh__pb2 -from . import radioconfig_pb2 as radioconfig__pb2 from . import channel_pb2 as channel__pb2 +from . import radioconfig_pb2 as radioconfig__pb2 DESCRIPTOR = _descriptor.FileDescriptor( @@ -22,13 +22,76 @@ DESCRIPTOR = _descriptor.FileDescriptor( syntax='proto3', serialized_options=b'\n\023com.geeksville.meshB\nDeviceOnlyH\003', create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\x10\x64\x65viceonly.proto\x1a\nmesh.proto\x1a\x11radioconfig.proto\x1a\rchannel.proto\"\x9f\x02\n\x0b\x44\x65viceState\x12\x1b\n\x05radio\x18\x01 \x01(\x0b\x32\x0c.RadioConfig\x12\x1c\n\x07my_node\x18\x02 \x01(\x0b\x32\x0b.MyNodeInfo\x12\x14\n\x05owner\x18\x03 \x01(\x0b\x32\x05.User\x12\x1a\n\x07node_db\x18\x04 \x03(\x0b\x32\t.NodeInfo\x12\"\n\rreceive_queue\x18\x05 \x03(\x0b\x32\x0b.MeshPacket\x12\x0f\n\x07version\x18\x08 \x01(\r\x12$\n\x0frx_text_message\x18\x07 \x01(\x0b\x32\x0b.MeshPacket\x12\x0f\n\x07no_save\x18\t \x01(\x08\x12\x15\n\rdid_gps_reset\x18\x0b \x01(\x08\x12\x1a\n\x08\x63hannels\x18\r \x03(\x0b\x32\x08.ChannelJ\x04\x08\x0c\x10\rB#\n\x13\x63om.geeksville.meshB\nDeviceOnlyH\x03\x62\x06proto3' + serialized_pb=b'\n\x10\x64\x65viceonly.proto\x1a\nmesh.proto\x1a\rchannel.proto\x1a\x11radioconfig.proto\"\x80\x01\n\x11LegacyRadioConfig\x12\x39\n\x0bpreferences\x18\x01 \x01(\x0b\x32$.LegacyRadioConfig.LegacyPreferences\x1a\x30\n\x11LegacyPreferences\x12\x1b\n\x06region\x18\x0f \x01(\x0e\x32\x0b.RegionCode\"\x8f\x02\n\x0b\x44\x65viceState\x12\'\n\x0blegacyRadio\x18\x01 \x01(\x0b\x32\x12.LegacyRadioConfig\x12\x1c\n\x07my_node\x18\x02 \x01(\x0b\x32\x0b.MyNodeInfo\x12\x14\n\x05owner\x18\x03 \x01(\x0b\x32\x05.User\x12\x1a\n\x07node_db\x18\x04 \x03(\x0b\x32\t.NodeInfo\x12\"\n\rreceive_queue\x18\x05 \x03(\x0b\x32\x0b.MeshPacket\x12\x0f\n\x07version\x18\x08 \x01(\r\x12$\n\x0frx_text_message\x18\x07 \x01(\x0b\x32\x0b.MeshPacket\x12\x0f\n\x07no_save\x18\t \x01(\x08\x12\x15\n\rdid_gps_reset\x18\x0b \x01(\x08J\x04\x08\x0c\x10\r\")\n\x0b\x43hannelFile\x12\x1a\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x08.ChannelB#\n\x13\x63om.geeksville.meshB\nDeviceOnlyH\x03\x62\x06proto3' , - dependencies=[mesh__pb2.DESCRIPTOR,radioconfig__pb2.DESCRIPTOR,channel__pb2.DESCRIPTOR,]) + dependencies=[mesh__pb2.DESCRIPTOR,channel__pb2.DESCRIPTOR,radioconfig__pb2.DESCRIPTOR,]) +_LEGACYRADIOCONFIG_LEGACYPREFERENCES = _descriptor.Descriptor( + name='LegacyPreferences', + full_name='LegacyRadioConfig.LegacyPreferences', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='region', full_name='LegacyRadioConfig.LegacyPreferences.region', index=0, + number=15, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=147, + serialized_end=195, +) + +_LEGACYRADIOCONFIG = _descriptor.Descriptor( + name='LegacyRadioConfig', + full_name='LegacyRadioConfig', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='preferences', full_name='LegacyRadioConfig.preferences', index=0, + number=1, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[_LEGACYRADIOCONFIG_LEGACYPREFERENCES, ], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=67, + serialized_end=195, +) + + _DEVICESTATE = _descriptor.Descriptor( name='DeviceState', full_name='DeviceState', @@ -38,7 +101,7 @@ _DEVICESTATE = _descriptor.Descriptor( create_key=_descriptor._internal_create_key, fields=[ _descriptor.FieldDescriptor( - name='radio', full_name='DeviceState.radio', index=0, + name='legacyRadio', full_name='DeviceState.legacyRadio', index=0, number=1, type=11, cpp_type=10, label=1, has_default_value=False, default_value=None, message_type=None, enum_type=None, containing_type=None, @@ -100,9 +163,34 @@ _DEVICESTATE = _descriptor.Descriptor( message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=198, + serialized_end=469, +) + + +_CHANNELFILE = _descriptor.Descriptor( + name='ChannelFile', + full_name='ChannelFile', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ _descriptor.FieldDescriptor( - name='channels', full_name='DeviceState.channels', index=9, - number=13, type=11, cpp_type=10, label=3, + name='channels', full_name='ChannelFile.channels', index=0, + number=1, type=11, cpp_type=10, label=3, has_default_value=False, default_value=[], message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, @@ -119,20 +207,40 @@ _DEVICESTATE = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=67, - serialized_end=354, + serialized_start=471, + serialized_end=512, ) -_DEVICESTATE.fields_by_name['radio'].message_type = radioconfig__pb2._RADIOCONFIG +_LEGACYRADIOCONFIG_LEGACYPREFERENCES.fields_by_name['region'].enum_type = radioconfig__pb2._REGIONCODE +_LEGACYRADIOCONFIG_LEGACYPREFERENCES.containing_type = _LEGACYRADIOCONFIG +_LEGACYRADIOCONFIG.fields_by_name['preferences'].message_type = _LEGACYRADIOCONFIG_LEGACYPREFERENCES +_DEVICESTATE.fields_by_name['legacyRadio'].message_type = _LEGACYRADIOCONFIG _DEVICESTATE.fields_by_name['my_node'].message_type = mesh__pb2._MYNODEINFO _DEVICESTATE.fields_by_name['owner'].message_type = mesh__pb2._USER _DEVICESTATE.fields_by_name['node_db'].message_type = mesh__pb2._NODEINFO _DEVICESTATE.fields_by_name['receive_queue'].message_type = mesh__pb2._MESHPACKET _DEVICESTATE.fields_by_name['rx_text_message'].message_type = mesh__pb2._MESHPACKET -_DEVICESTATE.fields_by_name['channels'].message_type = channel__pb2._CHANNEL +_CHANNELFILE.fields_by_name['channels'].message_type = channel__pb2._CHANNEL +DESCRIPTOR.message_types_by_name['LegacyRadioConfig'] = _LEGACYRADIOCONFIG DESCRIPTOR.message_types_by_name['DeviceState'] = _DEVICESTATE +DESCRIPTOR.message_types_by_name['ChannelFile'] = _CHANNELFILE _sym_db.RegisterFileDescriptor(DESCRIPTOR) +LegacyRadioConfig = _reflection.GeneratedProtocolMessageType('LegacyRadioConfig', (_message.Message,), { + + 'LegacyPreferences' : _reflection.GeneratedProtocolMessageType('LegacyPreferences', (_message.Message,), { + 'DESCRIPTOR' : _LEGACYRADIOCONFIG_LEGACYPREFERENCES, + '__module__' : 'deviceonly_pb2' + # @@protoc_insertion_point(class_scope:LegacyRadioConfig.LegacyPreferences) + }) + , + 'DESCRIPTOR' : _LEGACYRADIOCONFIG, + '__module__' : 'deviceonly_pb2' + # @@protoc_insertion_point(class_scope:LegacyRadioConfig) + }) +_sym_db.RegisterMessage(LegacyRadioConfig) +_sym_db.RegisterMessage(LegacyRadioConfig.LegacyPreferences) + DeviceState = _reflection.GeneratedProtocolMessageType('DeviceState', (_message.Message,), { 'DESCRIPTOR' : _DEVICESTATE, '__module__' : 'deviceonly_pb2' @@ -140,6 +248,13 @@ DeviceState = _reflection.GeneratedProtocolMessageType('DeviceState', (_message. }) _sym_db.RegisterMessage(DeviceState) +ChannelFile = _reflection.GeneratedProtocolMessageType('ChannelFile', (_message.Message,), { + 'DESCRIPTOR' : _CHANNELFILE, + '__module__' : 'deviceonly_pb2' + # @@protoc_insertion_point(class_scope:ChannelFile) + }) +_sym_db.RegisterMessage(ChannelFile) + DESCRIPTOR._options = None # @@protoc_insertion_point(module_scope) diff --git a/meshtastic/util.py b/meshtastic/util.py index 1b062e3..62ec06c 100644 --- a/meshtastic/util.py +++ b/meshtastic/util.py @@ -2,6 +2,9 @@ from collections import defaultdict import serial import serial.tools.list_ports +from queue import Queue +import threading +import logging """Some devices such as a seger jlink we never want to accidentally open""" blacklistVids = dict.fromkeys([0x1366]) @@ -42,3 +45,25 @@ class dotdict(dict): __getattr__ = dict.get __setattr__ = dict.__setitem__ __delattr__ = dict.__delitem__ + + +class DeferredExecution(): + """A thread that accepts closures to run, and runs them as they are received""" + + def __init__(self, name=None): + self.queue = Queue() + self.thread = threading.Thread(target=self._run, args=(), name=name) + self.thread.daemon = True + self.thread.start() + + def queueWork(self, runnable): + self.queue.put(runnable) + + def _run(self): + while True: + try: + o = self.queue.get() + o() + except Exception as ex: + logging.error( + f"Unexpected exception in deferred execution {ex}") diff --git a/proto b/proto index 6dac309..5c1062e 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit 6dac3099be6cd27848b92365e11b0d84202a6405 +Subproject commit 5c1062ea839f97cfc6d33d428a89d1702c39bd93