mirror of
https://github.com/meshtastic/python.git
synced 2026-01-01 20:38:03 -05:00
Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f08ec1885b | ||
|
|
feca49faed | ||
|
|
dfaf1a275d | ||
|
|
da7fa31805 | ||
|
|
12fd29b203 | ||
|
|
a43dd201ba | ||
|
|
a64a9d203a | ||
|
|
10136962d7 | ||
|
|
3afb294f9b | ||
|
|
ee405fec41 | ||
|
|
3eabaf91d0 | ||
|
|
78b92cecc9 | ||
|
|
7088b90514 | ||
|
|
2ae81f8602 | ||
|
|
923f5e82d0 | ||
|
|
05731128fa | ||
|
|
0523d4c94f | ||
|
|
90e901de79 | ||
|
|
6606851135 | ||
|
|
33fecbd74d | ||
|
|
6b9db7abd9 | ||
|
|
ece6286d82 | ||
|
|
0bb4b31b6a | ||
|
|
915066e0af | ||
|
|
6be3969577 | ||
|
|
b73cc1f499 | ||
|
|
65305af184 |
@@ -2,41 +2,44 @@
|
|||||||
# A library for the Meshtastic Client API
|
# A library for the Meshtastic Client API
|
||||||
|
|
||||||
Primary interfaces: SerialInterface, TCPInterface, BLEInterface
|
Primary interfaces: SerialInterface, TCPInterface, BLEInterface
|
||||||
|
|
||||||
Install with pip: "[pip3 install meshtastic](https://pypi.org/project/meshtastic/)"
|
Install with pip: "[pip3 install meshtastic](https://pypi.org/project/meshtastic/)"
|
||||||
|
|
||||||
Source code on [github](https://github.com/meshtastic/python)
|
Source code on [github](https://github.com/meshtastic/python)
|
||||||
|
|
||||||
notable properties of interface classes:
|
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.
|
node in the mesh. This is a read-only datastructure.
|
||||||
- nodesByNum - like "nodes" but keyed by nodeNum instead of nodeId
|
- `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)
|
- `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
|
- `localNode` - Pointer to a node object for the local node
|
||||||
|
|
||||||
notable properties of nodes:
|
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.
|
- `localConfig` - Current radio settings, can be written to the radio with the `writeConfig` method.
|
||||||
- channels - The node's channels, keyed by index.
|
- `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
|
# Published PubSub topics
|
||||||
|
|
||||||
We use a [publish-subscribe](https://pypubsub.readthedocs.io/en/v4.0.3/) model to communicate asynchronous events. Available
|
We use a [publish-subscribe](https://pypubsub.readthedocs.io/en/v4.0.3/) model to communicate asynchronous events. Available
|
||||||
topics:
|
topics:
|
||||||
|
|
||||||
- meshtastic.connection.established - published once we've successfully connected to the radio and downloaded the node DB
|
- `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.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.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".
|
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.position(packet)`
|
||||||
- meshtastic.receive.user(packet)
|
- `meshtastic.receive.user(packet)`
|
||||||
- meshtastic.receive.data.portnum(packet) (where portnum is an integer or well known PortNum enum)
|
- `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.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.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
|
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
|
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
|
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
|
`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.
|
unicode scripts they can be different.
|
||||||
|
|
||||||
# Example Usage
|
# Example Usage
|
||||||
@@ -194,13 +197,31 @@ def _onNodeInfoReceive(iface, asDict):
|
|||||||
def _onTelemetryReceive(iface, asDict):
|
def _onTelemetryReceive(iface, asDict):
|
||||||
"""Automatically update device metrics on received packets"""
|
"""Automatically update device metrics on received packets"""
|
||||||
logging.debug(f"in _onTelemetryReceive() asDict:{asDict}")
|
logging.debug(f"in _onTelemetryReceive() asDict:{asDict}")
|
||||||
deviceMetrics = asDict.get("decoded", {}).get("telemetry", {}).get("deviceMetrics")
|
if "from" not in asDict:
|
||||||
if "from" in asDict and deviceMetrics is not None:
|
return
|
||||||
node = iface._getOrCreateByNum(asDict["from"])
|
|
||||||
newMetrics = node.get("deviceMetrics", {})
|
toUpdate = None
|
||||||
newMetrics.update(deviceMetrics)
|
|
||||||
logging.debug(f"updating metrics for {asDict['from']} to {newMetrics}")
|
telemetry = asDict.get("decoded", {}).get("telemetry", {})
|
||||||
node["deviceMetrics"] = newMetrics
|
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):
|
def _receiveInfoUpdate(iface, asDict):
|
||||||
if "from" in asDict:
|
if "from" in asDict:
|
||||||
|
|||||||
@@ -475,13 +475,22 @@ def onConnected(interface):
|
|||||||
else:
|
else:
|
||||||
channelIndex = mt_config.channel_index or 0
|
channelIndex = mt_config.channel_index or 0
|
||||||
if checkChannel(interface, channelIndex):
|
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(
|
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(
|
interface.sendTelemetry(
|
||||||
destinationId=args.dest,
|
destinationId=args.dest,
|
||||||
wantResponse=True,
|
wantResponse=True,
|
||||||
channelIndex=channelIndex,
|
channelIndex=channelIndex,
|
||||||
|
telemetryType=telemType,
|
||||||
)
|
)
|
||||||
|
|
||||||
if args.request_position:
|
if args.request_position:
|
||||||
@@ -622,19 +631,15 @@ def onConnected(interface):
|
|||||||
|
|
||||||
if "alt" in configuration["location"]:
|
if "alt" in configuration["location"]:
|
||||||
alt = int(configuration["location"]["alt"] or 0)
|
alt = int(configuration["location"]["alt"] or 0)
|
||||||
localConfig.position.fixed_position = True
|
|
||||||
print(f"Fixing altitude at {alt} meters")
|
print(f"Fixing altitude at {alt} meters")
|
||||||
if "lat" in configuration["location"]:
|
if "lat" in configuration["location"]:
|
||||||
lat = float(configuration["location"]["lat"] or 0)
|
lat = float(configuration["location"]["lat"] or 0)
|
||||||
localConfig.position.fixed_position = True
|
|
||||||
print(f"Fixing latitude at {lat} degrees")
|
print(f"Fixing latitude at {lat} degrees")
|
||||||
if "lon" in configuration["location"]:
|
if "lon" in configuration["location"]:
|
||||||
lon = float(configuration["location"]["lon"] or 0)
|
lon = float(configuration["location"]["lon"] or 0)
|
||||||
localConfig.position.fixed_position = True
|
|
||||||
print(f"Fixing longitude at {lon} degrees")
|
print(f"Fixing longitude at {lon} degrees")
|
||||||
print("Setting device position")
|
print("Setting device position")
|
||||||
interface.sendPosition(lat, lon, alt)
|
interface.localNode.setFixedPosition(lat, lon, alt)
|
||||||
interface.localNode.writeConfig("position")
|
|
||||||
|
|
||||||
if "config" in configuration:
|
if "config" in configuration:
|
||||||
localConfig = interface.getNode(args.dest, **getNode_kwargs).localConfig
|
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]
|
prefs[meshtastic.util.snake_to_camel(pref)] = config[pref]
|
||||||
else:
|
else:
|
||||||
prefs[pref] = config[pref]
|
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:
|
if mt_config.camel_case:
|
||||||
configObj["config"] = config
|
configObj["config"] = config
|
||||||
else:
|
else:
|
||||||
@@ -1228,7 +1242,7 @@ def common():
|
|||||||
|
|
||||||
|
|
||||||
def addConnectionArgs(parser: argparse.ArgumentParser) -> argparse.ArgumentParser:
|
def addConnectionArgs(parser: argparse.ArgumentParser) -> argparse.ArgumentParser:
|
||||||
"""Add connection specifiation arguments"""
|
"""Add connection specification arguments"""
|
||||||
|
|
||||||
outer = parser.add_argument_group(
|
outer = parser.add_argument_group(
|
||||||
"Connection",
|
"Connection",
|
||||||
@@ -1264,64 +1278,92 @@ def addConnectionArgs(parser: argparse.ArgumentParser) -> argparse.ArgumentParse
|
|||||||
const="any",
|
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
|
return parser
|
||||||
|
|
||||||
|
def addSelectionArgs(parser: argparse.ArgumentParser) -> argparse.ArgumentParser:
|
||||||
def initParser():
|
"""Add node/channel specification arguments"""
|
||||||
"""Initialize the command line argument parsing."""
|
group = parser.add_argument_group(
|
||||||
parser = mt_config.parser
|
"Selection",
|
||||||
args = mt_config.args
|
"Arguments that select channels to use, destination nodes, etc."
|
||||||
|
|
||||||
# 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()
|
group.add_argument(
|
||||||
helpGroup.add_argument("--version", action="version", version=f"{the_version}")
|
"--dest",
|
||||||
|
help="The destination node id for any sent commands, if not set '^all' or '^local' is assumed as appropriate",
|
||||||
helpGroup.add_argument(
|
default=None,
|
||||||
"--support",
|
metavar="!xxxxxxxx",
|
||||||
action="store_true",
|
|
||||||
help="Show support info (useful when troubleshooting an issue)",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Connection arguments to indicate a device to connect to
|
group.add_argument(
|
||||||
parser = addConnectionArgs(parser)
|
"--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(
|
group.add_argument(
|
||||||
"--configure",
|
"--configure",
|
||||||
help="Specify a path to a yaml(.yml) file containing the desired settings for the connected device.",
|
help="Specify a path to a yaml(.yml) file containing the desired settings for the connected device.",
|
||||||
action="append",
|
action="append",
|
||||||
)
|
)
|
||||||
|
|
||||||
group.add_argument(
|
group.add_argument(
|
||||||
"--export-config",
|
"--export-config",
|
||||||
help="Export the configuration in yaml(.yml) format.",
|
help="Export the configuration in yaml(.yml) format.",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
)
|
)
|
||||||
|
return parser
|
||||||
|
|
||||||
group.add_argument(
|
def addConfigArgs(parser: argparse.ArgumentParser) -> argparse.ArgumentParser:
|
||||||
"--seriallog",
|
"""Add arguments to do with configuring a device"""
|
||||||
help="Log device serial output to either 'none' or a filename to append to. Defaults to 'stdout' if no filename specified.",
|
|
||||||
nargs="?",
|
group = parser.add_argument_group(
|
||||||
const="stdout",
|
"Configuration",
|
||||||
default=None,
|
"Arguments that concern general configuration of Meshtastic devices",
|
||||||
)
|
)
|
||||||
|
|
||||||
group.add_argument(
|
group.add_argument(
|
||||||
"--info",
|
"--get",
|
||||||
help="Read and display the radio config information",
|
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",
|
action="store_true",
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1331,16 +1373,119 @@ def initParser():
|
|||||||
action="store_true",
|
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(
|
group.add_argument(
|
||||||
"--get-ringtone", help="Show the stored ringtone", action="store_true"
|
"--get-ringtone", help="Show the stored ringtone", action="store_true"
|
||||||
)
|
)
|
||||||
|
|
||||||
group.add_argument(
|
group.add_argument(
|
||||||
"--nodes",
|
"--set-ringtone",
|
||||||
help="Print Node List in a pretty formatted table",
|
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",
|
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(
|
group.add_argument(
|
||||||
"--qr",
|
"--qr",
|
||||||
help=(
|
help=(
|
||||||
@@ -1356,44 +1501,9 @@ def initParser():
|
|||||||
action="store_true",
|
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(
|
group.add_argument(
|
||||||
"--ch-enable",
|
"--ch-enable",
|
||||||
help="Enable the specified channel",
|
help="Enable the specified channel. Use --ch-add instead whenever possible.",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
dest="ch_enable",
|
dest="ch_enable",
|
||||||
default=False,
|
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.)
|
# Note: We are doing a double negative here (Do we want to disable? If ch_disable==True, then disable.)
|
||||||
group.add_argument(
|
group.add_argument(
|
||||||
"--ch-disable",
|
"--ch-disable",
|
||||||
help="Disable the specified channel",
|
help="Disable the specified channel Use --ch-del instead whenever possible.",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
dest="ch_disable",
|
dest="ch_disable",
|
||||||
default=False,
|
default=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
group.add_argument(
|
return parser
|
||||||
"--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",
|
|
||||||
)
|
|
||||||
|
|
||||||
group.add_argument(
|
def addPositionConfigArgs(parser: argparse.ArgumentParser) -> argparse.ArgumentParser:
|
||||||
"--channel-fetch-attempts",
|
"""Add arguments to do with fixed positions and position config"""
|
||||||
help=("Attempt to retrieve channel settings for --ch-set this many times before giving up."),
|
|
||||||
default=3,
|
|
||||||
type=int,
|
|
||||||
)
|
|
||||||
|
|
||||||
group.add_argument(
|
group = parser.add_argument_group(
|
||||||
"--timeout",
|
"Position Configuration",
|
||||||
help="How long to wait for replies",
|
"Arguments that modify fixed position and other position-related configuration.",
|
||||||
default=300,
|
|
||||||
type=int,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
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(
|
group.add_argument(
|
||||||
"--setalt",
|
"--setalt",
|
||||||
help="Set device altitude in meters (allows use without GPS), and enable fixed position. "
|
help="Set device altitude in meters (allows use without GPS), and enable fixed position. "
|
||||||
@@ -1659,6 +1559,221 @@ def initParser():
|
|||||||
nargs="*",
|
nargs="*",
|
||||||
action="store",
|
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(
|
group.add_argument(
|
||||||
"--debug", help="Show API library debug log messages", action="store_true"
|
"--debug", help="Show API library debug log messages", action="store_true"
|
||||||
@@ -1670,6 +1785,33 @@ def initParser():
|
|||||||
action="store_true",
|
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_group = parser.add_argument_group(
|
||||||
"Power Testing", "Options for power testing/logging."
|
"Power Testing", "Options for power testing/logging."
|
||||||
)
|
)
|
||||||
@@ -1718,37 +1860,12 @@ def initParser():
|
|||||||
|
|
||||||
power_group.add_argument(
|
power_group.add_argument(
|
||||||
"--slog",
|
"--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="?",
|
nargs="?",
|
||||||
default=None,
|
default=None,
|
||||||
const="default",
|
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(
|
remoteHardwareArgs = parser.add_argument_group(
|
||||||
"Remote Hardware", "Arguments related to the Remote Hardware module"
|
"Remote Hardware", "Arguments related to the Remote Hardware module"
|
||||||
|
|||||||
@@ -218,7 +218,7 @@ class BLEInterface(MeshInterface):
|
|||||||
logging.error(f"Error closing mesh interface: {e}")
|
logging.error(f"Error closing mesh interface: {e}")
|
||||||
|
|
||||||
if self._want_receive:
|
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:
|
if self._receiveThread:
|
||||||
self._receiveThread.join(
|
self._receiveThread.join(
|
||||||
timeout=2
|
timeout=2
|
||||||
@@ -230,6 +230,7 @@ class BLEInterface(MeshInterface):
|
|||||||
self.client.disconnect()
|
self.client.disconnect()
|
||||||
self.client.close()
|
self.client.close()
|
||||||
self.client = None
|
self.client = None
|
||||||
|
self._disconnected() # send the disconnected indicator up to clients
|
||||||
|
|
||||||
|
|
||||||
class BLEClient:
|
class BLEClient:
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
"""Mesh Interface class
|
"""Mesh Interface class
|
||||||
"""
|
"""
|
||||||
|
# pylint: disable=R0917
|
||||||
|
|
||||||
import collections
|
import collections
|
||||||
import json
|
import json
|
||||||
@@ -605,32 +606,38 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
destinationId: Union[int, str] = BROADCAST_ADDR,
|
destinationId: Union[int, str] = BROADCAST_ADDR,
|
||||||
wantResponse: bool = False,
|
wantResponse: bool = False,
|
||||||
channelIndex: int = 0,
|
channelIndex: int = 0,
|
||||||
|
telemetryType: str = "device_metrics"
|
||||||
):
|
):
|
||||||
"""Send telemetry and optionally ask for a response"""
|
"""Send telemetry and optionally ask for a response"""
|
||||||
r = telemetry_pb2.Telemetry()
|
r = telemetry_pb2.Telemetry()
|
||||||
|
|
||||||
if self.nodes is not None:
|
if telemetryType == "environment_metrics":
|
||||||
node = next(
|
r.environment_metrics.CopyFrom(telemetry_pb2.EnvironmentMetrics())
|
||||||
n for n in self.nodes.values() if n["num"] == self.localNode.nodeNum
|
elif telemetryType == "air_quality_metrics":
|
||||||
)
|
r.air_quality_metrics.CopyFrom(telemetry_pb2.AirQualityMetrics())
|
||||||
if node is not None:
|
elif telemetryType == "power_metrics":
|
||||||
metrics = node.get("deviceMetrics")
|
r.power_metrics.CopyFrom(telemetry_pb2.PowerMetrics())
|
||||||
if metrics:
|
else: # fall through to device metrics
|
||||||
batteryLevel = metrics.get("batteryLevel")
|
if self.nodesByNum is not None:
|
||||||
if batteryLevel is not None:
|
node = self.nodesByNum.get(self.localNode.nodeNum)
|
||||||
r.device_metrics.battery_level = batteryLevel
|
if node is not None:
|
||||||
voltage = metrics.get("voltage")
|
metrics = node.get("deviceMetrics")
|
||||||
if voltage is not None:
|
if metrics:
|
||||||
r.device_metrics.voltage = voltage
|
batteryLevel = metrics.get("batteryLevel")
|
||||||
channel_utilization = metrics.get("channelUtilization")
|
if batteryLevel is not None:
|
||||||
if channel_utilization is not None:
|
r.device_metrics.battery_level = batteryLevel
|
||||||
r.device_metrics.channel_utilization = channel_utilization
|
voltage = metrics.get("voltage")
|
||||||
air_util_tx = metrics.get("airUtilTx")
|
if voltage is not None:
|
||||||
if air_util_tx is not None:
|
r.device_metrics.voltage = voltage
|
||||||
r.device_metrics.air_util_tx = air_util_tx
|
channel_utilization = metrics.get("channelUtilization")
|
||||||
uptime_seconds = metrics.get("uptimeSeconds")
|
if channel_utilization is not None:
|
||||||
if uptime_seconds is not None:
|
r.device_metrics.channel_utilization = channel_utilization
|
||||||
r.device_metrics.uptime_seconds = uptime_seconds
|
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:
|
if wantResponse:
|
||||||
onResponse = self.onResponseTelemetry
|
onResponse = self.onResponseTelemetry
|
||||||
@@ -654,22 +661,32 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
self._acknowledgment.receivedTelemetry = True
|
self._acknowledgment.receivedTelemetry = True
|
||||||
telemetry = telemetry_pb2.Telemetry()
|
telemetry = telemetry_pb2.Telemetry()
|
||||||
telemetry.ParseFromString(p["decoded"]["payload"])
|
telemetry.ParseFromString(p["decoded"]["payload"])
|
||||||
|
|
||||||
print("Telemetry received:")
|
print("Telemetry received:")
|
||||||
if telemetry.device_metrics.battery_level is not None:
|
# Check if the telemetry message has the device_metrics field
|
||||||
print(f"Battery level: {telemetry.device_metrics.battery_level:.2f}%")
|
# This is the original code that was the default for --request-telemetry and is kept for compatibility
|
||||||
if telemetry.device_metrics.voltage is not None:
|
if telemetry.HasField("device_metrics"):
|
||||||
print(f"Voltage: {telemetry.device_metrics.voltage:.2f} V")
|
if telemetry.device_metrics.battery_level is not None:
|
||||||
if telemetry.device_metrics.channel_utilization is not None:
|
print(f"Battery level: {telemetry.device_metrics.battery_level:.2f}%")
|
||||||
print(
|
if telemetry.device_metrics.voltage is not None:
|
||||||
f"Total channel utilization: {telemetry.device_metrics.channel_utilization:.2f}%"
|
print(f"Voltage: {telemetry.device_metrics.voltage:.2f} V")
|
||||||
)
|
if telemetry.device_metrics.channel_utilization is not None:
|
||||||
if telemetry.device_metrics.air_util_tx is not None:
|
print(
|
||||||
print(
|
f"Total channel utilization: {telemetry.device_metrics.channel_utilization:.2f}%"
|
||||||
f"Transmit air utilization: {telemetry.device_metrics.air_util_tx:.2f}%"
|
)
|
||||||
)
|
if telemetry.device_metrics.air_util_tx is not None:
|
||||||
if telemetry.device_metrics.uptime_seconds is not None:
|
print(
|
||||||
print(f"Uptime: {telemetry.device_metrics.uptime_seconds} s")
|
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":
|
elif p["decoded"]["portnum"] == "ROUTING_APP":
|
||||||
if p["decoded"]["routing"]["errorReason"] == "NO_RESPONSE":
|
if p["decoded"]["routing"]["errorReason"] == "NO_RESPONSE":
|
||||||
@@ -914,6 +931,8 @@ class MeshInterface: # pylint: disable=R0902
|
|||||||
startConfig = mesh_pb2.ToRadio()
|
startConfig = mesh_pb2.ToRadio()
|
||||||
if self.configId is None or not self.noNodes:
|
if self.configId is None or not self.noNodes:
|
||||||
self.configId = random.randint(0, 0xFFFFFFFF)
|
self.configId = random.randint(0, 0xFFFFFFFF)
|
||||||
|
if self.configId == NODELESS_WANT_CONFIG_ID:
|
||||||
|
self.configId = self.configId + 1
|
||||||
startConfig.want_config_id = self.configId
|
startConfig.want_config_id = self.configId
|
||||||
self._sendToRadio(startConfig)
|
self._sendToRadio(startConfig)
|
||||||
|
|
||||||
|
|||||||
12
meshtastic/protobuf/mesh_pb2.py
generated
12
meshtastic/protobuf/mesh_pb2.py
generated
File diff suppressed because one or more lines are too long
44
meshtastic/protobuf/mesh_pb2.pyi
generated
44
meshtastic/protobuf/mesh_pb2.pyi
generated
@@ -129,6 +129,10 @@ class _HardwareModelEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._
|
|||||||
"""
|
"""
|
||||||
Heltec HRU-3601: https://heltec.org/project/hru-3601/
|
Heltec HRU-3601: https://heltec.org/project/hru-3601/
|
||||||
"""
|
"""
|
||||||
|
HELTEC_WIRELESS_BRIDGE: _HardwareModel.ValueType # 24
|
||||||
|
"""
|
||||||
|
Heltec Wireless Bridge
|
||||||
|
"""
|
||||||
STATION_G1: _HardwareModel.ValueType # 25
|
STATION_G1: _HardwareModel.ValueType # 25
|
||||||
"""
|
"""
|
||||||
B&Q Consulting Station Edition G1: https://uniteng.com/wiki/doku.php?id=meshtastic:station
|
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
|
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
|
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
|
^^^ short A0 to switch to I2C address 0x3C
|
||||||
"""
|
"""
|
||||||
M5STACK_COREBASIC: _HardwareModel.ValueType # 77
|
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
|
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
|
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 HRU-3601: https://heltec.org/project/hru-3601/
|
||||||
"""
|
"""
|
||||||
|
HELTEC_WIRELESS_BRIDGE: HardwareModel.ValueType # 24
|
||||||
|
"""
|
||||||
|
Heltec Wireless Bridge
|
||||||
|
"""
|
||||||
STATION_G1: HardwareModel.ValueType # 25
|
STATION_G1: HardwareModel.ValueType # 25
|
||||||
"""
|
"""
|
||||||
B&Q Consulting Station Edition G1: https://uniteng.com/wiki/doku.php?id=meshtastic:station
|
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
|
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
|
HELTEC_V3: HardwareModel.ValueType # 43
|
||||||
"""
|
"""
|
||||||
@@ -704,8 +726,22 @@ https://www.adafruit.com/product/938
|
|||||||
^^^ short A0 to switch to I2C address 0x3C
|
^^^ short A0 to switch to I2C address 0x3C
|
||||||
"""
|
"""
|
||||||
M5STACK_COREBASIC: HardwareModel.ValueType # 77
|
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
|
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
|
PRIVATE_HW: HardwareModel.ValueType # 255
|
||||||
"""
|
"""
|
||||||
------------------------------------------------------------------------------------------------------------------------------------------
|
------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|||||||
68
meshtastic/protobuf/module_config_pb2.py
generated
68
meshtastic/protobuf/module_config_pb2.py
generated
File diff suppressed because one or more lines are too long
62
meshtastic/protobuf/module_config_pb2.pyi
generated
62
meshtastic/protobuf/module_config_pb2.pyi
generated
@@ -250,13 +250,54 @@ class ModuleConfig(google.protobuf.message.Message):
|
|||||||
|
|
||||||
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
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
|
ENABLED_FIELD_NUMBER: builtins.int
|
||||||
MINIMUM_BROADCAST_SECS_FIELD_NUMBER: builtins.int
|
MINIMUM_BROADCAST_SECS_FIELD_NUMBER: builtins.int
|
||||||
STATE_BROADCAST_SECS_FIELD_NUMBER: builtins.int
|
STATE_BROADCAST_SECS_FIELD_NUMBER: builtins.int
|
||||||
SEND_BELL_FIELD_NUMBER: builtins.int
|
SEND_BELL_FIELD_NUMBER: builtins.int
|
||||||
NAME_FIELD_NUMBER: builtins.int
|
NAME_FIELD_NUMBER: builtins.int
|
||||||
MONITOR_PIN_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
|
USE_PULLUP_FIELD_NUMBER: builtins.int
|
||||||
enabled: builtins.bool
|
enabled: builtins.bool
|
||||||
"""
|
"""
|
||||||
@@ -264,13 +305,15 @@ class ModuleConfig(google.protobuf.message.Message):
|
|||||||
"""
|
"""
|
||||||
minimum_broadcast_secs: builtins.int
|
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
|
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
|
Interval in seconds of how often we should send a message to the mesh
|
||||||
When set to 0, only state changes will be broadcasted
|
with the current state regardless of trigger events When set to 0, only
|
||||||
Works as a sort of status heartbeat for peace of mind
|
trigger events will be broadcasted Works as a sort of status heartbeat
|
||||||
|
for peace of mind
|
||||||
"""
|
"""
|
||||||
send_bell: builtins.bool
|
send_bell: builtins.bool
|
||||||
"""
|
"""
|
||||||
@@ -287,10 +330,9 @@ class ModuleConfig(google.protobuf.message.Message):
|
|||||||
"""
|
"""
|
||||||
GPIO pin to monitor for state changes
|
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)
|
The type of trigger event to be used
|
||||||
Otherwise LOW (0)
|
|
||||||
"""
|
"""
|
||||||
use_pullup: builtins.bool
|
use_pullup: builtins.bool
|
||||||
"""
|
"""
|
||||||
@@ -306,10 +348,10 @@ class ModuleConfig(google.protobuf.message.Message):
|
|||||||
send_bell: builtins.bool = ...,
|
send_bell: builtins.bool = ...,
|
||||||
name: builtins.str = ...,
|
name: builtins.str = ...,
|
||||||
monitor_pin: builtins.int = ...,
|
monitor_pin: builtins.int = ...,
|
||||||
detection_triggered_high: builtins.bool = ...,
|
detection_trigger_type: global___ModuleConfig.DetectionSensorConfig.TriggerType.ValueType = ...,
|
||||||
use_pullup: builtins.bool = ...,
|
use_pullup: builtins.bool = ...,
|
||||||
) -> None: ...
|
) -> 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
|
@typing.final
|
||||||
class AudioConfig(google.protobuf.message.Message):
|
class AudioConfig(google.protobuf.message.Message):
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
""" Serial interface class
|
""" Serial interface class
|
||||||
"""
|
"""
|
||||||
|
# pylint: disable=R0917
|
||||||
import logging
|
import logging
|
||||||
import platform
|
import platform
|
||||||
import time
|
import time
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
""" Supported Meshtastic Devices - This is a class and collection of Meshtastic devices.
|
""" 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.
|
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
|
# 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
|
# without installing any libraries that are not currently in the python meshtastic library
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
"""TCPInterface class for interfacing with http endpoint
|
"""TCPInterface class for interfacing with http endpoint
|
||||||
"""
|
"""
|
||||||
|
# pylint: disable=R0917
|
||||||
import logging
|
import logging
|
||||||
import socket
|
import socket
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
"""Meshtastic unit tests for __main__.py"""
|
"""Meshtastic unit tests for __main__.py"""
|
||||||
# pylint: disable=C0302,W0613
|
# pylint: disable=C0302,W0613,R0917
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
"""Meshtastic unit tests for serial_interface.py"""
|
"""Meshtastic unit tests for serial_interface.py"""
|
||||||
|
# pylint: disable=R0917
|
||||||
|
|
||||||
import re
|
import re
|
||||||
from unittest.mock import mock_open, patch
|
from unittest.mock import mock_open, patch
|
||||||
|
|||||||
2827
poetry.lock
generated
2827
poetry.lock
generated
File diff suppressed because it is too large
Load Diff
Submodule protobufs updated: 5709c0a05e...83c78e26e3
@@ -1,13 +1,13 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "meshtastic"
|
name = "meshtastic"
|
||||||
version = "2.5.1"
|
version = "2.5.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"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|
||||||
[tool.poetry.dependencies]
|
[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"
|
pyserial = "^3.5"
|
||||||
protobuf = ">=5.26.0"
|
protobuf = ">=5.26.0"
|
||||||
dotmap = "^1.3.30"
|
dotmap = "^1.3.30"
|
||||||
@@ -19,13 +19,14 @@ requests = "^2.31.0"
|
|||||||
pyparsing = "^3.1.2"
|
pyparsing = "^3.1.2"
|
||||||
pyyaml = "^6.0.1"
|
pyyaml = "^6.0.1"
|
||||||
pypubsub = "^4.0.3"
|
pypubsub = "^4.0.3"
|
||||||
bleak = "^0.21.1"
|
bleak = "^0.22.3"
|
||||||
packaging = "^24.0"
|
packaging = "^24.0"
|
||||||
print-color = "^0.4.6"
|
print-color = "^0.4.6"
|
||||||
dash = { version = "^2.17.1", optional = true }
|
dash = { version = "^2.17.1", optional = true }
|
||||||
pytap2 = { version = "^2.3.0", optional = true }
|
pytap2 = { version = "^2.3.0", optional = true }
|
||||||
dash-bootstrap-components = { version = "^1.6.0", optional = true }
|
dash-bootstrap-components = { version = "^1.6.0", optional = true }
|
||||||
pandas = { version = "^2.2.2", optional = true }
|
pandas = { version = "^2.2.2", optional = true }
|
||||||
|
pandas-stubs = { version = "^2.2.2.240603", optional = true }
|
||||||
|
|
||||||
[tool.poetry.group.dev.dependencies]
|
[tool.poetry.group.dev.dependencies]
|
||||||
hypothesis = "^6.103.2"
|
hypothesis = "^6.103.2"
|
||||||
@@ -43,7 +44,6 @@ types-requests = "^2.31.0.20240406"
|
|||||||
types-setuptools = "^69.5.0.20240423"
|
types-setuptools = "^69.5.0.20240423"
|
||||||
types-pyyaml = "^6.0.12.20240311"
|
types-pyyaml = "^6.0.12.20240311"
|
||||||
pyarrow-stubs = "^10.0.1.7"
|
pyarrow-stubs = "^10.0.1.7"
|
||||||
pandas-stubs = "^2.2.2.240603"
|
|
||||||
|
|
||||||
[tool.poetry.group.powermon]
|
[tool.poetry.group.powermon]
|
||||||
optional = true
|
optional = true
|
||||||
|
|||||||
Reference in New Issue
Block a user