Compare commits

..

27 Commits
2.5.1 ... 2.5.3

Author SHA1 Message Date
github-actions
f08ec1885b bump version to 2.5.3 2024-10-18 17:02:53 +00:00
Ian McEwen
feca49faed Merge pull request #696 from fmoessbauer/master
fix base64 encoding of key field in config
2024-10-18 09:57:13 -07:00
Felix Moessbauer
dfaf1a275d fix base64 encoding of key field in config
The security.privateKey and security.publicKey fields are of type bytes,
but the protobuf MessageToDict converts them to base64 encoded strings.
When importing the config again, this is read as a string, which breaks
the import. Instead, the value needs to be prefixed with "base64:", so
the type handling logic on import kicks in and decodes the value to a
bytes array again.

Fixes: #678
2024-10-18 16:13:25 +02:00
Ian McEwen
da7fa31805 tweak documentation formatting 2024-10-16 20:52:10 -07:00
Ian McEwen
12fd29b203 Merge pull request #694 from ianmcorvidae/configure-fixed-position
Use dedicated fixed position admin message for --configure
2024-10-16 16:01:06 -07:00
Ian McEwen
a43dd201ba Merge pull request #695 from ianmcorvidae/ble-disconnect
Send the meshtastic.connection.lost message from BLEInterface's close method
2024-10-16 16:00:49 -07:00
Ian McEwen
a64a9d203a Send the meshtastic.connection.lost message from BLEInterface's close method 2024-10-16 12:13:30 -07:00
Ian McEwen
10136962d7 Use dedicated fixed position admin message for --configure 2024-10-15 07:28:55 -07:00
Ian McEwen
3afb294f9b Merge pull request #691 from logikal/telemetry_output
autoprint other types of telemetry when returned from --request-telemetry
2024-10-14 17:11:00 -07:00
Sean Kilgore
ee405fec41 autoprint other types of telemetry when returned from --request-telemetry 2024-10-14 16:58:52 -07:00
Ian McEwen
3eabaf91d0 Merge pull request #687 from ianmcorvidae/telemetry-types
Support requesting different telemetry types
2024-10-13 21:50:04 -07:00
Ian McEwen
78b92cecc9 fix type check 2024-10-13 20:40:22 -07:00
Ian McEwen
7088b90514 Support requesting different telemetry types 2024-10-13 20:35:11 -07:00
Ian McEwen
2ae81f8602 Merge pull request #686 from jose1711/typofix
Fix typo.
2024-10-13 18:00:16 -07:00
Jose Riha
923f5e82d0 Fix typo. 2024-10-14 02:48:41 +02:00
Ian McEwen
05731128fa missed a spot 2024-10-12 12:52:36 -07:00
Ian McEwen
0523d4c94f disable R0917 pylint failures 2024-10-12 12:49:14 -07:00
Ian McEwen
90e901de79 Upgrade bleak and therefore also the supported python versions 2024-10-12 12:32:43 -07:00
Ian McEwen
6606851135 Merge pull request #685 from ianmcorvidae/more-telemetry
Add other telemetry variants to automatic handling/adding to node information
2024-10-12 11:59:45 -07:00
Ian McEwen
33fecbd74d Add other telemetry variants to automatic handling/adding to node information 2024-10-12 11:56:55 -07:00
Ian McEwen
6b9db7abd9 2.5.3 setup 2024-10-12 09:33:28 -07:00
github-actions
ece6286d82 bump version to 2.5.2 2024-10-12 16:31:48 +00:00
Ian McEwen
0bb4b31b6a Merge pull request #679 from ianmcorvidae/argument-groups
Add a number of argument groups to organize the help output
2024-10-03 19:45:13 -07:00
Ian McEwen
915066e0af add metavars for a bunch of arguments for nicer docs 2024-10-03 19:45:03 -07:00
Ian McEwen
6be3969577 Add a number of argument groups to organize the help output 2024-10-01 18:10:14 -07:00
Ian McEwen
b73cc1f499 Make it so wantconfig isn't a 1 in 4294967296 lottery for getting no nodes 2024-10-01 14:00:06 -07:00
Ian McEwen
65305af184 protobufs/alpha version: 2.5.2 2024-09-29 14:34:53 -07:00
16 changed files with 2197 additions and 1764 deletions

View File

