diff --git a/docs/meshtastic/admin_pb2.html b/docs/meshtastic/admin_pb2.html index 881a415..1c2363f 100644 --- a/docs/meshtastic/admin_pb2.html +++ b/docs/meshtastic/admin_pb2.html @@ -51,7 +51,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\"\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' + serialized_pb=b'\n\x0b\x61\x64min.proto\x1a\nmesh.proto\x1a\x11radioconfig.proto\x1a\rchannel.proto\"\xe1\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\x12\x18\n\x0e\x65xit_simulator\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,]) @@ -129,6 +129,13 @@ _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='exit_simulator', full_name='AdminMessage.exit_simulator', index=9, + number=34, 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=[ ], @@ -147,7 +154,7 @@ _ADMINMESSAGE = _descriptor.Descriptor( fields=[]), ], serialized_start=62, - serialized_end=389, + serialized_end=415, ) _ADMINMESSAGE.fields_by_name['set_radio'].message_type = radioconfig__pb2._RADIOCONFIG @@ -182,6 +189,9 @@ _ADMINMESSAGE.fields_by_name['confirm_set_channel'].containing_oneof = _ _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'] +_ADMINMESSAGE.oneofs_by_name['variant'].fields.append( + _ADMINMESSAGE.fields_by_name['exit_simulator']) +_ADMINMESSAGE.fields_by_name['exit_simulator'].containing_oneof = _ADMINMESSAGE.oneofs_by_name['variant'] DESCRIPTOR.message_types_by_name['AdminMessage'] = _ADMINMESSAGE _sym_db.RegisterFileDescriptor(DESCRIPTOR) @@ -234,6 +244,10 @@ DESCRIPTOR._options = None

Field AdminMessage.confirm_set_radio

+
var exit_simulator
+
+

Field AdminMessage.exit_simulator

+
var get_channel_request

Field AdminMessage.get_channel_request

