Compare commits

...

20 Commits

Author SHA1 Message Date
github-actions
6ec506fe3b bump version to 2.5.11 2025-01-24 17:22:13 +00:00
Ian McEwen
fc3b81dfde protobufs: v2.5.20 2025-01-24 10:19:13 -07:00
Ben Meadors
9c53ea017c Merge pull request #730 from migillett/feature/nodes-add-role
#692 feature/nodes-add-role
2025-01-23 19:07:46 -06:00
Michael Gillett
1e6625d062 feature/nodes-add-role 2025-01-21 22:31:33 -05:00
github-actions
0487ce5e1a bump version to 2.5.10 2025-01-10 17:10:16 +00:00
Ian McEwen
aac19b2ecc protobufs: v2.5.19 2025-01-10 10:06:40 -07:00
github-actions
872fbef5d6 bump version to 2.5.9 2024-12-29 03:22:52 +00:00
Ian McEwen
ec4fbe3a59 protobufs: v2.5.18 2024-12-28 20:18:13 -07:00
Ian McEwen
6bab385380 Tweak error messaging in the onReceive function for sendtext packets 2024-12-27 10:04:36 -07:00
Ian McEwen
b8178d513a bump alpha version 2024-12-27 09:54:15 -07:00
Ian McEwen
f4c085fc50 Merge pull request #714 from loic-fejoz/feature/waypoint
Waypoint creation/move/deletion
2024-12-27 09:46:45 -07:00
Ian McEwen
57f0598082 Fix some pylint complaints 2024-12-27 09:40:17 -07:00
github-actions
55d3188408 bump version to 2.5.8 2024-12-27 12:06:45 +00:00
Ben Meadors
7b64fbb71b Merge pull request #725 from meshtastic/dependabot/pip/jinja2-3.1.5
Bump jinja2 from 3.1.4 to 3.1.5
2024-12-25 08:42:58 -06:00
dependabot[bot]
7f85eb0285 Bump jinja2 from 3.1.4 to 3.1.5
Bumps [jinja2](https://github.com/pallets/jinja) from 3.1.4 to 3.1.5.
- [Release notes](https://github.com/pallets/jinja/releases)
- [Changelog](https://github.com/pallets/jinja/blob/main/CHANGES.rst)
- [Commits](https://github.com/pallets/jinja/compare/3.1.4...3.1.5)

---
updated-dependencies:
- dependency-name: jinja2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-24 00:32:14 +00:00
Ben Meadors
d05ef17ab3 Merge pull request #724 from meshtastic/send-alert
Add sendAlert method on mesh interface
2024-12-23 10:48:49 -06:00
Ben Meadors
d161291ca4 Add sendAlert method on mesh interface 2024-12-22 09:09:54 -06:00
Ian McEwen
4e267c75b0 bump to 2.5.8a0 2024-12-21 20:49:32 -07:00
Loïc Fejoz
7cc18e9df6 fix(waypoint): Missing methods
Add missing methods callbacks regarding waypoints.
2024-12-21 10:36:36 +01:00
Loïc Fejoz
9284a848f2 feat(waypoint): Waypoint creation/deletion
Add methods to send (create or move), delete waypoint.
Add an example script to create, move, delete waypoint.
2024-12-18 17:36:58 +01:00
16 changed files with 463 additions and 134 deletions

55
examples/waypoint.py Normal file
View File

@@ -0,0 +1,55 @@
"""Program to create and delete waypoint
To run:
python3 examples/waypoint.py --port /dev/ttyUSB0 create 45 test the_desc_2 '2024-12-18T23:05:23' 48.74 7.35
python3 examples/waypoint.py delete 45
"""
import argparse
import datetime
import sys
import meshtastic
import meshtastic.serial_interface
parser = argparse.ArgumentParser(
prog='waypoint',
description='Create and delete Meshtastic waypoint')
parser.add_argument('--port', default=None)
parser.add_argument('--debug', default=False, action='store_true')
subparsers = parser.add_subparsers(dest='cmd')
parser_delete = subparsers.add_parser('delete', help='Delete a waypoint')
parser_delete.add_argument('id', help="id of the waypoint")
parser_create = subparsers.add_parser('create', help='Create a new waypoint')
parser_create.add_argument('id', help="id of the waypoint")
parser_create.add_argument('name', help="name of the waypoint")
parser_create.add_argument('description', help="description of the waypoint")
parser_create.add_argument('expire', help="expiration date of the waypoint as interpreted by datetime.fromisoformat")
parser_create.add_argument('latitude', help="latitude of the waypoint")
parser_create.add_argument('longitude', help="longitude of the waypoint")
args = parser.parse_args()
print(args)
# By default will try to find a meshtastic device,
# otherwise provide a device path like /dev/ttyUSB0
if args.debug:
d = sys.stderr
else:
d = None
with meshtastic.serial_interface.SerialInterface(args.port, debugOut=d) as iface:
if args.cmd == 'create':
p = iface.sendWaypoint(
waypoint_id=int(args.id),
name=args.name,
description=args.description,
expire=int(datetime.datetime.fromisoformat(args.expire).timestamp()),
latitude=float(args.latitude),
longitude=float(args.longitude),
)
else:
p = iface.deleteWaypoint(int(args.id))
print(p)
# iface.close()

View File

@@ -74,7 +74,7 @@ def onReceive(packet, interface) -> None:
args
and args.sendtext
and packet["to"] == interface.myInfo.my_node_num
and d["portnum"] == portnums_pb2.PortNum.TEXT_MESSAGE_APP
and d.get("portnum", portnums_pb2.PortNum.UNKNOWN_APP) == portnums_pb2.PortNum.TEXT_MESSAGE_APP
):
interface.close() # after running command then exit
@@ -90,7 +90,7 @@ def onReceive(packet, interface) -> None:
interface.sendText(reply)
except Exception as ex:
print(f"Warning: There is no field {ex} in the packet.")
print(f"Warning: Error processing received packet: {ex}.")
def onConnection(interface, topic=pub.AUTO_TOPIC) -> None: # pylint: disable=W0613

View File

@@ -5,7 +5,9 @@
import collections
import json
import logging
import math
import random
import secrets
import sys
import threading
import time
@@ -267,6 +269,7 @@ class MeshInterface: # pylint: disable=R0902
"ID": user["id"],
"Hardware": user.get("hwModel", "UNSET"),
"Pubkey": user.get("publicKey", "UNSET"),
"Role": user.get("role", "N/A"),
}
)
@@ -389,6 +392,40 @@ class MeshInterface: # pylint: disable=R0902
channelIndex=channelIndex,
)
def sendAlert(
self,
text: str,
destinationId: Union[int, str] = BROADCAST_ADDR,
onResponse: Optional[Callable[[dict], Any]] = None,
channelIndex: int = 0,
):
"""Send an alert text to some other node. This is similar to a text message,
but carries a higher priority and is capable of generating special notifications
on certain clients.
Arguments:
text {string} -- The text of the alert to send
Keyword Arguments:
destinationId {nodeId or nodeNum} -- where to send this
message (default: {BROADCAST_ADDR})
Returns the sent packet. The id field will be populated in this packet
and can be used to track future message acks/naks.
"""
return self.sendData(
text.encode("utf-8"),
destinationId,
portNum=portnums_pb2.PortNum.ALERT_APP,
wantAck=False,
wantResponse=False,
onResponse=onResponse,
channelIndex=channelIndex,
priority=mesh_pb2.MeshPacket.Priority.ALERT
)
def sendData(
self,
data,
@@ -402,6 +439,7 @@ class MeshInterface: # pylint: disable=R0902
hopLimit: Optional[int]=None,
pkiEncrypted: Optional[bool]=False,
publicKey: Optional[bytes]=None,
priority: mesh_pb2.MeshPacket.Priority.ValueType=mesh_pb2.MeshPacket.Priority.RELIABLE,
): # pylint: disable=R0913
"""Send a data packet to some other node
@@ -453,6 +491,8 @@ class MeshInterface: # pylint: disable=R0902
meshPacket.decoded.portnum = portNum
meshPacket.decoded.want_response = wantResponse
meshPacket.id = self._generatePacketId()
if priority is not None:
meshPacket.priority = priority
if onResponse is not None:
logging.debug(f"Setting a response handler for requestId {meshPacket.id}")
@@ -700,6 +740,113 @@ class MeshInterface: # pylint: disable=R0902
"No response from node. At least firmware 2.1.22 is required on the destination node."
)
def onResponseWaypoint(self, p: dict):
"""on response for waypoint"""
if p["decoded"]["portnum"] == "WAYPOINT_APP":
self._acknowledgment.receivedWaypoint = True
w = mesh_pb2.Waypoint()
w.ParseFromString(p["decoded"]["payload"])
print(f"Waypoint received: {w}")
elif p["decoded"]["portnum"] == "ROUTING_APP":
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 sendWaypoint(
self,
name,
description,
expire: int,
waypoint_id: Optional[int] = None,
latitude: float = 0.0,
longitude: float = 0.0,
destinationId: Union[int, str] = BROADCAST_ADDR,
wantAck: bool = True,
wantResponse: bool = False,
channelIndex: int = 0,
): # pylint: disable=R0913
"""
Send a waypoint packet to some other node (normally a broadcast)
Returns the sent packet. The id field will be populated in this packet and
can be used to track future message acks/naks.
"""
w = mesh_pb2.Waypoint()
w.name = name
w.description = description
w.expire = expire
if waypoint_id is None:
# Generate a waypoint's id, NOT a packet ID.
# same algorithm as https://github.com/meshtastic/js/blob/715e35d2374276a43ffa93c628e3710875d43907/src/meshDevice.ts#L791
seed = secrets.randbits(32)
w.id = math.floor(seed * math.pow(2, -32) * 1e9)
logging.debug(f"w.id:{w.id}")
else:
w.id = waypoint_id
if latitude != 0.0:
w.latitude_i = int(latitude * 1e7)
logging.debug(f"w.latitude_i:{w.latitude_i}")
if longitude != 0.0:
w.longitude_i = int(longitude * 1e7)
logging.debug(f"w.longitude_i:{w.longitude_i}")
if wantResponse:
onResponse = self.onResponseWaypoint
else:
onResponse = None
d = self.sendData(
w,
destinationId,
portNum=portnums_pb2.PortNum.WAYPOINT_APP,
wantAck=wantAck,
wantResponse=wantResponse,
onResponse=onResponse,
channelIndex=channelIndex,
)
if wantResponse:
self.waitForWaypoint()
return d
def deleteWaypoint(
self,
waypoint_id: int,
destinationId: Union[int, str] = BROADCAST_ADDR,
wantAck: bool = True,
wantResponse: bool = False,
channelIndex: int = 0,
):
"""
Send a waypoint deletion packet to some other node (normally a broadcast)
NB: The id must be the waypoint's id and not the id of the packet creation.
Returns the sent packet. The id field will be populated in this packet and
can be used to track future message acks/naks.
"""
p = mesh_pb2.Waypoint()
p.id = waypoint_id
p.expire = 0
if wantResponse:
onResponse = self.onResponseWaypoint
else:
onResponse = None
d = self.sendData(
p,
destinationId,
portNum=portnums_pb2.PortNum.WAYPOINT_APP,
wantAck=wantAck,
wantResponse=wantResponse,
onResponse=onResponse,
channelIndex=channelIndex,
)
if wantResponse:
self.waitForWaypoint()
return d
def _addResponseHandler(
self,
requestId: int,
@@ -824,6 +971,12 @@ class MeshInterface: # pylint: disable=R0902
if not success:
raise MeshInterface.MeshInterfaceError("Timed out waiting for position")
def waitForWaypoint(self):
"""Wait for waypoint"""
success = self._timeout.waitForWaypoint(self._acknowledgment)
if not success:
raise MeshInterface.MeshInterfaceError("Timed out waiting for waypoint")
def getMyNodeInfo(self) -> Optional[Dict]:
"""Get info about my node."""
if self.myInfo is None or self.nodesByNum is None:

View File

File diff suppressed because one or more lines are too long

View File

@@ -108,6 +108,14 @@ class Config(google.protobuf.message.Message):
and automatic TAK PLI (position location information) broadcasts.
Uses position module configuration to determine TAK PLI broadcast interval.
"""
ROUTER_LATE: Config.DeviceConfig._Role.ValueType # 11
"""
Description: Will always rebroadcast packets, but will do so after all other modes.
Technical Details: Used for router nodes that are intended to provide additional coverage
in areas not already covered by other routers, or to bridge around problematic terrain,
but should not be given priority over other routers in order to avoid unnecessaraily
consuming hops.
"""
class Role(_Role, metaclass=_RoleEnumTypeWrapper):
"""
@@ -184,6 +192,14 @@ class Config(google.protobuf.message.Message):
and automatic TAK PLI (position location information) broadcasts.
Uses position module configuration to determine TAK PLI broadcast interval.
"""
ROUTER_LATE: Config.DeviceConfig.Role.ValueType # 11
"""
Description: Will always rebroadcast packets, but will do so after all other modes.
Technical Details: Used for router nodes that are intended to provide additional coverage
in areas not already covered by other routers, or to bridge around problematic terrain,
but should not be given priority over other routers in order to avoid unnecessaraily
consuming hops.
"""
class _RebroadcastMode:
ValueType = typing.NewType("ValueType", builtins.int)
@@ -693,6 +709,35 @@ class Config(google.protobuf.message.Message):
use static ip address
"""
class _ProtocolFlags:
ValueType = typing.NewType("ValueType", builtins.int)
V: typing_extensions.TypeAlias = ValueType
class _ProtocolFlagsEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[Config.NetworkConfig._ProtocolFlags.ValueType], builtins.type):
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
NO_BROADCAST: Config.NetworkConfig._ProtocolFlags.ValueType # 0
"""
Do not broadcast packets over any network protocol
"""
UDP_BROADCAST: Config.NetworkConfig._ProtocolFlags.ValueType # 1
"""
Enable broadcasting packets via UDP over the local network
"""
class ProtocolFlags(_ProtocolFlags, metaclass=_ProtocolFlagsEnumTypeWrapper):
"""
Available flags auxiliary network protocols
"""
NO_BROADCAST: Config.NetworkConfig.ProtocolFlags.ValueType # 0
"""
Do not broadcast packets over any network protocol
"""
UDP_BROADCAST: Config.NetworkConfig.ProtocolFlags.ValueType # 1
"""
Enable broadcasting packets via UDP over the local network
"""
@typing.final
class IpV4Config(google.protobuf.message.Message):
DESCRIPTOR: google.protobuf.descriptor.Descriptor
@@ -735,6 +780,7 @@ class Config(google.protobuf.message.Message):
ADDRESS_MODE_FIELD_NUMBER: builtins.int
IPV4_CONFIG_FIELD_NUMBER: builtins.int
RSYSLOG_SERVER_FIELD_NUMBER: builtins.int
ENABLED_PROTOCOLS_FIELD_NUMBER: builtins.int
wifi_enabled: builtins.bool
"""
Enable WiFi (disables Bluetooth)
@@ -764,6 +810,10 @@ class Config(google.protobuf.message.Message):
"""
rsyslog Server and Port
"""
enabled_protocols: builtins.int
"""
Flags for enabling/disabling network protocols
"""
@property
def ipv4_config(self) -> global___Config.NetworkConfig.IpV4Config:
"""
@@ -781,9 +831,10 @@ class Config(google.protobuf.message.Message):
address_mode: global___Config.NetworkConfig.AddressMode.ValueType = ...,
ipv4_config: global___Config.NetworkConfig.IpV4Config | None = ...,
rsyslog_server: builtins.str = ...,
enabled_protocols: builtins.int = ...,
) -> None: ...
def HasField(self, field_name: typing.Literal["ipv4_config", b"ipv4_config"]) -> builtins.bool: ...
def ClearField(self, field_name: typing.Literal["address_mode", b"address_mode", "eth_enabled", b"eth_enabled", "ipv4_config", b"ipv4_config", "ntp_server", b"ntp_server", "rsyslog_server", b"rsyslog_server", "wifi_enabled", b"wifi_enabled", "wifi_psk", b"wifi_psk", "wifi_ssid", b"wifi_ssid"]) -> None: ...
def ClearField(self, field_name: typing.Literal["address_mode", b"address_mode", "enabled_protocols", b"enabled_protocols", "eth_enabled", b"eth_enabled", "ipv4_config", b"ipv4_config", "ntp_server", b"ntp_server", "rsyslog_server", b"rsyslog_server", "wifi_enabled", b"wifi_enabled", "wifi_psk", b"wifi_psk", "wifi_ssid", b"wifi_ssid"]) -> None: ...
@typing.final
class DisplayConfig(google.protobuf.message.Message):

View File

@@ -13,7 +13,7 @@ _sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n#meshtastic/protobuf/device_ui.proto\x12\x13meshtastic.protobuf\"\xbf\x03\n\x0e\x44\x65viceUIConfig\x12\x0f\n\x07version\x18\x01 \x01(\r\x12\x19\n\x11screen_brightness\x18\x02 \x01(\r\x12\x16\n\x0escreen_timeout\x18\x03 \x01(\r\x12\x13\n\x0bscreen_lock\x18\x04 \x01(\x08\x12\x15\n\rsettings_lock\x18\x05 \x01(\x08\x12\x10\n\x08pin_code\x18\x06 \x01(\r\x12)\n\x05theme\x18\x07 \x01(\x0e\x32\x1a.meshtastic.protobuf.Theme\x12\x15\n\ralert_enabled\x18\x08 \x01(\x08\x12\x16\n\x0e\x62\x61nner_enabled\x18\t \x01(\x08\x12\x14\n\x0cring_tone_id\x18\n \x01(\r\x12/\n\x08language\x18\x0b \x01(\x0e\x32\x1d.meshtastic.protobuf.Language\x12\x34\n\x0bnode_filter\x18\x0c \x01(\x0b\x32\x1f.meshtastic.protobuf.NodeFilter\x12:\n\x0enode_highlight\x18\r \x01(\x0b\x32\".meshtastic.protobuf.NodeHighlight\x12\x18\n\x10\x63\x61libration_data\x18\x0e \x01(\x0c\"\x96\x01\n\nNodeFilter\x12\x16\n\x0eunknown_switch\x18\x01 \x01(\x08\x12\x16\n\x0eoffline_switch\x18\x02 \x01(\x08\x12\x19\n\x11public_key_switch\x18\x03 \x01(\x08\x12\x11\n\thops_away\x18\x04 \x01(\x05\x12\x17\n\x0fposition_switch\x18\x05 \x01(\x08\x12\x11\n\tnode_name\x18\x06 \x01(\t\"~\n\rNodeHighlight\x12\x13\n\x0b\x63hat_switch\x18\x01 \x01(\x08\x12\x17\n\x0fposition_switch\x18\x02 \x01(\x08\x12\x18\n\x10telemetry_switch\x18\x03 \x01(\x08\x12\x12\n\niaq_switch\x18\x04 \x01(\x08\x12\x11\n\tnode_name\x18\x05 \x01(\t*%\n\x05Theme\x12\x08\n\x04\x44\x41RK\x10\x00\x12\t\n\x05LIGHT\x10\x01\x12\x07\n\x03RED\x10\x02*\xfc\x01\n\x08Language\x12\x0b\n\x07\x45NGLISH\x10\x00\x12\n\n\x06\x46RENCH\x10\x01\x12\n\n\x06GERMAN\x10\x02\x12\x0b\n\x07ITALIAN\x10\x03\x12\x0e\n\nPORTUGUESE\x10\x04\x12\x0b\n\x07SPANISH\x10\x05\x12\x0b\n\x07SWEDISH\x10\x06\x12\x0b\n\x07\x46INNISH\x10\x07\x12\n\n\x06POLISH\x10\x08\x12\x0b\n\x07TURKISH\x10\t\x12\x0b\n\x07SERBIAN\x10\n\x12\x0b\n\x07RUSSIAN\x10\x0b\x12\t\n\x05\x44UTCH\x10\x0c\x12\t\n\x05GREEK\x10\r\x12\r\n\tNORWEGIAN\x10\x0e\x12\x16\n\x12SIMPLIFIED_CHINESE\x10\x1e\x12\x17\n\x13TRADITIONAL_CHINESE\x10\x1f\x42\x63\n\x13\x63om.geeksville.meshB\x0e\x44\x65viceUIProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n#meshtastic/protobuf/device_ui.proto\x12\x13meshtastic.protobuf\"\xbf\x03\n\x0e\x44\x65viceUIConfig\x12\x0f\n\x07version\x18\x01 \x01(\r\x12\x19\n\x11screen_brightness\x18\x02 \x01(\r\x12\x16\n\x0escreen_timeout\x18\x03 \x01(\r\x12\x13\n\x0bscreen_lock\x18\x04 \x01(\x08\x12\x15\n\rsettings_lock\x18\x05 \x01(\x08\x12\x10\n\x08pin_code\x18\x06 \x01(\r\x12)\n\x05theme\x18\x07 \x01(\x0e\x32\x1a.meshtastic.protobuf.Theme\x12\x15\n\ralert_enabled\x18\x08 \x01(\x08\x12\x16\n\x0e\x62\x61nner_enabled\x18\t \x01(\x08\x12\x14\n\x0cring_tone_id\x18\n \x01(\r\x12/\n\x08language\x18\x0b \x01(\x0e\x32\x1d.meshtastic.protobuf.Language\x12\x34\n\x0bnode_filter\x18\x0c \x01(\x0b\x32\x1f.meshtastic.protobuf.NodeFilter\x12:\n\x0enode_highlight\x18\r \x01(\x0b\x32\".meshtastic.protobuf.NodeHighlight\x12\x18\n\x10\x63\x61libration_data\x18\x0e \x01(\x0c\"\xa7\x01\n\nNodeFilter\x12\x16\n\x0eunknown_switch\x18\x01 \x01(\x08\x12\x16\n\x0eoffline_switch\x18\x02 \x01(\x08\x12\x19\n\x11public_key_switch\x18\x03 \x01(\x08\x12\x11\n\thops_away\x18\x04 \x01(\x05\x12\x17\n\x0fposition_switch\x18\x05 \x01(\x08\x12\x11\n\tnode_name\x18\x06 \x01(\t\x12\x0f\n\x07\x63hannel\x18\x07 \x01(\x05\"~\n\rNodeHighlight\x12\x13\n\x0b\x63hat_switch\x18\x01 \x01(\x08\x12\x17\n\x0fposition_switch\x18\x02 \x01(\x08\x12\x18\n\x10telemetry_switch\x18\x03 \x01(\x08\x12\x12\n\niaq_switch\x18\x04 \x01(\x08\x12\x11\n\tnode_name\x18\x05 \x01(\t*%\n\x05Theme\x12\x08\n\x04\x44\x41RK\x10\x00\x12\t\n\x05LIGHT\x10\x01\x12\x07\n\x03RED\x10\x02*\x8b\x02\n\x08Language\x12\x0b\n\x07\x45NGLISH\x10\x00\x12\n\n\x06\x46RENCH\x10\x01\x12\n\n\x06GERMAN\x10\x02\x12\x0b\n\x07ITALIAN\x10\x03\x12\x0e\n\nPORTUGUESE\x10\x04\x12\x0b\n\x07SPANISH\x10\x05\x12\x0b\n\x07SWEDISH\x10\x06\x12\x0b\n\x07\x46INNISH\x10\x07\x12\n\n\x06POLISH\x10\x08\x12\x0b\n\x07TURKISH\x10\t\x12\x0b\n\x07SERBIAN\x10\n\x12\x0b\n\x07RUSSIAN\x10\x0b\x12\t\n\x05\x44UTCH\x10\x0c\x12\t\n\x05GREEK\x10\r\x12\r\n\tNORWEGIAN\x10\x0e\x12\r\n\tSLOVENIAN\x10\x0f\x12\x16\n\x12SIMPLIFIED_CHINESE\x10\x1e\x12\x17\n\x13TRADITIONAL_CHINESE\x10\x1f\x42\x63\n\x13\x63om.geeksville.meshB\x0e\x44\x65viceUIProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
@@ -21,14 +21,14 @@ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.device_
if _descriptor._USE_C_DESCRIPTORS == False:
DESCRIPTOR._options = None
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\016DeviceUIProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
_globals['_THEME']._serialized_start=791
_globals['_THEME']._serialized_end=828
_globals['_LANGUAGE']._serialized_start=831
_globals['_LANGUAGE']._serialized_end=1083
_globals['_THEME']._serialized_start=808
_globals['_THEME']._serialized_end=845
_globals['_LANGUAGE']._serialized_start=848
_globals['_LANGUAGE']._serialized_end=1115
_globals['_DEVICEUICONFIG']._serialized_start=61
_globals['_DEVICEUICONFIG']._serialized_end=508
_globals['_NODEFILTER']._serialized_start=511
_globals['_NODEFILTER']._serialized_end=661
_globals['_NODEHIGHLIGHT']._serialized_start=663
_globals['_NODEHIGHLIGHT']._serialized_end=789
_globals['_NODEFILTER']._serialized_end=678
_globals['_NODEHIGHLIGHT']._serialized_start=680
_globals['_NODEHIGHLIGHT']._serialized_end=806
# @@protoc_insertion_point(module_scope)

View File

@@ -118,6 +118,10 @@ class _LanguageEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumT
"""
Norwegian
"""
SLOVENIAN: _Language.ValueType # 15
"""
Slovenian
"""
SIMPLIFIED_CHINESE: _Language.ValueType # 30
"""
Simplified Chinese (experimental)
@@ -192,6 +196,10 @@ NORWEGIAN: Language.ValueType # 14
"""
Norwegian
"""
SLOVENIAN: Language.ValueType # 15
"""
Slovenian
"""
SIMPLIFIED_CHINESE: Language.ValueType # 30
"""
Simplified Chinese (experimental)
@@ -305,6 +313,7 @@ class NodeFilter(google.protobuf.message.Message):
HOPS_AWAY_FIELD_NUMBER: builtins.int
POSITION_SWITCH_FIELD_NUMBER: builtins.int
NODE_NAME_FIELD_NUMBER: builtins.int
CHANNEL_FIELD_NUMBER: builtins.int
unknown_switch: builtins.bool
"""
Filter unknown nodes
@@ -329,6 +338,10 @@ class NodeFilter(google.protobuf.message.Message):
"""
Filter nodes by matching name string
"""
channel: builtins.int
"""
Filter based on channel
"""
def __init__(
self,
*,
@@ -338,8 +351,9 @@ class NodeFilter(google.protobuf.message.Message):
hops_away: builtins.int = ...,
position_switch: builtins.bool = ...,
node_name: builtins.str = ...,
channel: builtins.int = ...,
) -> None: ...
def ClearField(self, field_name: typing.Literal["hops_away", b"hops_away", "node_name", b"node_name", "offline_switch", b"offline_switch", "position_switch", b"position_switch", "public_key_switch", b"public_key_switch", "unknown_switch", b"unknown_switch"]) -> None: ...
def ClearField(self, field_name: typing.Literal["channel", b"channel", "hops_away", b"hops_away", "node_name", b"node_name", "offline_switch", b"offline_switch", "position_switch", b"position_switch", "public_key_switch", b"public_key_switch", "unknown_switch", b"unknown_switch"]) -> None: ...
global___NodeFilter = NodeFilter

