Compare commits

...

14 Commits
2.3.6 ... 2.3.7

Author SHA1 Message Date
Ian McEwen
3d6fa369e8 Merge pull request #556 from ianmcorvidae/enter-dfu
Add --enter-dfu for entering DFU mode on NRF52 devices via admin message
2024-04-25 11:29:09 -07:00
Ian McEwen
6812f508bc Add --enter-dfu for entering DFU mode on NRF52 devices via admin message 2024-04-25 11:21:27 -07:00
Ian McEwen
158cac6b0b Merge pull request #555 from ianmcorvidae/channel-indices
Better support --ch-index for other commands (traceroute, telemetry, position)
2024-04-24 19:35:44 -07:00
Ian McEwen
4d10b6e1bd Better support --ch-index for other commands (traceroute, telemetry, position) 2024-04-24 17:18:17 -07:00
Ian McEwen
bb6e3b7a49 protobufs: v2.3.7 2024-04-23 21:35:15 -07:00
Ian McEwen
8b29dc683a Merge pull request #554 from GUVWAF/storeForward
Add missing StoreForward config from radio
2024-04-22 11:14:21 -07:00
GUVWAF
d4156d78c6 Add missing StoreForward config from radio 2024-04-22 20:09:30 +02:00
Ian McEwen
c52977781e Merge pull request #553 from ianmcorvidae/more-dest-not-allowed
Disallow --dest in remaining spots that don't actually use/allow it
2024-04-21 13:03:58 -07:00
Ian McEwen
5ebc8e6f95 Don't allow --dest with --tunnel either 2024-04-21 10:38:01 -07:00
Ian McEwen
56d1dcafe5 --dest doesn't work with --export-config 2024-04-21 10:35:59 -07:00
Ian McEwen
393c765557 Fix up tests 2024-04-21 10:32:07 -07:00
Ian McEwen
243b310eb2 Allow generating QR codes for remote nodes and for all channels 2024-04-21 09:41:07 -07:00
Ian McEwen
cd1ecab4e0 Don't allow --dest with --setlat/lon/alt. Fixes #545 2024-04-21 09:38:32 -07:00
github-actions
c710953b85 bump version 2024-04-20 17:50:14 +00:00
15 changed files with 220 additions and 113 deletions

View File

