Compare commits

..

15 Commits

Author SHA1 Message Date
github-actions
9a72e36ca6 bump version to 2.7.6 2026-01-05 02:25:14 +00:00
Ian McEwen
4d54ee7431 protobufs: v2.7.17 2026-01-04 19:21:54 -07:00
Ian McEwen
5cc0dae394 protobufs: v2.7.15 2025-11-20 14:30:39 -07:00
Ian McEwen
1a50377d34 Merge pull request #865 from ianmcorvidae/device-metadata-details
Add a bunch more detail to --device-metadata output, from fields we weren't formerly using in the output.
2025-11-20 14:29:20 -07:00
Ian McEwen
2a44be9269 Add a bunch more detail to --device-metadata output, from fields we weren't formerly using in the output. 2025-11-20 11:42:57 -07:00
github-actions
776debcc86 bump version to 2.7.5 2025-11-18 19:48:40 +00:00
Ian McEwen
1088cc607e Merge branch 'revert-close-refactor' 2025-11-13 12:10:46 -07:00
Ian McEwen
aeec5447ed Revert "Merge pull request #841 from SpudGunMan/master"
This reverts commit b4662251ed, reversing
changes made to 2065598754.
2025-11-13 12:08:42 -07:00
Ian McEwen
096fec95c8 Revert "Wrap double-close in a try-catch. Slightly ugly but oh well."
This reverts commit dbc0101a7a.
2025-11-13 12:07:48 -07:00
Ian McEwen
dea5f788a2 Revert "Add more exception logging, fix some additional stream read/write issues"
This reverts commit f15a0bdc0b.
2025-11-13 12:07:32 -07:00
Ian McEwen
f15a0bdc0b Add more exception logging, fix some additional stream read/write issues 2025-11-13 11:38:00 -07:00
Ian McEwen
0906fc6bc0 Merge pull request #858 from Travis-L-R/conf_work
Splitting out true_defaults for moduleConfig in export_config()
2025-11-12 10:16:19 -07:00
Travis-L-R
ccb530574b Splitting true_defaults for moduleConfig out in export_config() 2025-11-11 21:03:01 +10:30
Ian McEwen
dbc0101a7a Wrap double-close in a try-catch. Slightly ugly but oh well. 2025-11-10 13:58:12 -07:00
Ian McEwen
debbb8caeb Update some dependencies 2025-11-10 13:09:44 -07:00
17 changed files with 3698 additions and 1833 deletions

View File

@@ -1147,13 +1147,16 @@ def export_config(interface) -> str:
configObj = {} configObj = {}
# A list of configuration keys that should be set to False if they are missing # A list of configuration keys that should be set to False if they are missing
true_defaults = { config_true_defaults = {
("bluetooth", "enabled"), ("bluetooth", "enabled"),
("lora", "sx126xRxBoostedGain"), ("lora", "sx126xRxBoostedGain"),
("lora", "txEnabled"), ("lora", "txEnabled"),
("lora", "usePreset"), ("lora", "usePreset"),
("position", "positionBroadcastSmartEnabled"), ("position", "positionBroadcastSmartEnabled"),
("security", "serialEnabled"), ("security", "serialEnabled"),
}
module_true_defaults = {
("mqtt", "encryptionEnabled"), ("mqtt", "encryptionEnabled"),
} }
@@ -1215,7 +1218,7 @@ def export_config(interface) -> str:
else: else:
configObj["config"] = config configObj["config"] = config
set_missing_flags_false(configObj["config"], true_defaults) set_missing_flags_false(configObj["config"], config_true_defaults)
module_config = MessageToDict(interface.localNode.moduleConfig) module_config = MessageToDict(interface.localNode.moduleConfig)
if module_config: if module_config:
@@ -1229,6 +1232,8 @@ def export_config(interface) -> str:
else: else:
configObj["module_config"] = prefs configObj["module_config"] = prefs
set_missing_flags_false(configObj["module_config"], module_true_defaults)
config_txt = "# start of Meshtastic configure yaml\n" #checkme - "config" (now changed to config_out) config_txt = "# start of Meshtastic configure yaml\n" #checkme - "config" (now changed to config_out)
#was used as a string here and a Dictionary above #was used as a string here and a Dictionary above
config_txt += yaml.dump(configObj) config_txt += yaml.dump(configObj)

View File

