mirror of
https://github.com/meshtastic/python.git
synced 2025-12-28 18:37:52 -05:00
Compare commits
11 Commits
2.6.2
...
key-verifi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
54ecb3feb8 | ||
|
|
0deb98b4c6 | ||
|
|
04a0ff6322 | ||
|
|
b4764d3bc3 | ||
|
|
9281c4a335 | ||
|
|
3c2dd6f4ff | ||
|
|
8e48d141c8 | ||
|
|
622a435465 | ||
|
|
56680f8da6 | ||
|
|
321a960c13 | ||
|
|
4668852b0b |
@@ -472,6 +472,21 @@ def onConnected(interface):
|
|||||||
waitForAckNak = True
|
waitForAckNak = True
|
||||||
interface.getNode(args.dest, False, **getNode_kwargs).removeIgnored(args.remove_ignored_node)
|
interface.getNode(args.dest, False, **getNode_kwargs).removeIgnored(args.remove_ignored_node)
|
||||||
|
|
||||||
|
if args.key_verification:
|
||||||
|
closeNow = True
|
||||||
|
waitForAckNak = True
|
||||||
|
interface.getNode(args.dest, False, **getNode_kwargs).keyVerification(args.key_verification)
|
||||||
|
|
||||||
|
if args.key_verification_number:
|
||||||
|
closeNow = True
|
||||||
|
waitForAckNak = True
|
||||||
|
interface.getNode(args.dest, False, **getNode_kwargs).keyVerificationNumber(args.key_verification_number, args.key_verification_nonce)
|
||||||
|
|
||||||
|
if args.key_verification_confirm:
|
||||||
|
closeNow = True
|
||||||
|
waitForAckNak = True
|
||||||
|
interface.getNode(args.dest, False, **getNode_kwargs).keyVerificationConfirm(args.key_verification_confirm)
|
||||||
|
|
||||||
if args.reset_nodedb:
|
if args.reset_nodedb:
|
||||||
closeNow = True
|
closeNow = True
|
||||||
waitForAckNak = True
|
waitForAckNak = True
|
||||||
@@ -647,6 +662,7 @@ def onConnected(interface):
|
|||||||
print(f"Setting device owner to {configuration['owner']}")
|
print(f"Setting device owner to {configuration['owner']}")
|
||||||
waitForAckNak = True
|
waitForAckNak = True
|
||||||
interface.getNode(args.dest, False, **getNode_kwargs).setOwner(configuration["owner"])
|
interface.getNode(args.dest, False, **getNode_kwargs).setOwner(configuration["owner"])
|
||||||
|
time.sleep(0.5)
|
||||||
|
|
||||||
if "owner_short" in configuration:
|
if "owner_short" in configuration:
|
||||||
print(
|
print(
|
||||||
@@ -656,6 +672,7 @@ def onConnected(interface):
|
|||||||
interface.getNode(args.dest, False, **getNode_kwargs).setOwner(
|
interface.getNode(args.dest, False, **getNode_kwargs).setOwner(
|
||||||
long_name=None, short_name=configuration["owner_short"]
|
long_name=None, short_name=configuration["owner_short"]
|
||||||
)
|
)
|
||||||
|
time.sleep(0.5)
|
||||||
|
|
||||||
if "ownerShort" in configuration:
|
if "ownerShort" in configuration:
|
||||||
print(
|
print(
|
||||||
@@ -665,14 +682,17 @@ def onConnected(interface):
|
|||||||
interface.getNode(args.dest, False, **getNode_kwargs).setOwner(
|
interface.getNode(args.dest, False, **getNode_kwargs).setOwner(
|
||||||
long_name=None, short_name=configuration["ownerShort"]
|
long_name=None, short_name=configuration["ownerShort"]
|
||||||
)
|
)
|
||||||
|
time.sleep(0.5)
|
||||||
|
|
||||||
if "channel_url" in configuration:
|
if "channel_url" in configuration:
|
||||||
print("Setting channel url to", configuration["channel_url"])
|
print("Setting channel url to", configuration["channel_url"])
|
||||||
interface.getNode(args.dest, **getNode_kwargs).setURL(configuration["channel_url"])
|
interface.getNode(args.dest, **getNode_kwargs).setURL(configuration["channel_url"])
|
||||||
|
time.sleep(0.5)
|
||||||
|
|
||||||
if "channelUrl" in configuration:
|
if "channelUrl" in configuration:
|
||||||
print("Setting channel url to", configuration["channelUrl"])
|
print("Setting channel url to", configuration["channelUrl"])
|
||||||
interface.getNode(args.dest, **getNode_kwargs).setURL(configuration["channelUrl"])
|
interface.getNode(args.dest, **getNode_kwargs).setURL(configuration["channelUrl"])
|
||||||
|
time.sleep(0.5)
|
||||||
|
|
||||||
if "location" in configuration:
|
if "location" in configuration:
|
||||||
alt = 0
|
alt = 0
|
||||||
@@ -691,6 +711,7 @@ def onConnected(interface):
|
|||||||
print(f"Fixing longitude at {lon} degrees")
|
print(f"Fixing longitude at {lon} degrees")
|
||||||
print("Setting device position")
|
print("Setting device position")
|
||||||
interface.localNode.setFixedPosition(lat, lon, alt)
|
interface.localNode.setFixedPosition(lat, lon, alt)
|
||||||
|
time.sleep(0.5)
|
||||||
|
|
||||||
if "config" in configuration:
|
if "config" in configuration:
|
||||||
localConfig = interface.getNode(args.dest, **getNode_kwargs).localConfig
|
localConfig = interface.getNode(args.dest, **getNode_kwargs).localConfig
|
||||||
@@ -701,6 +722,7 @@ def onConnected(interface):
|
|||||||
interface.getNode(args.dest, **getNode_kwargs).writeConfig(
|
interface.getNode(args.dest, **getNode_kwargs).writeConfig(
|
||||||
meshtastic.util.camel_to_snake(section)
|
meshtastic.util.camel_to_snake(section)
|
||||||
)
|
)
|
||||||
|
time.sleep(0.5)
|
||||||
|
|
||||||
if "module_config" in configuration:
|
if "module_config" in configuration:
|
||||||
moduleConfig = interface.getNode(args.dest, **getNode_kwargs).moduleConfig
|
moduleConfig = interface.getNode(args.dest, **getNode_kwargs).moduleConfig
|
||||||
@@ -713,6 +735,7 @@ def onConnected(interface):
|
|||||||
interface.getNode(args.dest, **getNode_kwargs).writeConfig(
|
interface.getNode(args.dest, **getNode_kwargs).writeConfig(
|
||||||
meshtastic.util.camel_to_snake(section)
|
meshtastic.util.camel_to_snake(section)
|
||||||
)
|
)
|
||||||
|
time.sleep(0.5)
|
||||||
|
|
||||||
interface.getNode(args.dest, False, **getNode_kwargs).commitSettingsTransaction()
|
interface.getNode(args.dest, False, **getNode_kwargs).commitSettingsTransaction()
|
||||||
print("Writing modified configuration to device")
|
print("Writing modified configuration to device")
|
||||||
@@ -1824,6 +1847,22 @@ def addRemoteAdminArgs(parser: argparse.ArgumentParser) -> argparse.ArgumentPars
|
|||||||
"Use the node ID with a '!' or '0x' prefix or the node number.",
|
"Use the node ID with a '!' or '0x' prefix or the node number.",
|
||||||
metavar="!xxxxxxxx"
|
metavar="!xxxxxxxx"
|
||||||
)
|
)
|
||||||
|
group.add_argument(
|
||||||
|
"--key-verification",
|
||||||
|
help="start key verification. "
|
||||||
|
"Use the node ID with a '!' or '0x' prefix or the node number.",
|
||||||
|
metavar="!xxxxxxxx"
|
||||||
|
)
|
||||||
|
group.add_argument(
|
||||||
|
"--key-verification-number",
|
||||||
|
help="start key verification. "
|
||||||
|
"Use the node ID with a '!' or '0x' prefix or the node number.",
|
||||||
|
)
|
||||||
|
group.add_argument(
|
||||||
|
"--key-verification-confirm",
|
||||||
|
help="start key verification. "
|
||||||
|
"Use the node ID with a '!' or '0x' prefix or the node number.",
|
||||||
|
)
|
||||||
group.add_argument(
|
group.add_argument(
|
||||||
"--reset-nodedb",
|
"--reset-nodedb",
|
||||||
help="Tell the destination node to clear its list of nodes",
|
help="Tell the destination node to clear its list of nodes",
|
||||||
@@ -1953,6 +1992,12 @@ def initParser():
|
|||||||
action="store_true",
|
action="store_true",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
group.add_argument(
|
||||||
|
"--key-verification-nonce",
|
||||||
|
help="start key verification. "
|
||||||
|
"Use the node ID with a '!' or '0x' prefix or the node number.",
|
||||||
|
)
|
||||||
|
|
||||||
power_group = parser.add_argument_group(
|
power_group = parser.add_argument_group(
|
||||||
"Power Testing", "Options for power testing/logging."
|
"Power Testing", "Options for power testing/logging."
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -83,6 +83,17 @@ class BLEInterface(MeshInterface):
|
|||||||
# Note: the on disconnected callback will call our self.close which will make us nicely wait for threads to exit
|
# Note: the on disconnected callback will call our self.close which will make us nicely wait for threads to exit
|
||||||
self._exit_handler = atexit.register(self.client.disconnect)
|
self._exit_handler = atexit.register(self.client.disconnect)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
rep = f"BLEInterface(address={self.client.address if self.client else None!r}"
|
||||||
|
if self.debugOut is not None:
|
||||||
|
rep += f", debugOut={self.debugOut!r}"
|
||||||
|
if self.noProto:
|
||||||
|
rep += ", noProto=True"
|
||||||
|
if self.noNodes:
|
||||||
|
rep += ", noNodes=True"
|
||||||
|
rep += ")"
|
||||||
|
return rep
|
||||||
|
|
||||||
def from_num_handler(self, _, b: bytes) -> None: # pylint: disable=C0116
|
def from_num_handler(self, _, b: bytes) -> None: # pylint: disable=C0116
|
||||||
"""Handle callbacks for fromnum notify.
|
"""Handle callbacks for fromnum notify.
|
||||||
Note: this method does not need to be async because it is just setting a bool.
|
Note: this method does not need to be async because it is just setting a bool.
|
||||||
@@ -163,7 +174,7 @@ class BLEInterface(MeshInterface):
|
|||||||
|
|
||||||
# Bleak docs recommend always doing a scan before connecting (even if we know addr)
|
# Bleak docs recommend always doing a scan before connecting (even if we know addr)
|
||||||
device = self.find_device(address)
|
device = self.find_device(address)
|
||||||
client = BLEClient(device.address, disconnected_callback=lambda _: self.close)
|
client = BLEClient(device.address, disconnected_callback=lambda _: self.close())
|
||||||
client.connect()
|
client.connect()
|
||||||
client.discover()
|
client.discover()
|
||||||
return client
|
return client
|
||||||
|
|||||||
@@ -42,6 +42,15 @@ class Node:
|
|||||||
|
|
||||||
self.gotResponse = None
|
self.gotResponse = None
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
r = f"Node({self.iface!r}, 0x{self.nodeNum:08x}"
|
||||||
|
if self.noProto:
|
||||||
|
r += ", noProto=True"
|
||||||
|
if self._timeout.expireTimeout != 300:
|
||||||
|
r += ", timeout={self._timeout.expireTimeout!r}"
|
||||||
|
r += ")"
|
||||||
|
return r
|
||||||
|
|
||||||
def showChannels(self):
|
def showChannels(self):
|
||||||
"""Show human readable description of our channels."""
|
"""Show human readable description of our channels."""
|
||||||
print("Channels:")
|
print("Channels:")
|
||||||
@@ -761,6 +770,49 @@ class Node:
|
|||||||
onResponse = self.onAckNak
|
onResponse = self.onAckNak
|
||||||
return self._sendAdmin(p, onResponse=onResponse)
|
return self._sendAdmin(p, onResponse=onResponse)
|
||||||
|
|
||||||
|
def keyVerification(self, nodeId: Union[int, str]):
|
||||||
|
if isinstance(nodeId, str):
|
||||||
|
if nodeId.startswith("!"):
|
||||||
|
nodeId = int(nodeId[1:], 16)
|
||||||
|
else:
|
||||||
|
nodeId = int(nodeId)
|
||||||
|
p = admin_pb2.KeyVerificationAdmin()
|
||||||
|
p.message_type = p.MessageType.INITIATE_VERIFICATION
|
||||||
|
p.remote_nodenum = nodeId
|
||||||
|
p.nonce = 0
|
||||||
|
a = admin_pb2.AdminMessage()
|
||||||
|
a.key_verification_admin.CopyFrom(p)
|
||||||
|
if self == self.iface.localNode:
|
||||||
|
onResponse = None
|
||||||
|
else:
|
||||||
|
onResponse = self.onAckNak
|
||||||
|
return self._sendAdmin(a, onResponse=onResponse)
|
||||||
|
def keyVerificationNumber(self, number: int, nonce: int,):
|
||||||
|
print(int(number))
|
||||||
|
print(int(nonce))
|
||||||
|
p = admin_pb2.KeyVerificationAdmin()
|
||||||
|
p.message_type = p.MessageType.PROVIDE_SECURITY_NUMBER
|
||||||
|
p.nonce = int(nonce)
|
||||||
|
p.security_number = int(number)
|
||||||
|
a = admin_pb2.AdminMessage()
|
||||||
|
a.key_verification_admin.CopyFrom(p)
|
||||||
|
if self == self.iface.localNode:
|
||||||
|
onResponse = None
|
||||||
|
else:
|
||||||
|
onResponse = self.onAckNak
|
||||||
|
return self._sendAdmin(a, onResponse=onResponse)
|
||||||
|
def keyVerificationConfirm(self, nonce: int,):
|
||||||
|
print(int(nonce))
|
||||||
|
p = admin_pb2.KeyVerificationAdmin()
|
||||||
|
p.message_type = p.MessageType.DO_VERIFY
|
||||||
|
p.nonce = int(nonce)
|
||||||
|
a = admin_pb2.AdminMessage()
|
||||||
|
a.key_verification_admin.CopyFrom(p)
|
||||||
|
if self == self.iface.localNode:
|
||||||
|
onResponse = None
|
||||||
|
else:
|
||||||
|
onResponse = self.onAckNak
|
||||||
|
return self._sendAdmin(a, onResponse=onResponse)
|
||||||
def resetNodeDb(self):
|
def resetNodeDb(self):
|
||||||
"""Tell the node to reset its list of nodes."""
|
"""Tell the node to reset its list of nodes."""
|
||||||
self.ensureSessionKey()
|
self.ensureSessionKey()
|
||||||
@@ -983,7 +1035,7 @@ class Node:
|
|||||||
p,
|
p,
|
||||||
self.nodeNum,
|
self.nodeNum,
|
||||||
portNum=portnums_pb2.PortNum.ADMIN_APP,
|
portNum=portnums_pb2.PortNum.ADMIN_APP,
|
||||||
wantAck=False,
|
wantAck=True,
|
||||||
wantResponse=wantResponse,
|
wantResponse=wantResponse,
|
||||||
onResponse=onResponse,
|
onResponse=onResponse,
|
||||||
channelIndex=adminIndex,
|
channelIndex=adminIndex,
|
||||||
|
|||||||
44
meshtastic/protobuf/module_config_pb2.py
generated
44
meshtastic/protobuf/module_config_pb2.py
generated
File diff suppressed because one or more lines are too long
8
meshtastic/protobuf/module_config_pb2.pyi
generated
8
meshtastic/protobuf/module_config_pb2.pyi
generated
@@ -554,6 +554,10 @@ class ModuleConfig(google.protobuf.message.Message):
|
|||||||
"""NMEA messages specifically tailored for CalTopo"""
|
"""NMEA messages specifically tailored for CalTopo"""
|
||||||
WS85: ModuleConfig.SerialConfig._Serial_Mode.ValueType # 6
|
WS85: ModuleConfig.SerialConfig._Serial_Mode.ValueType # 6
|
||||||
"""Ecowitt WS85 weather station"""
|
"""Ecowitt WS85 weather station"""
|
||||||
|
VE_DIRECT: ModuleConfig.SerialConfig._Serial_Mode.ValueType # 7
|
||||||
|
"""VE.Direct is a serial protocol used by Victron Energy products
|
||||||
|
https://beta.ivc.no/wiki/index.php/Victron_VE_Direct_DIY_Cable
|
||||||
|
"""
|
||||||
|
|
||||||
class Serial_Mode(_Serial_Mode, metaclass=_Serial_ModeEnumTypeWrapper):
|
class Serial_Mode(_Serial_Mode, metaclass=_Serial_ModeEnumTypeWrapper):
|
||||||
"""
|
"""
|
||||||
@@ -569,6 +573,10 @@ class ModuleConfig(google.protobuf.message.Message):
|
|||||||
"""NMEA messages specifically tailored for CalTopo"""
|
"""NMEA messages specifically tailored for CalTopo"""
|
||||||
WS85: ModuleConfig.SerialConfig.Serial_Mode.ValueType # 6
|
WS85: ModuleConfig.SerialConfig.Serial_Mode.ValueType # 6
|
||||||
"""Ecowitt WS85 weather station"""
|
"""Ecowitt WS85 weather station"""
|
||||||
|
VE_DIRECT: ModuleConfig.SerialConfig.Serial_Mode.ValueType # 7
|
||||||
|
"""VE.Direct is a serial protocol used by Victron Energy products
|
||||||
|
https://beta.ivc.no/wiki/index.php/Victron_VE_Direct_DIY_Cable
|
||||||
|
"""
|
||||||
|
|
||||||
ENABLED_FIELD_NUMBER: builtins.int
|
ENABLED_FIELD_NUMBER: builtins.int
|
||||||
ECHO_FIELD_NUMBER: builtins.int
|
ECHO_FIELD_NUMBER: builtins.int
|
||||||
|
|||||||
@@ -66,6 +66,17 @@ class SerialInterface(StreamInterface):
|
|||||||
self, debugOut=debugOut, noProto=noProto, connectNow=connectNow, noNodes=noNodes
|
self, debugOut=debugOut, noProto=noProto, connectNow=connectNow, noNodes=noNodes
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
rep = f"SerialInterface(devPath={self.devPath!r}"
|
||||||
|
if hasattr(self, 'debugOut') and self.debugOut is not None:
|
||||||
|
rep += f", debugOut={self.debugOut!r}"
|
||||||
|
if self.noProto:
|
||||||
|
rep += ", noProto=True"
|
||||||
|
if hasattr(self, 'noNodes') and self.noNodes:
|
||||||
|
rep += ", noNodes=True"
|
||||||
|
rep += ")"
|
||||||
|
return rep
|
||||||
|
|
||||||
def close(self) -> None:
|
def close(self) -> None:
|
||||||
"""Close a connection to the device"""
|
"""Close a connection to the device"""
|
||||||
if self.stream: # Stream can be null if we were already closed
|
if self.stream: # Stream can be null if we were already closed
|
||||||
|
|||||||
@@ -43,6 +43,21 @@ class TCPInterface(StreamInterface):
|
|||||||
|
|
||||||
super().__init__(debugOut=debugOut, noProto=noProto, connectNow=connectNow, noNodes=noNodes)
|
super().__init__(debugOut=debugOut, noProto=noProto, connectNow=connectNow, noNodes=noNodes)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
rep = f"TCPInterface({self.hostname!r}"
|
||||||
|
if self.debugOut is not None:
|
||||||
|
rep += f", debugOut={self.debugOut!r}"
|
||||||
|
if self.noProto:
|
||||||
|
rep += ", noProto=True"
|
||||||
|
if self.socket is None:
|
||||||
|
rep += ", connectNow=False"
|
||||||
|
if self.portNumber != DEFAULT_TCP_PORT:
|
||||||
|
rep += f", portNumber={self.portNumber!r}"
|
||||||
|
if self.noNodes:
|
||||||
|
rep += ", noNodes=True"
|
||||||
|
rep += ")"
|
||||||
|
return rep
|
||||||
|
|
||||||
def _socket_shutdown(self) -> None:
|
def _socket_shutdown(self) -> None:
|
||||||
"""Shutdown the socket.
|
"""Shutdown the socket.
|
||||||
Note: Broke out this line so the exception could be unit tested.
|
Note: Broke out this line so the exception could be unit tested.
|
||||||
|
|||||||
@@ -198,9 +198,9 @@ class Timeout:
|
|||||||
self.sleepInterval: float = 0.1
|
self.sleepInterval: float = 0.1
|
||||||
self.expireTimeout: int = maxSecs
|
self.expireTimeout: int = maxSecs
|
||||||
|
|
||||||
def reset(self) -> None:
|
def reset(self, expireTimeout=None):
|
||||||
"""Restart the waitForSet timer"""
|
"""Restart the waitForSet timer"""
|
||||||
self.expireTime = time.time() + self.expireTimeout
|
self.expireTime = time.time() + (self.expireTimeout if expireTimeout is None else expireTimeout)
|
||||||
|
|
||||||
def waitForSet(self, target, attrs=()) -> bool:
|
def waitForSet(self, target, attrs=()) -> bool:
|
||||||
"""Block until the specified attributes are set. Returns True if config has been received."""
|
"""Block until the specified attributes are set. Returns True if config has been received."""
|
||||||
@@ -225,8 +225,7 @@ class Timeout:
|
|||||||
|
|
||||||
def waitForTraceRoute(self, waitFactor, acknowledgment, attr="receivedTraceRoute") -> bool:
|
def waitForTraceRoute(self, waitFactor, acknowledgment, attr="receivedTraceRoute") -> bool:
|
||||||
"""Block until traceroute response is received. Returns True if traceroute response has been received."""
|
"""Block until traceroute response is received. Returns True if traceroute response has been received."""
|
||||||
self.expireTimeout *= waitFactor
|
self.reset(self.expireTimeout * waitFactor)
|
||||||
self.reset()
|
|
||||||
while time.time() < self.expireTime:
|
while time.time() < self.expireTime:
|
||||||
if getattr(acknowledgment, attr, None):
|
if getattr(acknowledgment, attr, None):
|
||||||
acknowledgment.reset()
|
acknowledgment.reset()
|
||||||
|
|||||||
Submodule protobufs updated: 27fac39141...078ac8dfbe
@@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "meshtastic"
|
name = "meshtastic"
|
||||||
version = "2.6.2"
|
version = "2.6.3"
|
||||||
description = "Python API & client shell for talking to Meshtastic devices"
|
description = "Python API & client shell for talking to Meshtastic devices"
|
||||||
authors = ["Meshtastic Developers <contact@meshtastic.org>"]
|
authors = ["Meshtastic Developers <contact@meshtastic.org>"]
|
||||||
license = "GPL-3.0-only"
|
license = "GPL-3.0-only"
|
||||||
|
|||||||
Reference in New Issue
Block a user