@@ -20,6 +20,7 @@ from meshtastic import mt_config
from meshtastic import channel_pb2, config_pb2, portnums_pb2, remote_hardware, BROADCAST_ADDR from meshtastic import channel_pb2, config_pb2, portnums_pb2, remote_hardware, BROADCAST_ADDR
from meshtastic.version import get_active_version from meshtastic.version import get_active_version
from meshtastic.ble_interface import BLEInterface from meshtastic.ble_interface import BLEInterface
from meshtastic.mesh_interface import MeshInterface
def onReceive(packet, interface): def onReceive(packet, interface):
"""Callback invoked when a packet arrives""" """Callback invoked when a packet arrives"""
@@ -56,6 +57,11 @@ def onConnection(interface, topic=pub.AUTO_TOPIC): # pylint: disable=W0613
"""Callback invoked when we connect/disconnect from a radio""" """Callback invoked when we connect/disconnect from a radio"""
print(f"Connection changed: {topic.getName()}") print(f"Connection changed: {topic.getName()}")
def checkChannel(interface: MeshInterface, channelIndex: int) -> bool:
"""Given an interface and channel index, return True if that channel is non-disabled on the local node"""
ch = interface.localNode.getChannelByChannelIndex(channelIndex)
logging.debug(f"ch:{ch}")
return (ch and ch.role != channel_pb2.Channel.Role.DISABLED)
def getPref(node, comp_name): def getPref(node, comp_name):
"""Get a channel or preferences value""" """Get a channel or preferences value"""
@@ -251,6 +257,9 @@ def onConnected(interface):
print("Connected to radio") print("Connected to radio")
if args.setlat or args.setlon or args.setalt: if args.setlat or args.setlon or args.setalt:
if args.dest != BROADCAST_ADDR:
print("Setting latitude, longitude, and altitude of remote nodes is not supported.")
return
closeNow = True closeNow = True
alt = 0 alt = 0
@@ -366,6 +375,11 @@ def onConnected(interface):
waitForAckNak = True waitForAckNak = True
interface.getNode(args.dest, False).rebootOTA() interface.getNode(args.dest, False).rebootOTA()
if args.enter_dfu:
closeNow = True
waitForAckNak = True
interface.getNode(args.dest, False).enterDFUMode()
if args.shutdown: if args.shutdown:
closeNow = True closeNow = True
waitForAckNak = True waitForAckNak = True
@@ -400,12 +414,8 @@ def onConnected(interface):
if args.sendtext: if args.sendtext:
closeNow = True closeNow = True
channelIndex = 0 channelIndex = mt_config.channel_index or 0
if args.ch_index is not None: if checkChannel(interface, channelIndex):
channelIndex = int(args.ch_index)
ch = interface.localNode.getChannelByChannelIndex(channelIndex)
logging.debug(f"ch:{ch}")
if ch and ch.role != channel_pb2.Channel.Role.DISABLED:
print( print(
f"Sending text message {args.sendtext} to {args.dest} on channelIndex:{channelIndex}" f"Sending text message {args.sendtext} to {args.dest} on channelIndex:{channelIndex}"
) )
@@ -425,22 +435,28 @@ def onConnected(interface):
loraConfig = getattr(interface.localNode.localConfig, "lora") loraConfig = getattr(interface.localNode.localConfig, "lora")
hopLimit = getattr(loraConfig, "hop_limit") hopLimit = getattr(loraConfig, "hop_limit")
dest = str(args.traceroute) dest = str(args.traceroute)
print(f"Sending traceroute request to {dest} (this could take a while)") channelIndex = mt_config.channel_index or 0
interface.sendTraceRoute(dest, hopLimit) if checkChannel(interface, channelIndex):
print(f"Sending traceroute request to {dest} on channelIndex:{channelIndex} (this could take a while)")
interface.sendTraceRoute(dest, hopLimit, channelIndex=channelIndex)
if args.request_telemetry: if args.request_telemetry:
if args.dest == BROADCAST_ADDR: if args.dest == BROADCAST_ADDR:
meshtastic.util.our_exit("Warning: Must use a destination node ID.") meshtastic.util.our_exit("Warning: Must use a destination node ID.")
else: else:
print(f"Sending telemetry request to {args.dest} (this could take a while)") channelIndex = mt_config.channel_index or 0
interface.sendTelemetry(destinationId=args.dest, wantResponse=True) if checkChannel(interface, channelIndex):
print(f"Sending telemetry request to {args.dest} on channelIndex:{channelIndex} (this could take a while)")
interface.sendTelemetry(destinationId=args.dest, wantResponse=True, channelIndex=channelIndex)
if args.request_position: if args.request_position:
if args.dest == BROADCAST_ADDR: if args.dest == BROADCAST_ADDR:
meshtastic.util.our_exit("Warning: Must use a destination node ID.") meshtastic.util.our_exit("Warning: Must use a destination node ID.")
else: else:
print(f"Sending position request to {args.dest} (this could take a while)") channelIndex = mt_config.channel_index or 0
interface.sendPosition(destinationId=args.dest, wantResponse=True) if checkChannel(interface, channelIndex):
print(f"Sending position request to {args.dest} on channelIndex:{channelIndex} (this could take a while)")
interface.sendPosition(destinationId=args.dest, wantResponse=True, channelIndex=channelIndex)
if args.gpio_wrb or args.gpio_rd or args.gpio_watch: if args.gpio_wrb or args.gpio_rd or args.gpio_watch:
if args.dest == BROADCAST_ADDR: if args.dest == BROADCAST_ADDR:
@@ -599,6 +615,9 @@ def onConnected(interface):
print("Writing modified configuration to device") print("Writing modified configuration to device")
if args.export_config: if args.export_config:
if args.dest != BROADCAST_ADDR:
print("Exporting configuration of remote nodes is not supported.")
return
# export the configuration (the opposite of '--configure') # export the configuration (the opposite of '--configure')
closeNow = True closeNow = True
export_config(interface) export_config(interface)
@@ -801,10 +820,14 @@ def onConnected(interface):
return return
interface.showNodes() interface.showNodes()
if args.qr: if args.qr or args.qr_all:
closeNow = True closeNow = True
url = interface.localNode.getURL(includeAll=False) url = interface.getNode(args.dest, True).getURL(includeAll=args.qr_all)
print(f"Primary channel URL {url}") if args.qr_all:
urldesc = "Complete URL (includes all channels)"
else:
urldesc = "Primary channel URL"
print(f"{urldesc}: {url}")
qr = pyqrcode.create(url) qr = pyqrcode.create(url)
print(qr.terminal()) print(qr.terminal())
@@ -813,6 +836,9 @@ def onConnected(interface):
have_tunnel = platform.system() == "Linux" have_tunnel = platform.system() == "Linux"
if have_tunnel and args.tunnel: if have_tunnel and args.tunnel:
if args.dest != BROADCAST_ADDR:
print("A tunnel can only be created using the local node.")
return
# pylint: disable=C0415 # pylint: disable=C0415
from . import tunnel from . import tunnel
@@ -1154,7 +1180,16 @@ def initParser():
group.add_argument( group.add_argument(
"--qr", "--qr",
help="Display the QR code that corresponds to the current channel", help=(
"Display a QR code for the node's primary channel (or all channels with --qr-all). "
"Also shows the shareable channel URL."
),
action="store_true",
)
group.add_argument(
"--qr-all",
help="Display a QR code and URL for all of the node's channels.",
action="store_true", action="store_true",
) )
@@ -1334,7 +1369,13 @@ def initParser():
group.add_argument( group.add_argument(
"--reboot-ota", "--reboot-ota",
help="Tell the destination node to reboot into factory firmware", help="Tell the destination node to reboot into factory firmware (ESP32)",
action="store_true",
)
group.add_argument(
"--enter-dfu",
help="Tell the destination node to enter DFU mode (NRF52)",
action="store_true", action="store_true",
) )