@@ -7,7 +7,7 @@ import time
from typing import Optional, Union, List from typing import Optional, Union, List
from meshtastic.protobuf import admin_pb2, apponly_pb2, channel_pb2, localonly_pb2, mesh_pb2, portnums_pb2 from meshtastic.protobuf import admin_pb2, apponly_pb2, channel_pb2, config_pb2, localonly_pb2, mesh_pb2, portnums_pb2
from meshtastic.util import ( from meshtastic.util import (
Timeout, Timeout,
camel_to_snake, camel_to_snake,
@@ -18,6 +18,7 @@ from meshtastic.util import (
message_to_json, message_to_json,
generate_channel_hash, generate_channel_hash,
to_node_num, to_node_num,
flags_to_list,
) )
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -54,6 +55,16 @@ class Node:
r += ")" r += ")"
return r return r
@staticmethod
def position_flags_list(position_flags: int) -> List[str]:
"Return a list of position flags from the given flags integer"
return flags_to_list(config_pb2.Config.PositionConfig.PositionFlags, position_flags)
@staticmethod
def excluded_modules_list(excluded_modules: int) -> List[str]:
"Return a list of excluded modules from the given flags integer"
return flags_to_list(mesh_pb2.ExcludedModules, excluded_modules)
def module_available(self, excluded_bit: int) -> bool: def module_available(self, excluded_bit: int) -> bool:
"""Check DeviceMetadata.excluded_modules to see if a module is available.""" """Check DeviceMetadata.excluded_modules to see if a module is available."""
meta = getattr(self.iface, "metadata", None) meta = getattr(self.iface, "metadata", None)
@@ -902,6 +913,18 @@ class Node:
logger.debug(f"Received metadata {stripnl(c)}") logger.debug(f"Received metadata {stripnl(c)}")
print(f"\nfirmware_version: {c.firmware_version}") print(f"\nfirmware_version: {c.firmware_version}")
print(f"device_state_version: {c.device_state_version}") print(f"device_state_version: {c.device_state_version}")
if c.role in config_pb2.Config.DeviceConfig.Role.values():
print(f"role: {config_pb2.Config.DeviceConfig.Role.Name(c.role)}")
else:
print(f"role: {c.role}")
print(f"position_flags: {self.position_flags_list(c.position_flags)}")
if c.hw_model in mesh_pb2.HardwareModel.values():
print(f"hw_model: {mesh_pb2.HardwareModel.Name(c.hw_model)}")
else:
print(f"hw_model: {c.hw_model}")
print(f"hasPKC: {c.hasPKC}")
if c.excluded_modules > 0:
print(f"excluded_modules: {self.excluded_modules_list(c.excluded_modules)}")
def onResponseRequestChannel(self, p): def onResponseRequestChannel(self, p):
"""Handle the response packet for requesting a channel _requestChannel()""" """Handle the response packet for requesting a channel _requestChannel()"""

View File

File diff suppressed because one or more lines are too long

View File

@@ -25,6 +25,44 @@ else:
DESCRIPTOR: google.protobuf.descriptor.FileDescriptor DESCRIPTOR: google.protobuf.descriptor.FileDescriptor
class _OTAMode:
ValueType = typing.NewType("ValueType", builtins.int)
V: typing_extensions.TypeAlias = ValueType
class _OTAModeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_OTAMode.ValueType], builtins.type):
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
NO_REBOOT_OTA: _OTAMode.ValueType # 0
"""
Do not reboot into OTA mode
"""
OTA_BLE: _OTAMode.ValueType # 1
"""
Reboot into OTA mode for BLE firmware update
"""
OTA_WIFI: _OTAMode.ValueType # 2
"""
Reboot into OTA mode for WiFi firmware update
"""
class OTAMode(_OTAMode, metaclass=_OTAModeEnumTypeWrapper):
"""
Firmware update mode for OTA updates
"""
NO_REBOOT_OTA: OTAMode.ValueType # 0
"""
Do not reboot into OTA mode
"""
OTA_BLE: OTAMode.ValueType # 1
"""
Reboot into OTA mode for BLE firmware update
"""
OTA_WIFI: OTAMode.ValueType # 2
"""
Reboot into OTA mode for WiFi firmware update
"""
global___OTAMode = OTAMode
@typing.final @typing.final
class AdminMessage(google.protobuf.message.Message): class AdminMessage(google.protobuf.message.Message):
""" """
@@ -356,6 +394,7 @@ class AdminMessage(google.protobuf.message.Message):
COMMIT_EDIT_SETTINGS_FIELD_NUMBER: builtins.int COMMIT_EDIT_SETTINGS_FIELD_NUMBER: builtins.int
ADD_CONTACT_FIELD_NUMBER: builtins.int ADD_CONTACT_FIELD_NUMBER: builtins.int
KEY_VERIFICATION_FIELD_NUMBER: builtins.int KEY_VERIFICATION_FIELD_NUMBER: builtins.int
REBOOT_OTA_MODE_FIELD_NUMBER: builtins.int
FACTORY_RESET_DEVICE_FIELD_NUMBER: builtins.int FACTORY_RESET_DEVICE_FIELD_NUMBER: builtins.int
REBOOT_OTA_SECONDS_FIELD_NUMBER: builtins.int REBOOT_OTA_SECONDS_FIELD_NUMBER: builtins.int
EXIT_SIMULATOR_FIELD_NUMBER: builtins.int EXIT_SIMULATOR_FIELD_NUMBER: builtins.int
@@ -489,6 +528,10 @@ class AdminMessage(google.protobuf.message.Message):
""" """
Commits an open transaction for any edits made to config, module config, owner, and channel settings Commits an open transaction for any edits made to config, module config, owner, and channel settings
""" """
reboot_ota_mode: global___OTAMode.ValueType
"""
Tell the node to reboot into OTA mode for firmware update via BLE or WiFi (ESP32 only for now)
"""
factory_reset_device: builtins.int factory_reset_device: builtins.int
""" """
Tell the node to factory reset config everything; all device state and configuration will be returned to factory defaults and BLE bonds will be cleared. Tell the node to factory reset config everything; all device state and configuration will be returned to factory defaults and BLE bonds will be cleared.
@@ -497,6 +540,7 @@ class AdminMessage(google.protobuf.message.Message):
""" """
Tell the node to reboot into the OTA Firmware in this many seconds (or <0 to cancel reboot) Tell the node to reboot into the OTA Firmware in this many seconds (or <0 to cancel reboot)
Only Implemented for ESP32 Devices. This needs to be issued to send a new main firmware via bluetooth. Only Implemented for ESP32 Devices. This needs to be issued to send a new main firmware via bluetooth.
Deprecated in favor of reboot_ota_mode in 2.7.17
""" """
exit_simulator: builtins.bool exit_simulator: builtins.bool
""" """
@@ -515,9 +559,10 @@ class AdminMessage(google.protobuf.message.Message):
""" """
Tell the node to factory reset config; all device state and configuration will be returned to factory defaults; BLE bonds will be preserved. Tell the node to factory reset config; all device state and configuration will be returned to factory defaults; BLE bonds will be preserved.
""" """
nodedb_reset: builtins.int nodedb_reset: builtins.bool
""" """
Tell the node to reset the nodedb. Tell the node to reset the nodedb.
When true, favorites are preserved through reset.
""" """
@property @property
def get_channel_response(self) -> meshtastic.protobuf.channel_pb2.Channel: def get_channel_response(self) -> meshtastic.protobuf.channel_pb2.Channel:
@@ -683,17 +728,18 @@ class AdminMessage(google.protobuf.message.Message):
commit_edit_settings: builtins.bool = ..., commit_edit_settings: builtins.bool = ...,
add_contact: global___SharedContact | None = ..., add_contact: global___SharedContact | None = ...,
key_verification: global___KeyVerificationAdmin | None = ..., key_verification: global___KeyVerificationAdmin | None = ...,
reboot_ota_mode: global___OTAMode.ValueType = ...,
factory_reset_device: builtins.int = ..., factory_reset_device: builtins.int = ...,
reboot_ota_seconds: builtins.int = ..., reboot_ota_seconds: builtins.int = ...,
exit_simulator: builtins.bool = ..., exit_simulator: builtins.bool = ...,
reboot_seconds: builtins.int = ..., reboot_seconds: builtins.int = ...,
shutdown_seconds: builtins.int = ..., shutdown_seconds: builtins.int = ...,
factory_reset_config: builtins.int = ..., factory_reset_config: builtins.int = ...,
nodedb_reset: builtins.int = ..., nodedb_reset: builtins.bool = ...,
) -> None: ... ) -> None: ...
def HasField(self, field_name: typing.Literal["add_contact", b"add_contact", "backup_preferences", b"backup_preferences", "begin_edit_settings", b"begin_edit_settings", "commit_edit_settings", b"commit_edit_settings", "delete_file_request", b"delete_file_request", "enter_dfu_mode_request", b"enter_dfu_mode_request", "exit_simulator", b"exit_simulator", "factory_reset_config", b"factory_reset_config", "factory_reset_device", b"factory_reset_device", "get_canned_message_module_messages_request", b"get_canned_message_module_messages_request", "get_canned_message_module_messages_response", b"get_canned_message_module_messages_response", "get_channel_request", b"get_channel_request", "get_channel_response", b"get_channel_response", "get_config_request", b"get_config_request", "get_config_response", b"get_config_response", "get_device_connection_status_request", b"get_device_connection_status_request", "get_device_connection_status_response", b"get_device_connection_status_response", "get_device_metadata_request", b"get_device_metadata_request", "get_device_metadata_response", b"get_device_metadata_response", "get_module_config_request", b"get_module_config_request", "get_module_config_response", b"get_module_config_response", "get_node_remote_hardware_pins_request", b"get_node_remote_hardware_pins_request", "get_node_remote_hardware_pins_response", b"get_node_remote_hardware_pins_response", "get_owner_request", b"get_owner_request", "get_owner_response", b"get_owner_response", "get_ringtone_request", b"get_ringtone_request", "get_ringtone_response", b"get_ringtone_response", "get_ui_config_request", b"get_ui_config_request", "get_ui_config_response", b"get_ui_config_response", "key_verification", b"key_verification", "nodedb_reset", b"nodedb_reset", "payload_variant", b"payload_variant", "reboot_ota_seconds", b"reboot_ota_seconds", "reboot_seconds", b"reboot_seconds", "remove_backup_preferences", b"remove_backup_preferences", "remove_by_nodenum", b"remove_by_nodenum", "remove_favorite_node", b"remove_favorite_node", "remove_fixed_position", b"remove_fixed_position", "remove_ignored_node", b"remove_ignored_node", "restore_preferences", b"restore_preferences", "send_input_event", b"send_input_event", "set_canned_message_module_messages", b"set_canned_message_module_messages", "set_channel", b"set_channel", "set_config", b"set_config", "set_favorite_node", b"set_favorite_node", "set_fixed_position", b"set_fixed_position", "set_ham_mode", b"set_ham_mode", "set_ignored_node", b"set_ignored_node", "set_module_config", b"set_module_config", "set_owner", b"set_owner", "set_ringtone_message", b"set_ringtone_message", "set_scale", b"set_scale", "set_time_only", b"set_time_only", "shutdown_seconds", b"shutdown_seconds", "store_ui_config", b"store_ui_config"]) -> builtins.bool: ... def HasField(self, field_name: typing.Literal["add_contact", b"add_contact", "backup_preferences", b"backup_preferences", "begin_edit_settings", b"begin_edit_settings", "commit_edit_settings", b"commit_edit_settings", "delete_file_request", b"delete_file_request", "enter_dfu_mode_request", b"enter_dfu_mode_request", "exit_simulator", b"exit_simulator", "factory_reset_config", b"factory_reset_config", "factory_reset_device", b"factory_reset_device", "get_canned_message_module_messages_request", b"get_canned_message_module_messages_request", "get_canned_message_module_messages_response", b"get_canned_message_module_messages_response", "get_channel_request", b"get_channel_request", "get_channel_response", b"get_channel_response", "get_config_request", b"get_config_request", "get_config_response", b"get_config_response", "get_device_connection_status_request", b"get_device_connection_status_request", "get_device_connection_status_response", b"get_device_connection_status_response", "get_device_metadata_request", b"get_device_metadata_request", "get_device_metadata_response", b"get_device_metadata_response", "get_module_config_request", b"get_module_config_request", "get_module_config_response", b"get_module_config_response", "get_node_remote_hardware_pins_request", b"get_node_remote_hardware_pins_request", "get_node_remote_hardware_pins_response", b"get_node_remote_hardware_pins_response", "get_owner_request", b"get_owner_request", "get_owner_response", b"get_owner_response", "get_ringtone_request", b"get_ringtone_request", "get_ringtone_response", b"get_ringtone_response", "get_ui_config_request", b"get_ui_config_request", "get_ui_config_response", b"get_ui_config_response", "key_verification", b"key_verification", "nodedb_reset", b"nodedb_reset", "payload_variant", b"payload_variant", "reboot_ota_mode", b"reboot_ota_mode", "reboot_ota_seconds", b"reboot_ota_seconds", "reboot_seconds", b"reboot_seconds", "remove_backup_preferences", b"remove_backup_preferences", "remove_by_nodenum", b"remove_by_nodenum", "remove_favorite_node", b"remove_favorite_node", "remove_fixed_position", b"remove_fixed_position", "remove_ignored_node", b"remove_ignored_node", "restore_preferences", b"restore_preferences", "send_input_event", b"send_input_event", "set_canned_message_module_messages", b"set_canned_message_module_messages", "set_channel", b"set_channel", "set_config", b"set_config", "set_favorite_node", b"set_favorite_node", "set_fixed_position", b"set_fixed_position", "set_ham_mode", b"set_ham_mode", "set_ignored_node", b"set_ignored_node", "set_module_config", b"set_module_config", "set_owner", b"set_owner", "set_ringtone_message", b"set_ringtone_message", "set_scale", b"set_scale", "set_time_only", b"set_time_only", "shutdown_seconds", b"shutdown_seconds", "store_ui_config", b"store_ui_config"]) -> builtins.bool: ...
def ClearField(self, field_name: typing.Literal["add_contact", b"add_contact", "backup_preferences", b"backup_preferences", "begin_edit_settings", b"begin_edit_settings", "commit_edit_settings", b"commit_edit_settings", "delete_file_request", b"delete_file_request", "enter_dfu_mode_request", b"enter_dfu_mode_request", "exit_simulator", b"exit_simulator", "factory_reset_config", b"factory_reset_config", "factory_reset_device", b"factory_reset_device", "get_canned_message_module_messages_request", b"get_canned_message_module_messages_request", "get_canned_message_module_messages_response", b"get_canned_message_module_messages_response", "get_channel_request", b"get_channel_request", "get_channel_response", b"get_channel_response", "get_config_request", b"get_config_request", "get_config_response", b"get_config_response", "get_device_connection_status_request", b"get_device_connection_status_request", "get_device_connection_status_response", b"get_device_connection_status_response", "get_device_metadata_request", b"get_device_metadata_request", "get_device_metadata_response", b"get_device_metadata_response", "get_module_config_request", b"get_module_config_request", "get_module_config_response", b"get_module_config_response", "get_node_remote_hardware_pins_request", b"get_node_remote_hardware_pins_request", "get_node_remote_hardware_pins_response", b"get_node_remote_hardware_pins_response", "get_owner_request", b"get_owner_request", "get_owner_response", b"get_owner_response", "get_ringtone_request", b"get_ringtone_request", "get_ringtone_response", b"get_ringtone_response", "get_ui_config_request", b"get_ui_config_request", "get_ui_config_response", b"get_ui_config_response", "key_verification", b"key_verification", "nodedb_reset", b"nodedb_reset", "payload_variant", b"payload_variant", "reboot_ota_seconds", b"reboot_ota_seconds", "reboot_seconds", b"reboot_seconds", "remove_backup_preferences", b"remove_backup_preferences", "remove_by_nodenum", b"remove_by_nodenum", "remove_favorite_node", b"remove_favorite_node", "remove_fixed_position", b"remove_fixed_position", "remove_ignored_node", b"remove_ignored_node", "restore_preferences", b"restore_preferences", "send_input_event", b"send_input_event", "session_passkey", b"session_passkey", "set_canned_message_module_messages", b"set_canned_message_module_messages", "set_channel", b"set_channel", "set_config", b"set_config", "set_favorite_node", b"set_favorite_node", "set_fixed_position", b"set_fixed_position", "set_ham_mode", b"set_ham_mode", "set_ignored_node", b"set_ignored_node", "set_module_config", b"set_module_config", "set_owner", b"set_owner", "set_ringtone_message", b"set_ringtone_message", "set_scale", b"set_scale", "set_time_only", b"set_time_only", "shutdown_seconds", b"shutdown_seconds", "store_ui_config", b"store_ui_config"]) -> None: ... def ClearField(self, field_name: typing.Literal["add_contact", b"add_contact", "backup_preferences", b"backup_preferences", "begin_edit_settings", b"begin_edit_settings", "commit_edit_settings", b"commit_edit_settings", "delete_file_request", b"delete_file_request", "enter_dfu_mode_request", b"enter_dfu_mode_request", "exit_simulator", b"exit_simulator", "factory_reset_config", b"factory_reset_config", "factory_reset_device", b"factory_reset_device", "get_canned_message_module_messages_request", b"get_canned_message_module_messages_request", "get_canned_message_module_messages_response", b"get_canned_message_module_messages_response", "get_channel_request", b"get_channel_request", "get_channel_response", b"get_channel_response", "get_config_request", b"get_config_request", "get_config_response", b"get_config_response", "get_device_connection_status_request", b"get_device_connection_status_request", "get_device_connection_status_response", b"get_device_connection_status_response", "get_device_metadata_request", b"get_device_metadata_request", "get_device_metadata_response", b"get_device_metadata_response", "get_module_config_request", b"get_module_config_request", "get_module_config_response", b"get_module_config_response", "get_node_remote_hardware_pins_request", b"get_node_remote_hardware_pins_request", "get_node_remote_hardware_pins_response", b"get_node_remote_hardware_pins_response", "get_owner_request", b"get_owner_request", "get_owner_response", b"get_owner_response", "get_ringtone_request", b"get_ringtone_request", "get_ringtone_response", b"get_ringtone_response", "get_ui_config_request", b"get_ui_config_request", "get_ui_config_response", b"get_ui_config_response", "key_verification", b"key_verification", "nodedb_reset", b"nodedb_reset", "payload_variant", b"payload_variant", "reboot_ota_mode", b"reboot_ota_mode", "reboot_ota_seconds", b"reboot_ota_seconds", "reboot_seconds", b"reboot_seconds", "remove_backup_preferences", b"remove_backup_preferences", "remove_by_nodenum", b"remove_by_nodenum", "remove_favorite_node", b"remove_favorite_node", "remove_fixed_position", b"remove_fixed_position", "remove_ignored_node", b"remove_ignored_node", "restore_preferences", b"restore_preferences", "send_input_event", b"send_input_event", "session_passkey", b"session_passkey", "set_canned_message_module_messages", b"set_canned_message_module_messages", "set_channel", b"set_channel", "set_config", b"set_config", "set_favorite_node", b"set_favorite_node", "set_fixed_position", b"set_fixed_position", "set_ham_mode", b"set_ham_mode", "set_ignored_node", b"set_ignored_node", "set_module_config", b"set_module_config", "set_owner", b"set_owner", "set_ringtone_message", b"set_ringtone_message", "set_scale", b"set_scale", "set_time_only", b"set_time_only", "shutdown_seconds", b"shutdown_seconds", "store_ui_config", b"store_ui_config"]) -> None: ...
def WhichOneof(self, oneof_group: typing.Literal["payload_variant", b"payload_variant"]) -> typing.Literal["get_channel_request", "get_channel_response", "get_owner_request", "get_owner_response", "get_config_request", "get_config_response", "get_module_config_request", "get_module_config_response", "get_canned_message_module_messages_request", "get_canned_message_module_messages_response", "get_device_metadata_request", "get_device_metadata_response", "get_ringtone_request", "get_ringtone_response", "get_device_connection_status_request", "get_device_connection_status_response", "set_ham_mode", "get_node_remote_hardware_pins_request", "get_node_remote_hardware_pins_response", "enter_dfu_mode_request", "delete_file_request", "set_scale", "backup_preferences", "restore_preferences", "remove_backup_preferences", "send_input_event", "set_owner", "set_channel", "set_config", "set_module_config", "set_canned_message_module_messages", "set_ringtone_message", "remove_by_nodenum", "set_favorite_node", "remove_favorite_node", "set_fixed_position", "remove_fixed_position", "set_time_only", "get_ui_config_request", "get_ui_config_response", "store_ui_config", "set_ignored_node", "remove_ignored_node", "begin_edit_settings", "commit_edit_settings", "add_contact", "key_verification", "factory_reset_device", "reboot_ota_seconds", "exit_simulator", "reboot_seconds", "shutdown_seconds", "factory_reset_config", "nodedb_reset"] | None: ... def WhichOneof(self, oneof_group: typing.Literal["payload_variant", b"payload_variant"]) -> typing.Literal["get_channel_request", "get_channel_response", "get_owner_request", "get_owner_response", "get_config_request", "get_config_response", "get_module_config_request", "get_module_config_response", "get_canned_message_module_messages_request", "get_canned_message_module_messages_response", "get_device_metadata_request", "get_device_metadata_response", "get_ringtone_request", "get_ringtone_response", "get_device_connection_status_request", "get_device_connection_status_response", "set_ham_mode", "get_node_remote_hardware_pins_request", "get_node_remote_hardware_pins_response", "enter_dfu_mode_request", "delete_file_request", "set_scale", "backup_preferences", "restore_preferences", "remove_backup_preferences", "send_input_event", "set_owner", "set_channel", "set_config", "set_module_config", "set_canned_message_module_messages", "set_ringtone_message", "remove_by_nodenum", "set_favorite_node", "remove_favorite_node", "set_fixed_position", "remove_fixed_position", "set_time_only", "get_ui_config_request", "get_ui_config_response", "store_ui_config", "set_ignored_node", "remove_ignored_node", "begin_edit_settings", "commit_edit_settings", "add_contact", "key_verification", "reboot_ota_mode", "factory_reset_device", "reboot_ota_seconds", "exit_simulator", "reboot_seconds", "shutdown_seconds", "factory_reset_config", "nodedb_reset"] | None: ...
global___AdminMessage = AdminMessage global___AdminMessage = AdminMessage

