Skip to content

Commit 14ff3eb

Browse files
authored
Merge branch 'master' into loggingHandler2
2 parents fe093ac + 47e5b04 commit 14ff3eb

16 files changed

+369
-174
lines changed

.github/CONTRIBUTING.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Contributing to Meshtastic Python
2+
3+
## Development resources
4+
- [API Documentation](https://python.meshtastic.org/)
5+
- [Meshtastic Python Development](https://meshtastic.org/docs/development/python/)
6+
- [Building Meshtastic Python](https://meshtastic.org/docs/development/python/building/)
7+
- [Using the Meshtastic Python Library](https://meshtastic.org/docs/development/python/library/)
8+
9+
## How to check your code (pytest/pylint) before a PR
10+
- [Pre-requisites](https://meshtastic.org/docs/development/python/building/#pre-requisites)
11+
- also execute `poetry install --all-extras --with dev,powermon` for all optional dependencies
12+
- check your code with github ci actions locally
13+
- You need to have act installed. You can get it at https://nektosact.com/
14+
- on linux: `act -P ubuntu-latest=-self-hosted --matrix "python-version:3.12"`
15+
- on windows:
16+
- linux checks (linux docker): `act --matrix "python-version:3.12"`
17+
- windows checks (windows host): `act -P ubuntu-latest=-self-hosted --matrix "python-version:3.12"`
18+
- or run all locally:
19+
- run `poetry run pylint meshtastic examples/ --ignore-patterns ".*_pb2.pyi?$"`
20+
- run `poetry run mypy meshtastic/`
21+
- run `poetry run pytest`
22+
- more commands see [CI workflow](https://github.com/meshtastic/python/blob/master/.github/workflows/ci.yml)

meshtastic/__main__.py

Lines changed: 37 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060
have_powermon = False
6161
powermon_exception = e
6262
meter = None
63-
from meshtastic.protobuf import channel_pb2, config_pb2, portnums_pb2
63+
from meshtastic.protobuf import channel_pb2, config_pb2, portnums_pb2, mesh_pb2
6464
from meshtastic.version import get_active_version
6565

6666
logger = logging.getLogger(__name__)
@@ -346,51 +346,56 @@ def onConnected(interface):
346346
closeNow = True
347347
waitForAckNak = True
348348

349-
# Validate owner names before connecting to device
350-
if args.set_owner is not None:
351-
stripped_long_name = args.set_owner.strip()
352-
if not stripped_long_name:
353-
meshtastic.util.our_exit("ERROR: Long Name cannot be empty or contain only whitespace characters")
354-
355-
if hasattr(args, 'set_owner_short') and args.set_owner_short is not None:
356-
stripped_short_name = args.set_owner_short.strip()
357-
if not stripped_short_name:
358-
meshtastic.util.our_exit("ERROR: Short Name cannot be empty or contain only whitespace characters")
359-
360-
if args.set_owner and args.set_owner_short:
361-
print(f"Setting device owner to {args.set_owner} and short name to {args.set_owner_short}")
362-
elif args.set_owner:
363-
print(f"Setting device owner to {args.set_owner}")
364-
elif args.set_owner_short and not args.set_owner:
365-
print(f"Setting device owner short to {args.set_owner_short}")
366-
367-
if args.set_is_unmessageable:
349+
long_name = args.set_owner.strip() if args.set_owner else None
350+
short_name = args.set_owner_short.strip() if args.set_owner_short else None
351+
352+
if long_name is not None and not long_name:
353+
meshtastic.util.our_exit("ERROR: Long Name cannot be empty or contain only whitespace characters")
354+
355+
if short_name is not None and not short_name:
356+
meshtastic.util.our_exit("ERROR: Short Name cannot be empty or contain only whitespace characters")
357+
358+
if long_name and short_name:
359+
print(f"Setting device owner to {long_name} and short name to {short_name}")
360+
elif long_name:
361+
print(f"Setting device owner to {long_name}")
362+
elif short_name:
363+
print(f"Setting device owner short to {short_name}")
364+
365+
unmessagable = None
366+
if args.set_is_unmessageable is not None:
368367
unmessagable = (
369368
meshtastic.util.fromStr(args.set_is_unmessageable)
370369
if isinstance(args.set_is_unmessageable, str)
371370
else args.set_is_unmessageable
372371
)
372+
print(f"Setting device owner is_unmessageable to {unmessagable}")
373373

374-
if unmessagable is not None:
375-
print(f"Setting device owner is_unmessageable to {unmessagable}")
376-
interface.getNode(
377-
args.dest, False, **getNode_kwargs).setOwner(long_name=args.set_owner,
378-
short_name=args.set_owner_short, is_unmessagable=unmessagable
379-
)
374+
interface.getNode(args.dest, False, **getNode_kwargs).setOwner(
375+
long_name=long_name,
376+
short_name=short_name,
377+
is_unmessagable=unmessagable
378+
)
380379

381380
if args.set_canned_message:
382381
closeNow = True
383382
waitForAckNak = True
384-
print(f"Setting canned plugin message to {args.set_canned_message}")
385-
interface.getNode(args.dest, False, **getNode_kwargs).set_canned_message(
386-
args.set_canned_message
387-
)
383+
node = interface.getNode(args.dest, False, **getNode_kwargs)
384+
if node.module_available(mesh_pb2.CANNEDMSG_CONFIG):
385+
print(f"Setting canned plugin message to {args.set_canned_message}")
386+
node.set_canned_message(args.set_canned_message)
387+
else:
388+
print("Canned Message module is excluded by firmware; skipping set.")
388389

389390
if args.set_ringtone:
390391
closeNow = True
391392
waitForAckNak = True
392-
print(f"Setting ringtone to {args.set_ringtone}")
393-
interface.getNode(args.dest, False, **getNode_kwargs).set_ringtone(args.set_ringtone)
393+
node = interface.getNode(args.dest, False, **getNode_kwargs)
394+
if node.module_available(mesh_pb2.EXTNOTIF_CONFIG):
395+
print(f"Setting ringtone to {args.set_ringtone}")
396+
node.set_ringtone(args.set_ringtone)
397+
else:
398+
print("External Notification is excluded by firmware; skipping ringtone set.")
394399

395400
if args.pos_fields:
396401
# If --pos-fields invoked with args, set position fields

meshtastic/node.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,16 @@ def __repr__(self):
5252
r += ")"
5353
return r
5454

55+
def module_available(self, excluded_bit: int) -> bool:
56+
"""Check DeviceMetadata.excluded_modules to see if a module is available."""
57+
meta = getattr(self.iface, "metadata", None)
58+
if meta is None:
59+
return True
60+
try:
61+
return (meta.excluded_modules & excluded_bit) == 0
62+
except Exception:
63+
return True
64+
5565
def showChannels(self):
5666
"""Show human readable description of our channels."""
5767
print("Channels:")
@@ -442,6 +452,10 @@ def onResponseRequestRingtone(self, p):
442452
def get_ringtone(self):
443453
"""Get the ringtone. Concatenate all pieces together and return a single string."""
444454
logger.debug(f"in get_ringtone()")
455+
if not self.module_available(mesh_pb2.EXTNOTIF_CONFIG):
456+
logging.warning("External Notification module not present (excluded by firmware)")
457+
return None
458+
445459
if not self.ringtone:
446460
p1 = admin_pb2.AdminMessage()
447461
p1.get_ringtone_request = True
@@ -463,6 +477,9 @@ def get_ringtone(self):
463477

464478
def set_ringtone(self, ringtone):
465479
"""Set the ringtone. The ringtone length must be less than 230 character."""
480+
if not self.module_available(mesh_pb2.EXTNOTIF_CONFIG):
481+
logging.warning("External Notification module not present (excluded by firmware)")
482+
return None
466483

467484
if len(ringtone) > 230:
468485
our_exit("Warning: The ringtone must be less than 230 characters.")
@@ -513,6 +530,9 @@ def onResponseRequestCannedMessagePluginMessageMessages(self, p):
513530
def get_canned_message(self):
514531
"""Get the canned message string. Concatenate all pieces together and return a single string."""
515532
logger.debug(f"in get_canned_message()")
533+
if not self.module_available(mesh_pb2.CANNEDMSG_CONFIG):
534+
logging.warning("Canned Message module not present (excluded by firmware)")
535+
return None
516536
if not self.cannedPluginMessage:
517537
p1 = admin_pb2.AdminMessage()
518538
p1.get_canned_message_module_messages_request = True
@@ -538,6 +558,9 @@ def get_canned_message(self):
538558

539559
def set_canned_message(self, message):
540560
"""Set the canned message. The canned messages length must be less than 200 character."""
561+
if not self.module_available(mesh_pb2.CANNEDMSG_CONFIG):
562+
logging.warning("Canned Message module not present (excluded by firmware)")
563+
return None
541564

542565
if len(message) > 200:
543566
our_exit("Warning: The canned message must be less than 200 characters.")

meshtastic/protobuf/config_pb2.py

Lines changed: 50 additions & 50 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

meshtastic/protobuf/config_pb2.pyi

Lines changed: 20 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

meshtastic/protobuf/device_ui_pb2.py

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

meshtastic/protobuf/device_ui_pb2.pyi

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

meshtastic/protobuf/mesh_pb2.py

Lines changed: 64 additions & 62 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)