View File

File diff suppressed because one or more lines are too long

View File

@@ -245,6 +245,7 @@ class Config(google.protobuf.message.Message):
IS_MANAGED_FIELD_NUMBER: builtins.int IS_MANAGED_FIELD_NUMBER: builtins.int
DISABLE_TRIPLE_CLICK_FIELD_NUMBER: builtins.int DISABLE_TRIPLE_CLICK_FIELD_NUMBER: builtins.int
TZDEF_FIELD_NUMBER: builtins.int TZDEF_FIELD_NUMBER: builtins.int
LED_HEARTBEAT_DISABLED_FIELD_NUMBER: builtins.int
role: global___Config.DeviceConfig.Role.ValueType role: global___Config.DeviceConfig.Role.ValueType
""" """
Sets the role of node Sets the role of node
@@ -294,6 +295,10 @@ class Config(google.protobuf.message.Message):
""" """
POSIX Timezone definition string from https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv. POSIX Timezone definition string from https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv.
""" """
led_heartbeat_disabled: builtins.bool
"""
If true, disable the default blinking LED (LED_PIN) behavior on the device
"""
def __init__( def __init__(
self, self,
*, *,
@@ -308,8 +313,9 @@ class Config(google.protobuf.message.Message):
is_managed: builtins.bool = ..., is_managed: builtins.bool = ...,
disable_triple_click: builtins.bool = ..., disable_triple_click: builtins.bool = ...,
tzdef: builtins.str = ..., tzdef: builtins.str = ...,
led_heartbeat_disabled: builtins.bool = ...,
) -> None: ... ) -> None: ...
def ClearField(self, field_name: typing_extensions.Literal["button_gpio", b"button_gpio", "buzzer_gpio", b"buzzer_gpio", "debug_log_enabled", b"debug_log_enabled", "disable_triple_click", b"disable_triple_click", "double_tap_as_button_press", b"double_tap_as_button_press", "is_managed", b"is_managed", "node_info_broadcast_secs", b"node_info_broadcast_secs", "rebroadcast_mode", b"rebroadcast_mode", "role", b"role", "serial_enabled", b"serial_enabled", "tzdef", b"tzdef"]) -> None: ... def ClearField(self, field_name: typing_extensions.Literal["button_gpio", b"button_gpio", "buzzer_gpio", b"buzzer_gpio", "debug_log_enabled", b"debug_log_enabled", "disable_triple_click", b"disable_triple_click", "double_tap_as_button_press", b"double_tap_as_button_press", "is_managed", b"is_managed", "led_heartbeat_disabled", b"led_heartbeat_disabled", "node_info_broadcast_secs", b"node_info_broadcast_secs", "rebroadcast_mode", b"rebroadcast_mode", "role", b"role", "serial_enabled", b"serial_enabled", "tzdef", b"tzdef"]) -> None: ...
@typing_extensions.final @typing_extensions.final
class PositionConfig(google.protobuf.message.Message): class PositionConfig(google.protobuf.message.Message):

View File