View File

@@ -1,7 +1,7 @@
""" """
@generated by mypy-protobuf. Do not edit manually! @generated by mypy-protobuf. Do not edit manually!
isort:skip_file isort:skip_file
""" trunk-ignore(buf-lint/PACKAGE_DIRECTORY_MATCH)"""
import builtins import builtins
import google.protobuf.descriptor import google.protobuf.descriptor

View File

@@ -1,7 +1,7 @@
""" """
@generated by mypy-protobuf. Do not edit manually! @generated by mypy-protobuf. Do not edit manually!
isort:skip_file isort:skip_file
""" trunk-ignore(buf-lint/PACKAGE_DIRECTORY_MATCH)"""
import builtins import builtins
import google.protobuf.descriptor import google.protobuf.descriptor

View File

File diff suppressed because one or more lines are too long

View File

@@ -1487,6 +1487,7 @@ class Config(google.protobuf.message.Message):
LONG_SLOW: Config.LoRaConfig._ModemPreset.ValueType # 1 LONG_SLOW: Config.LoRaConfig._ModemPreset.ValueType # 1
""" """
Long Range - Slow Long Range - Slow
Deprecated in 2.7: Unpopular slow preset.
""" """
VERY_LONG_SLOW: Config.LoRaConfig._ModemPreset.ValueType # 2 VERY_LONG_SLOW: Config.LoRaConfig._ModemPreset.ValueType # 2
""" """
@@ -1519,6 +1520,11 @@ class Config(google.protobuf.message.Message):
This is the fastest preset and the only one with 500kHz bandwidth. This is the fastest preset and the only one with 500kHz bandwidth.
It is not legal to use in all regions due to this wider bandwidth. It is not legal to use in all regions due to this wider bandwidth.
""" """
LONG_TURBO: Config.LoRaConfig._ModemPreset.ValueType # 9
"""
Long Range - Turbo
This preset performs similarly to LongFast, but with 500Khz bandwidth.
"""
class ModemPreset(_ModemPreset, metaclass=_ModemPresetEnumTypeWrapper): class ModemPreset(_ModemPreset, metaclass=_ModemPresetEnumTypeWrapper):
""" """
@@ -1533,6 +1539,7 @@ class Config(google.protobuf.message.Message):
LONG_SLOW: Config.LoRaConfig.ModemPreset.ValueType # 1 LONG_SLOW: Config.LoRaConfig.ModemPreset.ValueType # 1
""" """
Long Range - Slow Long Range - Slow
Deprecated in 2.7: Unpopular slow preset.
""" """
VERY_LONG_SLOW: Config.LoRaConfig.ModemPreset.ValueType # 2 VERY_LONG_SLOW: Config.LoRaConfig.ModemPreset.ValueType # 2
""" """
@@ -1565,6 +1572,11 @@ class Config(google.protobuf.message.Message):
This is the fastest preset and the only one with 500kHz bandwidth. This is the fastest preset and the only one with 500kHz bandwidth.
It is not legal to use in all regions due to this wider bandwidth. It is not legal to use in all regions due to this wider bandwidth.
""" """
LONG_TURBO: Config.LoRaConfig.ModemPreset.ValueType # 9
"""
Long Range - Turbo
This preset performs similarly to LongFast, but with 500Khz bandwidth.
"""
USE_PRESET_FIELD_NUMBER: builtins.int USE_PRESET_FIELD_NUMBER: builtins.int
MODEM_PRESET_FIELD_NUMBER: builtins.int MODEM_PRESET_FIELD_NUMBER: builtins.int

