mirror of
https://github.com/meshtastic/python.git
synced 2025-12-26 09:27:52 -05:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
022a8a1017 | ||
|
|
21f6e25ab0 | ||
|
|
243e297505 | ||
|
|
9fc86f9450 | ||
|
|
e5999f5be6 | ||
|
|
3886bc16d7 | ||
|
|
7fe98bc266 | ||
|
|
2f9307fd99 | ||
|
|
3d6fa369e8 | ||
|
|
6812f508bc | ||
|
|
158cac6b0b | ||
|
|
4d10b6e1bd | ||
|
|
bb6e3b7a49 | ||
|
|
8b29dc683a | ||
|
|
d4156d78c6 | ||
|
|
c52977781e | ||
|
|
5ebc8e6f95 | ||
|
|
56d1dcafe5 | ||
|
|
393c765557 | ||
|
|
243b310eb2 | ||
|
|
cd1ecab4e0 | ||
|
|
c710953b85 |
@@ -20,6 +20,7 @@ from meshtastic import mt_config
|
||||
from meshtastic import channel_pb2, config_pb2, portnums_pb2, remote_hardware, BROADCAST_ADDR
|
||||
from meshtastic.version import get_active_version
|
||||
from meshtastic.ble_interface import BLEInterface
|
||||
from meshtastic.mesh_interface import MeshInterface
|
||||
|
||||
def onReceive(packet, interface):
|
||||
"""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"""
|
||||
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):
|
||||
"""Get a channel or preferences value"""
|
||||
@@ -251,6 +257,9 @@ def onConnected(interface):
|
||||
print("Connected to radio")
|
||||
|
||||
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
|
||||
|
||||
alt = 0
|
||||
@@ -366,6 +375,11 @@ def onConnected(interface):
|
||||
waitForAckNak = True
|
||||
interface.getNode(args.dest, False).rebootOTA()
|
||||
|
||||
if args.enter_dfu:
|
||||
closeNow = True
|
||||
waitForAckNak = True
|
||||
interface.getNode(args.dest, False).enterDFUMode()
|
||||
|
||||
if args.shutdown:
|
||||
closeNow = True
|
||||
waitForAckNak = True
|
||||
@@ -400,12 +414,8 @@ def onConnected(interface):
|
||||
|
||||
if args.sendtext:
|
||||
closeNow = True
|
||||
channelIndex = 0
|
||||
if args.ch_index is not None:
|
||||
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:
|
||||
channelIndex = mt_config.channel_index or 0
|
||||
if checkChannel(interface, channelIndex):
|
||||
print(
|
||||
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")
|
||||
hopLimit = getattr(loraConfig, "hop_limit")
|
||||
dest = str(args.traceroute)
|
||||
print(f"Sending traceroute request to {dest} (this could take a while)")
|
||||
interface.sendTraceRoute(dest, hopLimit)
|
||||
channelIndex = mt_config.channel_index or 0
|
||||
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.dest == BROADCAST_ADDR:
|
||||
meshtastic.util.our_exit("Warning: Must use a destination node ID.")
|
||||
else:
|
||||
print(f"Sending telemetry request to {args.dest} (this could take a while)")
|
||||
interface.sendTelemetry(destinationId=args.dest, wantResponse=True)
|
||||
channelIndex = mt_config.channel_index or 0
|
||||
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.dest == BROADCAST_ADDR:
|
||||
meshtastic.util.our_exit("Warning: Must use a destination node ID.")
|
||||
else:
|
||||
print(f"Sending position request to {args.dest} (this could take a while)")
|
||||
interface.sendPosition(destinationId=args.dest, wantResponse=True)
|
||||
channelIndex = mt_config.channel_index or 0
|
||||
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.dest == BROADCAST_ADDR:
|
||||
@@ -599,6 +615,9 @@ def onConnected(interface):
|
||||
print("Writing modified configuration to device")
|
||||
|
||||
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')
|
||||
closeNow = True
|
||||
export_config(interface)
|
||||
@@ -801,10 +820,14 @@ def onConnected(interface):
|
||||
return
|
||||
interface.showNodes()
|
||||
|
||||
if args.qr:
|
||||
if args.qr or args.qr_all:
|
||||
closeNow = True
|
||||
url = interface.localNode.getURL(includeAll=False)
|
||||
print(f"Primary channel URL {url}")
|
||||
url = interface.getNode(args.dest, True).getURL(includeAll=args.qr_all)
|
||||
if args.qr_all:
|
||||
urldesc = "Complete URL (includes all channels)"
|
||||
else:
|
||||
urldesc = "Primary channel URL"
|
||||
print(f"{urldesc}: {url}")
|
||||
qr = pyqrcode.create(url)
|
||||
print(qr.terminal())
|
||||
|
||||
@@ -813,6 +836,9 @@ def onConnected(interface):
|
||||
|
||||
have_tunnel = platform.system() == "Linux"
|
||||
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
|
||||
from . import tunnel
|
||||
|
||||
@@ -1154,7 +1180,16 @@ def initParser():
|
||||
|
||||
group.add_argument(
|
||||
"--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",
|
||||
)
|
||||
|
||||
@@ -1334,7 +1369,13 @@ def initParser():
|
||||
|
||||
group.add_argument(
|
||||
"--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",
|
||||
)
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ _sym_db = _symbol_database.Default()
|
||||
|
||||
|
||||
|
||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x18meshtastic/channel.proto\x12\nmeshtastic\"\xb8\x01\n\x0f\x43hannelSettings\x12\x17\n\x0b\x63hannel_num\x18\x01 \x01(\rB\x02\x18\x01\x12\x0b\n\x03psk\x18\x02 \x01(\x0c\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\n\n\x02id\x18\x04 \x01(\x07\x12\x16\n\x0euplink_enabled\x18\x05 \x01(\x08\x12\x18\n\x10\x64ownlink_enabled\x18\x06 \x01(\x08\x12\x33\n\x0fmodule_settings\x18\x07 \x01(\x0b\x32\x1a.meshtastic.ModuleSettings\",\n\x0eModuleSettings\x12\x1a\n\x12position_precision\x18\x01 \x01(\r\"\xa1\x01\n\x07\x43hannel\x12\r\n\x05index\x18\x01 \x01(\x05\x12-\n\x08settings\x18\x02 \x01(\x0b\x32\x1b.meshtastic.ChannelSettings\x12&\n\x04role\x18\x03 \x01(\x0e\x32\x18.meshtastic.Channel.Role\"0\n\x04Role\x12\x0c\n\x08\x44ISABLED\x10\x00\x12\x0b\n\x07PRIMARY\x10\x01\x12\r\n\tSECONDARY\x10\x02\x42\x62\n\x13\x63om.geeksville.meshB\rChannelProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
|
||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x18meshtastic/channel.proto\x12\nmeshtastic\"\xb8\x01\n\x0f\x43hannelSettings\x12\x17\n\x0b\x63hannel_num\x18\x01 \x01(\rB\x02\x18\x01\x12\x0b\n\x03psk\x18\x02 \x01(\x0c\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\n\n\x02id\x18\x04 \x01(\x07\x12\x16\n\x0euplink_enabled\x18\x05 \x01(\x08\x12\x18\n\x10\x64ownlink_enabled\x18\x06 \x01(\x08\x12\x33\n\x0fmodule_settings\x18\x07 \x01(\x0b\x32\x1a.meshtastic.ModuleSettings\"E\n\x0eModuleSettings\x12\x1a\n\x12position_precision\x18\x01 \x01(\r\x12\x17\n\x0fis_client_muted\x18\x02 \x01(\x08\"\xa1\x01\n\x07\x43hannel\x12\r\n\x05index\x18\x01 \x01(\x05\x12-\n\x08settings\x18\x02 \x01(\x0b\x32\x1b.meshtastic.ChannelSettings\x12&\n\x04role\x18\x03 \x01(\x0e\x32\x18.meshtastic.Channel.Role\"0\n\x04Role\x12\x0c\n\x08\x44ISABLED\x10\x00\x12\x0b\n\x07PRIMARY\x10\x01\x12\r\n\tSECONDARY\x10\x02\x42\x62\n\x13\x63om.geeksville.meshB\rChannelProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
|
||||
|
||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
|
||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.channel_pb2', globals())
|
||||
@@ -26,9 +26,9 @@ if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
_CHANNELSETTINGS._serialized_start=41
|
||||
_CHANNELSETTINGS._serialized_end=225
|
||||
_MODULESETTINGS._serialized_start=227
|
||||
_MODULESETTINGS._serialized_end=271
|
||||
_CHANNEL._serialized_start=274
|
||||
_CHANNEL._serialized_end=435
|
||||
_CHANNEL_ROLE._serialized_start=387
|
||||
_CHANNEL_ROLE._serialized_end=435
|
||||
_MODULESETTINGS._serialized_end=296
|
||||
_CHANNEL._serialized_start=299
|
||||
_CHANNEL._serialized_end=460
|
||||
_CHANNEL_ROLE._serialized_start=412
|
||||
_CHANNEL_ROLE._serialized_end=460
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
|
||||
@@ -125,16 +125,23 @@ class ModuleSettings(google.protobuf.message.Message):
|
||||
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
||||
|
||||
POSITION_PRECISION_FIELD_NUMBER: builtins.int
|
||||
IS_CLIENT_MUTED_FIELD_NUMBER: builtins.int
|
||||
position_precision: builtins.int
|
||||
"""
|
||||
Bits of precision for the location sent in position packets.
|
||||
"""
|
||||
is_client_muted: builtins.bool
|
||||
"""
|
||||
Controls whether or not the phone / clients should mute the current channel
|
||||
Useful for noisy public channels you don't necessarily want to disable
|
||||
"""
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
position_precision: builtins.int = ...,
|
||||
is_client_muted: builtins.bool = ...,
|
||||
) -> None: ...
|
||||
def ClearField(self, field_name: typing_extensions.Literal["position_precision", b"position_precision"]) -> None: ...
|
||||
def ClearField(self, field_name: typing_extensions.Literal["is_client_muted", b"is_client_muted", "position_precision", b"position_precision"]) -> None: ...
|
||||
|
||||
global___ModuleSettings = ModuleSettings
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -245,6 +245,7 @@ class Config(google.protobuf.message.Message):
|
||||
IS_MANAGED_FIELD_NUMBER: builtins.int
|
||||
DISABLE_TRIPLE_CLICK_FIELD_NUMBER: builtins.int
|
||||
TZDEF_FIELD_NUMBER: builtins.int
|
||||
LED_HEARTBEAT_DISABLED_FIELD_NUMBER: builtins.int
|
||||
role: global___Config.DeviceConfig.Role.ValueType
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
led_heartbeat_disabled: builtins.bool
|
||||
"""
|
||||
If true, disable the default blinking LED (LED_PIN) behavior on the device
|
||||
"""
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
@@ -308,8 +313,9 @@ class Config(google.protobuf.message.Message):
|
||||
is_managed: builtins.bool = ...,
|
||||
disable_triple_click: builtins.bool = ...,
|
||||
tzdef: builtins.str = ...,
|
||||
led_heartbeat_disabled: builtins.bool = ...,
|
||||
) -> 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
|
||||
class PositionConfig(google.protobuf.message.Message):
|
||||
|
||||
@@ -85,6 +85,7 @@ class MeshInterface:
|
||||
self.mask: Optional[int] = None # used in gpio read and gpio watch
|
||||
self.queueStatus: Optional[mesh_pb2.QueueStatus] = None
|
||||
self.queue: collections.OrderedDict = collections.OrderedDict()
|
||||
self._localChannels = None
|
||||
|
||||
def close(self):
|
||||
"""Shutdown this interface"""
|
||||
@@ -133,7 +134,7 @@ class MeshInterface:
|
||||
# use id as dictionary key for correct json format in list of nodes
|
||||
nodeid = n2["user"]["id"]
|
||||
nodes[nodeid] = n2
|
||||
infos = owner + myinfo + metadata + mesh + json.dumps(nodes)
|
||||
infos = owner + myinfo + metadata + mesh + json.dumps(nodes, indent=2)
|
||||
print(infos)
|
||||
return infos
|
||||
|
||||
@@ -351,6 +352,7 @@ class MeshInterface:
|
||||
destinationId: Union[int, str]=BROADCAST_ADDR,
|
||||
wantAck: bool=False,
|
||||
wantResponse: bool=False,
|
||||
channelIndex: int=0,
|
||||
):
|
||||
"""
|
||||
Send a position packet to some other node (normally a broadcast)
|
||||
@@ -393,6 +395,7 @@ class MeshInterface:
|
||||
wantAck=wantAck,
|
||||
wantResponse=wantResponse,
|
||||
onResponse=onResponse,
|
||||
channelIndex=channelIndex,
|
||||
)
|
||||
if wantResponse:
|
||||
self.waitForPosition()
|
||||
@@ -426,7 +429,7 @@ class MeshInterface:
|
||||
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.")
|
||||
|
||||
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"""
|
||||
r = mesh_pb2.RouteDiscovery()
|
||||
self.sendData(
|
||||
@@ -435,6 +438,7 @@ class MeshInterface:
|
||||
portNum=portnums_pb2.PortNum.TRACEROUTE_APP,
|
||||
wantResponse=True,
|
||||
onResponse=self.onResponseTraceRoute,
|
||||
channelIndex=channelIndex,
|
||||
)
|
||||
# extend timeout based on number of nodes, limit by configured hopLimit
|
||||
waitFactor = min(len(self.nodes) - 1 if self.nodes else 0, hopLimit)
|
||||
@@ -456,26 +460,27 @@ class MeshInterface:
|
||||
|
||||
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"""
|
||||
r = telemetry_pb2.Telemetry()
|
||||
|
||||
node = next(n for n in self.nodes.values() if n["num"] == self.localNode.nodeNum)
|
||||
if node is not None:
|
||||
metrics = node.get("deviceMetrics")
|
||||
if metrics:
|
||||
batteryLevel = metrics.get("batteryLevel")
|
||||
if batteryLevel is not None:
|
||||
r.device_metrics.battery_level = batteryLevel
|
||||
voltage = metrics.get("voltage")
|
||||
if voltage is not None:
|
||||
r.device_metrics.voltage = voltage
|
||||
channel_utilization = metrics.get("channelUtilization")
|
||||
if channel_utilization is not None:
|
||||
r.device_metrics.channel_utilization = channel_utilization
|
||||
air_util_tx = metrics.get("airUtilTx")
|
||||
if air_util_tx is not None:
|
||||
r.device_metrics.air_util_tx = air_util_tx
|
||||
if self.nodes is not None:
|
||||
node = next(n for n in self.nodes.values() if n["num"] == self.localNode.nodeNum)
|
||||
if node is not None:
|
||||
metrics = node.get("deviceMetrics")
|
||||
if metrics:
|
||||
batteryLevel = metrics.get("batteryLevel")
|
||||
if batteryLevel is not None:
|
||||
r.device_metrics.battery_level = batteryLevel
|
||||
voltage = metrics.get("voltage")
|
||||
if voltage is not None:
|
||||
r.device_metrics.voltage = voltage
|
||||
channel_utilization = metrics.get("channelUtilization")
|
||||
if channel_utilization is not None:
|
||||
r.device_metrics.channel_utilization = channel_utilization
|
||||
air_util_tx = metrics.get("airUtilTx")
|
||||
if air_util_tx is not None:
|
||||
r.device_metrics.air_util_tx = air_util_tx
|
||||
|
||||
if wantResponse:
|
||||
onResponse = self.onResponseTelemetry
|
||||
@@ -488,6 +493,7 @@ class MeshInterface:
|
||||
portNum=portnums_pb2.PortNum.TELEMETRY_APP,
|
||||
wantResponse=wantResponse,
|
||||
onResponse=onResponse,
|
||||
channelIndex=channelIndex,
|
||||
)
|
||||
if wantResponse:
|
||||
self.waitForTelemetry()
|
||||
@@ -677,6 +683,7 @@ class MeshInterface:
|
||||
self.heartbeatTimer = threading.Timer(i, callback)
|
||||
self.heartbeatTimer.start()
|
||||
p = mesh_pb2.ToRadio()
|
||||
p.heartbeat.CopyFrom(mesh_pb2.Heartbeat())
|
||||
self._sendToRadio(p)
|
||||
|
||||
callback() # run our periodic callback now, it will make another timer if necessary
|
||||
@@ -700,6 +707,7 @@ class MeshInterface:
|
||||
self.myInfo = None
|
||||
self.nodes = {} # nodes keyed by ID
|
||||
self.nodesByNum = {} # nodes keyed by nodenum
|
||||
self._localChannels = [] # empty until we start getting channels pushed from the device (during config)
|
||||
|
||||
startConfig = mesh_pb2.ToRadio()
|
||||
self.configId = random.randint(0, 0xFFFFFFFF)
|
||||
@@ -781,7 +789,12 @@ class MeshInterface:
|
||||
Done with initial config messages, now send regular MeshPackets
|
||||
to ask for settings and channels
|
||||
"""
|
||||
self.localNode.requestChannels()
|
||||
# This is no longer necessary because the current protocol statemachine has already proactively sent us the locally visible channels
|
||||
# self.localNode.requestChannels()
|
||||
self.localNode.setChannels(self._localChannels)
|
||||
|
||||
# the following should only be called after we have settings and channels
|
||||
self._connected() # Tell everyone else we are ready to go
|
||||
|
||||
def _handleQueueStatusFromRadio(self, queueStatus) -> None:
|
||||
self.queueStatus = queueStatus
|
||||
@@ -854,7 +867,8 @@ class MeshInterface:
|
||||
# stream API fromRadio.config_complete_id
|
||||
logging.debug(f"Config complete ID {self.configId}")
|
||||
self._handleConfigComplete()
|
||||
|
||||
elif fromRadio.HasField("channel"):
|
||||
self._handleChannel(fromRadio.channel)
|
||||
elif fromRadio.HasField("packet"):
|
||||
self._handlePacketFromRadio(fromRadio.packet)
|
||||
|
||||
@@ -896,6 +910,10 @@ class MeshInterface:
|
||||
self.localNode.moduleConfig.external_notification.CopyFrom(
|
||||
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"):
|
||||
self.localNode.moduleConfig.range_test.CopyFrom(
|
||||
fromRadio.moduleConfig.range_test
|
||||
@@ -977,6 +995,10 @@ class MeshInterface:
|
||||
self.nodesByNum[nodeNum] = n
|
||||
return n
|
||||
|
||||
def _handleChannel(self, channel):
|
||||
"""During initial config the local node will proactively send all N (8) channels it knows"""
|
||||
self._localChannels.append(channel)
|
||||
|
||||
def _handlePacketFromRadio(self, meshPacket, hack=False):
|
||||
"""Handle a MeshPacket that just arrived from the radio
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -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
|
||||
"""
|
||||
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
|
||||
"""
|
||||
------------------------------------------------------------------------------------------------------------------------------------------
|
||||
@@ -527,6 +532,11 @@ CDEBYTE_EORA_S3: HardwareModel.ValueType # 61
|
||||
"""
|
||||
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
|
||||
"""
|
||||
------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -64,14 +64,19 @@ class Node:
|
||||
"""Show human readable description of our node"""
|
||||
prefs = ""
|
||||
if self.localConfig:
|
||||
prefs = message_to_json(self.localConfig)
|
||||
prefs = message_to_json(self.localConfig, multiline=True)
|
||||
print(f"Preferences: {prefs}\n")
|
||||
prefs = ""
|
||||
if self.moduleConfig:
|
||||
prefs = message_to_json(self.moduleConfig)
|
||||
prefs = message_to_json(self.moduleConfig, multiline=True)
|
||||
print(f"Module preferences: {prefs}\n")
|
||||
self.showChannels()
|
||||
|
||||
def setChannels(self, channels):
|
||||
"""Set the channels for this node"""
|
||||
self.channels = channels
|
||||
self._fixupChannels()
|
||||
|
||||
def requestChannels(self):
|
||||
"""Send regular MeshPackets to ask channels."""
|
||||
logging.debug(f"requestChannels for nodeNum:{self.nodeNum}")
|
||||
@@ -313,6 +318,8 @@ class Node:
|
||||
):
|
||||
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)
|
||||
some_bytes = channelSet.SerializeToString()
|
||||
s = base64.urlsafe_b64encode(some_bytes).decode("ascii")
|
||||
@@ -569,6 +576,19 @@ class Node:
|
||||
onResponse = self.onAckNak
|
||||
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):
|
||||
"""Tell the node to shutdown."""
|
||||
p = admin_pb2.AdminMessage()
|
||||
@@ -639,7 +659,7 @@ class Node:
|
||||
"""Fixup indexes and add disabled channels as needed"""
|
||||
|
||||
# Add extra disabled channels as needed
|
||||
# TODO: These 2 lines seem to not do anything.
|
||||
# This is needed because the protobufs will have index **missing** if the channel number is zero
|
||||
for index, ch in enumerate(self.channels):
|
||||
ch.index = index # fixup indexes
|
||||
|
||||
@@ -711,9 +731,6 @@ class Node:
|
||||
|
||||
self.channels = self.partialChannels
|
||||
self._fixupChannels()
|
||||
|
||||
# FIXME, the following should only be called after we have settings and channels
|
||||
self.iface._connected() # Tell everyone else we are ready to go
|
||||
else:
|
||||
self._requestChannel(index + 1)
|
||||
|
||||
|
||||
@@ -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.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.telemetry_pb2', globals())
|
||||
@@ -21,16 +21,16 @@ if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
|
||||
DESCRIPTOR._options = None
|
||||
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_end=1303
|
||||
_TELEMETRYSENSORTYPE._serialized_start=1097
|
||||
_TELEMETRYSENSORTYPE._serialized_end=1335
|
||||
_DEVICEMETRICS._serialized_start=43
|
||||
_DEVICEMETRICS._serialized_end=172
|
||||
_ENVIRONMENTMETRICS._serialized_start=175
|
||||
_ENVIRONMENTMETRICS._serialized_end=343
|
||||
_POWERMETRICS._serialized_start=346
|
||||
_POWERMETRICS._serialized_end=486
|
||||
_AIRQUALITYMETRICS._serialized_start=489
|
||||
_AIRQUALITYMETRICS._serialized_end=808
|
||||
_TELEMETRY._serialized_start=811
|
||||
_TELEMETRY._serialized_end=1076
|
||||
_ENVIRONMENTMETRICS._serialized_end=361
|
||||
_POWERMETRICS._serialized_start=364
|
||||
_POWERMETRICS._serialized_end=504
|
||||
_AIRQUALITYMETRICS._serialized_start=507
|
||||
_AIRQUALITYMETRICS._serialized_end=826
|
||||
_TELEMETRY._serialized_start=829
|
||||
_TELEMETRY._serialized_end=1094
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
|
||||
@@ -86,6 +86,10 @@ class _TelemetrySensorTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wra
|
||||
"""
|
||||
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):
|
||||
"""
|
||||
@@ -156,6 +160,10 @@ BMP085: TelemetrySensorType.ValueType # 15
|
||||
"""
|
||||
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
|
||||
|
||||
@typing_extensions.final
|
||||
@@ -219,6 +227,7 @@ class EnvironmentMetrics(google.protobuf.message.Message):
|
||||
VOLTAGE_FIELD_NUMBER: builtins.int
|
||||
CURRENT_FIELD_NUMBER: builtins.int
|
||||
IAQ_FIELD_NUMBER: builtins.int
|
||||
DISTANCE_FIELD_NUMBER: builtins.int
|
||||
temperature: builtins.float
|
||||
"""
|
||||
Temperature measured
|
||||
@@ -248,6 +257,10 @@ class EnvironmentMetrics(google.protobuf.message.Message):
|
||||
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.
|
||||
"""
|
||||
distance: builtins.float
|
||||
"""
|
||||
RCWL9620 Doppler Radar Distance Sensor, used for water level detection. Float value in mm.
|
||||
"""
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
@@ -258,8 +271,9 @@ class EnvironmentMetrics(google.protobuf.message.Message):
|
||||
voltage: builtins.float = ...,
|
||||
current: builtins.float = ...,
|
||||
iaq: builtins.int = ...,
|
||||
distance: builtins.float = ...,
|
||||
) -> 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
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ from unittest.mock import MagicMock, patch
|
||||
|
||||
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 ..node import Node
|
||||
|
||||
@@ -36,12 +36,15 @@ def test_MeshInterface(capsys):
|
||||
"lastHeard": 1640204888,
|
||||
}
|
||||
|
||||
|
||||
iface.nodes = {NODE_ID: node}
|
||||
iface.nodesByNum = {NODE_NUM: node}
|
||||
|
||||
myInfo = MagicMock()
|
||||
iface.myInfo = myInfo
|
||||
|
||||
iface.localNode.localConfig.lora.CopyFrom(config_pb2.Config.LoRaConfig())
|
||||
|
||||
iface.showInfo()
|
||||
iface.localNode.showInfo()
|
||||
iface.showNodes()
|
||||
|
||||
@@ -6,7 +6,7 @@ from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
# from ..admin_pb2 import AdminMessage
|
||||
from .. import localonly_pb2, config_pb2
|
||||
from ..channel_pb2 import Channel # pylint: disable=E0611
|
||||
from ..node import Node
|
||||
from ..serial_interface import SerialInterface
|
||||
@@ -19,21 +19,26 @@ from ..mesh_interface import MeshInterface
|
||||
# from ..util import Timeout
|
||||
|
||||
|
||||
# TODO
|
||||
# @pytest.mark.unit
|
||||
# def test_node(capsys):
|
||||
# """Test that we can instantiate a Node"""
|
||||
# anode = Node('foo', 'bar')
|
||||
# radioConfig = RadioConfig()
|
||||
# anode.radioConfig = radioConfig
|
||||
# anode.showChannels()
|
||||
# anode.showInfo()
|
||||
# out, err = capsys.readouterr()
|
||||
# assert re.search(r'Preferences', out)
|
||||
# assert re.search(r'Channels', out)
|
||||
# assert re.search(r'Primary channel URL', out)
|
||||
# assert err == ''
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_node(capsys):
|
||||
"""Test that we can instantiate a Node"""
|
||||
iface = MagicMock(autospec=SerialInterface)
|
||||
with patch("meshtastic.serial_interface.SerialInterface", return_value=iface) as mo:
|
||||
mo.localNode.getChannelByName.return_value = None
|
||||
mo.myInfo.max_channels = 8
|
||||
anode = Node(mo, "bar", noProto=True)
|
||||
lc = localonly_pb2.LocalConfig()
|
||||
anode.localConfig = lc
|
||||
lc.lora.CopyFrom(config_pb2.Config.LoRaConfig())
|
||||
anode.moduleConfig = localonly_pb2.LocalModuleConfig()
|
||||
anode.showInfo()
|
||||
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
|
||||
# @pytest.mark.unit
|
||||
|
||||
@@ -6,6 +6,7 @@ from unittest.mock import mock_open, patch
|
||||
import pytest
|
||||
|
||||
from ..serial_interface import SerialInterface
|
||||
from .. import config_pb2
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@@ -20,6 +21,7 @@ def test_SerialInterface_single_port(
|
||||
):
|
||||
"""Test that we can instantiate a SerialInterface with a single port"""
|
||||
iface = SerialInterface(noProto=True)
|
||||
iface.localNode.localConfig.lora.CopyFrom(config_pb2.Config.LoRaConfig())
|
||||
iface.showInfo()
|
||||
iface.localNode.showInfo()
|
||||
iface.close()
|
||||
|
||||
@@ -5,6 +5,7 @@ from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from .. import config_pb2
|
||||
from ..tcp_interface import TCPInterface
|
||||
|
||||
|
||||
@@ -13,6 +14,7 @@ def test_TCPInterface(capsys):
|
||||
"""Test that we can instantiate a TCPInterface"""
|
||||
with patch("socket.socket") as mock_socket:
|
||||
iface = TCPInterface(hostname="localhost", noProto=True)
|
||||
iface.localNode.localConfig.lora.CopyFrom(config_pb2.Config.LoRaConfig())
|
||||
iface.myConnect()
|
||||
iface.showInfo()
|
||||
iface.localNode.showInfo()
|
||||
|
||||
@@ -627,6 +627,8 @@ def check_if_newer_version():
|
||||
|
||||
return pypi_version
|
||||
|
||||
def message_to_json(message):
|
||||
"Return protobuf message as JSON. Always print all fields, even when not present in data."
|
||||
return stripnl(MessageToJson(message, always_print_fields_with_no_presence=True))
|
||||
|
||||
def message_to_json(message, multiline=False):
|
||||
"""Return protobuf message as JSON. Always print all fields, even when not present in data."""
|
||||
json = MessageToJson(message, always_print_fields_with_no_presence=True)
|
||||
return stripnl(json) if not multiline else json
|
||||
|
||||
Submodule protobufs updated: 0d08acd9c5...e21899aa6b
2
setup.py
2
setup.py
@@ -13,7 +13,7 @@ with open("README.md", "r") as fh:
|
||||
# This call to setup() does all the work
|
||||
setup(
|
||||
name="meshtastic",
|
||||
version="2.3.5",
|
||||
version="2.3.7",
|
||||
description="Python API & client shell for talking to Meshtastic devices",
|
||||
long_description=long_description,
|
||||
long_description_content_type="text/markdown",
|
||||
|
||||
Reference in New Issue
Block a user