@@ -351,6 +351,7 @@ class MeshInterface:
destinationId: Union[int, str]=BROADCAST_ADDR, destinationId: Union[int, str]=BROADCAST_ADDR,
wantAck: bool=False, wantAck: bool=False,
wantResponse: bool=False, wantResponse: bool=False,
channelIndex: int=0,
): ):
""" """
Send a position packet to some other node (normally a broadcast) Send a position packet to some other node (normally a broadcast)
@@ -393,6 +394,7 @@ class MeshInterface:
wantAck=wantAck, wantAck=wantAck,
wantResponse=wantResponse, wantResponse=wantResponse,
onResponse=onResponse, onResponse=onResponse,
channelIndex=channelIndex,
) )
if wantResponse: if wantResponse:
self.waitForPosition() self.waitForPosition()
@@ -426,7 +428,7 @@ class MeshInterface:
if p["decoded"]["routing"]["errorReason"] == 'NO_RESPONSE': if p["decoded"]["routing"]["errorReason"] == 'NO_RESPONSE':
our_exit("No response from node. At least firmware 2.1.22 is required on the destination node.") our_exit("No response from node. At least firmware 2.1.22 is required on the destination node.")
def sendTraceRoute(self, dest: Union[int, str], hopLimit: int): def sendTraceRoute(self, dest: Union[int, str], hopLimit: int, channelIndex: int=0):
"""Send the trace route""" """Send the trace route"""
r = mesh_pb2.RouteDiscovery() r = mesh_pb2.RouteDiscovery()
self.sendData( self.sendData(
@@ -435,6 +437,7 @@ class MeshInterface:
portNum=portnums_pb2.PortNum.TRACEROUTE_APP, portNum=portnums_pb2.PortNum.TRACEROUTE_APP,
wantResponse=True, wantResponse=True,
onResponse=self.onResponseTraceRoute, onResponse=self.onResponseTraceRoute,
channelIndex=channelIndex,
) )
# extend timeout based on number of nodes, limit by configured hopLimit # extend timeout based on number of nodes, limit by configured hopLimit
waitFactor = min(len(self.nodes) - 1 if self.nodes else 0, hopLimit) waitFactor = min(len(self.nodes) - 1 if self.nodes else 0, hopLimit)
@@ -456,10 +459,11 @@ class MeshInterface:
self._acknowledgment.receivedTraceRoute = True self._acknowledgment.receivedTraceRoute = True
def sendTelemetry(self, destinationId=BROADCAST_ADDR, wantResponse=False): def sendTelemetry(self, destinationId: Union[int,str]=BROADCAST_ADDR, wantResponse: bool=False, channelIndex: int=0):
"""Send telemetry and optionally ask for a response""" """Send telemetry and optionally ask for a response"""
r = telemetry_pb2.Telemetry() r = telemetry_pb2.Telemetry()
if self.nodes is not None:
node = next(n for n in self.nodes.values() if n["num"] == self.localNode.nodeNum) node = next(n for n in self.nodes.values() if n["num"] == self.localNode.nodeNum)
if node is not None: if node is not None:
metrics = node.get("deviceMetrics") metrics = node.get("deviceMetrics")
@@ -488,6 +492,7 @@ class MeshInterface:
portNum=portnums_pb2.PortNum.TELEMETRY_APP, portNum=portnums_pb2.PortNum.TELEMETRY_APP,
wantResponse=wantResponse, wantResponse=wantResponse,
onResponse=onResponse, onResponse=onResponse,
channelIndex=channelIndex,
) )
if wantResponse: if wantResponse:
self.waitForTelemetry() self.waitForTelemetry()
@@ -896,6 +901,10 @@ class MeshInterface:
self.localNode.moduleConfig.external_notification.CopyFrom( self.localNode.moduleConfig.external_notification.CopyFrom(
fromRadio.moduleConfig.external_notification fromRadio.moduleConfig.external_notification
) )
elif fromRadio.moduleConfig.HasField("store_forward"):
self.localNode.moduleConfig.store_forward.CopyFrom(
fromRadio.moduleConfig.store_forward
)
elif fromRadio.moduleConfig.HasField("range_test"): elif fromRadio.moduleConfig.HasField("range_test"):
self.localNode.moduleConfig.range_test.CopyFrom( self.localNode.moduleConfig.range_test.CopyFrom(
fromRadio.moduleConfig.range_test fromRadio.moduleConfig.range_test

View File

File diff suppressed because one or more lines are too long

View File

@@ -271,6 +271,11 @@ class _HardwareModelEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._
""" """
CDEBYTE EoRa-S3 board using their own MM modules, clone of LILYGO T3S3 CDEBYTE EoRa-S3 board using their own MM modules, clone of LILYGO T3S3
""" """
TWC_MESH_V4: _HardwareModel.ValueType # 62
"""
TWC_MESH_V4
Adafruit NRF52840 feather express with SX1262, SSD1306 OLED and NEO6M GPS
"""
PRIVATE_HW: _HardwareModel.ValueType # 255 PRIVATE_HW: _HardwareModel.ValueType # 255
""" """
------------------------------------------------------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------------------------------------------------------
@@ -527,6 +532,11 @@ CDEBYTE_EORA_S3: HardwareModel.ValueType # 61
""" """
CDEBYTE EoRa-S3 board using their own MM modules, clone of LILYGO T3S3 CDEBYTE EoRa-S3 board using their own MM modules, clone of LILYGO T3S3
""" """
TWC_MESH_V4: HardwareModel.ValueType # 62
"""
TWC_MESH_V4
Adafruit NRF52840 feather express with SX1262, SSD1306 OLED and NEO6M GPS
"""
PRIVATE_HW: HardwareModel.ValueType # 255 PRIVATE_HW: HardwareModel.ValueType # 255
""" """
------------------------------------------------------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------------------------------------------------------

View File

@@ -313,6 +313,8 @@ class Node:
): ):
channelSet.settings.append(c.settings) channelSet.settings.append(c.settings)
if len(self.localConfig.ListFields()) == 0:
self.requestConfig(self.localConfig.DESCRIPTOR.fields_by_name.get('lora'))
channelSet.lora_config.CopyFrom(self.localConfig.lora) channelSet.lora_config.CopyFrom(self.localConfig.lora)
some_bytes = channelSet.SerializeToString() some_bytes = channelSet.SerializeToString()
s = base64.urlsafe_b64encode(some_bytes).decode("ascii") s = base64.urlsafe_b64encode(some_bytes).decode("ascii")
@@ -569,6 +571,19 @@ class Node:
onResponse = self.onAckNak onResponse = self.onAckNak
return self._sendAdmin(p, onResponse=onResponse) return self._sendAdmin(p, onResponse=onResponse)
def enterDFUMode(self):
"""Tell the node to enter DFU mode (NRF52)."""
p = admin_pb2.AdminMessage()
p.enter_dfu_mode_request = True
logging.info(f"Telling node to enable DFU mode")
# If sending to a remote node, wait for ACK/NAK
if self == self.iface.localNode:
onResponse = None
else:
onResponse = self.onAckNak
return self._sendAdmin(p, onResponse=onResponse)
def shutdown(self, secs: int = 10): def shutdown(self, secs: int = 10):
"""Tell the node to shutdown.""" """Tell the node to shutdown."""
p = admin_pb2.AdminMessage() p = admin_pb2.AdminMessage()