@@ -286,6 +300,7 @@ DESCRIPTOR._options = None
  • DESCRIPTOR
  • confirm_set_channel
  • confirm_set_radio
  • +
  • exit_simulator
  • get_channel_request
  • get_channel_response
  • get_radio_request
  • diff --git a/docs/meshtastic/index.html b/docs/meshtastic/index.html index 70e1759..81797b8 100644 --- a/docs/meshtastic/index.html +++ b/docs/meshtastic/index.html @@ -380,6 +380,15 @@ class Node: wantResponse=True, onResponse=onResponse) + def exitSimulator(self): + """ + Tell a simulator node to exit (this message is ignored for other nodes) + """ + p = admin_pb2.AdminMessage() + p.exit_simulator = True + + return self._sendAdmin(p) + def _requestChannel(self, channelNum: int): """ Done with initial config messages, now send regular MeshPackets to ask for settings @@ -2434,6 +2443,15 @@ wantResponse – True if you want the service on the other side to send an a wantResponse=True, onResponse=onResponse) + def exitSimulator(self): + """ + Tell a simulator node to exit (this message is ignored for other nodes) + """ + p = admin_pb2.AdminMessage() + p.exit_simulator = True + + return self._sendAdmin(p) + def _requestChannel(self, channelNum: int): """ Done with initial config messages, now send regular MeshPackets to ask for settings @@ -2515,6 +2533,25 @@ def channelURL(self):

    Methods

    +
    +def exitSimulator(self) +
    +
    +

    Tell a simulator node to exit (this message is ignored for other nodes)

    +
    + +Expand source code + +
    def exitSimulator(self):
    +    """
    +    Tell a simulator node to exit (this message is ignored for other nodes)
    +    """
    +    p = admin_pb2.AdminMessage()
    +    p.exit_simulator = True
    +
    +    return self._sendAdmin(p)                               
    +
    +
    def getChannelByName(self, name)
    @@ -3221,6 +3258,7 @@ hostname {string} – Hostname/IP address of the device to connect to

    Node

    Instance variables

    -
    var next_hop
    -
    -

    Field NodeInfo.next_hop

    -
    var num

    Field NodeInfo.num

    @@ -1770,6 +1881,10 @@ _MYNODEINFO.fields_by_name['region']._options = None

    Instance variables

    +
    var hw_model
    +
    +

    Field User.hw_model

    +
    var id

    Field User.id

    @@ -1881,7 +1996,7 @@ _MYNODEINFO.fields_by_name['region']._options = None
  • error_count
  • firmware_version
  • has_gps
  • -
  • hw_model
  • +
  • hw_model_deprecated
  • max_channels
  • message_timeout_msec
  • min_app_version
  • @@ -1892,9 +2007,8 @@ _MYNODEINFO.fields_by_name['region']._options = None
  • NodeInfo

    -
      +
      • DESCRIPTOR
      • -
      • next_hop
      • num
      • position
      • snr
      • @@ -1948,8 +2062,9 @@ _MYNODEINFO.fields_by_name['region']._options = None
      • User

        -
          +
          • DESCRIPTOR
          • +
          • hw_model
          • id
          • long_name
          • macaddr
          • diff --git a/docs/meshtastic/remote_hardware.html b/docs/meshtastic/remote_hardware.html index a0ffd0b..7524b03 100644 --- a/docs/meshtastic/remote_hardware.html +++ b/docs/meshtastic/remote_hardware.html @@ -29,14 +29,16 @@
            from . import portnums_pb2, remote_hardware_pb2
             from pubsub import pub
             
            +
             def onGPIOreceive(packet, interface):
                 """Callback for received GPIO responses
            -    
            +
                 FIXME figure out how to do closures with methods in python"""
                 pb = remote_hardware_pb2.HardwareMessage()
                 pb.ParseFromString(packet["decoded"]["data"]["payload"])
                 print(f"Received RemoteHardware typ={pb.typ}, gpio_value={pb.gpio_value}")
             
            +
             class RemoteHardwareClient:
                 """
                 This is the client code to control/monitor simple hardware built into the 
            @@ -51,8 +53,17 @@ class RemoteHardwareClient:
                     iface is the already open MeshInterface instance
                     """
                     self.iface = iface
            +        ch = iface.localNode.getChannelByName("gpio")
            +        if not ch:
            +            raise Exception(
            +                "No gpio channel found, please create before using this (secured) service")
            +        self.channelIndex = ch.index
             
            -        pub.subscribe(onGPIOreceive, "meshtastic.receive.data.REMOTE_HARDWARE_APP")
            +        pub.subscribe(
            +            onGPIOreceive, "meshtastic.receive.data.REMOTE_HARDWARE_APP")
            +
            +    def _sendHardware(self, nodeid, r):
            +        return self.iface.sendData(r, nodeid, portnums_pb2.REMOTE_HARDWARE_APP, wantAck=True, channelIndex=self.channelIndex)
             
                 def writeGPIOs(self, nodeid, mask, vals):
                     """
            @@ -63,21 +74,21 @@ class RemoteHardwareClient:
                     r.typ = remote_hardware_pb2.HardwareMessage.Type.WRITE_GPIOS
                     r.gpio_mask = mask
                     r.gpio_value = vals
            -        return self.iface.sendData(r, nodeid, portnums_pb2.REMOTE_HARDWARE_APP, wantAck = True)
            +        return self._sendHardware(nodeid, r)
             
                 def readGPIOs(self, nodeid, mask):
                     """Read the specified bits from GPIO inputs on the device"""
                     r = remote_hardware_pb2.HardwareMessage()
                     r.typ = remote_hardware_pb2.HardwareMessage.Type.READ_GPIOS
                     r.gpio_mask = mask
            -        return self.iface.sendData(r, nodeid, portnums_pb2.REMOTE_HARDWARE_APP, wantAck = True)
            +        return self._sendHardware(nodeid, r)
             
                 def watchGPIOs(self, nodeid, mask):
                     """Watch the specified bits from GPIO inputs on the device for changes"""
                     r = remote_hardware_pb2.HardwareMessage()
                     r.typ = remote_hardware_pb2.HardwareMessage.Type.WATCH_GPIOS
                     r.gpio_mask = mask
            -        return self.iface.sendData(r, nodeid, portnums_pb2.REMOTE_HARDWARE_APP, wantAck = True)        
            + return self._sendHardware(nodeid, r)
            @@ -99,7 +110,7 @@ class RemoteHardwareClient:
            def onGPIOreceive(packet, interface):
                 """Callback for received GPIO responses
            -    
            +
                 FIXME figure out how to do closures with methods in python"""
                 pb = remote_hardware_pb2.HardwareMessage()
                 pb.ParseFromString(packet["decoded"]["data"]["payload"])
            @@ -140,8 +151,17 @@ code for how you can connect to your own custom meshtastic services

            iface is the already open MeshInterface instance """ self.iface = iface + ch = iface.localNode.getChannelByName("gpio") + if not ch: + raise Exception( + "No gpio channel found, please create before using this (secured) service") + self.channelIndex = ch.index - pub.subscribe(onGPIOreceive, "meshtastic.receive.data.REMOTE_HARDWARE_APP") + pub.subscribe( + onGPIOreceive, "meshtastic.receive.data.REMOTE_HARDWARE_APP") + + def _sendHardware(self, nodeid, r): + return self.iface.sendData(r, nodeid, portnums_pb2.REMOTE_HARDWARE_APP, wantAck=True, channelIndex=self.channelIndex) def writeGPIOs(self, nodeid, mask, vals): """ @@ -152,21 +172,21 @@ code for how you can connect to your own custom meshtastic services

            r.typ = remote_hardware_pb2.HardwareMessage.Type.WRITE_GPIOS r.gpio_mask = mask r.gpio_value = vals - return self.iface.sendData(r, nodeid, portnums_pb2.REMOTE_HARDWARE_APP, wantAck = True) + return self._sendHardware(nodeid, r) def readGPIOs(self, nodeid, mask): """Read the specified bits from GPIO inputs on the device""" r = remote_hardware_pb2.HardwareMessage() r.typ = remote_hardware_pb2.HardwareMessage.Type.READ_GPIOS r.gpio_mask = mask - return self.iface.sendData(r, nodeid, portnums_pb2.REMOTE_HARDWARE_APP, wantAck = True) + return self._sendHardware(nodeid, r) def watchGPIOs(self, nodeid, mask): """Watch the specified bits from GPIO inputs on the device for changes""" r = remote_hardware_pb2.HardwareMessage() r.typ = remote_hardware_pb2.HardwareMessage.Type.WATCH_GPIOS r.gpio_mask = mask - return self.iface.sendData(r, nodeid, portnums_pb2.REMOTE_HARDWARE_APP, wantAck = True)
            + return self._sendHardware(nodeid, r)

            Methods

            @@ -184,7 +204,7 @@ code for how you can connect to your own custom meshtastic services

            r = remote_hardware_pb2.HardwareMessage() r.typ = remote_hardware_pb2.HardwareMessage.Type.READ_GPIOS r.gpio_mask = mask - return self.iface.sendData(r, nodeid, portnums_pb2.REMOTE_HARDWARE_APP, wantAck = True) + return self._sendHardware(nodeid, r)
  • @@ -201,7 +221,7 @@ code for how you can connect to your own custom meshtastic services

    r = remote_hardware_pb2.HardwareMessage() r.typ = remote_hardware_pb2.HardwareMessage.Type.WATCH_GPIOS r.gpio_mask = mask - return self.iface.sendData(r, nodeid, portnums_pb2.REMOTE_HARDWARE_APP, wantAck = True)
    + return self._sendHardware(nodeid, r)
    @@ -224,7 +244,7 @@ are 1 will be changed

    r.typ = remote_hardware_pb2.HardwareMessage.Type.WRITE_GPIOS r.gpio_mask = mask r.gpio_value = vals - return self.iface.sendData(r, nodeid, portnums_pb2.REMOTE_HARDWARE_APP, wantAck = True)
    + return self._sendHardware(nodeid, r) diff --git a/docs/meshtastic/test.html b/docs/meshtastic/test.html index 0a4ddc7..9b68426 100644 --- a/docs/meshtastic/test.html +++ b/docs/meshtastic/test.html @@ -28,7 +28,7 @@
    import logging
     from . import util
    -from . import SerialInterface, BROADCAST_NUM
    +from . import SerialInterface, TCPInterface, BROADCAST_NUM
     from pubsub import pub
     import time
     import sys
    @@ -178,7 +178,26 @@ def testAll():
         testThread()
     
         for i in interfaces:
    -        i.close()
    + i.close() + +def testSimulator(): + """ + Assume that someone has launched meshtastic-native as a simulated node. + Talk to that node over TCP, do some operations and if they are successful + exit the process with a success code, else exit with a non zero exit code. + + Run with + python3 -c 'from meshtastic.test import testSimulator; testSimulator()' + """ + logging.basicConfig(level=logging.DEBUG if False else logging.INFO) + logging.info("Connecting to simulator on localhost!") + iface = TCPInterface("localhost") + iface.showInfo() + iface.localNode.showInfo() + iface.localNode.exitSimulator() + iface.close() + logging.info("Integration test successful!") + sys.exit(0)
    @@ -405,6 +424,39 @@ toInterface {[type]} – [description]

    return False # Failed to send +
    +def testSimulator() +
    +
    +

    Assume that someone has launched meshtastic-native as a simulated node. +Talk to that node over TCP, do some operations and if they are successful +exit the process with a success code, else exit with a non zero exit code.

    +

    Run with +python3 -c 'from meshtastic.test import testSimulator; testSimulator()'

    +
    + +Expand source code + +
    def testSimulator():
    +    """
    +    Assume that someone has launched meshtastic-native as a simulated node.
    +    Talk to that node over TCP, do some operations and if they are successful
    +    exit the process with a success code, else exit with a non zero exit code.
    +
    +    Run with
    +    python3 -c 'from meshtastic.test import testSimulator; testSimulator()'
    +    """
    +    logging.basicConfig(level=logging.DEBUG if False else logging.INFO)
    +    logging.info("Connecting to simulator on localhost!")
    +    iface = TCPInterface("localhost")
    +    iface.showInfo()
    +    iface.localNode.showInfo()
    +    iface.localNode.exitSimulator()
    +    iface.close()
    +    logging.info("Integration test successful!")
    +    sys.exit(0)
    +
    +
    def testThread(numTests=50)
    @@ -451,6 +503,7 @@ toInterface {[type]} – [description]

  • subscribe
  • testAll
  • testSend
  • +
  • testSimulator
  • testThread
  • diff --git a/proto b/proto index 943c3c2..b8c0499 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit 943c3c24ec1e5116536cdabe31f496cb5b58a18e +Subproject commit b8c0499f28f9673d1df17d04da562e30703f01cb diff --git a/setup.py b/setup.py index c540aa7..5f768c5 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ with open("README.md", "r") as fh: # This call to setup() does all the work setup( name="meshtastic", - version="1.2.10", + version="1.2.11", description="Python API & client shell for talking to Meshtastic devices", long_description=long_description, long_description_content_type="text/markdown",