View File

File diff suppressed because one or more lines are too long

View File

@@ -421,9 +421,9 @@ class _HardwareModelEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._
""" """
Heltec HRI-3621 industrial probe Heltec HRI-3621 industrial probe
""" """
RESERVED_FRIED_CHICKEN: _HardwareModel.ValueType # 93 MUZI_BASE: _HardwareModel.ValueType # 93
""" """
Reserved Fried Chicken ID for future use Muzi Works Muzi-Base device
""" """
HELTEC_MESH_POCKET: _HardwareModel.ValueType # 94 HELTEC_MESH_POCKET: _HardwareModel.ValueType # 94
""" """
@@ -523,6 +523,18 @@ class _HardwareModelEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._
""" """
RAK3401 RAK3401
""" """
RAK6421: _HardwareModel.ValueType # 118
"""
RAK6421 Hat+
"""
THINKNODE_M4: _HardwareModel.ValueType # 119
"""
Elecrow ThinkNode M4
"""
THINKNODE_M6: _HardwareModel.ValueType # 120
"""
Elecrow ThinkNode M6
"""
PRIVATE_HW: _HardwareModel.ValueType # 255 PRIVATE_HW: _HardwareModel.ValueType # 255
""" """
------------------------------------------------------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------------------------------------------------------
@@ -927,9 +939,9 @@ HELTEC_SENSOR_HUB: HardwareModel.ValueType # 92
""" """
Heltec HRI-3621 industrial probe Heltec HRI-3621 industrial probe
""" """
RESERVED_FRIED_CHICKEN: HardwareModel.ValueType # 93 MUZI_BASE: HardwareModel.ValueType # 93
""" """
Reserved Fried Chicken ID for future use Muzi Works Muzi-Base device
""" """
HELTEC_MESH_POCKET: HardwareModel.ValueType # 94 HELTEC_MESH_POCKET: HardwareModel.ValueType # 94
""" """
@@ -1029,6 +1041,18 @@ RAK3401: HardwareModel.ValueType # 117
""" """
RAK3401 RAK3401
""" """
RAK6421: HardwareModel.ValueType # 118
"""
RAK6421 Hat+
"""
THINKNODE_M4: HardwareModel.ValueType # 119
"""
Elecrow ThinkNode M4
"""
THINKNODE_M6: HardwareModel.ValueType # 120
"""
Elecrow ThinkNode M6
"""
PRIVATE_HW: HardwareModel.ValueType # 255 PRIVATE_HW: HardwareModel.ValueType # 255
""" """
------------------------------------------------------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------------------------------------------------------
@@ -2179,6 +2203,137 @@ class KeyVerification(google.protobuf.message.Message):
global___KeyVerification = KeyVerification global___KeyVerification = KeyVerification
@typing.final
class StoreForwardPlusPlus(google.protobuf.message.Message):
"""
The actual over-the-mesh message doing store and forward++
"""
DESCRIPTOR: google.protobuf.descriptor.Descriptor
class _SFPP_message_type:
ValueType = typing.NewType("ValueType", builtins.int)
V: typing_extensions.TypeAlias = ValueType
class _SFPP_message_typeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[StoreForwardPlusPlus._SFPP_message_type.ValueType], builtins.type):
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
CANON_ANNOUNCE: StoreForwardPlusPlus._SFPP_message_type.ValueType # 0
"""
Send an announcement of the canonical tip of a chain
"""
CHAIN_QUERY: StoreForwardPlusPlus._SFPP_message_type.ValueType # 1
"""
Query whether a specific link is on the chain
"""
LINK_REQUEST: StoreForwardPlusPlus._SFPP_message_type.ValueType # 3
"""
Request the next link in the chain
"""
LINK_PROVIDE: StoreForwardPlusPlus._SFPP_message_type.ValueType # 4
"""
Provide a link to add to the chain
"""
LINK_PROVIDE_FIRSTHALF: StoreForwardPlusPlus._SFPP_message_type.ValueType # 5
"""
If we must fragment, send the first half
"""
LINK_PROVIDE_SECONDHALF: StoreForwardPlusPlus._SFPP_message_type.ValueType # 6
"""
If we must fragment, send the second half
"""
class SFPP_message_type(_SFPP_message_type, metaclass=_SFPP_message_typeEnumTypeWrapper):
"""
Enum of message types
"""
CANON_ANNOUNCE: StoreForwardPlusPlus.SFPP_message_type.ValueType # 0
"""
Send an announcement of the canonical tip of a chain
"""
CHAIN_QUERY: StoreForwardPlusPlus.SFPP_message_type.ValueType # 1
"""
Query whether a specific link is on the chain
"""
LINK_REQUEST: StoreForwardPlusPlus.SFPP_message_type.ValueType # 3
"""
Request the next link in the chain
"""
LINK_PROVIDE: StoreForwardPlusPlus.SFPP_message_type.ValueType # 4
"""
Provide a link to add to the chain
"""
LINK_PROVIDE_FIRSTHALF: StoreForwardPlusPlus.SFPP_message_type.ValueType # 5
"""
If we must fragment, send the first half
"""
LINK_PROVIDE_SECONDHALF: StoreForwardPlusPlus.SFPP_message_type.ValueType # 6
"""
If we must fragment, send the second half
"""
SFPP_MESSAGE_TYPE_FIELD_NUMBER: builtins.int
MESSAGE_HASH_FIELD_NUMBER: builtins.int
COMMIT_HASH_FIELD_NUMBER: builtins.int
ROOT_HASH_FIELD_NUMBER: builtins.int
MESSAGE_FIELD_NUMBER: builtins.int
ENCAPSULATED_ID_FIELD_NUMBER: builtins.int
ENCAPSULATED_TO_FIELD_NUMBER: builtins.int
ENCAPSULATED_FROM_FIELD_NUMBER: builtins.int
ENCAPSULATED_RXTIME_FIELD_NUMBER: builtins.int
sfpp_message_type: global___StoreForwardPlusPlus.SFPP_message_type.ValueType
"""
Which message type is this
"""
message_hash: builtins.bytes
"""
The hash of the specific message
"""
commit_hash: builtins.bytes
"""
The hash of a link on a chain
"""
root_hash: builtins.bytes
"""
the root hash of a chain
"""
message: builtins.bytes
"""
The encrypted bytes from a message
"""
encapsulated_id: builtins.int
"""
Message ID of the contained message
"""
encapsulated_to: builtins.int
"""
Destination of the contained message
"""
encapsulated_from: builtins.int
"""
Sender of the contained message
"""
encapsulated_rxtime: builtins.int
"""
The receive time of the message in question
"""
def __init__(
self,
*,
sfpp_message_type: global___StoreForwardPlusPlus.SFPP_message_type.ValueType = ...,
message_hash: builtins.bytes = ...,
commit_hash: builtins.bytes = ...,
root_hash: builtins.bytes = ...,
message: builtins.bytes = ...,
encapsulated_id: builtins.int = ...,
encapsulated_to: builtins.int = ...,
encapsulated_from: builtins.int = ...,
encapsulated_rxtime: builtins.int = ...,
) -> None: ...
def ClearField(self, field_name: typing.Literal["commit_hash", b"commit_hash", "encapsulated_from", b"encapsulated_from", "encapsulated_id", b"encapsulated_id", "encapsulated_rxtime", b"encapsulated_rxtime", "encapsulated_to", b"encapsulated_to", "message", b"message", "message_hash", b"message_hash", "root_hash", b"root_hash", "sfpp_message_type", b"sfpp_message_type"]) -> None: ...
global___StoreForwardPlusPlus = StoreForwardPlusPlus
@typing.final @typing.final
class Waypoint(google.protobuf.message.Message): class Waypoint(google.protobuf.message.Message):
""" """
@@ -2557,6 +2712,10 @@ class MeshPacket(google.protobuf.message.Message):
to: builtins.int to: builtins.int
""" """
The (immediate) destination for this packet The (immediate) destination for this packet
If the value is 4,294,967,295 (maximum value of an unsigned 32bit integer), this indicates that the packet was
not destined for a specific node, but for a channel as indicated by the value of `channel` below.
If the value is another, this indicates that the packet was destined for a specific
node (i.e. a kind of "Direct Message" to this node) and not broadcast on a channel.
""" """
channel: builtins.int channel: builtins.int
""" """