View File

@@ -13,7 +13,7 @@ _sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1ameshtastic/telemetry.proto\x12\nmeshtastic\"\x81\x01\n\rDeviceMetrics\x12\x15\n\rbattery_level\x18\x01 \x01(\r\x12\x0f\n\x07voltage\x18\x02 \x01(\x02\x12\x1b\n\x13\x63hannel_utilization\x18\x03 \x01(\x02\x12\x13\n\x0b\x61ir_util_tx\x18\x04 \x01(\x02\x12\x16\n\x0euptime_seconds\x18\x05 \x01(\r\"\xa8\x01\n\x12\x45nvironmentMetrics\x12\x13\n\x0btemperature\x18\x01 \x01(\x02\x12\x19\n\x11relative_humidity\x18\x02 \x01(\x02\x12\x1b\n\x13\x62\x61rometric_pressure\x18\x03 \x01(\x02\x12\x16\n\x0egas_resistance\x18\x04 \x01(\x02\x12\x0f\n\x07voltage\x18\x05 \x01(\x02\x12\x0f\n\x07\x63urrent\x18\x06 \x01(\x02\x12\x0b\n\x03iaq\x18\x07 \x01(\r\"\x8c\x01\n\x0cPowerMetrics\x12\x13\n\x0b\x63h1_voltage\x18\x01 \x01(\x02\x12\x13\n\x0b\x63h1_current\x18\x02 \x01(\x02\x12\x13\n\x0b\x63h2_voltage\x18\x03 \x01(\x02\x12\x13\n\x0b\x63h2_current\x18\x04 \x01(\x02\x12\x13\n\x0b\x63h3_voltage\x18\x05 \x01(\x02\x12\x13\n\x0b\x63h3_current\x18\x06 \x01(\x02\"\xbf\x02\n\x11\x41irQualityMetrics\x12\x15\n\rpm10_standard\x18\x01 \x01(\r\x12\x15\n\rpm25_standard\x18\x02 \x01(\r\x12\x16\n\x0epm100_standard\x18\x03 \x01(\r\x12\x1a\n\x12pm10_environmental\x18\x04 \x01(\r\x12\x1a\n\x12pm25_environmental\x18\x05 \x01(\r\x12\x1b\n\x13pm100_environmental\x18\x06 \x01(\r\x12\x16\n\x0eparticles_03um\x18\x07 \x01(\r\x12\x16\n\x0eparticles_05um\x18\x08 \x01(\r\x12\x16\n\x0eparticles_10um\x18\t \x01(\r\x12\x16\n\x0eparticles_25um\x18\n \x01(\r\x12\x16\n\x0eparticles_50um\x18\x0b \x01(\r\x12\x17\n\x0fparticles_100um\x18\x0c \x01(\r\"\x89\x02\n\tTelemetry\x12\x0c\n\x04time\x18\x01 \x01(\x07\x12\x33\n\x0e\x64\x65vice_metrics\x18\x02 \x01(\x0b\x32\x19.meshtastic.DeviceMetricsH\x00\x12=\n\x13\x65nvironment_metrics\x18\x03 \x01(\x0b\x32\x1e.meshtastic.EnvironmentMetricsH\x00\x12<\n\x13\x61ir_quality_metrics\x18\x04 \x01(\x0b\x32\x1d.meshtastic.AirQualityMetricsH\x00\x12\x31\n\rpower_metrics\x18\x05 \x01(\x0b\x32\x18.meshtastic.PowerMetricsH\x00\x42\t\n\x07variant*\xe0\x01\n\x13TelemetrySensorType\x12\x10\n\x0cSENSOR_UNSET\x10\x00\x12\n\n\x06\x42ME280\x10\x01\x12\n\n\x06\x42ME680\x10\x02\x12\x0b\n\x07MCP9808\x10\x03\x12\n\n\x06INA260\x10\x04\x12\n\n\x06INA219\x10\x05\x12\n\n\x06\x42MP280\x10\x06\x12\t\n\x05SHTC3\x10\x07\x12\t\n\x05LPS22\x10\x08\x12\x0b\n\x07QMC6310\x10\t\x12\x0b\n\x07QMI8658\x10\n\x12\x0c\n\x08QMC5883L\x10\x0b\x12\t\n\x05SHT31\x10\x0c\x12\x0c\n\x08PMSA003I\x10\r\x12\x0b\n\x07INA3221\x10\x0e\x12\n\n\x06\x42MP085\x10\x0f\x42\x64\n\x13\x63om.geeksville.meshB\x0fTelemetryProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3') DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1ameshtastic/telemetry.proto\x12\nmeshtastic\"\x81\x01\n\rDeviceMetrics\x12\x15\n\rbattery_level\x18\x01 \x01(\r\x12\x0f\n\x07voltage\x18\x02 \x01(\x02\x12\x1b\n\x13\x63hannel_utilization\x18\x03 \x01(\x02\x12\x13\n\x0b\x61ir_util_tx\x18\x04 \x01(\x02\x12\x16\n\x0euptime_seconds\x18\x05 \x01(\r\"\xba\x01\n\x12\x45nvironmentMetrics\x12\x13\n\x0btemperature\x18\x01 \x01(\x02\x12\x19\n\x11relative_humidity\x18\x02 \x01(\x02\x12\x1b\n\x13\x62\x61rometric_pressure\x18\x03 \x01(\x02\x12\x16\n\x0egas_resistance\x18\x04 \x01(\x02\x12\x0f\n\x07voltage\x18\x05 \x01(\x02\x12\x0f\n\x07\x63urrent\x18\x06 \x01(\x02\x12\x0b\n\x03iaq\x18\x07 \x01(\r\x12\x10\n\x08\x64istance\x18\x08 \x01(\x02\"\x8c\x01\n\x0cPowerMetrics\x12\x13\n\x0b\x63h1_voltage\x18\x01 \x01(\x02\x12\x13\n\x0b\x63h1_current\x18\x02 \x01(\x02\x12\x13\n\x0b\x63h2_voltage\x18\x03 \x01(\x02\x12\x13\n\x0b\x63h2_current\x18\x04 \x01(\x02\x12\x13\n\x0b\x63h3_voltage\x18\x05 \x01(\x02\x12\x13\n\x0b\x63h3_current\x18\x06 \x01(\x02\"\xbf\x02\n\x11\x41irQualityMetrics\x12\x15\n\rpm10_standard\x18\x01 \x01(\r\x12\x15\n\rpm25_standard\x18\x02 \x01(\r\x12\x16\n\x0epm100_standard\x18\x03 \x01(\r\x12\x1a\n\x12pm10_environmental\x18\x04 \x01(\r\x12\x1a\n\x12pm25_environmental\x18\x05 \x01(\r\x12\x1b\n\x13pm100_environmental\x18\x06 \x01(\r\x12\x16\n\x0eparticles_03um\x18\x07 \x01(\r\x12\x16\n\x0eparticles_05um\x18\x08 \x01(\r\x12\x16\n\x0eparticles_10um\x18\t \x01(\r\x12\x16\n\x0eparticles_25um\x18\n \x01(\r\x12\x16\n\x0eparticles_50um\x18\x0b \x01(\r\x12\x17\n\x0fparticles_100um\x18\x0c \x01(\r\"\x89\x02\n\tTelemetry\x12\x0c\n\x04time\x18\x01 \x01(\x07\x12\x33\n\x0e\x64\x65vice_metrics\x18\x02 \x01(\x0b\x32\x19.meshtastic.DeviceMetricsH\x00\x12=\n\x13\x65nvironment_metrics\x18\x03 \x01(\x0b\x32\x1e.meshtastic.EnvironmentMetricsH\x00\x12<\n\x13\x61ir_quality_metrics\x18\x04 \x01(\x0b\x32\x1d.meshtastic.AirQualityMetricsH\x00\x12\x31\n\rpower_metrics\x18\x05 \x01(\x0b\x32\x18.meshtastic.PowerMetricsH\x00\x42\t\n\x07variant*\xee\x01\n\x13TelemetrySensorType\x12\x10\n\x0cSENSOR_UNSET\x10\x00\x12\n\n\x06\x42ME280\x10\x01\x12\n\n\x06\x42ME680\x10\x02\x12\x0b\n\x07MCP9808\x10\x03\x12\n\n\x06INA260\x10\x04\x12\n\n\x06INA219\x10\x05\x12\n\n\x06\x42MP280\x10\x06\x12\t\n\x05SHTC3\x10\x07\x12\t\n\x05LPS22\x10\x08\x12\x0b\n\x07QMC6310\x10\t\x12\x0b\n\x07QMI8658\x10\n\x12\x0c\n\x08QMC5883L\x10\x0b\x12\t\n\x05SHT31\x10\x0c\x12\x0c\n\x08PMSA003I\x10\r\x12\x0b\n\x07INA3221\x10\x0e\x12\n\n\x06\x42MP085\x10\x0f\x12\x0c\n\x08RCWL9620\x10\x10\x42\x64\n\x13\x63om.geeksville.meshB\x0fTelemetryProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.telemetry_pb2', globals()) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.telemetry_pb2', globals())
@@ -21,16 +21,16 @@ if _descriptor._USE_C_DESCRIPTORS == False:
DESCRIPTOR._options = None DESCRIPTOR._options = None
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\017TelemetryProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\017TelemetryProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
_TELEMETRYSENSORTYPE._serialized_start=1079 _TELEMETRYSENSORTYPE._serialized_start=1097
_TELEMETRYSENSORTYPE._serialized_end=1303 _TELEMETRYSENSORTYPE._serialized_end=1335
_DEVICEMETRICS._serialized_start=43 _DEVICEMETRICS._serialized_start=43
_DEVICEMETRICS._serialized_end=172 _DEVICEMETRICS._serialized_end=172
_ENVIRONMENTMETRICS._serialized_start=175 _ENVIRONMENTMETRICS._serialized_start=175
_ENVIRONMENTMETRICS._serialized_end=343 _ENVIRONMENTMETRICS._serialized_end=361
_POWERMETRICS._serialized_start=346 _POWERMETRICS._serialized_start=364
_POWERMETRICS._serialized_end=486 _POWERMETRICS._serialized_end=504
_AIRQUALITYMETRICS._serialized_start=489 _AIRQUALITYMETRICS._serialized_start=507
_AIRQUALITYMETRICS._serialized_end=808 _AIRQUALITYMETRICS._serialized_end=826
_TELEMETRY._serialized_start=811 _TELEMETRY._serialized_start=829
_TELEMETRY._serialized_end=1076 _TELEMETRY._serialized_end=1094
# @@protoc_insertion_point(module_scope) # @@protoc_insertion_point(module_scope)