@@ -2,41 +2,44 @@
# A library for the Meshtastic Client API
Primary interfaces: SerialInterface, TCPInterface, BLEInterface
Install with pip: "[pip3 install meshtastic](https://pypi.org/project/meshtastic/)"
Source code on [github](https://github.com/meshtastic/python)
notable properties of interface classes:
- nodes - The database of received nodes. Includes always up-to-date location and username information for each
- `nodes` - The database of received nodes. Includes always up-to-date location and username information for each
node in the mesh. This is a read-only datastructure.
- nodesByNum - like "nodes" but keyed by nodeNum instead of nodeId
- myInfo & metadata - Contain read-only information about the local radio device (software version, hardware version, etc)
- localNode - Pointer to a node object for the local node
- `nodesByNum` - like "nodes" but keyed by nodeNum instead of nodeId. As such, includes "unknown" nodes which haven't seen a User packet yet
- `myInfo` & `metadata` - Contain read-only information about the local radio device (software version, hardware version, etc)
- `localNode` - Pointer to a node object for the local node
notable properties of nodes:
- localConfig - Current radio settings, can be written to the radio with the `writeConfig` method.
- moduleConfig - Current module settings, can be written to the radio with the `writeConfig` method.
- channels - The node's channels, keyed by index.
- `localConfig` - Current radio settings, can be written to the radio with the `writeConfig` method.
- `moduleConfig` - Current module settings, can be written to the radio with the `writeConfig` method.
- `channels` - The node's channels, keyed by index.
# Published PubSub topics
We use a [publish-subscribe](https://pypubsub.readthedocs.io/en/v4.0.3/) model to communicate asynchronous events. Available
topics:
- meshtastic.connection.established - published once we've successfully connected to the radio and downloaded the node DB
- meshtastic.connection.lost - published once we've lost our link to the radio
- meshtastic.receive.text(packet) - delivers a received packet as a dictionary, if you only care about a particular
- `meshtastic.connection.established` - published once we've successfully connected to the radio and downloaded the node DB
- `meshtastic.connection.lost` - published once we've lost our link to the radio
- `meshtastic.receive.text(packet)` - delivers a received packet as a dictionary, if you only care about a particular
type of packet, you should subscribe to the full topic name. If you want to see all packets, simply subscribe to "meshtastic.receive".
- meshtastic.receive.position(packet)
- meshtastic.receive.user(packet)
- meshtastic.receive.data.portnum(packet) (where portnum is an integer or well known PortNum enum)
- meshtastic.node.updated(node = NodeInfo) - published when a node in the DB changes (appears, location changed, username changed, etc...)
- meshtastic.log.line(line) - a raw unparsed log line from the radio
- `meshtastic.receive.position(packet)`
- `meshtastic.receive.user(packet)`
- `meshtastic.receive.data.portnum(packet)` (where portnum is an integer or well known PortNum enum)
- `meshtastic.node.updated(node = NodeInfo)` - published when a node in the DB changes (appears, location changed, username changed, etc...)
- `meshtastic.log.line(line)` - a raw unparsed log line from the radio
We receive position, user, or data packets from the mesh. You probably only care about meshtastic.receive.data. The first argument for
that publish will be the packet. Text or binary data packets (from sendData or sendText) will both arrive this way. If you print packet
you'll see the fields in the dictionary. decoded.data.payload will contain the raw bytes that were sent. If the packet was sent with
sendText, decoded.data.text will **also** be populated with the decoded string. For ASCII these two strings will be the same, but for
We receive position, user, or data packets from the mesh. You probably only care about `meshtastic.receive.data`. The first argument for
that publish will be the packet. Text or binary data packets (from `sendData` or `sendText`) will both arrive this way. If you print packet
you'll see the fields in the dictionary. `decoded.data.payload` will contain the raw bytes that were sent. If the packet was sent with
`sendText`, `decoded.data.text` will **also** be populated with the decoded string. For ASCII these two strings will be the same, but for
unicode scripts they can be different.
# Example Usage
@@ -194,13 +197,31 @@ def _onNodeInfoReceive(iface, asDict):
def _onTelemetryReceive(iface, asDict):
"""Automatically update device metrics on received packets"""
logging.debug(f"in _onTelemetryReceive() asDict:{asDict}")
deviceMetrics = asDict.get("decoded", {}).get("telemetry", {}).get("deviceMetrics")
if "from" in asDict and deviceMetrics is not None:
node = iface._getOrCreateByNum(asDict["from"])
newMetrics = node.get("deviceMetrics", {})
newMetrics.update(deviceMetrics)
logging.debug(f"updating metrics for {asDict['from']} to {newMetrics}")
node["deviceMetrics"] = newMetrics
if "from" not in asDict:
return
toUpdate = None
telemetry = asDict.get("decoded", {}).get("telemetry", {})
node = iface._getOrCreateByNum(asDict["from"])
if "deviceMetrics" in telemetry:
toUpdate = "deviceMetrics"
elif "environmentMetrics" in telemetry:
toUpdate = "environmentMetrics"
elif "airQualityMetrics" in telemetry:
toUpdate = "airQualityMetrics"
elif "powerMetrics" in telemetry:
toUpdate = "powerMetrics"
elif "localStats" in telemetry:
toUpdate = "localStats"
else:
return
updateObj = telemetry.get(toUpdate)
newMetrics = node.get(toUpdate, {})
newMetrics.update(updateObj)
logging.debug(f"updating {toUpdate} metrics for {asDict['from']} to {newMetrics}")
node[toUpdate] = newMetrics
def _receiveInfoUpdate(iface, asDict):
if "from" in asDict:

View File

@@ -475,13 +475,22 @@ def onConnected(interface):
else:
channelIndex = mt_config.channel_index or 0
if checkChannel(interface, channelIndex):
telemMap = {
"device": "device_metrics",
"environment": "environment_metrics",
"air_quality": "air_quality_metrics",
"airquality": "air_quality_metrics",
"power": "power_metrics",
}
telemType = telemMap.get(args.request_telemetry, "device_metrics")
print(
f"Sending telemetry request to {args.dest} on channelIndex:{channelIndex} (this could take a while)"
f"Sending {telemType} telemetry request to {args.dest} on channelIndex:{channelIndex} (this could take a while)"
)
interface.sendTelemetry(
destinationId=args.dest,
wantResponse=True,
channelIndex=channelIndex,
telemetryType=telemType,
)
if args.request_position:
@@ -622,19 +631,15 @@ def onConnected(interface):
if "alt" in configuration["location"]:
alt = int(configuration["location"]["alt"] or 0)
localConfig.position.fixed_position = True
print(f"Fixing altitude at {alt} meters")
if "lat" in configuration["location"]:
lat = float(configuration["location"]["lat"] or 0)
localConfig.position.fixed_position = True
print(f"Fixing latitude at {lat} degrees")
if "lon" in configuration["location"]:
lon = float(configuration["location"]["lon"] or 0)
localConfig.position.fixed_position = True
print(f"Fixing longitude at {lon} degrees")
print("Setting device position")
interface.sendPosition(lat, lon, alt)
interface.localNode.writeConfig("position")
interface.localNode.setFixedPosition(lat, lon, alt)
if "config" in configuration:
localConfig = interface.getNode(args.dest, **getNode_kwargs).localConfig
@@ -1029,6 +1034,15 @@ def export_config(interface):
prefs[meshtastic.util.snake_to_camel(pref)] = config[pref]
else:
prefs[pref] = config[pref]
# mark base64 encoded fields as such
if pref == "security":
if 'privateKey' in prefs[pref]:
prefs[pref]['privateKey'] = 'base64:' + prefs[pref]['privateKey']
if 'publicKey' in prefs[pref]:
prefs[pref]['publicKey'] = 'base64:' + prefs[pref]['publicKey']
if 'adminKey' in prefs[pref]:
for i in range(len(prefs[pref]['adminKey'])):
prefs[pref]['adminKey'][i] = 'base64:' + prefs[pref]['adminKey'][i]
if mt_config.camel_case:
configObj["config"] = config
else:
@@ -1228,7 +1242,7 @@ def common():
def addConnectionArgs(parser: argparse.ArgumentParser) -> argparse.ArgumentParser:
"""Add connection specifiation arguments"""
"""Add connection specification arguments"""
outer = parser.add_argument_group(
"Connection",
@@ -1264,64 +1278,92 @@ def addConnectionArgs(parser: argparse.ArgumentParser) -> argparse.ArgumentParse
const="any",
)
outer.add_argument(
"--ble-scan",
help="Scan for Meshtastic BLE devices that may be available to connect to",
action="store_true",
)
return parser
def initParser():
"""Initialize the command line argument parsing."""
parser = mt_config.parser
args = mt_config.args
# The "Help" group includes the help option and other informational stuff about the CLI itself
outerHelpGroup = parser.add_argument_group("Help")
helpGroup = outerHelpGroup.add_mutually_exclusive_group()
helpGroup.add_argument(
"-h", "--help", action="help", help="show this help message and exit"
def addSelectionArgs(parser: argparse.ArgumentParser) -> argparse.ArgumentParser:
"""Add node/channel specification arguments"""
group = parser.add_argument_group(
"Selection",
"Arguments that select channels to use, destination nodes, etc."
)
the_version = get_active_version()
helpGroup.add_argument("--version", action="version", version=f"{the_version}")
helpGroup.add_argument(
"--support",
action="store_true",
help="Show support info (useful when troubleshooting an issue)",
group.add_argument(
"--dest",
help="The destination node id for any sent commands, if not set '^all' or '^local' is assumed as appropriate",
default=None,
metavar="!xxxxxxxx",
)
# Connection arguments to indicate a device to connect to
parser = addConnectionArgs(parser)
group.add_argument(
"--ch-index",
help="Set the specified channel index for channel-specific commands. Channels start at 0 (0 is the PRIMARY channel).",
action="store",
metavar="INDEX",
)
# Arguments concerning viewing and setting configuration
return parser
# Arguments for sending or requesting things from the local device
def addImportExportArgs(parser: argparse.ArgumentParser) -> argparse.ArgumentParser:
"""Add import/export config arguments"""
group = parser.add_argument_group(
"Import/Export",
"Arguments that concern importing and exporting configuration of Meshtastic devices",
)
# Arguments for sending or requesting things from the mesh
# All the rest of the arguments
group = parser.add_argument_group("optional arguments")
group.add_argument(
"--configure",
help="Specify a path to a yaml(.yml) file containing the desired settings for the connected device.",
action="append",
)
group.add_argument(
"--export-config",
help="Export the configuration in yaml(.yml) format.",
action="store_true",
)
return parser
group.add_argument(
"--seriallog",
help="Log device serial output to either 'none' or a filename to append to. Defaults to 'stdout' if no filename specified.",
nargs="?",
const="stdout",
default=None,
def addConfigArgs(parser: argparse.ArgumentParser) -> argparse.ArgumentParser:
"""Add arguments to do with configuring a device"""
group = parser.add_argument_group(
"Configuration",
"Arguments that concern general configuration of Meshtastic devices",
)
group.add_argument(
"--info",
help="Read and display the radio config information",
"--get",
help=(
"Get a preferences field. Use an invalid field such as '0' to get a list of all fields."
" Can use either snake_case or camelCase format. (ex: 'ls_secs' or 'lsSecs')"
),
nargs=1,
action="append",
metavar="FIELD"
)
group.add_argument(
"--set",
help="Set a preferences field. Can use either snake_case or camelCase format. (ex: 'ls_secs' or 'lsSecs')",
nargs=2,
action="append",
metavar=("FIELD", "VALUE"),
)
group.add_argument(
"--begin-edit",
help="Tell the node to open a transaction to edit settings",
action="store_true",
)
group.add_argument(
"--commit-edit",
help="Tell the node to commit open settings transaction",
action="store_true",
)
@@ -1331,16 +1373,119 @@ def initParser():
action="store_true",
)
group.add_argument(
"--set-canned-message",
help="Set the canned messages plugin message (up to 200 characters).",
action="store",
)
group.add_argument(
"--get-ringtone", help="Show the stored ringtone", action="store_true"
)
group.add_argument(
"--nodes",
help="Print Node List in a pretty formatted table",
"--set-ringtone",
help="Set the Notification Ringtone (up to 230 characters).",
action="store",
metavar="RINGTONE",
)
group.add_argument(
"--ch-vlongslow",
help="Change to the very long-range and slow modem preset",
action="store_true",
)
group.add_argument(
"--ch-longslow",
help="Change to the long-range and slow modem preset",
action="store_true",
)
group.add_argument(
"--ch-longfast",
help="Change to the long-range and fast modem preset",
action="store_true",
)
group.add_argument(
"--ch-medslow",
help="Change to the med-range and slow modem preset",
action="store_true",
)
group.add_argument(
"--ch-medfast",
help="Change to the med-range and fast modem preset",
action="store_true",
)
group.add_argument(
"--ch-shortslow",
help="Change to the short-range and slow modem preset",
action="store_true",
)
group.add_argument(
"--ch-shortfast",
help="Change to the short-range and fast modem preset",
action="store_true",
)
group.add_argument("--set-owner", help="Set device owner name", action="store")
group.add_argument(
"--set-owner-short", help="Set device owner short name", action="store"
)
group.add_argument(
"--set-ham", help="Set licensed Ham ID and turn off encryption", action="store"
)
group.add_argument("--seturl", help="Set a channel URL", action="store")
return parser
def addChannelConfigArgs(parser: argparse.ArgumentParser) -> argparse.ArgumentParser:
"""Add arguments to do with configuring channels"""
group = parser.add_argument_group(
"Channel Configuration",
"Arguments that concern configuration of channels",
)
group.add_argument(
"--ch-add",
help="Add a secondary channel, you must specify a channel name",
default=None,
)
group.add_argument(
"--ch-del", help="Delete the ch-index channel", action="store_true"
)
group.add_argument(
"--ch-set",
help=(
"Set a channel parameter. To see channel settings available:'--ch-set all all --ch-index 0'. "
"Can set the 'psk' using this command. To disable encryption on primary channel:'--ch-set psk none --ch-index 0'. "
"To set encryption with a new random key on second channel:'--ch-set psk random --ch-index 1'. "
"To set encryption back to the default:'--ch-set psk default --ch-index 0'. To set encryption with your "
"own key: '--ch-set psk 0x1a1a1a1a2b2b2b2b1a1a1a1a2b2b2b2b1a1a1a1a2b2b2b2b1a1a1a1a2b2b2b2b --ch-index 0'."
),
nargs=2,
action="append",
metavar=("FIELD", "VALUE"),
)
group.add_argument(
"--channel-fetch-attempts",
help=("Attempt to retrieve channel settings for --ch-set this many times before giving up. Default %(default)s."),
default=3,
type=int,
metavar="ATTEMPTS",
)
group.add_argument(
"--qr",
help=(
@@ -1356,44 +1501,9 @@ def initParser():
action="store_true",
)
group.add_argument(
"--get",
help=(
"Get a preferences field. Use an invalid field such as '0' to get a list of all fields."
" Can use either snake_case or camelCase format. (ex: 'ls_secs' or 'lsSecs')"
),
nargs=1,
action="append",
)
group.add_argument(
"--set",
help="Set a preferences field. Can use either snake_case or camelCase format. (ex: 'ls_secs' or 'lsSecs')",
nargs=2,
action="append",
)
group.add_argument("--seturl", help="Set a channel URL", action="store")
group.add_argument(
"--ch-index",
help="Set the specified channel index. Channels start at 0 (0 is the PRIMARY channel).",
action="store",
)
group.add_argument(
"--ch-add",
help="Add a secondary channel, you must specify a channel name",
default=None,
)
group.add_argument(
"--ch-del", help="Delete the ch-index channel", action="store_true"
)
group.add_argument(
"--ch-enable",
help="Enable the specified channel",
help="Enable the specified channel. Use --ch-add instead whenever possible.",
action="store_true",
dest="ch_enable",
default=False,
@@ -1402,231 +1512,21 @@ def initParser():
# Note: We are doing a double negative here (Do we want to disable? If ch_disable==True, then disable.)
group.add_argument(
"--ch-disable",
help="Disable the specified channel",
help="Disable the specified channel Use --ch-del instead whenever possible.",
action="store_true",
dest="ch_disable",
default=False,
)
group.add_argument(
"--ch-set",
help=(
"Set a channel parameter. To see channel settings available:'--ch-set all all --ch-index 0'. "
"Can set the 'psk' using this command. To disable encryption on primary channel:'--ch-set psk none --ch-index 0'. "
"To set encryption with a new random key on second channel:'--ch-set psk random --ch-index 1'. "
"To set encryption back to the default:'--ch-set psk default --ch-index 0'. To set encryption with your "
"own key: '--ch-set psk 0x1a1a1a1a2b2b2b2b1a1a1a1a2b2b2b2b1a1a1a1a2b2b2b2b1a1a1a1a2b2b2b2b --ch-index 0'."
),
nargs=2,
action="append",
)
return parser
group.add_argument(
"--channel-fetch-attempts",
help=("Attempt to retrieve channel settings for --ch-set this many times before giving up."),
default=3,
type=int,
)
def addPositionConfigArgs(parser: argparse.ArgumentParser) -> argparse.ArgumentParser:
"""Add arguments to do with fixed positions and position config"""
group.add_argument(
"--timeout",
help="How long to wait for replies",
default=300,
type=int,
group = parser.add_argument_group(
"Position Configuration",
"Arguments that modify fixed position and other position-related configuration.",
)
group.add_argument(
"--ch-vlongslow",
help="Change to the very long-range and slow channel",
action="store_true",
)
group.add_argument(
"--ch-longslow",
help="Change to the long-range and slow channel",
action="store_true",
)
group.add_argument(
"--ch-longfast",
help="Change to the long-range and fast channel",
action="store_true",
)
group.add_argument(
"--ch-medslow",
help="Change to the med-range and slow channel",
action="store_true",
)
group.add_argument(
"--ch-medfast",
help="Change to the med-range and fast channel",
action="store_true",
)
group.add_argument(
"--ch-shortslow",
help="Change to the short-range and slow channel",
action="store_true",
)
group.add_argument(
"--ch-shortfast",
help="Change to the short-range and fast channel",
action="store_true",
)
group.add_argument("--set-owner", help="Set device owner name", action="store")
group.add_argument(
"--set-owner-short", help="Set device owner short name", action="store"
)
group.add_argument(
"--set-canned-message",
help="Set the canned messages plugin message (up to 200 characters).",
action="store",
)
group.add_argument(
"--set-ringtone",
help="Set the Notification Ringtone (up to 230 characters).",
action="store",
)
group.add_argument(
"--set-ham", help="Set licensed Ham ID and turn off encryption", action="store"
)
group.add_argument(
"--dest",
help="The destination node id for any sent commands, if not set '^all' or '^local' is assumed as appropriate",
default=None,
)
group.add_argument(
"--sendtext",
help="Send a text message. Can specify a destination '--dest' and/or channel index '--ch-index'.",
)
group.add_argument(
"--traceroute",
help="Traceroute from connected node to a destination. "
"You need pass the destination ID as argument, like "
"this: '--traceroute !ba4bf9d0' "
"Only nodes that have the encryption key can be traced.",
)
group.add_argument(
"--request-telemetry",
help="Request telemetry from a node. "
"You need to pass the destination ID as argument with '--dest'. "
"For repeaters, the nodeNum is required.",
action="store_true",
)
group.add_argument(
"--request-position",
help="Request the position from a node. "
"You need to pass the destination ID as an argument with '--dest'. "
"For repeaters, the nodeNum is required.",
action="store_true",
)
group.add_argument(
"--ack",
help="Use in combination with --sendtext to wait for an acknowledgment.",
action="store_true",
)
group.add_argument(
"--reboot", help="Tell the destination node to reboot", action="store_true"
)
group.add_argument(
"--reboot-ota",
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",
)
group.add_argument(
"--shutdown", help="Tell the destination node to shutdown", action="store_true"
)
group.add_argument(
"--device-metadata",
help="Get the device metadata from the node",
action="store_true",
)
group.add_argument(
"--begin-edit",
help="Tell the node to open a transaction to edit settings",
action="store_true",
)
group.add_argument(
"--commit-edit",
help="Tell the node to commit open settings transaction",
action="store_true",
)
group.add_argument(
"--factory-reset", "--factory-reset-config",
help="Tell the destination node to install the default config, preserving BLE bonds & PKI keys",
action="store_true",
)
group.add_argument(
"--factory-reset-device",
help="Tell the destination node to install the default config and clear BLE bonds & PKI keys",
action="store_true",
)
group.add_argument(
"--remove-node",
help="Tell the destination node to remove a specific node from its DB, by node number or ID",
)
group.add_argument(
"--reset-nodedb",
help="Tell the destination node to clear its list of nodes",
action="store_true",
)
group.add_argument(
"--set-time",
help="Set the time to the provided unix epoch timestamp, or the system's current time if omitted or 0.",
action="store",
type=int,
nargs="?",
default=None,
const=0,
)
group.add_argument(
"--reply", help="Reply to received messages", action="store_true"
)
group.add_argument(
"--no-time",
help="Deprecated. Retained for backwards compatibility in scripts, but is a no-op.",
action="store_true",
)
group.add_argument(
"--no-nodes",
help="Request that the node not send node info to the client. "
"Will break things that depend on the nodedb, but will speed up startup. Requires 2.3.11+ firmware.",
action="store_true",
)
group.add_argument(
"--setalt",
help="Set device altitude in meters (allows use without GPS), and enable fixed position. "
@@ -1659,6 +1559,221 @@ def initParser():
nargs="*",
action="store",
)
return parser
def addLocalActionArgs(parser: argparse.ArgumentParser) -> argparse.ArgumentParser:
"""Add arguments concerning local-only information & actions"""
group = parser.add_argument_group(
"Local Actions",
"Arguments that take actions or request information from the local node only.",
)
group.add_argument(
"--info",
help="Read and display the radio config information",
action="store_true",
)
group.add_argument(
"--nodes",
help="Print Node List in a pretty formatted table",
action="store_true",
)
return parser
def addRemoteActionArgs(parser: argparse.ArgumentParser) -> argparse.ArgumentParser:
"""Add arguments concerning information & actions that may interact with the mesh"""
group = parser.add_argument_group(
"Remote Actions",
"Arguments that take actions or request information from either the local node or remote nodes via the mesh.",
)
group.add_argument(
"--sendtext",
help="Send a text message. Can specify a destination '--dest' and/or channel index '--ch-index'.",
metavar="TEXT",
)
group.add_argument(
"--traceroute",
help="Traceroute from connected node to a destination. "
"You need pass the destination ID as argument, like "
"this: '--traceroute !ba4bf9d0' "
"Only nodes with a shared channel can be traced.",
metavar="!xxxxxxxx",
)
group.add_argument(
"--request-telemetry",
help="Request telemetry from a node. With an argument, requests that specific type of telemetry. "
"You need to pass the destination ID as argument with '--dest'. "
"For repeaters, the nodeNum is required.",
action="store",
nargs="?",
default=None,
const="device",
metavar="TYPE",
)
group.add_argument(
"--request-position",
help="Request the position from a node. "
"You need to pass the destination ID as an argument with '--dest'. "
"For repeaters, the nodeNum is required.",
action="store_true",
)
group.add_argument(
"--reply", help="Reply to received messages", action="store_true"
)
return parser
def addRemoteAdminArgs(parser: argparse.ArgumentParser) -> argparse.ArgumentParser:
"""Add arguments concerning admin actions that may interact with the mesh"""
outer = parser.add_argument_group(
"Remote Admin Actions",
"Arguments that interact with local node or remote nodes via the mesh, requiring admin access.",
)
group = outer.add_mutually_exclusive_group()
group.add_argument(
"--reboot", help="Tell the destination node to reboot", action="store_true"
)
group.add_argument(
"--reboot-ota",
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",
)
group.add_argument(
"--shutdown", help="Tell the destination node to shutdown", action="store_true"
)
group.add_argument(
"--device-metadata",
help="Get the device metadata from the node",
action="store_true",
)
group.add_argument(
"--factory-reset", "--factory-reset-config",
help="Tell the destination node to install the default config, preserving BLE bonds & PKI keys",
action="store_true",
)
group.add_argument(
"--factory-reset-device",
help="Tell the destination node to install the default config and clear BLE bonds & PKI keys",
action="store_true",
)
group.add_argument(
"--remove-node",
help="Tell the destination node to remove a specific node from its DB, by node number or ID",
metavar="!xxxxxxxx"
)
group.add_argument(
"--reset-nodedb",
help="Tell the destination node to clear its list of nodes",
action="store_true",
)
group.add_argument(
"--set-time",
help="Set the time to the provided unix epoch timestamp, or the system's current time if omitted or 0.",
action="store",
type=int,
nargs="?",
default=None,
const=0,
metavar="TIMESTAMP",
)
return parser
def initParser():
"""Initialize the command line argument parsing."""
parser = mt_config.parser
args = mt_config.args
# The "Help" group includes the help option and other informational stuff about the CLI itself
outerHelpGroup = parser.add_argument_group("Help")
helpGroup = outerHelpGroup.add_mutually_exclusive_group()
helpGroup.add_argument(
"-h", "--help", action="help", help="show this help message and exit"
)
the_version = get_active_version()
helpGroup.add_argument("--version", action="version", version=f"{the_version}")
helpGroup.add_argument(
"--support",
action="store_true",
help="Show support info (useful when troubleshooting an issue)",
)
# Connection arguments to indicate a device to connect to
parser = addConnectionArgs(parser)
# Selection arguments to denote nodes and channels to use
parser = addSelectionArgs(parser)
# Arguments concerning viewing and setting configuration
parser = addImportExportArgs(parser)
parser = addConfigArgs(parser)
parser = addPositionConfigArgs(parser)
parser = addChannelConfigArgs(parser)
# Arguments for sending or requesting things from the local device
parser = addLocalActionArgs(parser)
# Arguments for sending or requesting things from the mesh
parser = addRemoteActionArgs(parser)
parser = addRemoteAdminArgs(parser)
# All the rest of the arguments
group = parser.add_argument_group("Miscellaneous arguments")
group.add_argument(
"--seriallog",
help="Log device serial output to either 'none' or a filename to append to. Defaults to '%(const)s' if no filename specified.",
nargs="?",
const="stdout",
default=None,
metavar="LOG_DESTINATION",
)
group.add_argument(
"--ack",
help="Use in combination with compatible actions (e.g. --sendtext) to wait for an acknowledgment.",
action="store_true",
)
group.add_argument(
"--timeout",
help="How long to wait for replies. Default %(default)ss.",
default=300,
type=int,
metavar="SECONDS",
)
group.add_argument(
"--no-nodes",
help="Request that the node not send node info to the client. "
"Will break things that depend on the nodedb, but will speed up startup. Requires 2.3.11+ firmware.",
action="store_true",
)
group.add_argument(
"--debug", help="Show API library debug log messages", action="store_true"
@@ -1670,6 +1785,33 @@ def initParser():
action="store_true",
)
group.add_argument(
"--wait-to-disconnect",
help="How many seconds to wait before disconnecting from the device.",
const="5",
nargs="?",
action="store",
metavar="SECONDS",
)
group.add_argument(
"--noproto",
help="Don't start the API, just function as a dumb serial terminal.",
action="store_true",
)
group.add_argument(
"--listen",
help="Just stay open and listen to the protobuf stream. Enables debug logging.",
action="store_true",
)
group.add_argument(
"--no-time",
help="Deprecated. Retained for backwards compatibility in scripts, but is a no-op.",
action="store_true",
)
power_group = parser.add_argument_group(
"Power Testing", "Options for power testing/logging."
)
@@ -1718,37 +1860,12 @@ def initParser():
power_group.add_argument(
"--slog",
help="Store structured-logs (slogs) for this run, optionally you can specifiy a destination directory",
help="Store structured-logs (slogs) for this run, optionally you can specify a destination directory",
nargs="?",
default=None,
const="default",
)
group.add_argument(
"--ble-scan",
help="Scan for Meshtastic BLE devices",
action="store_true",
)
group.add_argument(
"--wait-to-disconnect",
help="How many seconds to wait before disconnecting from the device.",
const="5",
nargs="?",
action="store",
)
group.add_argument(
"--noproto",
help="Don't start the API, just function as a dumb serial terminal.",
action="store_true",
)
group.add_argument(
"--listen",
help="Just stay open and listen to the protobuf stream. Enables debug logging.",
action="store_true",
)
remoteHardwareArgs = parser.add_argument_group(
"Remote Hardware", "Arguments related to the Remote Hardware module"

View File

@@ -218,7 +218,7 @@ class BLEInterface(MeshInterface):
logging.error(f"Error closing mesh interface: {e}")
if self._want_receive:
self.want_receive = False # Tell the thread we want it to stop
self._want_receive = False # Tell the thread we want it to stop
if self._receiveThread:
self._receiveThread.join(
timeout=2
@@ -230,6 +230,7 @@ class BLEInterface(MeshInterface):
self.client.disconnect()
self.client.close()
self.client = None
self._disconnected() # send the disconnected indicator up to clients
class BLEClient:

View File

@@ -1,5 +1,6 @@
"""Mesh Interface class
"""
# pylint: disable=R0917
import collections
import json
@@ -605,32 +606,38 @@ class MeshInterface: # pylint: disable=R0902
destinationId: Union[int, str] = BROADCAST_ADDR,
wantResponse: bool = False,
channelIndex: int = 0,
telemetryType: str = "device_metrics"
):
"""Send telemetry and optionally ask for a response"""
r = telemetry_pb2.Telemetry()
if self.nodes is not None:
node = next(
n for n in self.nodes.values() if n["num"] == self.localNode.nodeNum
)
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
uptime_seconds = metrics.get("uptimeSeconds")
if uptime_seconds is not None:
r.device_metrics.uptime_seconds = uptime_seconds
if telemetryType == "environment_metrics":
r.environment_metrics.CopyFrom(telemetry_pb2.EnvironmentMetrics())
elif telemetryType == "air_quality_metrics":
r.air_quality_metrics.CopyFrom(telemetry_pb2.AirQualityMetrics())
elif telemetryType == "power_metrics":
r.power_metrics.CopyFrom(telemetry_pb2.PowerMetrics())
else: # fall through to device metrics
if self.nodesByNum is not None:
node = self.nodesByNum.get(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
uptime_seconds = metrics.get("uptimeSeconds")
if uptime_seconds is not None:
r.device_metrics.uptime_seconds = uptime_seconds
if wantResponse:
onResponse = self.onResponseTelemetry
@@ -654,22 +661,32 @@ class MeshInterface: # pylint: disable=R0902
self._acknowledgment.receivedTelemetry = True
telemetry = telemetry_pb2.Telemetry()
telemetry.ParseFromString(p["decoded"]["payload"])
print("Telemetry received:")
if telemetry.device_metrics.battery_level is not None:
print(f"Battery level: {telemetry.device_metrics.battery_level:.2f}%")
if telemetry.device_metrics.voltage is not None:
print(f"Voltage: {telemetry.device_metrics.voltage:.2f} V")
if telemetry.device_metrics.channel_utilization is not None:
print(
f"Total channel utilization: {telemetry.device_metrics.channel_utilization:.2f}%"
)
if telemetry.device_metrics.air_util_tx is not None:
print(
f"Transmit air utilization: {telemetry.device_metrics.air_util_tx:.2f}%"
)
if telemetry.device_metrics.uptime_seconds is not None:
print(f"Uptime: {telemetry.device_metrics.uptime_seconds} s")
# Check if the telemetry message has the device_metrics field
# This is the original code that was the default for --request-telemetry and is kept for compatibility
if telemetry.HasField("device_metrics"):
if telemetry.device_metrics.battery_level is not None:
print(f"Battery level: {telemetry.device_metrics.battery_level:.2f}%")
if telemetry.device_metrics.voltage is not None:
print(f"Voltage: {telemetry.device_metrics.voltage:.2f} V")
if telemetry.device_metrics.channel_utilization is not None:
print(
f"Total channel utilization: {telemetry.device_metrics.channel_utilization:.2f}%"
)
if telemetry.device_metrics.air_util_tx is not None:
print(
f"Transmit air utilization: {telemetry.device_metrics.air_util_tx:.2f}%"
)
if telemetry.device_metrics.uptime_seconds is not None:
print(f"Uptime: {telemetry.device_metrics.uptime_seconds} s")
else:
# this is the new code if --request-telemetry <type> is used.
telemetry_dict = google.protobuf.json_format.MessageToDict(telemetry)
for key, value in telemetry_dict.items():
if key != "time": # protobuf includes a time field that we don't print for device_metrics.
print(f"{key}:")
for sub_key, sub_value in value.items():
print(f" {sub_key}: {sub_value}")
elif p["decoded"]["portnum"] == "ROUTING_APP":
if p["decoded"]["routing"]["errorReason"] == "NO_RESPONSE":
@@ -914,6 +931,8 @@ class MeshInterface: # pylint: disable=R0902
startConfig = mesh_pb2.ToRadio()
if self.configId is None or not self.noNodes:
self.configId = random.randint(0, 0xFFFFFFFF)
if self.configId == NODELESS_WANT_CONFIG_ID:
self.configId = self.configId + 1
startConfig.want_config_id = self.configId
self._sendToRadio(startConfig)

View File

File diff suppressed because one or more lines are too long

View File

@@ -129,6 +129,10 @@ class _HardwareModelEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._
"""
Heltec HRU-3601: https://heltec.org/project/hru-3601/
"""
HELTEC_WIRELESS_BRIDGE: _HardwareModel.ValueType # 24
"""
Heltec Wireless Bridge
"""
STATION_G1: _HardwareModel.ValueType # 25
"""
B&Q Consulting Station Edition G1: https://uniteng.com/wiki/doku.php?id=meshtastic:station
@@ -201,7 +205,7 @@ class _HardwareModelEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._
"""
M5STACK: _HardwareModel.ValueType # 42
"""
M5 esp32 based MCU modules with enclosure, TFT and LORA Shields. All Variants (Basic, Core, Fire, Core2, Paper) https://m5stack.com/
M5 esp32 based MCU modules with enclosure, TFT and LORA Shields. All Variants (Basic, Core, Fire, Core2, CoreS3, Paper) https://m5stack.com/
"""
HELTEC_V3: _HardwareModel.ValueType # 43
"""
@@ -359,8 +363,22 @@ class _HardwareModelEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._
^^^ short A0 to switch to I2C address 0x3C
"""
M5STACK_COREBASIC: _HardwareModel.ValueType # 77
"""M5 esp32 based MCU modules with enclosure, TFT and LORA Shields. All Variants (Basic, Core, Fire, Core2, Paper) https://m5stack.com/"""
"""M5 esp32 based MCU modules with enclosure, TFT and LORA Shields. All Variants (Basic, Core, Fire, Core2, CoreS3, Paper) https://m5stack.com/"""
M5STACK_CORE2: _HardwareModel.ValueType # 78
RPI_PICO2: _HardwareModel.ValueType # 79
"""Pico2 with Waveshare Hat, same as Pico"""
M5STACK_CORES3: _HardwareModel.ValueType # 80
"""M5 esp32 based MCU modules with enclosure, TFT and LORA Shields. All Variants (Basic, Core, Fire, Core2, CoreS3, Paper) https://m5stack.com/"""
SEEED_XIAO_S3: _HardwareModel.ValueType # 81
"""Seeed XIAO S3 DK"""
MS24SF1: _HardwareModel.ValueType # 82
"""
Nordic nRF52840+Semtech SX1262 LoRa BLE Combo Module. nRF52840+SX1262 MS24SF1
"""
TLORA_C6: _HardwareModel.ValueType # 83
"""
Lilygo TLora-C6 with the new ESP32-C6 MCU
"""
PRIVATE_HW: _HardwareModel.ValueType # 255
"""
------------------------------------------------------------------------------------------------------------------------------------------
@@ -474,6 +492,10 @@ HELTEC_HRU_3601: HardwareModel.ValueType # 23
"""
Heltec HRU-3601: https://heltec.org/project/hru-3601/
"""
HELTEC_WIRELESS_BRIDGE: HardwareModel.ValueType # 24
"""
Heltec Wireless Bridge
"""
STATION_G1: HardwareModel.ValueType # 25
"""
B&Q Consulting Station Edition G1: https://uniteng.com/wiki/doku.php?id=meshtastic:station
@@ -546,7 +568,7 @@ Custom Disaster Radio esp32 v3 device https://github.com/sudomesh/disaster-radio
"""
M5STACK: HardwareModel.ValueType # 42
"""
M5 esp32 based MCU modules with enclosure, TFT and LORA Shields. All Variants (Basic, Core, Fire, Core2, Paper) https://m5stack.com/
M5 esp32 based MCU modules with enclosure, TFT and LORA Shields. All Variants (Basic, Core, Fire, Core2, CoreS3, Paper) https://m5stack.com/
"""
HELTEC_V3: HardwareModel.ValueType # 43
"""
@@ -704,8 +726,22 @@ https://www.adafruit.com/product/938
^^^ short A0 to switch to I2C address 0x3C
"""
M5STACK_COREBASIC: HardwareModel.ValueType # 77
"""M5 esp32 based MCU modules with enclosure, TFT and LORA Shields. All Variants (Basic, Core, Fire, Core2, Paper) https://m5stack.com/"""
"""M5 esp32 based MCU modules with enclosure, TFT and LORA Shields. All Variants (Basic, Core, Fire, Core2, CoreS3, Paper) https://m5stack.com/"""
M5STACK_CORE2: HardwareModel.ValueType # 78
RPI_PICO2: HardwareModel.ValueType # 79
"""Pico2 with Waveshare Hat, same as Pico"""
M5STACK_CORES3: HardwareModel.ValueType # 80
"""M5 esp32 based MCU modules with enclosure, TFT and LORA Shields. All Variants (Basic, Core, Fire, Core2, CoreS3, Paper) https://m5stack.com/"""
SEEED_XIAO_S3: HardwareModel.ValueType # 81
"""Seeed XIAO S3 DK"""
MS24SF1: HardwareModel.ValueType # 82
"""
Nordic nRF52840+Semtech SX1262 LoRa BLE Combo Module. nRF52840+SX1262 MS24SF1
"""
TLORA_C6: HardwareModel.ValueType # 83
"""
Lilygo TLora-C6 with the new ESP32-C6 MCU
"""
PRIVATE_HW: HardwareModel.ValueType # 255
"""
------------------------------------------------------------------------------------------------------------------------------------------

View File

File diff suppressed because one or more lines are too long

View File

@@ -250,13 +250,54 @@ class ModuleConfig(google.protobuf.message.Message):
DESCRIPTOR: google.protobuf.descriptor.Descriptor
class _TriggerType:
ValueType = typing.NewType("ValueType", builtins.int)
V: typing_extensions.TypeAlias = ValueType
class _TriggerTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[ModuleConfig.DetectionSensorConfig._TriggerType.ValueType], builtins.type):
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
LOGIC_LOW: ModuleConfig.DetectionSensorConfig._TriggerType.ValueType # 0
"""Event is triggered if pin is low"""
LOGIC_HIGH: ModuleConfig.DetectionSensorConfig._TriggerType.ValueType # 1
"""Event is triggered if pin is high"""
FALLING_EDGE: ModuleConfig.DetectionSensorConfig._TriggerType.ValueType # 2
"""Event is triggered when pin goes high to low"""
RISING_EDGE: ModuleConfig.DetectionSensorConfig._TriggerType.ValueType # 3
"""Event is triggered when pin goes low to high"""
EITHER_EDGE_ACTIVE_LOW: ModuleConfig.DetectionSensorConfig._TriggerType.ValueType # 4
"""Event is triggered on every pin state change, low is considered to be
"active"
"""
EITHER_EDGE_ACTIVE_HIGH: ModuleConfig.DetectionSensorConfig._TriggerType.ValueType # 5
"""Event is triggered on every pin state change, high is considered to be
"active"
"""
class TriggerType(_TriggerType, metaclass=_TriggerTypeEnumTypeWrapper): ...
LOGIC_LOW: ModuleConfig.DetectionSensorConfig.TriggerType.ValueType # 0
"""Event is triggered if pin is low"""
LOGIC_HIGH: ModuleConfig.DetectionSensorConfig.TriggerType.ValueType # 1
"""Event is triggered if pin is high"""
FALLING_EDGE: ModuleConfig.DetectionSensorConfig.TriggerType.ValueType # 2
"""Event is triggered when pin goes high to low"""
RISING_EDGE: ModuleConfig.DetectionSensorConfig.TriggerType.ValueType # 3
"""Event is triggered when pin goes low to high"""
EITHER_EDGE_ACTIVE_LOW: ModuleConfig.DetectionSensorConfig.TriggerType.ValueType # 4
"""Event is triggered on every pin state change, low is considered to be
"active"
"""
EITHER_EDGE_ACTIVE_HIGH: ModuleConfig.DetectionSensorConfig.TriggerType.ValueType # 5
"""Event is triggered on every pin state change, high is considered to be
"active"
"""
ENABLED_FIELD_NUMBER: builtins.int
MINIMUM_BROADCAST_SECS_FIELD_NUMBER: builtins.int
STATE_BROADCAST_SECS_FIELD_NUMBER: builtins.int
SEND_BELL_FIELD_NUMBER: builtins.int
NAME_FIELD_NUMBER: builtins.int
MONITOR_PIN_FIELD_NUMBER: builtins.int
DETECTION_TRIGGERED_HIGH_FIELD_NUMBER: builtins.int
DETECTION_TRIGGER_TYPE_FIELD_NUMBER: builtins.int
USE_PULLUP_FIELD_NUMBER: builtins.int
enabled: builtins.bool
"""
@@ -264,13 +305,15 @@ class ModuleConfig(google.protobuf.message.Message):
"""
minimum_broadcast_secs: builtins.int
"""
Interval in seconds of how often we can send a message to the mesh when a state change is detected
Interval in seconds of how often we can send a message to the mesh when a
trigger event is detected
"""
state_broadcast_secs: builtins.int
"""
Interval in seconds of how often we should send a message to the mesh with the current state regardless of changes
When set to 0, only state changes will be broadcasted
Works as a sort of status heartbeat for peace of mind
Interval in seconds of how often we should send a message to the mesh
with the current state regardless of trigger events When set to 0, only
trigger events will be broadcasted Works as a sort of status heartbeat
for peace of mind
"""
send_bell: builtins.bool
"""
@@ -287,10 +330,9 @@ class ModuleConfig(google.protobuf.message.Message):
"""
GPIO pin to monitor for state changes
"""
detection_triggered_high: builtins.bool
detection_trigger_type: global___ModuleConfig.DetectionSensorConfig.TriggerType.ValueType
"""
Whether or not the GPIO pin state detection is triggered on HIGH (1)
Otherwise LOW (0)
The type of trigger event to be used
"""
use_pullup: builtins.bool
"""
@@ -306,10 +348,10 @@ class ModuleConfig(google.protobuf.message.Message):
send_bell: builtins.bool = ...,
name: builtins.str = ...,
monitor_pin: builtins.int = ...,
detection_triggered_high: builtins.bool = ...,
detection_trigger_type: global___ModuleConfig.DetectionSensorConfig.TriggerType.ValueType = ...,
use_pullup: builtins.bool = ...,
) -> None: ...
def ClearField(self, field_name: typing.Literal["detection_triggered_high", b"detection_triggered_high", "enabled", b"enabled", "minimum_broadcast_secs", b"minimum_broadcast_secs", "monitor_pin", b"monitor_pin", "name", b"name", "send_bell", b"send_bell", "state_broadcast_secs", b"state_broadcast_secs", "use_pullup", b"use_pullup"]) -> None: ...
def ClearField(self, field_name: typing.Literal["detection_trigger_type", b"detection_trigger_type", "enabled", b"enabled", "minimum_broadcast_secs", b"minimum_broadcast_secs", "monitor_pin", b"monitor_pin", "name", b"name", "send_bell", b"send_bell", "state_broadcast_secs", b"state_broadcast_secs", "use_pullup", b"use_pullup"]) -> None: ...
@typing.final
class AudioConfig(google.protobuf.message.Message):

View File

@@ -1,5 +1,6 @@
""" Serial interface class
"""
# pylint: disable=R0917
import logging
import platform
import time

View File

@@ -1,6 +1,7 @@
""" Supported Meshtastic Devices - This is a class and collection of Meshtastic devices.
It is used for auto detection as to which device might be connected.
"""
# pylint: disable=R0917
# Goal is to detect which device and port to use from the supported devices
# without installing any libraries that are not currently in the python meshtastic library

View File

@@ -1,5 +1,6 @@
"""TCPInterface class for interfacing with http endpoint
"""
# pylint: disable=R0917
import logging
import socket
from typing import Optional

View File

@@ -1,5 +1,5 @@
"""Meshtastic unit tests for __main__.py"""
# pylint: disable=C0302,W0613
# pylint: disable=C0302,W0613,R0917
import logging
import os

View File

@@ -1,4 +1,5 @@
"""Meshtastic unit tests for serial_interface.py"""
# pylint: disable=R0917
import re
from unittest.mock import mock_open, patch

2827
poetry.lock generated
View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,13 +1,13 @@
[tool.poetry]
name = "meshtastic"
version = "2.5.1"
version = "2.5.3"
description = "Python API & client shell for talking to Meshtastic devices"
authors = ["Meshtastic Developers <contact@meshtastic.org>"]
license = "GPL-3.0-only"
readme = "README.md"
[tool.poetry.dependencies]
python = "^3.9,<3.13" # 3.9 is needed for pandas, bleak requires a max of 3.13 for some reason
python = "^3.9,<3.14" # 3.9 is needed for pandas, bleak requires <3.14
pyserial = "^3.5"
protobuf = ">=5.26.0"
dotmap = "^1.3.30"
@@ -19,13 +19,14 @@ requests = "^2.31.0"
pyparsing = "^3.1.2"
pyyaml = "^6.0.1"
pypubsub = "^4.0.3"
bleak = "^0.21.1"
bleak = "^0.22.3"
packaging = "^24.0"
print-color = "^0.4.6"
dash = { version = "^2.17.1", optional = true }
pytap2 = { version = "^2.3.0", optional = true }
dash-bootstrap-components = { version = "^1.6.0", optional = true }
pandas = { version = "^2.2.2", optional = true }
pandas-stubs = { version = "^2.2.2.240603", optional = true }
[tool.poetry.group.dev.dependencies]
hypothesis = "^6.103.2"
@@ -43,7 +44,6 @@ types-requests = "^2.31.0.20240406"
types-setuptools = "^69.5.0.20240423"
types-pyyaml = "^6.0.12.20240311"
pyarrow-stubs = "^10.0.1.7"
pandas-stubs = "^2.2.2.240603"
[tool.poetry.group.powermon]
optional = true