View File

@@ -13,7 +13,7 @@ _sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\"meshtastic/protobuf/portnums.proto\x12\x13meshtastic.protobuf*\xf6\x04\n\x07PortNum\x12\x0f\n\x0bUNKNOWN_APP\x10\x00\x12\x14\n\x10TEXT_MESSAGE_APP\x10\x01\x12\x17\n\x13REMOTE_HARDWARE_APP\x10\x02\x12\x10\n\x0cPOSITION_APP\x10\x03\x12\x10\n\x0cNODEINFO_APP\x10\x04\x12\x0f\n\x0bROUTING_APP\x10\x05\x12\r\n\tADMIN_APP\x10\x06\x12\x1f\n\x1bTEXT_MESSAGE_COMPRESSED_APP\x10\x07\x12\x10\n\x0cWAYPOINT_APP\x10\x08\x12\r\n\tAUDIO_APP\x10\t\x12\x18\n\x14\x44\x45TECTION_SENSOR_APP\x10\n\x12\r\n\tALERT_APP\x10\x0b\x12\x18\n\x14KEY_VERIFICATION_APP\x10\x0c\x12\r\n\tREPLY_APP\x10 \x12\x11\n\rIP_TUNNEL_APP\x10!\x12\x12\n\x0ePAXCOUNTER_APP\x10\"\x12\x0e\n\nSERIAL_APP\x10@\x12\x15\n\x11STORE_FORWARD_APP\x10\x41\x12\x12\n\x0eRANGE_TEST_APP\x10\x42\x12\x11\n\rTELEMETRY_APP\x10\x43\x12\x0b\n\x07ZPS_APP\x10\x44\x12\x11\n\rSIMULATOR_APP\x10\x45\x12\x12\n\x0eTRACEROUTE_APP\x10\x46\x12\x14\n\x10NEIGHBORINFO_APP\x10G\x12\x0f\n\x0b\x41TAK_PLUGIN\x10H\x12\x12\n\x0eMAP_REPORT_APP\x10I\x12\x13\n\x0fPOWERSTRESS_APP\x10J\x12\x18\n\x14RETICULUM_TUNNEL_APP\x10L\x12\x0f\n\x0b\x43\x41YENNE_APP\x10M\x12\x10\n\x0bPRIVATE_APP\x10\x80\x02\x12\x13\n\x0e\x41TAK_FORWARDER\x10\x81\x02\x12\x08\n\x03MAX\x10\xff\x03\x42^\n\x14org.meshtastic.protoB\x08PortnumsZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3') DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\"meshtastic/protobuf/portnums.proto\x12\x13meshtastic.protobuf*\x96\x05\n\x07PortNum\x12\x0f\n\x0bUNKNOWN_APP\x10\x00\x12\x14\n\x10TEXT_MESSAGE_APP\x10\x01\x12\x17\n\x13REMOTE_HARDWARE_APP\x10\x02\x12\x10\n\x0cPOSITION_APP\x10\x03\x12\x10\n\x0cNODEINFO_APP\x10\x04\x12\x0f\n\x0bROUTING_APP\x10\x05\x12\r\n\tADMIN_APP\x10\x06\x12\x1f\n\x1bTEXT_MESSAGE_COMPRESSED_APP\x10\x07\x12\x10\n\x0cWAYPOINT_APP\x10\x08\x12\r\n\tAUDIO_APP\x10\t\x12\x18\n\x14\x44\x45TECTION_SENSOR_APP\x10\n\x12\r\n\tALERT_APP\x10\x0b\x12\x18\n\x14KEY_VERIFICATION_APP\x10\x0c\x12\r\n\tREPLY_APP\x10 \x12\x11\n\rIP_TUNNEL_APP\x10!\x12\x12\n\x0ePAXCOUNTER_APP\x10\"\x12\x1e\n\x1aSTORE_FORWARD_PLUSPLUS_APP\x10#\x12\x0e\n\nSERIAL_APP\x10@\x12\x15\n\x11STORE_FORWARD_APP\x10\x41\x12\x12\n\x0eRANGE_TEST_APP\x10\x42\x12\x11\n\rTELEMETRY_APP\x10\x43\x12\x0b\n\x07ZPS_APP\x10\x44\x12\x11\n\rSIMULATOR_APP\x10\x45\x12\x12\n\x0eTRACEROUTE_APP\x10\x46\x12\x14\n\x10NEIGHBORINFO_APP\x10G\x12\x0f\n\x0b\x41TAK_PLUGIN\x10H\x12\x12\n\x0eMAP_REPORT_APP\x10I\x12\x13\n\x0fPOWERSTRESS_APP\x10J\x12\x18\n\x14RETICULUM_TUNNEL_APP\x10L\x12\x0f\n\x0b\x43\x41YENNE_APP\x10M\x12\x10\n\x0bPRIVATE_APP\x10\x80\x02\x12\x13\n\x0e\x41TAK_FORWARDER\x10\x81\x02\x12\x08\n\x03MAX\x10\xff\x03\x42^\n\x14org.meshtastic.protoB\x08PortnumsZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
_globals = globals() _globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
@@ -22,5 +22,5 @@ if _descriptor._USE_C_DESCRIPTORS == False:
DESCRIPTOR._options = None DESCRIPTOR._options = None
DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\010PortnumsZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000' DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\010PortnumsZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
_globals['_PORTNUM']._serialized_start=60 _globals['_PORTNUM']._serialized_start=60
_globals['_PORTNUM']._serialized_end=690 _globals['_PORTNUM']._serialized_end=722
# @@protoc_insertion_point(module_scope) # @@protoc_insertion_point(module_scope)