View File

@@ -86,6 +86,10 @@ class _TelemetrySensorTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wra
""" """
BMP085/BMP180 High accuracy temperature and pressure (older Version of BMP280) BMP085/BMP180 High accuracy temperature and pressure (older Version of BMP280)
""" """
RCWL9620: _TelemetrySensorType.ValueType # 16
"""
RCWL-9620 Doppler Radar Distance Sensor, used for water level detection
"""
class TelemetrySensorType(_TelemetrySensorType, metaclass=_TelemetrySensorTypeEnumTypeWrapper): class TelemetrySensorType(_TelemetrySensorType, metaclass=_TelemetrySensorTypeEnumTypeWrapper):
""" """
@@ -156,6 +160,10 @@ BMP085: TelemetrySensorType.ValueType # 15
""" """
BMP085/BMP180 High accuracy temperature and pressure (older Version of BMP280) BMP085/BMP180 High accuracy temperature and pressure (older Version of BMP280)
""" """
RCWL9620: TelemetrySensorType.ValueType # 16
"""
RCWL-9620 Doppler Radar Distance Sensor, used for water level detection
"""
global___TelemetrySensorType = TelemetrySensorType global___TelemetrySensorType = TelemetrySensorType
@typing_extensions.final @typing_extensions.final
@@ -219,6 +227,7 @@ class EnvironmentMetrics(google.protobuf.message.Message):
VOLTAGE_FIELD_NUMBER: builtins.int VOLTAGE_FIELD_NUMBER: builtins.int
CURRENT_FIELD_NUMBER: builtins.int CURRENT_FIELD_NUMBER: builtins.int
IAQ_FIELD_NUMBER: builtins.int IAQ_FIELD_NUMBER: builtins.int
DISTANCE_FIELD_NUMBER: builtins.int
temperature: builtins.float temperature: builtins.float
""" """
Temperature measured Temperature measured
@@ -248,6 +257,10 @@ class EnvironmentMetrics(google.protobuf.message.Message):
relative scale IAQ value as measured by Bosch BME680 . value 0-500. relative scale IAQ value as measured by Bosch BME680 . value 0-500.
Belongs to Air Quality but is not particle but VOC measurement. Other VOC values can also be put in here. Belongs to Air Quality but is not particle but VOC measurement. Other VOC values can also be put in here.
""" """
distance: builtins.float
"""
RCWL9620 Doppler Radar Distance Sensor, used for water level detection. Float value in mm.
"""
def __init__( def __init__(
self, self,
*, *,
@@ -258,8 +271,9 @@ class EnvironmentMetrics(google.protobuf.message.Message):
voltage: builtins.float = ..., voltage: builtins.float = ...,
current: builtins.float = ..., current: builtins.float = ...,
iaq: builtins.int = ..., iaq: builtins.int = ...,
distance: builtins.float = ...,
) -> None: ... ) -> None: ...
def ClearField(self, field_name: typing_extensions.Literal["barometric_pressure", b"barometric_pressure", "current", b"current", "gas_resistance", b"gas_resistance", "iaq", b"iaq", "relative_humidity", b"relative_humidity", "temperature", b"temperature", "voltage", b"voltage"]) -> None: ... def ClearField(self, field_name: typing_extensions.Literal["barometric_pressure", b"barometric_pressure", "current", b"current", "distance", b"distance", "gas_resistance", b"gas_resistance", "iaq", b"iaq", "relative_humidity", b"relative_humidity", "temperature", b"temperature", "voltage", b"voltage"]) -> None: ...
global___EnvironmentMetrics = EnvironmentMetrics global___EnvironmentMetrics = EnvironmentMetrics

