mirror of
https://github.com/meshtastic/python.git
synced 2026-08-01 01:26:55 -04:00
Compare commits
1 Commits
2.7.10
...
dependabot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4f2ce3dfc2 |
@@ -66,14 +66,6 @@ from meshtastic.version import get_active_version
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Map dotted preference paths to the protobuf enum that defines their flags.
|
||||
# These fields are stored as uint32 bitmasks in the protobuf but have an
|
||||
# associated enum that names the individual flags.
|
||||
BITFIELD_ENUMS = {
|
||||
"network.enabled_protocols": config_pb2.Config.NetworkConfig.ProtocolFlags,
|
||||
"position.position_flags": config_pb2.Config.PositionConfig.PositionFlags,
|
||||
}
|
||||
|
||||
def onReceive(packet, interface) -> None:
|
||||
"""Callback invoked when a packet arrives"""
|
||||
args = mt_config.args
|
||||
@@ -246,28 +238,13 @@ def setPref(config, comp_name, raw_val) -> bool:
|
||||
print("Warning: network.wifi_psk must be 8 or more characters.")
|
||||
return False
|
||||
|
||||
# Handle uint32 bitfields that have an associated enum of flag names.
|
||||
bitfield_enum = None
|
||||
if config_type.message_type is not None:
|
||||
bitfield_path = f"{config_type.name}.{pref.name}"
|
||||
bitfield_enum = BITFIELD_ENUMS.get(bitfield_path)
|
||||
if bitfield_enum and isinstance(val, str):
|
||||
# At this point fromStr() could not parse val as int/float/bool/bytes,
|
||||
# so treat it as a comma-separated list of bitfield flag names.
|
||||
flag_names = [name.strip() for name in val.split(",") if name.strip()]
|
||||
try:
|
||||
val = meshtastic.util.flags_from_list(bitfield_enum, flag_names)
|
||||
except ValueError as e:
|
||||
print(f"ERROR: {e}")
|
||||
return False
|
||||
|
||||
enumType = pref.enum_type
|
||||
# pylint: disable=C0123
|
||||
if enumType and type(val) == str:
|
||||
# We've failed so far to convert this string into an enum, try to find it by reflection
|
||||
ev = enumType.values_by_name.get(val)
|
||||
if ev:
|
||||
val = ev.number
|
||||
e = enumType.values_by_name.get(val)
|
||||
if e:
|
||||
val = e.number
|
||||
else:
|
||||
print(
|
||||
f"{name[0]}.{uni_name} does not have an enum called {val}, so you can not set it."
|
||||
@@ -1406,11 +1383,6 @@ def common():
|
||||
if not stripped_ham_name:
|
||||
meshtastic.util.our_exit("ERROR: Ham radio callsign cannot be empty or contain only whitespace characters")
|
||||
|
||||
# Early validation for OTA firmware file before attempting device connection
|
||||
if hasattr(args, 'ota_update') and args.ota_update is not None:
|
||||
if not os.path.isfile(args.ota_update):
|
||||
meshtastic.util.our_exit(f"Error: OTA firmware file not found: {args.ota_update}", 1)
|
||||
|
||||
if have_powermon:
|
||||
create_power_meter()
|
||||
|
||||
@@ -1644,12 +1616,10 @@ def addConnectionArgs(parser: argparse.ArgumentParser) -> argparse.ArgumentParse
|
||||
"--host",
|
||||
"--tcp",
|
||||
"-t",
|
||||
help=("Connect to a device using TCP, optionally passing hostname or IP address to use. (defaults to '%(const)s'). "
|
||||
"A port number may be specified as well, e.g. meshtastic.local:4404. The default port is 4403."),
|
||||
help="Connect to a device using TCP, optionally passing hostname or IP address to use. (defaults to '%(const)s')",
|
||||
nargs="?",
|
||||
default=None,
|
||||
const="localhost",
|
||||
metavar="HOST[:PORT]",
|
||||
)
|
||||
|
||||
group.add_argument(
|
||||
@@ -1979,8 +1949,7 @@ def addPositionConfigArgs(parser: argparse.ArgumentParser) -> argparse.ArgumentP
|
||||
|
||||
group.add_argument(
|
||||
"--pos-fields",
|
||||
help="Deprecated: use '--set position.position_flags FLAG1,FLAG2' instead. "
|
||||
"Specify fields to send when sending a position. Use no argument for a list of valid values. "
|
||||
help="Specify fields to send when sending a position. Use no argument for a list of valid values. "
|
||||
"Can pass multiple values as a space separated list like "
|
||||
"this: '--pos-fields ALTITUDE HEADING SPEED'",
|
||||
nargs="*",
|
||||
|
||||
20
meshtastic/protobuf/mesh_pb2.py
generated
20
meshtastic/protobuf/mesh_pb2.py
generated
File diff suppressed because one or more lines are too long
32
meshtastic/protobuf/mesh_pb2.pyi
generated
32
meshtastic/protobuf/mesh_pb2.pyi
generated
@@ -594,22 +594,6 @@ class _HardwareModelEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._
|
||||
"""
|
||||
Lilygo T-Echo Card
|
||||
"""
|
||||
SEEED_WIO_TRACKER_L2: _HardwareModel.ValueType # 137
|
||||
"""
|
||||
Seeed Tracker L2
|
||||
"""
|
||||
CROWPANEL_P4: _HardwareModel.ValueType # 138
|
||||
"""
|
||||
Elecrow CrowPanel Advance P4 models, ESP32-P4 and TFT with SX1262 radio plugin
|
||||
"""
|
||||
HELTEC_MESH_TOWER_V2: _HardwareModel.ValueType # 139
|
||||
"""
|
||||
Heltec Mesh Tower V2
|
||||
"""
|
||||
MESHNOLOGY_W10: _HardwareModel.ValueType # 140
|
||||
"""
|
||||
Meshnology W10
|
||||
"""
|
||||
PRIVATE_HW: _HardwareModel.ValueType # 255
|
||||
"""
|
||||
------------------------------------------------------------------------------------------------------------------------------------------
|
||||
@@ -1187,22 +1171,6 @@ T_ECHO_CARD: HardwareModel.ValueType # 136
|
||||
"""
|
||||
Lilygo T-Echo Card
|
||||
"""
|
||||
SEEED_WIO_TRACKER_L2: HardwareModel.ValueType # 137
|
||||
"""
|
||||
Seeed Tracker L2
|
||||
"""
|
||||
CROWPANEL_P4: HardwareModel.ValueType # 138
|
||||
"""
|
||||
Elecrow CrowPanel Advance P4 models, ESP32-P4 and TFT with SX1262 radio plugin
|
||||
"""
|
||||
HELTEC_MESH_TOWER_V2: HardwareModel.ValueType # 139
|
||||
"""
|
||||
Heltec Mesh Tower V2
|
||||
"""
|
||||
MESHNOLOGY_W10: HardwareModel.ValueType # 140
|
||||
"""
|
||||
Meshnology W10
|
||||
"""
|
||||
PRIVATE_HW: HardwareModel.ValueType # 255
|
||||
"""
|
||||
------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -20,14 +20,12 @@ from meshtastic.__main__ import (
|
||||
onConnection,
|
||||
onNode,
|
||||
onReceive,
|
||||
setPref,
|
||||
tunnelMain,
|
||||
set_missing_flags_false,
|
||||
)
|
||||
from meshtastic import mt_config
|
||||
|
||||
from ..protobuf.channel_pb2 import Channel # pylint: disable=E0611
|
||||
from ..protobuf.config_pb2 import Config # pylint: disable=E0611
|
||||
|
||||
# from ..ble_interface import BLEInterface
|
||||
from ..mesh_interface import MeshInterface
|
||||
@@ -3159,9 +3157,6 @@ def test_main_ota_update_file_not_found(capsys):
|
||||
|
||||
assert pytest_wrapped_e.type == SystemExit
|
||||
assert pytest_wrapped_e.value.code == 1
|
||||
out, _ = capsys.readouterr()
|
||||
assert "OTA firmware file not found" in out
|
||||
assert "/nonexistent/firmware.bin" in out
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@@ -3206,48 +3201,3 @@ def test_main_ota_update_retries(mock_our_exit, mock_ota_class, capsys):
|
||||
|
||||
finally:
|
||||
os.unlink(firmware_file)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.usefixtures("reset_mt_config")
|
||||
def test_main_setPref_network_enabled_protocols_by_name(capsys):
|
||||
"""Test setPref() accepts bitfield flag names for network.enabled_protocols."""
|
||||
config = Config()
|
||||
assert setPref(config, "network.enabled_protocols", "UDP_BROADCAST") is True
|
||||
assert config.network.enabled_protocols == 1
|
||||
out, _ = capsys.readouterr()
|
||||
assert "Set network.enabled_protocols to UDP_BROADCAST" in out
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.usefixtures("reset_mt_config")
|
||||
def test_main_setPref_position_flags_multiple(capsys):
|
||||
"""Test setPref() accepts comma-separated bitfield flag names."""
|
||||
config = Config()
|
||||
assert setPref(config, "position.position_flags", "ALTITUDE,SPEED") is True
|
||||
assert config.position.position_flags == 513
|
||||
out, _ = capsys.readouterr()
|
||||
assert "Set position.position_flags to ALTITUDE,SPEED" in out
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.usefixtures("reset_mt_config")
|
||||
def test_main_setPref_bitfield_raw_integer(capsys):
|
||||
"""Test setPref() still accepts raw integers for bitfields."""
|
||||
config = Config()
|
||||
assert setPref(config, "network.enabled_protocols", "0") is True
|
||||
assert config.network.enabled_protocols == 0
|
||||
out, _ = capsys.readouterr()
|
||||
assert "Set network.enabled_protocols to 0" in out
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.usefixtures("reset_mt_config")
|
||||
def test_main_setPref_bitfield_invalid_name(capsys):
|
||||
"""Test setPref() rejects unknown bitfield flag names."""
|
||||
config = Config()
|
||||
assert setPref(config, "network.enabled_protocols", "TCP") is False
|
||||
out, _ = capsys.readouterr()
|
||||
assert "Unknown flag 'TCP'" in out
|
||||
assert "NO_BROADCAST" in out
|
||||
assert "UDP_BROADCAST" in out
|
||||
|
||||
@@ -153,6 +153,25 @@ def test_showNodes_favorite_asterisk_display(capsys, _iface_with_favorite_nodes)
|
||||
assert err == ""
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_showNodes_favorite_field_formatting():
|
||||
"""Test the formatting logic for isFavorite field"""
|
||||
# Test favorite node
|
||||
raw_value = True
|
||||
formatted_value = "*" if raw_value else ""
|
||||
assert formatted_value == "*"
|
||||
|
||||
# Test non-favorite node
|
||||
raw_value = False
|
||||
formatted_value = "*" if raw_value else ""
|
||||
assert formatted_value == ""
|
||||
|
||||
# Test None/missing value
|
||||
raw_value = None
|
||||
formatted_value = "*" if raw_value else ""
|
||||
assert formatted_value == ""
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_showNodes_with_custom_fields_including_favorite(capsys, _iface_with_favorite_nodes):
|
||||
"""Test that isFavorite can be specified in custom showFields"""
|
||||
|
||||
@@ -3,15 +3,13 @@
|
||||
import json
|
||||
import logging
|
||||
import re
|
||||
import time
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from hypothesis import given, strategies as st
|
||||
|
||||
from meshtastic.supported_device import SupportedDevice
|
||||
from meshtastic.protobuf import mesh_pb2, config_pb2
|
||||
from meshtastic.protobuf import mesh_pb2
|
||||
from meshtastic.util import (
|
||||
DEFAULT_KEY,
|
||||
Timeout,
|
||||
@@ -22,8 +20,6 @@ from meshtastic.util import (
|
||||
convert_mac_addr,
|
||||
eliminate_duplicate_port,
|
||||
findPorts,
|
||||
flags_from_list,
|
||||
flags_to_list,
|
||||
fixme,
|
||||
fromPSK,
|
||||
fromStr,
|
||||
@@ -239,111 +235,20 @@ def test_remove_keys_from_dict_nested():
|
||||
assert remove_keys_from_dict(keys, adict) == exp
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_Timeout_waitForSet_found():
|
||||
"""waitForSet returns True when all required attrs are truthy on target."""
|
||||
to = Timeout(0.01)
|
||||
target = SimpleNamespace(foo=1, bar="ok")
|
||||
assert to.waitForSet(target, ("foo", "bar")) is True
|
||||
@pytest.mark.unitslow
|
||||
def test_Timeout_not_found():
|
||||
"""Test Timeout()"""
|
||||
to = Timeout(0.2)
|
||||
attrs = "foo"
|
||||
to.waitForSet("bar", attrs)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@patch("meshtastic.util.time.sleep")
|
||||
def test_Timeout_waitForSet_not_found(mock_sleep): # pylint: disable=unused-argument
|
||||
"""waitForSet returns False when attrs remain falsy until timeout."""
|
||||
to = Timeout(0.01)
|
||||
target = SimpleNamespace(foo=None, bar=0)
|
||||
assert to.waitForSet(target, ("foo", "bar")) is False
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_Timeout_waitForSet_empty_attrs():
|
||||
"""waitForSet returns True immediately when attrs is empty (vacuous truth)."""
|
||||
to = Timeout(0.01)
|
||||
assert to.waitForSet(object(), ()) is True
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_Timeout_reset():
|
||||
"""reset() sets expireTime to now + expireTimeout."""
|
||||
to = Timeout(maxSecs=5)
|
||||
before = time.time()
|
||||
to.reset()
|
||||
assert to.expireTime == pytest.approx(before + 5, abs=0.1)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_Timeout_reset_custom():
|
||||
"""reset(expireTimeout) overrides the stored expireTimeout."""
|
||||
to = Timeout(maxSecs=5)
|
||||
before = time.time()
|
||||
to.reset(expireTimeout=10)
|
||||
assert to.expireTime == pytest.approx(before + 10, abs=0.1)
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_Timeout_waitForAckNak_found():
|
||||
"""waitForAckNak returns True when any acknowledgment attr is set, and clears it."""
|
||||
to = Timeout(0.01)
|
||||
ack = Acknowledgment()
|
||||
ack.receivedAck = True
|
||||
assert to.waitForAckNak(ack) is True
|
||||
assert ack.receivedAck is False # cleared after detection
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@patch("meshtastic.util.time.sleep")
|
||||
def test_Timeout_waitForAckNak_not_found(mock_sleep): # pylint: disable=unused-argument
|
||||
"""waitForAckNak returns False when no acknowledgment attr is set."""
|
||||
to = Timeout(0.01)
|
||||
ack = Acknowledgment()
|
||||
assert to.waitForAckNak(ack) is False
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_Timeout_waitForTraceRoute_found():
|
||||
"""waitForTraceRoute returns True on receivedTraceRoute, and clears it."""
|
||||
to = Timeout(0.01)
|
||||
ack = Acknowledgment()
|
||||
ack.receivedTraceRoute = True
|
||||
assert to.waitForTraceRoute(3.0, ack) is True
|
||||
assert ack.receivedTraceRoute is False
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@patch("meshtastic.util.time.sleep")
|
||||
def test_Timeout_waitForTraceRoute_not_found(mock_sleep): # pylint: disable=unused-argument
|
||||
"""waitForTraceRoute returns False on timeout."""
|
||||
to = Timeout(0.01)
|
||||
ack = Acknowledgment()
|
||||
assert to.waitForTraceRoute(3.0, ack) is False
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_Timeout_waitForTelemetry_found():
|
||||
"""waitForTelemetry returns True when receivedTelemetry is set."""
|
||||
to = Timeout(0.01)
|
||||
ack = Acknowledgment()
|
||||
ack.receivedTelemetry = True
|
||||
assert to.waitForTelemetry(ack) is True
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_Timeout_waitForPosition_found():
|
||||
"""waitForPosition returns True when receivedPosition is set."""
|
||||
to = Timeout(0.01)
|
||||
ack = Acknowledgment()
|
||||
ack.receivedPosition = True
|
||||
assert to.waitForPosition(ack) is True
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_Timeout_waitForWaypoint_found():
|
||||
"""waitForWaypoint returns True when receivedWaypoint is set."""
|
||||
to = Timeout(0.01)
|
||||
ack = Acknowledgment()
|
||||
ack.receivedWaypoint = True
|
||||
assert to.waitForWaypoint(ack) is True
|
||||
@pytest.mark.unitslow
|
||||
def test_Timeout_found():
|
||||
"""Test Timeout()"""
|
||||
to = Timeout(0.2)
|
||||
attrs = ()
|
||||
to.waitForSet("bar", attrs)
|
||||
|
||||
|
||||
@pytest.mark.unitslow
|
||||
@@ -877,96 +782,3 @@ def test_to_node_num_hypothesis_roundtrip(n):
|
||||
assert to_node_num(f"!{n:08x}") == n
|
||||
assert to_node_num(f"0x{n:x}") == n
|
||||
assert to_node_num(str(n)) == n
|
||||
|
||||
|
||||
_EXCLUDED_MODULES = mesh_pb2.ExcludedModules
|
||||
_POSITION_FLAGS = config_pb2.Config.PositionConfig.PositionFlags
|
||||
_NETWORK_PROTOCOLS = config_pb2.Config.NetworkConfig.ProtocolFlags
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.parametrize("flag_type,flags,expected", [
|
||||
(_EXCLUDED_MODULES, 0, []),
|
||||
(_EXCLUDED_MODULES, 1, ["MQTT_CONFIG"]),
|
||||
(_EXCLUDED_MODULES, 3, ["MQTT_CONFIG", "SERIAL_CONFIG"]),
|
||||
(_EXCLUDED_MODULES, 0x7FFF, [
|
||||
"MQTT_CONFIG", "SERIAL_CONFIG", "EXTNOTIF_CONFIG", "STOREFORWARD_CONFIG",
|
||||
"RANGETEST_CONFIG", "TELEMETRY_CONFIG", "CANNEDMSG_CONFIG", "AUDIO_CONFIG",
|
||||
"REMOTEHARDWARE_CONFIG", "NEIGHBORINFO_CONFIG", "AMBIENTLIGHTING_CONFIG",
|
||||
"DETECTIONSENSOR_CONFIG", "PAXCOUNTER_CONFIG", "BLUETOOTH_CONFIG",
|
||||
"NETWORK_CONFIG",
|
||||
]),
|
||||
(_EXCLUDED_MODULES, 0x8000, ["UNKNOWN_ADDITIONAL_FLAGS(32768)"]),
|
||||
(_EXCLUDED_MODULES, 0x8001, ["MQTT_CONFIG", "UNKNOWN_ADDITIONAL_FLAGS(32768)"]),
|
||||
(_POSITION_FLAGS, 0, []),
|
||||
(_POSITION_FLAGS, 0x09, ["ALTITUDE", "DOP"]),
|
||||
(_POSITION_FLAGS, 0x1FF, [
|
||||
"ALTITUDE", "ALTITUDE_MSL", "GEOIDAL_SEPARATION", "DOP", "HVDOP",
|
||||
"SATINVIEW", "SEQ_NO", "TIMESTAMP", "HEADING",
|
||||
]),
|
||||
])
|
||||
def test_flags_to_list(flag_type, flags, expected):
|
||||
"""Test flags_to_list decodes set bits in enum order and reports unknown remainders."""
|
||||
assert flags_to_list(flag_type, flags) == expected
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@given(st.integers(min_value=0, max_value=0xFFFFF))
|
||||
def test_flags_to_list_conservation(flags):
|
||||
"""Property: flags_to_list partitions `flags` into known names plus an exact unknown remainder.
|
||||
|
||||
Every known bit that is set must appear as a name, and the leftover reported in
|
||||
UNKNOWN_ADDITIONAL_FLAGS(...) must together with the named bits reconstruct the input.
|
||||
"""
|
||||
for flag_type in (_EXCLUDED_MODULES, _POSITION_FLAGS):
|
||||
known_union = 0
|
||||
for key in flag_type.keys():
|
||||
value = flag_type.Value(key)
|
||||
if key != "EXCLUDED_NONE" and value:
|
||||
known_union |= value
|
||||
|
||||
result = flags_to_list(flag_type, flags)
|
||||
|
||||
accounted = 0
|
||||
leftover = 0
|
||||
for name in result:
|
||||
if name.startswith("UNKNOWN_ADDITIONAL_FLAGS("):
|
||||
leftover = int(name[len("UNKNOWN_ADDITIONAL_FLAGS("):-1])
|
||||
else:
|
||||
accounted |= flag_type.Value(name)
|
||||
|
||||
assert accounted == (flags & known_union)
|
||||
assert (accounted | leftover) == flags
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.parametrize("flag_type, flags, expected", [
|
||||
(_NETWORK_PROTOCOLS, ["UDP_BROADCAST"], 1),
|
||||
(_NETWORK_PROTOCOLS, ["NO_BROADCAST"], 0),
|
||||
(_NETWORK_PROTOCOLS, [], 0),
|
||||
(_POSITION_FLAGS, ["ALTITUDE"], 1),
|
||||
(_POSITION_FLAGS, ["ALTITUDE", "SPEED"], 513),
|
||||
(_POSITION_FLAGS, ["ALTITUDE", " SPEED "], 513),
|
||||
])
|
||||
def test_flags_from_list(flag_type, flags, expected):
|
||||
"""Test flags_from_list combines named flags into the expected bitmask."""
|
||||
assert flags_from_list(flag_type, flags) == expected
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
def test_flags_from_list_unknown_flag():
|
||||
"""Test flags_from_list raises ValueError for an unknown flag name."""
|
||||
with pytest.raises(ValueError, match="Unknown flag 'TCP'"):
|
||||
flags_from_list(_NETWORK_PROTOCOLS, ["UDP_BROADCAST", "TCP"])
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@given(st.lists(st.sampled_from(list(_POSITION_FLAGS.keys())), unique=True))
|
||||
def test_flags_from_list_roundtrip(flags):
|
||||
"""Property: flags_from_list and flags_to_list are inverses for known position flags."""
|
||||
combined = flags_from_list(_POSITION_FLAGS, flags)
|
||||
decoded = flags_to_list(_POSITION_FLAGS, combined)
|
||||
# flags_to_list drops zero-value flags and may report unknown remainders,
|
||||
# but for combinations of known non-zero flags it should return the same set of names.
|
||||
nonzero_flags = {f for f in flags if _POSITION_FLAGS.Value(f)}
|
||||
assert set(decoded) == nonzero_flags
|
||||
|
||||
@@ -737,41 +737,14 @@ def to_node_num(node_id: Union[int, str]) -> int:
|
||||
return int(s, 16)
|
||||
|
||||
def flags_to_list(flag_type, flags: int) -> List[str]:
|
||||
"""Given a flag_type that's a protobuf EnumTypeWrapper, and a flag int, give a list of flags enabled.
|
||||
|
||||
Zero-valued members (e.g. EXCLUDED_NONE, UNSET, NO_BROADCAST) never appear in the
|
||||
result: they hold no bit, so `flags & value` is always False for them, and a flags
|
||||
value of 0 therefore decodes to an empty list rather than a named "no flags" entry.
|
||||
Any leftover bits not corresponding to a known member are reported via
|
||||
`UNKNOWN_ADDITIONAL_FLAGS(<leftover>)`.
|
||||
"""
|
||||
"""Given a flag_type that's a protobuf EnumTypeWrapper, and a flag int, give a list of flags enabled."""
|
||||
ret = []
|
||||
for key in flag_type.keys():
|
||||
if key == "EXCLUDED_NONE":
|
||||
continue
|
||||
if flags & flag_type.Value(key):
|
||||
ret.append(key)
|
||||
flags = flags - flag_type.Value(key)
|
||||
if flags > 0:
|
||||
ret.append(f"UNKNOWN_ADDITIONAL_FLAGS({flags})")
|
||||
return ret
|
||||
|
||||
|
||||
def flags_from_list(flag_type, flags: List[str]) -> int:
|
||||
"""Given a flag_type that's a protobuf EnumTypeWrapper, and a list of flag names, return the combined bitmask.
|
||||
|
||||
Zero-valued members (e.g. EXCLUDED_NONE, UNSET, NO_BROADCAST) are accepted but are
|
||||
no-ops: they OR in 0 and thus set nothing. A list consisting solely of such a member
|
||||
(or an empty list) yields 0, which round-trips back through flags_to_list as an
|
||||
empty list rather than the original member name -- see flags_to_list's docstring.
|
||||
"""
|
||||
result = 0
|
||||
valid_names = list(flag_type.keys())
|
||||
for flag_name in flags:
|
||||
flag_name = flag_name.strip()
|
||||
if not flag_name:
|
||||
continue
|
||||
if flag_name not in valid_names:
|
||||
raise ValueError(
|
||||
f"Unknown flag '{flag_name}'. Valid choices: {', '.join(sorted(valid_names))}"
|
||||
)
|
||||
result |= flag_type.Value(flag_name)
|
||||
return result
|
||||
|
||||
90
poetry.lock
generated
90
poetry.lock
generated
@@ -1,4 +1,4 @@
|
||||
# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand.
|
||||
# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand.
|
||||
|
||||
[[package]]
|
||||
name = "altgraph"
|
||||
@@ -3552,7 +3552,7 @@ description = "Type annotations for pandas"
|
||||
optional = true
|
||||
python-versions = ">=3.9"
|
||||
groups = ["main"]
|
||||
markers = "python_version < \"3.11\" and extra == \"analysis\""
|
||||
markers = "extra == \"analysis\""
|
||||
files = [
|
||||
{file = "pandas_stubs-2.2.2.240807-py3-none-any.whl", hash = "sha256:893919ad82be4275f0d07bb47a95d08bae580d3fdea308a7acfcb3f02e76186e"},
|
||||
{file = "pandas_stubs-2.2.2.240807.tar.gz", hash = "sha256:64a559725a57a449f46225fbafc422520b7410bff9252b661a225b5559192a93"},
|
||||
@@ -3562,23 +3562,6 @@ files = [
|
||||
numpy = ">=1.23.5"
|
||||
types-pytz = ">=2022.1.1"
|
||||
|
||||
[[package]]
|
||||
name = "pandas-stubs"
|
||||
version = "2.3.2.250926"
|
||||
description = "Type annotations for pandas"
|
||||
optional = true
|
||||
python-versions = ">=3.10"
|
||||
groups = ["main"]
|
||||
markers = "extra == \"analysis\" and python_version >= \"3.11\""
|
||||
files = [
|
||||
{file = "pandas_stubs-2.3.2.250926-py3-none-any.whl", hash = "sha256:81121818453dcfe00f45c852f4dceee043640b813830f6e7bd084a4ef7ff7270"},
|
||||
{file = "pandas_stubs-2.3.2.250926.tar.gz", hash = "sha256:c64b9932760ceefb96a3222b953e6a251321a9832a28548be6506df473a66406"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
numpy = ">=1.23.5"
|
||||
types-pytz = ">=2022.1.1"
|
||||
|
||||
[[package]]
|
||||
name = "pandocfilters"
|
||||
version = "1.5.1"
|
||||
@@ -4054,7 +4037,6 @@ description = ""
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["main", "dev"]
|
||||
markers = "python_version < \"3.11\""
|
||||
files = [
|
||||
{file = "protobuf-6.33.6-cp310-abi3-win32.whl", hash = "sha256:7d29d9b65f8afef196f8334e80d6bc1d5d4adedb449971fefd3723824e6e77d3"},
|
||||
{file = "protobuf-6.33.6-cp310-abi3-win_amd64.whl", hash = "sha256:0cd27b587afca21b7cfa59a74dcbd48a50f0a6400cfb59391340ad729d91d326"},
|
||||
@@ -4068,25 +4050,6 @@ files = [
|
||||
{file = "protobuf-6.33.6.tar.gz", hash = "sha256:a6768d25248312c297558af96a9f9c929e8c4cee0659cb07e780731095f38135"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "protobuf"
|
||||
version = "7.35.0"
|
||||
description = ""
|
||||
optional = false
|
||||
python-versions = ">=3.10"
|
||||
groups = ["main", "dev"]
|
||||
markers = "python_version >= \"3.11\""
|
||||
files = [
|
||||
{file = "protobuf-7.35.0-cp310-abi3-macosx_10_9_universal2.whl", hash = "sha256:66be6c513931c794fa92c080ffee41671390da3d79da219cf9c0c0907f035dda"},
|
||||
{file = "protobuf-7.35.0-cp310-abi3-manylinux2014_aarch64.whl", hash = "sha256:fcbe42a4ac09d3ec9c987ddfcd956afd0b15f1ff613bd8371bde9405ffd5c8e5"},
|
||||
{file = "protobuf-7.35.0-cp310-abi3-manylinux2014_s390x.whl", hash = "sha256:4cbf5cc286130e06a6c9bbefac442431173906dfcc979712183d4adcc01b37ee"},
|
||||
{file = "protobuf-7.35.0-cp310-abi3-manylinux2014_x86_64.whl", hash = "sha256:6c0f98f10c8a05ea30f8993dfef2de093d27b490fdae78bb60c8343795d55011"},
|
||||
{file = "protobuf-7.35.0-cp310-abi3-win32.whl", hash = "sha256:4c4617b83ade0e279d1d2bfe04025a1adb87f9ed657de038620dc0ff959357f6"},
|
||||
{file = "protobuf-7.35.0-cp310-abi3-win_amd64.whl", hash = "sha256:f05bcadf9a2a6b8dda047007075135fb7d08c73d9177aabc067e1be46881a201"},
|
||||
{file = "protobuf-7.35.0-py3-none-any.whl", hash = "sha256:c13f325cf242bad135c350629eeb5d54b24228eb472fb3e2e9ebbd4c5dc20ca0"},
|
||||
{file = "protobuf-7.35.0.tar.gz", hash = "sha256:a2efd84605f41e559f1881b0912b44099d0a2ac9bf46b3474823f10fb393b0e6"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "psutil"
|
||||
version = "7.1.3"
|
||||
@@ -4443,6 +4406,7 @@ python-versions = ">=3.3, <4"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "Pypubsub-4.0.3-py3-none-any.whl", hash = "sha256:7f716bae9388afe01ff82b264ba8a96a8ae78b42bb1f114f2716ca8f9e404e2a"},
|
||||
{file = "pypubsub-4.0.3.tar.gz", hash = "sha256:32d662de3ade0fb0880da92df209c62a4803684de5ccb8d19421c92747a258c7"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4874,7 +4838,6 @@ description = "Python HTTP for Humans."
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["main", "analysis"]
|
||||
markers = "python_version < \"3.11\""
|
||||
files = [
|
||||
{file = "requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6"},
|
||||
{file = "requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf"},
|
||||
@@ -4890,29 +4853,6 @@ urllib3 = ">=1.21.1,<3"
|
||||
socks = ["PySocks (>=1.5.6,!=1.5.7)"]
|
||||
use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
|
||||
|
||||
[[package]]
|
||||
name = "requests"
|
||||
version = "2.34.2"
|
||||
description = "Python HTTP for Humans."
|
||||
optional = false
|
||||
python-versions = ">=3.10"
|
||||
groups = ["main", "analysis"]
|
||||
markers = "python_version >= \"3.11\""
|
||||
files = [
|
||||
{file = "requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0"},
|
||||
{file = "requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
certifi = ">=2023.5.7"
|
||||
charset_normalizer = ">=2,<4"
|
||||
idna = ">=2.5,<4"
|
||||
urllib3 = ">=1.26,<3"
|
||||
|
||||
[package.extras]
|
||||
socks = ["PySocks (>=1.5.6,!=1.5.7)"]
|
||||
use-chardet-on-py3 = ["chardet (>=3.0.2,<8)"]
|
||||
|
||||
[[package]]
|
||||
name = "retrying"
|
||||
version = "1.4.2"
|
||||
@@ -5515,22 +5455,22 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "tornado"
|
||||
version = "6.5.6"
|
||||
version = "6.5.7"
|
||||
description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed."
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["analysis"]
|
||||
groups = ["analysis", "dev"]
|
||||
files = [
|
||||
{file = "tornado-6.5.6-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:65fcfaafb079435c2c19dc9e07c0f1cf0fa9051759ed0a7d0a3ba7ea7f64919c"},
|
||||
{file = "tornado-6.5.6-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:38bc01b4acacded2de63ae78023548e41ebe6fbed3ec05a796d7ae3ad893887e"},
|
||||
{file = "tornado-6.5.6-cp39-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b942e6a137fda31ff54bf8e6e2c8d1c37f1f50583f3ed53fb840b53b9601d104"},
|
||||
{file = "tornado-6.5.6-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8666946e70171b8c3f1fc9b7876fac492e84822c4c7f3746f4e8f8bc9ac92a79"},
|
||||
{file = "tornado-6.5.6-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1c34cfab7ad6d104f052f55de06d39bbafc5885cfeb4da688803308dbcfa90b7"},
|
||||
{file = "tornado-6.5.6-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:385f35e4e22fb52551dfcda4cdc8c30c61c2c001aef5ddad99cdfe116952efd3"},
|
||||
{file = "tornado-6.5.6-cp39-abi3-win32.whl", hash = "sha256:db475f1b67b2809b10bb16264829087724ca8d24fe4ed47f7b8675cae453ef86"},
|
||||
{file = "tornado-6.5.6-cp39-abi3-win_amd64.whl", hash = "sha256:6739bf1e8eb09230f1280ddbd3236f0309db70f2c551a8dbc40f62babdf82f79"},
|
||||
{file = "tornado-6.5.6-cp39-abi3-win_arm64.whl", hash = "sha256:2543597b24a695d72338a9a77818362d72387c03ae173f1f169eadc5c91466ac"},
|
||||
{file = "tornado-6.5.6.tar.gz", hash = "sha256:9a365179fe8ff6b8766f602c0f67c185d778193e9bdd828b19f0b6ed7764177d"},
|
||||
{file = "tornado-6.5.7-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:148b2eb15c2c765a50796172c1e499649b35f30d2e3c3d3e15913cfa56bfb163"},
|
||||
{file = "tornado-6.5.7-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:9da38de27f1da3b78a966f0dae12b5a1ea9afe72ca805d84ff06508272ddf100"},
|
||||
{file = "tornado-6.5.7-cp39-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8d759e71906ee783f8867b93bf26a265743da4c1e2f4a018464c1ba019862972"},
|
||||
{file = "tornado-6.5.7-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8a46347a18f23fb92b396beebe0fb78f61dda0cc302445202c16203d8a18848b"},
|
||||
{file = "tornado-6.5.7-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:7778b30bef919231265e91c69963ce0f49a1e9c07ac900bbe75b19ce2575ba92"},
|
||||
{file = "tornado-6.5.7-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e726f0c75da7726eec023aa62751ff8878bd2737e34fbdd33b1ae5897d2200f5"},
|
||||
{file = "tornado-6.5.7-cp39-abi3-win32.whl", hash = "sha256:f8de3bf12d3efdd0cbe7c8887868198f8a91415e3f29fcf258d9b8eb7b1d9ae4"},
|
||||
{file = "tornado-6.5.7-cp39-abi3-win_amd64.whl", hash = "sha256:de942f843533a039ef9fa3d9c88c7cd8a7c94553fb5ad0154270989b3d99a2c4"},
|
||||
{file = "tornado-6.5.7-cp39-abi3-win_arm64.whl", hash = "sha256:ff934fce95643af5f11efdae618eaa73d469dc588641e5c8d19295a0c65c4796"},
|
||||
{file = "tornado-6.5.7.tar.gz", hash = "sha256:66c513a76cda70d53907bc27cf1447557699c2e95aa48ba27a442ff61c3ddfc2"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
Submodule protobufs updated: da60cee584...dd6c3f850a
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "meshtastic"
|
||||
version = "2.7.10"
|
||||
version = "2.7.9"
|
||||
description = "Python API & client shell for talking to Meshtastic devices"
|
||||
authors = ["Meshtastic Developers <contact@meshtastic.org>"]
|
||||
license = "GPL-3.0-only"
|
||||
|
||||
Reference in New Issue
Block a user