View File

@@ -117,6 +117,13 @@ class _PortNumEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTy
Paxcounter lib included in the firmware Paxcounter lib included in the firmware
ENCODING: protobuf ENCODING: protobuf
""" """
STORE_FORWARD_PLUSPLUS_APP: _PortNum.ValueType # 35
"""
Store and Forward++ module included in the firmware
ENCODING: protobuf
This module is specifically for Native Linux nodes, and provides a Git-style
chain of messages.
"""
SERIAL_APP: _PortNum.ValueType # 64 SERIAL_APP: _PortNum.ValueType # 64
""" """
Provides a hardware serial interface to send and receive from the Meshtastic network. Provides a hardware serial interface to send and receive from the Meshtastic network.
@@ -321,6 +328,13 @@ PAXCOUNTER_APP: PortNum.ValueType # 34
Paxcounter lib included in the firmware Paxcounter lib included in the firmware
ENCODING: protobuf ENCODING: protobuf
""" """
STORE_FORWARD_PLUSPLUS_APP: PortNum.ValueType # 35
"""
Store and Forward++ module included in the firmware
ENCODING: protobuf
This module is specifically for Native Linux nodes, and provides a Git-style
chain of messages.
"""
SERIAL_APP: PortNum.ValueType # 64 SERIAL_APP: PortNum.ValueType # 64
""" """
Provides a hardware serial interface to send and receive from the Meshtastic network. Provides a hardware serial interface to send and receive from the Meshtastic network.