View File

@@ -6,7 +6,7 @@ from unittest.mock import MagicMock, patch
import pytest import pytest
from .. import mesh_pb2, BROADCAST_ADDR, LOCAL_ADDR from .. import mesh_pb2, config_pb2, BROADCAST_ADDR, LOCAL_ADDR
from ..mesh_interface import MeshInterface from ..mesh_interface import MeshInterface
from ..node import Node from ..node import Node
@@ -36,12 +36,15 @@ def test_MeshInterface(capsys):
"lastHeard": 1640204888, "lastHeard": 1640204888,
} }
iface.nodes = {NODE_ID: node} iface.nodes = {NODE_ID: node}
iface.nodesByNum = {NODE_NUM: node} iface.nodesByNum = {NODE_NUM: node}
myInfo = MagicMock() myInfo = MagicMock()
iface.myInfo = myInfo iface.myInfo = myInfo
iface.localNode.localConfig.lora.CopyFrom(config_pb2.Config.LoRaConfig())
iface.showInfo() iface.showInfo()
iface.localNode.showInfo() iface.localNode.showInfo()
iface.showNodes() iface.showNodes()

View File

@@ -6,7 +6,7 @@ from unittest.mock import MagicMock, patch
import pytest import pytest
# from ..admin_pb2 import AdminMessage from .. import localonly_pb2, config_pb2
from ..channel_pb2 import Channel # pylint: disable=E0611 from ..channel_pb2 import Channel # pylint: disable=E0611
from ..node import Node from ..node import Node
from ..serial_interface import SerialInterface from ..serial_interface import SerialInterface
@@ -19,21 +19,26 @@ from ..mesh_interface import MeshInterface
# from ..util import Timeout # from ..util import Timeout
# TODO @pytest.mark.unit
# @pytest.mark.unit def test_node(capsys):
# def test_node(capsys): """Test that we can instantiate a Node"""
# """Test that we can instantiate a Node""" iface = MagicMock(autospec=SerialInterface)
# anode = Node('foo', 'bar') with patch("meshtastic.serial_interface.SerialInterface", return_value=iface) as mo:
# radioConfig = RadioConfig() mo.localNode.getChannelByName.return_value = None
# anode.radioConfig = radioConfig mo.myInfo.max_channels = 8
# anode.showChannels() anode = Node(mo, "bar", noProto=True)
# anode.showInfo() lc = localonly_pb2.LocalConfig()
# out, err = capsys.readouterr() anode.localConfig = lc
# assert re.search(r'Preferences', out) lc.lora.CopyFrom(config_pb2.Config.LoRaConfig())
# assert re.search(r'Channels', out) anode.moduleConfig = localonly_pb2.LocalModuleConfig()
# assert re.search(r'Primary channel URL', out) anode.showInfo()
# assert err == '' out, err = capsys.readouterr()
assert re.search(r'Preferences', out)
assert re.search(r'Module preferences', out)
assert re.search(r'Channels', out)
assert re.search(r'Primary channel URL', out)
assert not re.search(r'remote node', out)
assert err == ''
# TODO # TODO
# @pytest.mark.unit # @pytest.mark.unit