View File

File diff suppressed because one or more lines are too long

View File

@@ -397,6 +397,11 @@ class _HardwareModelEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._
Mesh-Tab, esp32 based
https://github.com/valzzu/Mesh-Tab
"""
MESHLINK: _HardwareModel.ValueType # 87
"""
MeshLink board developed by LoraItalia. NRF52840, eByte E22900M22S (Will also come with other frequencies), 25w MPPT solar charger (5v,12v,18v selectable), support for gps, buzzer, oled or e-ink display, 10 gpios, hardware watchdog
https://www.loraitalia.it
"""
PRIVATE_HW: _HardwareModel.ValueType # 255
"""
------------------------------------------------------------------------------------------------------------------------------------------
@@ -777,6 +782,11 @@ MESH_TAB: HardwareModel.ValueType # 86
Mesh-Tab, esp32 based
https://github.com/valzzu/Mesh-Tab
"""
MESHLINK: HardwareModel.ValueType # 87
"""
MeshLink board developed by LoraItalia. NRF52840, eByte E22900M22S (Will also come with other frequencies), 25w MPPT solar charger (5v,12v,18v selectable), support for gps, buzzer, oled or e-ink display, 10 gpios, hardware watchdog
https://www.loraitalia.it
"""
PRIVATE_HW: HardwareModel.ValueType # 255
"""
------------------------------------------------------------------------------------------------------------------------------------------
@@ -2083,6 +2093,7 @@ class MeshPacket(google.protobuf.message.Message):
PKI_ENCRYPTED_FIELD_NUMBER: builtins.int
NEXT_HOP_FIELD_NUMBER: builtins.int
RELAY_NODE_FIELD_NUMBER: builtins.int
TX_AFTER_FIELD_NUMBER: builtins.int
to: builtins.int
"""
The (immediate) destination for this packet
@@ -2184,6 +2195,12 @@ class MeshPacket(google.protobuf.message.Message):
Last byte of the node number of the node that will relay/relayed this packet.
Set by the firmware internally, clients are not supposed to set this.
"""
tx_after: builtins.int
"""
*Never* sent over the radio links.
Timestamp after which this packet may be sent.
Set by the firmware internally, clients are not supposed to set this.
"""
@property
def decoded(self) -> global___Data:
"""
@@ -2211,9 +2228,10 @@ class MeshPacket(google.protobuf.message.Message):
pki_encrypted: builtins.bool = ...,
next_hop: builtins.int = ...,
relay_node: builtins.int = ...,
tx_after: builtins.int = ...,
) -> None: ...
def HasField(self, field_name: typing.Literal["decoded", b"decoded", "encrypted", b"encrypted", "payload_variant", b"payload_variant"]) -> builtins.bool: ...
def ClearField(self, field_name: typing.Literal["channel", b"channel", "decoded", b"decoded", "delayed", b"delayed", "encrypted", b"encrypted", "from", b"from", "hop_limit", b"hop_limit", "hop_start", b"hop_start", "id", b"id", "next_hop", b"next_hop", "payload_variant", b"payload_variant", "pki_encrypted", b"pki_encrypted", "priority", b"priority", "public_key", b"public_key", "relay_node", b"relay_node", "rx_rssi", b"rx_rssi", "rx_snr", b"rx_snr", "rx_time", b"rx_time", "to", b"to", "via_mqtt", b"via_mqtt", "want_ack", b"want_ack"]) -> None: ...
def ClearField(self, field_name: typing.Literal["channel", b"channel", "decoded", b"decoded", "delayed", b"delayed", "encrypted", b"encrypted", "from", b"from", "hop_limit", b"hop_limit", "hop_start", b"hop_start", "id", b"id", "next_hop", b"next_hop", "payload_variant", b"payload_variant", "pki_encrypted", b"pki_encrypted", "priority", b"priority", "public_key", b"public_key", "relay_node", b"relay_node", "rx_rssi", b"rx_rssi", "rx_snr", b"rx_snr", "rx_time", b"rx_time", "to", b"to", "tx_after", b"tx_after", "via_mqtt", b"via_mqtt", "want_ack", b"want_ack"]) -> None: ...
def WhichOneof(self, oneof_group: typing.Literal["payload_variant", b"payload_variant"]) -> typing.Literal["decoded", "encrypted"] | None: ...
global___MeshPacket = MeshPacket

View File

File diff suppressed because one or more lines are too long

View File

@@ -163,6 +163,10 @@ class _TelemetrySensorTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wra
"""
High accuracy current and voltage
"""
DFROBOT_RAIN: _TelemetrySensorType.ValueType # 35
"""
DFRobot Gravity tipping bucket rain gauge
"""
class TelemetrySensorType(_TelemetrySensorType, metaclass=_TelemetrySensorTypeEnumTypeWrapper):
"""
@@ -309,6 +313,10 @@ INA226: TelemetrySensorType.ValueType # 34
"""
High accuracy current and voltage
"""
DFROBOT_RAIN: TelemetrySensorType.ValueType # 35
"""
DFRobot Gravity tipping bucket rain gauge
"""
global___TelemetrySensorType = TelemetrySensorType
@typing.final
@@ -394,6 +402,8 @@ class EnvironmentMetrics(google.protobuf.message.Message):
WIND_GUST_FIELD_NUMBER: builtins.int
WIND_LULL_FIELD_NUMBER: builtins.int
RADIATION_FIELD_NUMBER: builtins.int
RAINFALL_1H_FIELD_NUMBER: builtins.int
RAINFALL_24H_FIELD_NUMBER: builtins.int
temperature: builtins.float
"""
Temperature measured
@@ -468,6 +478,14 @@ class EnvironmentMetrics(google.protobuf.message.Message):
"""
Radiation in µR/h
"""
rainfall_1h: builtins.float
"""
Rainfall in the last hour in mm
"""
rainfall_24h: builtins.float
"""
Rainfall in the last 24 hours in mm
"""
def __init__(
self,
*,
@@ -489,9 +507,11 @@ class EnvironmentMetrics(google.protobuf.message.Message):
wind_gust: builtins.float | None = ...,
wind_lull: builtins.float | None = ...,
radiation: builtins.float | None = ...,
rainfall_1h: builtins.float | None = ...,
rainfall_24h: builtins.float | None = ...,
) -> None: ...
def HasField(self, field_name: typing.Literal["_barometric_pressure", b"_barometric_pressure", "_current", b"_current", "_distance", b"_distance", "_gas_resistance", b"_gas_resistance", "_iaq", b"_iaq", "_ir_lux", b"_ir_lux", "_lux", b"_lux", "_radiation", b"_radiation", "_relative_humidity", b"_relative_humidity", "_temperature", b"_temperature", "_uv_lux", b"_uv_lux", "_voltage", b"_voltage", "_weight", b"_weight", "_white_lux", b"_white_lux", "_wind_direction", b"_wind_direction", "_wind_gust", b"_wind_gust", "_wind_lull", b"_wind_lull", "_wind_speed", b"_wind_speed", "barometric_pressure", b"barometric_pressure", "current", b"current", "distance", b"distance", "gas_resistance", b"gas_resistance", "iaq", b"iaq", "ir_lux", b"ir_lux", "lux", b"lux", "radiation", b"radiation", "relative_humidity", b"relative_humidity", "temperature", b"temperature", "uv_lux", b"uv_lux", "voltage", b"voltage", "weight", b"weight", "white_lux", b"white_lux", "wind_direction", b"wind_direction", "wind_gust", b"wind_gust", "wind_lull", b"wind_lull", "wind_speed", b"wind_speed"]) -> builtins.bool: ...
def ClearField(self, field_name: typing.Literal["_barometric_pressure", b"_barometric_pressure", "_current", b"_current", "_distance", b"_distance", "_gas_resistance", b"_gas_resistance", "_iaq", b"_iaq", "_ir_lux", b"_ir_lux", "_lux", b"_lux", "_radiation", b"_radiation", "_relative_humidity", b"_relative_humidity", "_temperature", b"_temperature", "_uv_lux", b"_uv_lux", "_voltage", b"_voltage", "_weight", b"_weight", "_white_lux", b"_white_lux", "_wind_direction", b"_wind_direction", "_wind_gust", b"_wind_gust", "_wind_lull", b"_wind_lull", "_wind_speed", b"_wind_speed", "barometric_pressure", b"barometric_pressure", "current", b"current", "distance", b"distance", "gas_resistance", b"gas_resistance", "iaq", b"iaq", "ir_lux", b"ir_lux", "lux", b"lux", "radiation", b"radiation", "relative_humidity", b"relative_humidity", "temperature", b"temperature", "uv_lux", b"uv_lux", "voltage", b"voltage", "weight", b"weight", "white_lux", b"white_lux", "wind_direction", b"wind_direction", "wind_gust", b"wind_gust", "wind_lull", b"wind_lull", "wind_speed", b"wind_speed"]) -> None: ...
def HasField(self, field_name: typing.Literal["_barometric_pressure", b"_barometric_pressure", "_current", b"_current", "_distance", b"_distance", "_gas_resistance", b"_gas_resistance", "_iaq", b"_iaq", "_ir_lux", b"_ir_lux", "_lux", b"_lux", "_radiation", b"_radiation", "_rainfall_1h", b"_rainfall_1h", "_rainfall_24h", b"_rainfall_24h", "_relative_humidity", b"_relative_humidity", "_temperature", b"_temperature", "_uv_lux", b"_uv_lux", "_voltage", b"_voltage", "_weight", b"_weight", "_white_lux", b"_white_lux", "_wind_direction", b"_wind_direction", "_wind_gust", b"_wind_gust", "_wind_lull", b"_wind_lull", "_wind_speed", b"_wind_speed", "barometric_pressure", b"barometric_pressure", "current", b"current", "distance", b"distance", "gas_resistance", b"gas_resistance", "iaq", b"iaq", "ir_lux", b"ir_lux", "lux", b"lux", "radiation", b"radiation", "rainfall_1h", b"rainfall_1h", "rainfall_24h", b"rainfall_24h", "relative_humidity", b"relative_humidity", "temperature", b"temperature", "uv_lux", b"uv_lux", "voltage", b"voltage", "weight", b"weight", "white_lux", b"white_lux", "wind_direction", b"wind_direction", "wind_gust", b"wind_gust", "wind_lull", b"wind_lull", "wind_speed", b"wind_speed"]) -> builtins.bool: ...
def ClearField(self, field_name: typing.Literal["_barometric_pressure", b"_barometric_pressure", "_current", b"_current", "_distance", b"_distance", "_gas_resistance", b"_gas_resistance", "_iaq", b"_iaq", "_ir_lux", b"_ir_lux", "_lux", b"_lux", "_radiation", b"_radiation", "_rainfall_1h", b"_rainfall_1h", "_rainfall_24h", b"_rainfall_24h", "_relative_humidity", b"_relative_humidity", "_temperature", b"_temperature", "_uv_lux", b"_uv_lux", "_voltage", b"_voltage", "_weight", b"_weight", "_white_lux", b"_white_lux", "_wind_direction", b"_wind_direction", "_wind_gust", b"_wind_gust", "_wind_lull", b"_wind_lull", "_wind_speed", b"_wind_speed", "barometric_pressure", b"barometric_pressure", "current", b"current", "distance", b"distance", "gas_resistance", b"gas_resistance", "iaq", b"iaq", "ir_lux", b"ir_lux", "lux", b"lux", "radiation", b"radiation", "rainfall_1h", b"rainfall_1h", "rainfall_24h", b"rainfall_24h", "relative_humidity", b"relative_humidity", "temperature", b"temperature", "uv_lux", b"uv_lux", "voltage", b"voltage", "weight", b"weight", "white_lux", b"white_lux", "wind_direction", b"wind_direction", "wind_gust", b"wind_gust", "wind_lull", b"wind_lull", "wind_speed", b"wind_speed"]) -> None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_barometric_pressure", b"_barometric_pressure"]) -> typing.Literal["barometric_pressure"] | None: ...
@typing.overload
@@ -509,6 +529,10 @@ class EnvironmentMetrics(google.protobuf.message.Message):
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_radiation", b"_radiation"]) -> typing.Literal["radiation"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_rainfall_1h", b"_rainfall_1h"]) -> typing.Literal["rainfall_1h"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_rainfall_24h", b"_rainfall_24h"]) -> typing.Literal["rainfall_24h"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_relative_humidity", b"_relative_humidity"]) -> typing.Literal["relative_humidity"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_temperature", b"_temperature"]) -> typing.Literal["temperature"] | None: ...

View File

@@ -1608,7 +1608,7 @@ def test_main_onReceive_empty(caplog, capsys):
assert re.search(r"in onReceive", caplog.text, re.MULTILINE)
out, err = capsys.readouterr()
assert re.search(
r"Warning: There is no field 'to' in the packet.", out, re.MULTILINE
r"Warning: Error processing received packet: 'to'.", out, re.MULTILINE
)
assert err == ""

View File

@@ -254,6 +254,16 @@ class Timeout:
time.sleep(self.sleepInterval)
return False
def waitForWaypoint(self, acknowledgment) -> bool:
"""Block until waypoint response is received. Returns True if waypoint response has been received."""
self.reset()
while time.time() < self.expireTime:
if getattr(acknowledgment, "receivedWaypoint", None):
acknowledgment.reset()
return True
time.sleep(self.sleepInterval)
return False
class Acknowledgment:
"A class that records which type of acknowledgment was just received, if any."
@@ -265,6 +275,7 @@ class Acknowledgment:
self.receivedTraceRoute = False
self.receivedTelemetry = False
self.receivedPosition = False
self.receivedWaypoint = False
def reset(self) -> None:
"""reset"""
@@ -274,6 +285,7 @@ class Acknowledgment:
self.receivedTraceRoute = False
self.receivedTelemetry = False
self.receivedPosition = False
self.receivedWaypoint = False
class DeferredExecution:

8
poetry.lock generated
View File

@@ -1,4 +1,4 @@
# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand.
# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand.
[[package]]
name = "altgraph"
@@ -1459,13 +1459,13 @@ testing = ["Django", "attrs", "colorama", "docopt", "pytest (<9.0.0)"]
[[package]]
name = "jinja2"
version = "3.1.4"
version = "3.1.5"
description = "A very fast and expressive template engine."
optional = false
python-versions = ">=3.7"
files = [
{file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"},
{file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"},
{file = "jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb"},
{file = "jinja2-3.1.5.tar.gz", hash = "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb"},
]
[package.dependencies]

View File

@@ -1,6 +1,6 @@
[tool.poetry]
name = "meshtastic"
version = "2.5.7"
version = "2.5.11"
description = "Python API & client shell for talking to Meshtastic devices"
authors = ["Meshtastic Developers <contact@meshtastic.org>"]
license = "GPL-3.0-only"