View File

@@ -94,16 +94,10 @@ class SerialInterface(StreamInterface):
def close(self) -> None: def close(self) -> None:
"""Close a connection to the device""" """Close a connection to the device"""
if hasattr(self, "stream") and self.stream and getattr(self.stream, "is_open", False): if self.stream: # Stream can be null if we were already closed
try: self.stream.flush() # FIXME: why are there these two flushes with 100ms sleeps? This shouldn't be necessary
time.sleep(0.1)
self.stream.flush() self.stream.flush()
time.sleep(0.1) time.sleep(0.1)
except Exception as e:
logger.debug(f"Exception during flush: {e}")
try:
self.stream.close()
except Exception as e:
logger.debug(f"Exception during close: {e}")
self.stream = None
logger.debug("Closing Serial stream") logger.debug("Closing Serial stream")
StreamInterface.close(self) StreamInterface.close(self)

View File

@@ -735,3 +735,16 @@ def to_node_num(node_id: Union[int, str]) -> int:
return int(s, 10) return int(s, 10)
except ValueError: except ValueError:
return int(s, 16) 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."""
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

4989
poetry.lock generated
View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,20 +1,20 @@
[tool.poetry] [tool.poetry]
name = "meshtastic" name = "meshtastic"
version = "2.7.4" version = "2.7.6"
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.14" # 3.9 is needed for pandas, bleak requires <3.14 python = "^3.9,<3.15" # 3.9 is needed for pandas
pyserial = "^3.5" pyserial = "^3.5"
protobuf = ">=4.21.12" protobuf = ">=4.21.12"
tabulate = "^0.9.0" tabulate = "^0.9.0"
requests = "^2.31.0" requests = "^2.31.0"
pyyaml = "^6.0.1" pyyaml = "^6.0.1"
pypubsub = "^4.0.3" pypubsub = "^4.0.3"
bleak = "^0.22.3" bleak = ">=0.22.3"
packaging = "^24.0" packaging = "^24.0"
argcomplete = { version = "^3.5.2", optional = true } argcomplete = { version = "^3.5.2", optional = true }
pyqrcode = { version = "^1.2.1", optional = true } pyqrcode = { version = "^1.2.1", optional = true }
@@ -34,7 +34,7 @@ pytest-cov = "^5.0.0"
pdoc3 = "^0.10.0" pdoc3 = "^0.10.0"
autopep8 = "^2.1.0" autopep8 = "^2.1.0"
pylint = "^3.2.3" pylint = "^3.2.3"
pyinstaller = "^6.8.0" pyinstaller = "^6.10.0"
mypy = "^1.10.0" mypy = "^1.10.0"
mypy-protobuf = "^3.3.0" mypy-protobuf = "^3.3.0"
types-protobuf = "^5.26.0.20240422" types-protobuf = "^5.26.0.20240422"