View File

@@ -6,6 +6,7 @@ from unittest.mock import mock_open, patch
import pytest import pytest
from ..serial_interface import SerialInterface from ..serial_interface import SerialInterface
from .. import config_pb2
@pytest.mark.unit @pytest.mark.unit
@@ -20,6 +21,7 @@ def test_SerialInterface_single_port(
): ):
"""Test that we can instantiate a SerialInterface with a single port""" """Test that we can instantiate a SerialInterface with a single port"""
iface = SerialInterface(noProto=True) iface = SerialInterface(noProto=True)
iface.localNode.localConfig.lora.CopyFrom(config_pb2.Config.LoRaConfig())
iface.showInfo() iface.showInfo()
iface.localNode.showInfo() iface.localNode.showInfo()
iface.close() iface.close()

View File

@@ -5,6 +5,7 @@ from unittest.mock import patch
import pytest import pytest
from .. import config_pb2
from ..tcp_interface import TCPInterface from ..tcp_interface import TCPInterface
@@ -13,6 +14,7 @@ def test_TCPInterface(capsys):
"""Test that we can instantiate a TCPInterface""" """Test that we can instantiate a TCPInterface"""
with patch("socket.socket") as mock_socket: with patch("socket.socket") as mock_socket:
iface = TCPInterface(hostname="localhost", noProto=True) iface = TCPInterface(hostname="localhost", noProto=True)
iface.localNode.localConfig.lora.CopyFrom(config_pb2.Config.LoRaConfig())
iface.myConnect() iface.myConnect()
iface.showInfo() iface.showInfo()
iface.localNode.showInfo() iface.localNode.showInfo()

View File

@@ -13,7 +13,7 @@ with open("README.md", "r") as fh:
# This call to setup() does all the work # This call to setup() does all the work
setup( setup(
name="meshtastic", name="meshtastic",
version="2.3.5", version="2.3.6",
description="Python API & client shell for talking to Meshtastic devices", description="Python API & client shell for talking to Meshtastic devices",
long_description=long_description, long_description=long_description,
long_description_content_type="text/markdown", long_description_content_type="text/markdown",