Compare commits

..

389 Commits

Author SHA1 Message Date
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
github-actions
3c6dba78a3 bump version to 2.7.4 2025-11-06 22:50:41 +00:00
Ian McEwen
8a3b114153 protobufs: v2.7.13 2025-11-06 15:37:32 -07:00
Ian McEwen
0bb04100e1 Merge pull request #843 from SpudGunMan/channel-hash-info
Add Channel Hash Utility to Node class
2025-11-06 15:35:36 -07:00
Ian McEwen
ad04c26d13 split out constant, improve logic some, add tests for channel_hash and generate_channel_hash 2025-11-06 15:19:12 -07:00
Ian McEwen
0e67ef37aa Return hash from channel_hash function 2025-11-06 14:55:41 -07:00
Ian McEwen
e924afd140 Merge branch 'master' into channel-hash-info 2025-11-06 14:51:32 -07:00
Ian McEwen
87682c153b Merge pull request #834 from NillRudd/feat/cli-ignore-formats
CLI: normalize lora.ignore_incoming IDs (dec/!hex/0x), dedupe, YAML [] clear, fix bytes→int crash
2025-11-06 14:19:57 -07:00
Ian McEwen
e6c276fe96 Fix trailing whitespace 2025-11-06 14:17:07 -07:00
Ian McEwen
b4662251ed Merge pull request #841 from SpudGunMan/master
refactor close()
2025-11-06 13:24:59 -07:00
SpudGunMan
a17cfe9d2b rename to generate_channel_hash 2025-11-06 12:01:53 -08:00
SpudGunMan
471e3ce145 generate_hash to util 2025-11-06 12:00:16 -08:00
SpudGunMan
39c9864682 move and rename hash function
this function, xor_hash, and a variable for the default key (as bytes, I think, rather than the base64 version) really all belong in meshtastic.util rather than here.
There's multiple forms of hashing in firmware so this should be named to denote that, perhaps channel_hash. If we later want to add the frequency-slot-style hash, better if it's distinguished better from the start.
2025-11-06 11:49:57 -08:00
Niklas Roslund
1d3a7d39f7 Move to_node_num to util and updated function calls 2025-11-06 19:05:34 +01:00
Ian McEwen
2065598754 Merge pull request #825 from trose/feature/tdeck-support
Add T-Deck device support
2025-11-06 11:00:56 -07:00
Ian McEwen
4cac15686b Merge pull request #851 from pdxlocations/waypoint-icon
add icon parameter to sendWaypoint()
2025-11-06 10:28:00 -07:00
pdxlocations
9285f7d13f add waypoint parameter 2025-10-29 08:58:25 -07:00
SpudGunMan
f6f1b748dc hashGen fix
using from https://raw.githubusercontent.com/pdxlocations/mudp/refs/heads/main/mudp/encryption.py

@pdxlocations
2025-10-22 14:21:46 -07:00
SpudGunMan
d2d9c03bc8 tidy up the autobot 2025-10-22 14:19:52 -07:00
SpudGunMan
c44f9b1bb4 Add Channel Hash Utility to Node class
### Summary

- Added a new method `get_channels_with_hash()` to the `Node` class.
- This method returns a list of dictionaries, each containing the channel index, role, name, and a hash value derived from the channel name and PSK.
- The hash is calculated using the existing `generate_hash` utility, ensuring consistency with other parts of the codebase.
- This utility makes it easier to programmatically access channel metadata, including a unique hash, for scripting, debugging, or integration purposes.

### Motivation

- The protobuf `Channel` objects do not include a hash value by default.
- This addition provides a Pythonic, easy-to-use way to access channel info and a unique hash for each channel, which can be useful for diagnostics, scripting, or UI display.

### Example Usage

```python
channels = node.get_channels_with_hash()
for ch in channels:
    print(f"Index {ch['index']}: {ch['role']} name='{ch['name']}' hash={ch['hash']}")
```

### Impact

- No breaking changes; existing APIs and CLI output remain unchanged.
- The new method is additive and can be used where needed for enhanced channel introspection.
2025-10-22 10:42:54 -07:00
Kelly
6f35eb3923 Merge branch 'meshtastic:master' into master 2025-10-22 09:40:37 -07:00
Ian McEwen
dcd077d85e Merge pull request #839 from horrible-knots/master
Plumb timeout from --timeout through MeshInterface
2025-10-17 08:47:42 -07:00
SpudGunMan
da416fcd20 refactor flush
The double flush() is not the root cause; the real issue is that code is trying to use the serial port after it has been closed.
The error occurs both in close() (during flush()) and later in _writeBytes() (during write()), indicating the port is closed or invalid at those times.
2025-10-14 07:54:37 -07:00
SpudGunMan
93da1da386 flush() is only called if the stream is open
This ensures flush() is only called if the stream is open, and logs (but ignores) any exceptions during flush. This should prevent the "Bad file descriptor" error.

I see this error a lot on a rak unit, I dont know this is the way but .. you be the judge.
2025-10-14 07:42:27 -07:00
horrible-knots
2de7c30a27 Plumb timeout from --timeout through MeshInterface
Fix C0301: Line too long

Ignore the pylint for 6 positional arguments
2025-10-13 17:35:30 -04:00
Niklas Roslund
43a685f012 cli: notmalize ignore IDs for dec, hex and 0x. support YAML [] clear
Signed-off-by: Niklas Roslund <nroslund@kth.se>
2025-10-05 12:55:59 +02:00
Ian McEwen
7554c03a26 protobufs: v2.7.10 2025-09-24 21:20:49 -07:00
Taylor Rose
49783d9108 Add T-Deck device support to Meshtastic CLI
- Add T-Deck device definition with verified USB IDs (303a:1001)
- Add T-Deck to supported_devices list
- Uses t-deck firmware identifier and ESP32 device class
- Supports Linux (ttyACM), macOS (cu.usbmodem), and Windows (COM) ports
- Update .gitignore to exclude virtual environment files

T-Deck is now fully supported and automatically detected by the CLI.
2025-09-18 14:39:46 -06:00
github-actions
3615135e97 bump version to 2.7.3 2025-09-18 16:42:32 +00:00
Ian McEwen
cfb23788e1 Merge pull request #812 from shukari/loggingHandler2
Added Logging Handler Names & windows pytest support
2025-09-18 09:26:55 -07:00
Ian McEwen
14ff3eb9c4 Merge branch 'master' into loggingHandler2 2025-09-18 09:26:32 -07:00
Ian McEwen
47e5b04d3b protobufs: v2.7.9 2025-09-18 09:24:33 -07:00
github-actions
e5159f1156 bump version to 2.7.2 2025-09-12 16:49:34 +00:00
Ian McEwen
593b05dbcd protobufs: v2.7.8 2025-09-12 09:46:52 -07:00
Ian McEwen
f519d1f2d2 Merge pull request #818 from pdxlocations/check-module-bitmask
Fix Getting and Setting Excluded Modules: Ringtone and Canned Messages
2025-09-12 09:44:29 -07:00
Ian McEwen
8b36561406 Merge pull request #821 from shukari/contributionHelp
Added Github CONTRIBUTING.md file
2025-09-12 09:43:30 -07:00
shukari
e2b4948d45 Github CONTRIBUTING.md file 2025-09-09 18:17:29 +02:00
pdxlocations
7e3d347b63 remove trailing whitespaces 2025-08-29 23:19:22 -07:00
pdxlocations
c6efccdbd2 init 2025-08-29 23:11:26 -07:00
shukari
fe093ac34b fix errors and better linux/windows handling in tests 2025-08-21 15:56:10 +02:00
Ian McEwen
2b10459db0 remove dangling submodule 2025-08-20 10:13:27 -07:00
shukari
dd238dcbe3 ignore pathlib import 2025-08-19 22:03:37 +02:00
shukari
e330afc899 more paths to os.path.join 2025-08-19 13:38:59 +02:00
shukari
a63f3f6e94 use os.path.join for path 2025-08-19 11:23:22 +02:00
shukari
52eb112b95 new Parameter --debuglib for only meshtastic debug, more termios fixes for windows tests
(cherry picked from commit 4fc4d41d3d29998bb7b697bf412be5c1449ea950)
2025-08-19 11:14:38 +02:00
github-actions
d53ced216c bump version to 2.7.1 2025-08-18 19:40:36 +00:00
Ian McEwen
f5ecd28705 protobufs: v2.7.5 2025-08-18 09:52:48 -07:00
Ian McEwen
82ad9b2f51 Merge pull request #813 from pdxlocations/unmess-unmessageable
Fix --set-owner by calling setOwner!
2025-08-18 09:50:28 -07:00
shukari
cbf7b9befe pylint: test_mesh_interface.py line too long 2025-08-09 16:58:48 +02:00
shukari
f3ba660cf4 fix: lint error 2025-08-07 13:30:47 +02:00
shukari
4b143030d3 system independent path separators; pytest for windows fixes 2025-08-07 12:05:43 +02:00
pdxlocations
03aaa4c98e always call setOwner 2025-08-06 21:58:42 -07:00
shukari
a79e17a575 fix test_exit_with_exception test, pytest for windows
- test are now runable on windows, some are ignored and a fake termios for the decorators
- test_exit_with_exception with a true exception
2025-08-06 23:01:40 +02:00
shukari
38b163fa89 move logger init to the end of the import block for mesh_interface 2025-08-06 20:00:42 +02:00
shukari
af4947d020 move logger init to the end of the import block 2025-08-06 19:54:08 +02:00
shukari
db1891b651 Added Logging Handler Names 2025-08-06 18:21:32 +02:00
github-actions
d7d9c7219a bump version to 2.7.0 2025-08-01 22:56:21 +00:00
Ian McEwen
c60b5d4b05 Add some extra fields that now appear in MyNodeInfo to tests 2025-08-01 15:53:13 -07:00
Ian McEwen
83d82c518a Merge pull request #807 from pdxlocations/export-true-defaults
Export missing defaults when set False
2025-08-01 15:51:05 -07:00
Ian McEwen
8a95ce4636 protobufs: v2.7.4 2025-08-01 15:48:32 -07:00
pdxlocations
0261313fc5 add test 2025-07-27 12:10:05 -07:00
pdxlocations
c1a6234694 make tuple type hint explicit 2025-07-26 23:16:50 -07:00
pdxlocations
66e32f812a fix launch.json punctuation 2025-07-26 22:35:18 -07:00
pdxlocations
eb85439000 rename function 2025-07-26 22:27:10 -07:00
pdxlocations
885eb4898d init 2025-07-26 22:20:19 -07:00
Ian McEwen
172c123990 protobufs: v2.7.3 2025-07-22 17:26:15 -07:00
Ian McEwen
fcdd83838b Merge pull request #795 from pdxlocations/canned-config
Add Ringtone and Canned Messages to --export-config and --configure
2025-07-22 17:22:11 -07:00
Ian McEwen
58967e1d91 Merge pull request #794 from pdxlocations/is-unmessageable
add --set-is-unmessageable to CLI commands
2025-07-22 17:07:50 -07:00
pdxlocations
17f7e8e20e don't check for typoed keyword 2025-07-22 11:23:45 -07:00
pdxlocations
9b5a889676 combine arguments 2025-07-22 10:17:40 -07:00
pdxlocations
ce7c61861f Merge branch 'master' into is-unmessageable 2025-07-22 10:15:19 -07:00
Ian McEwen
4adcbb6787 Merge pull request #743 from ianmcorvidae/client-notifications
Add handling of clientNotification on FromRadio
2025-07-22 09:55:19 -07:00
Ian McEwen
125f63419e Merge branch 'master' into client-notifications 2025-07-22 09:54:17 -07:00
Ian McEwen
cad5d18aff Merge pull request #798 from pdxlocations/export-utf8
Allow forced UTF-8 encoding for --export-config
2025-07-22 09:28:14 -07:00
Ian McEwen
706d0649c1 Merge pull request #801 from Cyclic3/master
Plumb reply_id into sendText and sendData
2025-07-22 09:23:02 -07:00
Ian McEwen
167044907d for some reason pylint likes this better 2025-07-16 19:16:16 -07:00
Ian McEwen
ab1669994f Merge pull request #804 from dangerdyke/fix-vids
Add seeed xiao esp32s3 support
2025-07-16 18:44:16 -07:00
Tom Fifield
6f67f33378 Merge pull request #806 from meshtastic/fifieldt-patch-1
Add check for poetry install to regen-protobufs.sh
2025-07-17 09:05:17 +08:00
Tom Fifield
e60c8ea105 Add check for poetry install to regen-protobufs.sh
This was failing in the CI because the poetry was not initialized.
2025-07-17 10:49:23 +10:00
flockofsparrows
d633f8c895 Add seeed xiao esp32s3 to supported devices 2025-07-09 16:23:21 -04:00
Cyclic3
ca82e1ce2b plumb reply_id into sendText and sendData 2025-07-08 23:38:02 +01:00
pdxlocations
0ae23eec7e don't think i needed that 2025-06-30 00:34:44 -07:00
pdxlocations
2fa85bac1f typo 2025-06-30 00:21:15 -07:00
pdxlocations
58fc614fb7 fix test 2025-06-30 00:12:42 -07:00
pdxlocations
795b652069 export utf-8 2025-06-30 00:03:11 -07:00
rcarteraz
213faa0cae rename and move license file 2025-06-25 12:23:00 -07:00
pdxlocations
68a2009e0e add more tests 2025-06-24 11:47:33 -07:00
pdxlocations
c76e4dac87 undo comment 2025-06-24 11:17:01 -07:00
pdxlocations
428be9fbce add mesh_interface tests 2025-06-24 11:16:23 -07:00
pdxlocations
d83f7b2307 add to example config 2025-06-24 10:12:12 -07:00
pdxlocations
eb453a2e8a add to tests 2025-06-24 08:16:45 -07:00
pdxlocations
308ac93399 add to configure 2025-06-24 07:40:11 -07:00
pdxlocations
84417f0bb1 working export-config 2025-06-23 22:39:24 -07:00
pdxlocations
0bb3389b3b init 2025-06-23 22:11:50 -07:00
pdxlocations
22b3062151 remove whitespace 2025-06-23 16:37:49 -07:00
pdxlocations
373b8a3139 combine with set_owner handling 2025-06-23 16:14:59 -07:00
github-actions
db21942244 bump version to 2.6.4 2025-06-23 22:15:45 +00:00
Ian McEwen
c55f1ef610 Merge pull request #782 from Crank-Git/validate-short-and-long-names
Add validation/prevent --set-owner, --set-owner-short, and --set-ham from accepting empty or whitespace-only names
2025-06-23 09:15:47 -07:00
pdxlocations
51b543ff40 add tests 2025-06-21 18:16:24 -07:00
pdxlocations
8752a0de6e remove whitespace 2025-06-21 16:25:48 -07:00
pdxlocations
7160e79fbf more not incorrect spelling 2025-06-21 13:51:58 -07:00
pdxlocations
b73fcbff88 didn't spell it wrong 2025-06-21 13:50:30 -07:00
pdxlocations
1b5b07e752 add --set-is-unmessageable 2025-06-21 13:44:25 -07:00
Ian McEwen
ab997aac84 Merge pull request #790 from ianmcorvidae/sendMqttClientProxyMessage
Add a sendMqttClientProxyMessage helper for client proxy implementation
2025-06-16 12:48:47 -07:00
Ian McEwen
a097161dbc Add a sendMqttClientProxyMessage helper for client proxy implementation 2025-06-16 12:45:25 -07:00
Ian McEwen
e6750507c8 protobufs: v2.6.12 2025-06-16 12:22:31 -07:00
Ian McEwen
0deb98b4c6 Merge pull request #785 from grleblanc/master
fix(util): update waitForTraceRoute reset logic
2025-06-09 16:22:45 -07:00
grleblanc
04a0ff6322 fix(util): fix typo 2025-06-09 15:50:21 -04:00
grleblanc
b4764d3bc3 fix(util): update waitForTraceRoute reset logic 2025-06-09 15:05:53 -04:00
Ian McEwen
9281c4a335 Correctly call self.close() in disconnected_callback for bleak (fixes #770) 2025-06-09 11:52:23 -07:00
Ben Meadors
3c2dd6f4ff Merge pull request #783 from pdxlocations/config-work
Fix --configure by adding delays after sections
2025-06-09 05:37:25 -05:00
pdxlocations
8e48d141c8 add sleeps 2025-06-08 23:32:32 -07:00
Crank-Git
8a6ee5fb35 fixed assertion errors by replace ham id with ham radio callsign, fixed _sendAdmin assertion 2025-06-08 20:39:26 -04:00
Crank-Git
aa786c7ebd Fix linting errors and duplicate function 2025-06-08 19:51:21 -04:00
Crank-Git
23be2d2189 Add validation/prevent --set-owner, --set-owner-short, and --set-ham from accepting empty or whitespace-only names. This is in relation to meshtastic#6867 firmware feature request https://github.com/meshtastic/firmware/issues/6867 2025-06-08 18:16:49 -04:00
Ian McEwen
622a435465 Add __repr__ methods to interface types and Node, for nicer printing/logging 2025-05-08 21:23:00 -07:00
github-actions
56680f8da6 bump version to 2.6.3 2025-05-08 22:27:56 +00:00
Ian McEwen
321a960c13 protobufs: v2.6.7 2025-05-08 15:26:47 -07:00
Ian McEwen
4668852b0b Admin messages should be requesting acknowledgements 2025-05-08 15:22:27 -07:00
github-actions
c3973117c8 bump version to 2.6.2 2025-04-23 16:49:59 +00:00
Ian McEwen
d456e4ce30 protobufs: v2.6.6 2025-04-23 09:47:13 -07:00
Ben Meadors
ec78f62992 Merge pull request #763 from meshtastic/revert-753-fix-rtsdtr-windows
Revert "Work around RTS/DTR serial reset issues on Windows"
2025-04-15 16:37:23 -05:00
Ben Meadors
dfc9547ffc Revert "Work around RTS/DTR serial reset issues on Windows" 2025-04-15 16:03:33 -05:00
Ian McEwen
ee0f73a20e Merge pull request #761 from derekn/master
fixing tcp_interface 100% cpu usage bug (#709)
2025-04-15 13:56:28 -07:00
Derek Nicol
2e73fe310c fixing tcp_interface 100% cpu usage bug (#709) 2025-04-15 15:23:09 -05:00
Ian McEwen
d4bc39153a bump to 2.6.2a0 2025-04-05 23:01:43 -07:00
github-actions
2e8f823431 bump version to 2.6.1 2025-04-06 05:56:17 +00:00
Ian McEwen
675169167c protobufs: v2.6.4 2025-04-05 22:53:02 -07:00
Ian McEwen
75f8e27799 Merge pull request #749 from cdanis/fix-default-pytest
Fix pytest test collection on default install
2025-03-30 22:28:28 -07:00
Ian McEwen
f426699d2b Merge pull request #753 from dwhinham/fix-rtsdtr-windows
Work around RTS/DTR serial reset issues on Windows
2025-03-30 22:27:34 -07:00
Dale Whinham
bc1664dade Work around RTS/DTR serial reset issues on Windows
Some devices (e.g. Heltec V3.1) are reset when the RTS or DTR pins are
messed with, which means that without workarounds, Meshtastic CLI will
cause a reset with any operation that opens the serial port.

This behavior is documented in this PySerial issue:
https://github.com/pyserial/pyserial/issues/124

On Linux, we already handle this by disabling the HUPCL termios flag,
which has the effect of preventing the offending lines being toggled. On
Windows, setting the initial state of RTS and DTR before opening the
port has a similar effect.

Implement this workaround so that Meshtastic CLI can be used on Windows
with these devices.
2025-03-20 13:47:25 +00:00
Ian McEwen
32a61b0209 Make nanopb.proto stuff work right again, hopefully 2025-03-12 14:13:43 -07:00
github-actions
0c38a9eb0b bump version to 2.6.0 2025-03-10 23:18:47 +00:00
Ian McEwen
e591cc184f set 2.6 version num 2025-03-10 16:17:40 -07:00
Chris Danis
b42d33824a Fix pytest test collection on default install
As is, with a default `poetry install`, a simple `poetry pytest run`
will encouter import errors while reading the test files themselves --
which causes pytest to fatally error during test collection, meaning no
tests are run.
2025-03-07 13:16:20 -05:00
rcarteraz
ed908fc4b6 Merge pull request #748 from rcarteraz/master
update README
2025-03-03 11:44:08 -07:00
rcarteraz
3710e6e909 update README 2025-03-03 11:42:15 -07:00
Ian McEwen
faa8064ccc protobufs: v2.6.0 2025-03-02 12:14:20 -07:00
Ian McEwen
2f44351945 Merge pull request #742 from ianmcorvidae/multiset
Enable setting things from more than one configuration section with `--set`, using a configuration transaction.
2025-02-21 10:08:15 -07:00
Ian McEwen
f5fa30cb22 try to get pylint happier 2025-02-20 12:40:39 -07:00
Ian McEwen
46a8db286c Add handling of clientNotification on FromRadio, sending a pubsub message 2025-02-20 12:36:21 -07:00
Ian McEwen
852949575b Enable setting things from more than one configuration section with --set, using a configuration transaction. 2025-02-20 11:39:16 -07:00
github-actions
e2c9c1315e bump version to 2.5.12 2025-02-19 17:21:25 +00:00
Ian McEwen
f41ef042a9 Merge pull request #708 from mikeymakesit/add-channel
Add new channels from an add URL with the new --ch-add-url option
2025-02-19 10:18:09 -07:00
Ian McEwen
84bec5a7c4 fix up misc. lint/type/test issues, streamline, align argument names and groupings 2025-02-19 10:15:27 -07:00
Ian McEwen
985366c812 Merge pull request #710 from digitaldisarray/master
Add text message port cli option
2025-02-19 09:55:01 -07:00
Ian McEwen
3954fbd404 fix misc lint, test, type complaints from CI 2025-02-19 09:52:16 -07:00
Ian McEwen
5f174b2850 Merge pull request #731 from migillett/464/0x-prefix
464: allow for 0x node prefix values
2025-02-19 09:30:15 -07:00
Michael Gillett
23ea19c00b linter fixes following failed build 2025-02-19 11:26:49 -05:00
Ian McEwen
acc47146c9 Merge pull request #736 from dandrzejewski/master
Add optional CLI parameter to specify node info fields to show with --nodes parameter.
2025-02-19 09:26:17 -07:00
Ian McEwen
dd8803793d fix test mock 2025-02-19 09:21:51 -07:00
Ian McEwen
68ec588804 remove trailing whitespace 2025-02-19 09:15:45 -07:00
David Andrzejewski
2f48594dc3 Fix a pylint error regarding typing. 2025-02-18 22:09:37 -05:00
David Andrzejewski
c7c3c69fc3 Fix a pylint error regarding typing. 2025-02-18 22:08:26 -05:00
David Andrzejewski
53e40d5aab Fix a pylint error 2025-02-18 22:05:46 -05:00
David Andrzejewski
dd3da6a670 Merge remote-tracking branch 'origin/master' 2025-02-18 21:18:59 -05:00
David Andrzejewski
e500b399f4 Merge branch 'meshtastic:master' into master 2025-02-18 21:18:48 -05:00
David Andrzejewski
27ac28e300 Fixing indentation per project coding standards. 2025-02-18 21:18:22 -05:00
David Andrzejewski
6bc5f5e305 Fix count vs len on a list (https://github.com/meshtastic/python/pull/736#discussion_r1960099918) 2025-02-18 21:15:31 -05:00
Michael Gillett
c844e4e0fe help details for new prefix values 2025-02-18 15:25:24 -05:00
Michael Gillett
060df86bb6 Update __main__.py 2025-02-18 15:10:55 -05:00
Michael Gillett
7d87d5037e Update __main__.py 2025-02-18 15:10:26 -05:00
Michael Gillett
4ec7698d94 metavars being a PAIN 2025-02-18 15:09:59 -05:00
Michael Gillett
7cc65aa08a missed one 2025-02-18 15:06:20 -05:00
Michael Gillett
cc411ce0bb remove nargs and unneeded fstrings 2025-02-18 15:05:31 -05:00
Michael Gillett
edff956f9d specify nargs 2025-02-18 14:53:01 -05:00
Michael Gillett
bd68739158 >= 8 instead of > 8 2025-02-18 14:30:28 -05:00
Michael Gillett
530d92ead2 remove redundant f string 2025-02-18 14:24:04 -05:00
Michael Gillett
60f9dc6266 new implementation 2025-02-18 14:23:55 -05:00
Michael Gillett
f9ae021e43 remove old implementation 2025-02-18 14:23:40 -05:00
Michael Gillett
317d81c983 list -> tuple fix for pytest 2025-02-18 14:02:48 -05:00
Ian McEwen
5837bd0172 Merge pull request #734 from dudash/master
catch unhandled OSError when serial port in use
2025-02-18 09:52:03 -07:00
Ian McEwen
5487f7a791 Merge pull request #735 from tache/master
Added descriptive FileNotFoundError handler for serial device connection
2025-02-18 09:51:17 -07:00
Ian McEwen
c6d8a540eb Bump to 2.5.12a0 2025-02-18 09:43:55 -07:00
Ian McEwen
0962c9b058 protobufs: v2.5.22 2025-02-18 09:43:32 -07:00
Ian McEwen
4f98602ac2 ignore argcomplete import in main 2025-02-18 09:41:54 -07:00
David Andrzejewski
6ebddb67c0 Refactored showNodes. It now uses a lookup table to determine the human-readable names of the fields. 2025-02-15 17:18:14 -05:00
David Andrzejewski
82554a1f18 Add optional parameter to specify what fields to show with --nodes 2025-02-15 02:23:13 -05:00
tache
8c115dc636 Added descriptive FileNotFoundError handler for serial device connection
- Added a detailed error message for FileNotFoundError when the serial device is not found.
- Included troubleshooting steps for common issues.
2025-02-09 14:23:31 -05:00
dudash
e2fe359527 catch unhandled OSError when serial port in use 2025-02-08 14:51:53 -05:00
Michael Gillett
5600ce92b0 update to comments 2025-01-26 13:10:56 -05:00
Michael Gillett
efb848adf9 comments explaining logic 2025-01-26 12:58:18 -05:00
Michael Gillett
0d8646189f remove logging.info used for local debugging 2025-01-26 12:56:17 -05:00
Michael Gillett
d0023df8ca slight logic rework 2025-01-26 12:55:03 -05:00
Michael Gillett
b522abf33e update docs 2025-01-26 12:06:31 -05:00
Michael Gillett
c086b6372e Update mesh_interface.py 2025-01-26 11:58:48 -05:00
github-actions
6ec506fe3b bump version to 2.5.11 2025-01-24 17:22:13 +00:00
Ian McEwen
fc3b81dfde protobufs: v2.5.20 2025-01-24 10:19:13 -07:00
Ben Meadors
9c53ea017c Merge pull request #730 from migillett/feature/nodes-add-role
#692 feature/nodes-add-role
2025-01-23 19:07:46 -06:00
Michael Gillett
1e6625d062 feature/nodes-add-role 2025-01-21 22:31:33 -05:00
github-actions
0487ce5e1a bump version to 2.5.10 2025-01-10 17:10:16 +00:00
Ian McEwen
aac19b2ecc protobufs: v2.5.19 2025-01-10 10:06:40 -07:00
digitaldisarray
0fb72b8ad1 Only allow PRIVATE_APP custom port 2024-12-31 15:15:18 -08:00
github-actions
872fbef5d6 bump version to 2.5.9 2024-12-29 03:22:52 +00:00
Ian McEwen
ec4fbe3a59 protobufs: v2.5.18 2024-12-28 20:18:13 -07:00
Ian McEwen
6bab385380 Tweak error messaging in the onReceive function for sendtext packets 2024-12-27 10:04:36 -07:00
Ian McEwen
b8178d513a bump alpha version 2024-12-27 09:54:15 -07:00
Ian McEwen
f4c085fc50 Merge pull request #714 from loic-fejoz/feature/waypoint
Waypoint creation/move/deletion
2024-12-27 09:46:45 -07:00
Ian McEwen
57f0598082 Fix some pylint complaints 2024-12-27 09:40:17 -07:00
github-actions
55d3188408 bump version to 2.5.8 2024-12-27 12:06:45 +00:00
Ben Meadors
7b64fbb71b Merge pull request #725 from meshtastic/dependabot/pip/jinja2-3.1.5
Bump jinja2 from 3.1.4 to 3.1.5
2024-12-25 08:42:58 -06:00
dependabot[bot]
7f85eb0285 Bump jinja2 from 3.1.4 to 3.1.5
Bumps [jinja2](https://github.com/pallets/jinja) from 3.1.4 to 3.1.5.
- [Release notes](https://github.com/pallets/jinja/releases)
- [Changelog](https://github.com/pallets/jinja/blob/main/CHANGES.rst)
- [Commits](https://github.com/pallets/jinja/compare/3.1.4...3.1.5)

---
updated-dependencies:
- dependency-name: jinja2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-24 00:32:14 +00:00
Ben Meadors
d05ef17ab3 Merge pull request #724 from meshtastic/send-alert
Add sendAlert method on mesh interface
2024-12-23 10:48:49 -06:00
Ben Meadors
d161291ca4 Add sendAlert method on mesh interface 2024-12-22 09:09:54 -06:00
Ian McEwen
4e267c75b0 bump to 2.5.8a0 2024-12-21 20:49:32 -07:00
github-actions
f56b9eefa6 bump version to 2.5.7 2024-12-22 03:46:10 +00:00
Ian McEwen
2de1f1921c Merge pull request #723 from ianmcorvidae/add-wcwidth-optional
Add 'wcwidth' as an optional cli dependency
2024-12-21 20:32:47 -07:00
Ian McEwen
227507780e Add 'wcwidth' as an optional cli dependency
Fixes #598 when installed as meshtastic[cli], as should now be
recommended by documentation.
2024-12-21 20:29:07 -07:00
Ian McEwen
9f286c9023 Merge pull request #721 from ianmcorvidae/shell-completion
Add very simple shell completion using argcomplete
2024-12-21 20:26:06 -07:00
Ian McEwen
0b1545393e Make mypy happy with the optional import of argcomplete 2024-12-21 20:23:35 -07:00
Ian McEwen
245a9e40b1 Add very simple shell completion using argcomplete 2024-12-21 20:12:26 -07:00
Ian McEwen
749c6a70bc Merge pull request #720 from ianmcorvidae/winserver-fix
Fix windows-11 detection for non-float platform.release() values
2024-12-21 14:22:55 -07:00
Ian McEwen
afd071c24e Fix windows-11 detection for non-float platform.release() values (fixes #639) 2024-12-21 14:17:41 -07:00
Ian McEwen
29f355bd61 Merge pull request #719 from ianmcorvidae/ignore-nodes
Support setting nodes ignored in nodedb (with 2.5.13+ firmware)
2024-12-21 13:50:44 -07:00
Ian McEwen
4b6d7a8587 Support setting nodes ignored in nodedb (with 2.5.13+ firmware) 2024-12-21 13:46:17 -07:00
Loïc Fejoz
7cc18e9df6 fix(waypoint): Missing methods
Add missing methods callbacks regarding waypoints.
2024-12-21 10:36:36 +01:00
Ian McEwen
a765bccf4d Merge pull request #718 from ianmcorvidae/show-unknown-more-nicely
Display unknown hop count as '?', and header as just 'Hops'
2024-12-20 23:08:19 -07:00
Ian McEwen
f950ecae2d Display unknown hop count as '?', and header as just 'Hops' 2024-12-20 23:04:36 -07:00
Ian McEwen
7c7170a5dd Merge pull request #706 from meshtastic/dependabot/pip/tornado-6.4.2
Bump tornado from 6.4.1 to 6.4.2
2024-12-20 22:50:01 -07:00
Ian McEwen
df191e149b Merge pull request #713 from esev/favorite
Support setting/removing nodes as favorites
2024-12-20 22:36:23 -07:00
Ian McEwen
843abe587f Back out release workflow changes from hotfix 2024-12-20 14:59:26 -07:00
Ian McEwen
ff7dcc3afb Merge branch '2.5.6-hotfix' 2024-12-20 14:47:52 -07:00
Ian McEwen
d66b8fa9dd fix import-related errors 2024-12-20 14:45:54 -07:00
Ian McEwen
f6f8ccfcbc Fix message_to_json_shows_all test for added MyNodeInfo fields 2024-12-20 13:51:27 -07:00
Ian McEwen
cace959ca4 Update protobufs (to 2.5.17) and main python version (to 2.5.7) 2024-12-20 13:45:58 -07:00
github-actions
01ffd83d64 bump version to 2.5.6 2024-12-20 20:42:21 +00:00
Loïc Fejoz
9284a848f2 feat(waypoint): Waypoint creation/deletion
Add methods to send (create or move), delete waypoint.
Add an example script to create, move, delete waypoint.
2024-12-18 17:36:58 +01:00
Eric Severance
18ac0d6d5c Support setting/removing nodes as favorites 2024-12-17 17:38:51 -08:00
Ian McEwen
7c89e231bd Merge pull request #711 from ianmcorvidae/optionalize-deps
Make several dependencies optional (dotmap, print_color, and pyqrcode)
2024-12-12 21:19:15 -07:00
Ian McEwen
4673824236 Add the newly optional dependencies to an extras group 2024-12-12 21:14:58 -07:00
Ian McEwen
d87eddfd33 Make pyqrcode optional 2024-12-12 21:01:38 -07:00
Ian McEwen
31f322f1c2 Make dotmap (via meshtastic.test) and print_color optional 2024-12-12 20:59:22 -07:00
Ian McEwen
89b41c1a19 Remove pexpect too 2024-12-12 20:40:59 -07:00
Ian McEwen
1a5ca789c2 Remove pyparsing and webencodings dependencies, unsure why they're here at all 2024-12-12 19:48:29 -07:00
digitaldisarray
74c911cb75 Add text message port cli option 2024-12-06 17:54:08 -08:00
Mike Hornung
579383cd5a Add new channels from an add URL with the new --ch-add-url option 2024-12-01 23:33:47 -08:00
Ian McEwen
03ac322583 reset mypy-protobuf to non-optional, hopefully without breaking stuff 2024-11-26 14:35:48 -07:00
Ian McEwen
c63814358a prep 2.5.6a0 & protobufs 2024-11-26 14:31:33 -07:00
github-actions
663fabce74 bump version to 2.5.5 2024-11-26 21:22:11 +00:00
Ian McEwen
6243965044 Merge pull request #707 from meshtastic/request-localstats
Allow request-telemetry to elicit LocalStats response
2024-11-24 11:31:01 -07:00
Ben Meadors
b180b6fb15 Allow request-telemetry to illicit LocalStats response 2024-11-24 08:19:40 -06:00
dependabot[bot]
7bb8e4e9dd Bump tornado from 6.4.1 to 6.4.2
Bumps [tornado](https://github.com/tornadoweb/tornado) from 6.4.1 to 6.4.2.
- [Changelog](https://github.com/tornadoweb/tornado/blob/v6.4.2/docs/releases.rst)
- [Commits](https://github.com/tornadoweb/tornado/compare/v6.4.1...v6.4.2)

---
updated-dependencies:
- dependency-name: tornado
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-22 22:17:33 +00:00
Ian McEwen
4c7ac60be6 appease mypy too 2024-11-15 11:55:25 -07:00
Ian McEwen
0b086d10f8 appease pylint 2024-11-15 11:50:35 -07:00
Ian McEwen
426795fccd semi-experimentally, add fallback code for message_to_json, allowing older protobuf library versions 2024-11-15 11:47:17 -07:00
Ian McEwen
fb88ee114c bump protobufs to 2.5.5 2024-11-01 09:06:02 -07:00
github-actions
a4630b53eb bump version to 2.5.4 2024-11-01 16:03:00 +00:00
Ian McEwen
646aa981d5 update readme to link to docs again 2024-10-29 14:22:09 -07:00
Ian McEwen
9381acd6ac Merge pull request #681 from william-stearns/wls_add_types2
Wls add types2
2024-10-29 06:50:52 -07:00
Ian McEwen
384063db19 Fix some remaining mypy complaints 2024-10-29 06:47:16 -07:00
Ian McEwen
20d75d9023 Merge branch 'master' into wls_add_types2 2024-10-29 06:19:16 -07:00
Ian McEwen
0deb1d788f Merge pull request #702 from meshtastic/dependabot/pip/werkzeug-3.0.6
Bump werkzeug from 3.0.4 to 3.0.6
2024-10-28 20:36:24 -07:00
Ian McEwen
1070d9202b Merge pull request #698 from lachesis/lachesis/fix-remote-module-cfg-get
Add missing camel to snake conversion
2024-10-28 20:35:10 -07:00
Ian McEwen
b90de8b73b Merge pull request #701 from fmoessbauer/master
A lot of fixes around setting / retrieving base64 encoded values
2024-10-28 20:29:05 -07:00
dependabot[bot]
2e79ecf759 Bump werkzeug from 3.0.4 to 3.0.6
Bumps [werkzeug](https://github.com/pallets/werkzeug) from 3.0.4 to 3.0.6.
- [Release notes](https://github.com/pallets/werkzeug/releases)
- [Changelog](https://github.com/pallets/werkzeug/blob/main/CHANGES.rst)
- [Commits](https://github.com/pallets/werkzeug/compare/3.0.4...3.0.6)

---
updated-dependencies:
- dependency-name: werkzeug
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-26 00:29:31 +00:00
Felix Moessbauer
578d3e4b24 do not double-print value when setting a repeated value
When clearning or appending to a repeated value, both the "Clearing..."
/ "Adding..." line and the "Set..." line were shown. However, this is
misleading as the only performed operation is the clearing / appending.

We fix this by directly returning from the function in case of clearing
/ appending.
2024-10-25 17:11:06 +02:00
Felix Moessbauer
4ca13bcede fix setting of list item on configure
When setting the whole configuration via --configure, list types like
adminKey need special handling. Previously this failed as a list cannot
be appended to a list. The new code adds dedicated logic to replace the
repeated value when passing a list. Also, all items of that list are
converted into the correct (typed) form before setting them.
2024-10-25 17:11:06 +02:00
Felix Moessbauer
6ceae7c72f setPref: pass typed value instead of string
By passing a typed value we can conserve the type information to later
use that to convert it back into the correct protobuf type. The type
conversion is now done inside setPref.
2024-10-25 17:11:06 +02:00
Felix Moessbauer
4c29d7dd0f refactor camel and snake naming in setPref
Same change as done in getPerf to have less branches (simplifies the
code).
2024-10-25 17:11:06 +02:00
Felix Moessbauer
839bbbcad2 config: correctly print byte and array types on get
When getting config values of type bytes or list (technically a protobuf
repeated container type), these were directly printed on the output.
However, the retrieved values could not be set by --set again, as the
format was different (e.g. python string representation of bytes vs.
base64 prefixed and encoded as expected by --set).

We fix this by adding a toStr utility function (similar to the fromStr)
function to convert byte types correctly to the base64 representation.
Further, we check if the type is repeated and apply this operation to
all values.
2024-10-25 17:11:06 +02:00
Felix Moessbauer
1abb9fb213 refactor getPref to use uniform printing logic
We add a local helper function to print a single setting. This is a
preparation to correctly print non-trivial types. The existing code
in getPref is ported over to use that function. By that, the output
of "wholeField" is changed slightly to always print the full path for
each setting (e.g. "security.serialEnabled" instead of
"security:\nserialEnabled"). This improves support for grepping on the
output.
2024-10-25 17:11:06 +02:00
Felix Moessbauer
7fcbbe9b80 refactor camel and snake naming in getPref
We have a lot of code duplication by checkin over and over again if
field should be named in camel or snake notation. We simplify this by
writing the choosen variant into a variable and just use that name
across the code.

No functional change.
2024-10-25 14:13:50 +02:00
Eric Swanson
073274cb00 Add missing camel to snake conversion 2024-10-21 22:06:43 -04:00
Ian McEwen
92a3986a8f Improve comments for pdoc 2024-10-21 16:39:47 -07:00
github-actions
f08ec1885b bump version to 2.5.3 2024-10-18 17:02:53 +00:00
Ian McEwen
feca49faed Merge pull request #696 from fmoessbauer/master
fix base64 encoding of key field in config
2024-10-18 09:57:13 -07:00
Felix Moessbauer
dfaf1a275d fix base64 encoding of key field in config
The security.privateKey and security.publicKey fields are of type bytes,
but the protobuf MessageToDict converts them to base64 encoded strings.
When importing the config again, this is read as a string, which breaks
the import. Instead, the value needs to be prefixed with "base64:", so
the type handling logic on import kicks in and decodes the value to a
bytes array again.

Fixes: #678
2024-10-18 16:13:25 +02:00
Ian McEwen
da7fa31805 tweak documentation formatting 2024-10-16 20:52:10 -07:00
Ian McEwen
12fd29b203 Merge pull request #694 from ianmcorvidae/configure-fixed-position
Use dedicated fixed position admin message for --configure
2024-10-16 16:01:06 -07:00
Ian McEwen
a43dd201ba Merge pull request #695 from ianmcorvidae/ble-disconnect
Send the meshtastic.connection.lost message from BLEInterface's close method
2024-10-16 16:00:49 -07:00
Ian McEwen
a64a9d203a Send the meshtastic.connection.lost message from BLEInterface's close method 2024-10-16 12:13:30 -07:00
Ian McEwen
10136962d7 Use dedicated fixed position admin message for --configure 2024-10-15 07:28:55 -07:00
Ian McEwen
3afb294f9b Merge pull request #691 from logikal/telemetry_output
autoprint other types of telemetry when returned from --request-telemetry
2024-10-14 17:11:00 -07:00
Sean Kilgore
ee405fec41 autoprint other types of telemetry when returned from --request-telemetry 2024-10-14 16:58:52 -07:00
Ian McEwen
3eabaf91d0 Merge pull request #687 from ianmcorvidae/telemetry-types
Support requesting different telemetry types
2024-10-13 21:50:04 -07:00
Ian McEwen
78b92cecc9 fix type check 2024-10-13 20:40:22 -07:00
Ian McEwen
7088b90514 Support requesting different telemetry types 2024-10-13 20:35:11 -07:00
Ian McEwen
2ae81f8602 Merge pull request #686 from jose1711/typofix
Fix typo.
2024-10-13 18:00:16 -07:00
Jose Riha
923f5e82d0 Fix typo. 2024-10-14 02:48:41 +02:00
Ian McEwen
05731128fa missed a spot 2024-10-12 12:52:36 -07:00
Ian McEwen
0523d4c94f disable R0917 pylint failures 2024-10-12 12:49:14 -07:00
Ian McEwen
90e901de79 Upgrade bleak and therefore also the supported python versions 2024-10-12 12:32:43 -07:00
Ian McEwen
6606851135 Merge pull request #685 from ianmcorvidae/more-telemetry
Add other telemetry variants to automatic handling/adding to node information
2024-10-12 11:59:45 -07:00
Ian McEwen
33fecbd74d Add other telemetry variants to automatic handling/adding to node information 2024-10-12 11:56:55 -07:00
Ian McEwen
6b9db7abd9 2.5.3 setup 2024-10-12 09:33:28 -07:00
github-actions
ece6286d82 bump version to 2.5.2 2024-10-12 16:31:48 +00:00
William Stearns
e335f12a3b attempts to fix mypy issues 2024-10-11 00:59:02 -04:00
William Stearns
0da405168f pylint cleanups 2024-10-10 23:49:20 -04:00
William Stearns
58d9039a04 another missing import 2024-10-10 16:49:06 -04:00
William Stearns
f77e788aa8 fix missing import 2024-10-10 16:33:07 -04:00
William Stearns
aba381fb54 Merge branch 'master' into wls_add_types 2024-10-08 23:28:25 -04:00
Ian McEwen
0bb4b31b6a Merge pull request #679 from ianmcorvidae/argument-groups
Add a number of argument groups to organize the help output
2024-10-03 19:45:13 -07:00
Ian McEwen
915066e0af add metavars for a bunch of arguments for nicer docs 2024-10-03 19:45:03 -07:00
Ian McEwen
6be3969577 Add a number of argument groups to organize the help output 2024-10-01 18:10:14 -07:00
Ian McEwen
b73cc1f499 Make it so wantconfig isn't a 1 in 4294967296 lottery for getting no nodes 2024-10-01 14:00:06 -07:00
Ian McEwen
65305af184 protobufs/alpha version: 2.5.2 2024-09-29 14:34:53 -07:00
github-actions
3fb1e67357 bump version to 2.5.1 2024-09-29 21:30:36 +00:00
Ian McEwen
cbd3c119fe Fix pylint errors 2024-09-28 20:15:08 -07:00
Ian McEwen
bbd6d6a541 Change order of logging and parsing fromRadioBytes, and add a bit more traceback logging at that point 2024-09-28 20:11:52 -07:00
Ian McEwen
6e1217c7ca Merge pull request #677 from ianmcorvidae/pkiencrypted_admin
Default to pkiEncrypted always on for admin messages
2024-09-28 18:40:31 -07:00
Ian McEwen
81db38956b Silence pylint 2024-09-28 18:37:05 -07:00
Ian McEwen
27729995d2 Default to pkiEncrypted always on for admin messages 2024-09-28 11:13:04 -07:00
Ian McEwen
d875a574b6 Merge pull request #676 from ianmcorvidae/set-time
Add a --set-time command that set's the node time using a provided timestamp or the host system clock
2024-09-22 10:06:09 -07:00
Ian McEwen
40019a9712 Add a --set-time command that set's the node time using a provided timestamp or the host system clock. 2024-09-22 09:32:04 -07:00
Ian McEwen
de657bab24 Merge pull request #675 from djholt/feature/remote-config-position
Enable setting and removing fixed position via remote admin
2024-09-19 11:24:23 -07:00
DJ Holt
40353a387e Fix tests for remote position configs 2024-09-19 03:31:01 -06:00
DJ Holt
9949d144a1 Enable setting and removing fixed position via remote admin 2024-09-19 02:39:49 -06:00
Ian McEwen
48a06c6e1e protobufs & alpha version number: v2.5.1 2024-09-18 14:58:05 -07:00
Ian McEwen
c696d59b90 Set list-type keys differently, excluding 0-like values and resetting whole list 2024-09-17 21:52:57 -07:00
Ian McEwen
4fdbcb9679 Fix test_fuzz_fromStr for floats 2024-09-17 21:10:57 -07:00
Ian McEwen
5d6dfb877b Merge pull request #673 from djholt/feature/tcp-port
Allow port number to be specified with tcp hostname
2024-09-17 21:08:03 -07:00
Ian McEwen
951edfe27b Merge pull request #665 from lysol/remote-admin-retry
Retry admin channel setting retrieval and add configurable timeout
2024-09-17 21:06:38 -07:00
DJ Holt
5cc9627e21 Refactor default port number to variable 2024-09-16 23:41:44 -06:00
DJ Holt
bf904c6906 Allow port number to be specified with tcp hostname 2024-09-16 23:24:03 -06:00
Derek Arnold
2026212a00 please pylint with a docstring and newline 2024-09-15 16:27:40 -05:00
Derek Arnold
34f9be255e fix unrelated bug when fromStr input is short hex
For example, 0x0 will generate an unhandled ValueError. This was caught
during a random unit test run for 3.9, so I figure I'd fix it.

This is unrelated to the PR otherwise.
2024-09-15 16:20:50 -05:00
Derek Arnold
e561222ea7 add docstrings 2024-09-15 16:06:44 -05:00
Derek Arnold
73a1bbc7d5 add test coverage for changes to requestChannels 2024-09-15 16:02:59 -05:00
Derek Arnold
8f2c397fbf missed one reference for requestChannelRetries 🤡 2024-09-15 14:01:41 -05:00
Derek Arnold
62f5201a38 Add test covering retry logic 2024-09-15 12:04:22 -05:00
Derek Arnold
9e7d5e96ab Rename "retries" to "attempts"
Otherwise, semantically, it's off-by-one.
2024-09-15 12:03:51 -05:00
Derek Arnold
aa74db46cb update message check in test to reflect new message 2024-09-15 11:59:03 -05:00
Derek Arnold
1967519deb correct type issue during initial assignment 2024-09-15 11:52:27 -05:00
Derek Arnold
662aea049a Merge branch 'master' into remote-admin-retry 2024-09-15 11:46:32 -05:00
Derek Arnold
44cfd72a80 make sure new_index is always an int 2024-09-15 11:39:10 -05:00
Derek Arnold
abe1dd47ca add type to argument 2024-09-15 11:36:23 -05:00
Ian McEwen
584a14f578 Merge pull request #668 from ianmcorvidae/channel-preset-optimize
Change modem preset shortcuts to not request channels, and to request remote config when needed
2024-09-12 16:20:59 -07:00
github-actions
f7724295f9 bump version to 2.5.0 2024-09-12 16:27:45 +00:00
Ian McEwen
cc2067b729 clean up settings response to use CopyFrom to be better at repeated and nested fields 2024-09-11 17:10:04 -07:00
Ian McEwen
180ddbcd1a Merge pull request #672 from ianmcorvidae/detect-repeating
Detect repeating fields using field labels, enabling admin key to be set
2024-09-11 16:36:29 -07:00
Ian McEwen
3bbd02c915 Detect repeating fields using field labels, enabling admin key to be set 2024-09-11 16:34:03 -07:00
Ian McEwen
23e6eca056 Merge pull request #671 from ianmcorvidae/factory-reset-update
Split factory reset into two variants
2024-09-11 09:43:16 -07:00
Ian McEwen
21ff4a1a4a Split factory reset into two variants 2024-09-11 09:40:26 -07:00
Ian McEwen
ef6db0e48c Merge branch 'master' into 2.5 2024-09-11 09:21:01 -07:00
Ian McEwen
43b0993aaa Merge branch 'master' into 2.5 2024-09-11 09:19:43 -07:00
github-actions
9423a8a8b9 bump version to 2.4.3 2024-09-11 16:16:12 +00:00
Ian McEwen
33a13f715e 2.4.3 prep (protobufs, alpha version) 2024-09-11 09:12:41 -07:00
github-actions
0aac077ce7 bump version to 2.4.2 2024-09-11 16:05:25 +00:00
Ian McEwen
8ba92da7cf Change modem preset shortcuts to not request channels, and to request remote config when needed 2024-09-06 23:38:52 -07:00
Ian McEwen
0d26c26f7e update protobufs to master 2024-09-06 23:13:31 -07:00
Ian McEwen
aa6f09635a Merge pull request #661 from meshtastic/dependabot/pip/jupyterlab-4.2.5
Bump jupyterlab from 4.2.3 to 4.2.5
2024-09-06 11:30:17 -07:00
Ian McEwen
83b0dcad56 Merge pull request #667 from ianmcorvidae/telemetry-updating
Update telemetry in interface.nodes on receiving device metrics packets
2024-09-06 11:10:17 -07:00
Ian McEwen
78d8403bbd Update telemetry in interface.nodes on receiving device metrics packets 2024-09-06 00:08:20 -07:00
Ian McEwen
0813e8dba6 protobufs: v2.4.2; bump to alpha version 2024-09-05 14:27:38 -07:00
Ian McEwen
399dd477b8 Merge branch 'master' into 2.5 2024-09-05 14:00:35 -07:00
Derek Arnold
3811226a61 add a configurable timeout 2024-09-03 22:12:03 -05:00
Derek Arnold
b6547c9737 actually link up the retry args from the commandline to getNode 2024-09-03 22:11:52 -05:00
Derek Arnold
9612aea9b9 Add in a retry mechanism for channel settings
Attempts multiple times to fetch things over the admin channel
before giving up.
2024-09-03 21:58:16 -05:00
dependabot[bot]
aedaa3748d Bump jupyterlab from 4.2.3 to 4.2.5
Bumps [jupyterlab](https://github.com/jupyterlab/jupyterlab) from 4.2.3 to 4.2.5.
- [Release notes](https://github.com/jupyterlab/jupyterlab/releases)
- [Changelog](https://github.com/jupyterlab/jupyterlab/blob/@jupyterlab/lsp@4.2.5/CHANGELOG.md)
- [Commits](https://github.com/jupyterlab/jupyterlab/compare/@jupyterlab/lsp@4.2.3...@jupyterlab/lsp@4.2.5)

---
updated-dependencies:
- dependency-name: jupyterlab
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-29 18:23:54 +00:00
Ian McEwen
4b60c5b457 Merge pull request #660 from GUVWAF/2way_traceroute
HopStart should be set for route back to be valid
2024-08-26 11:40:08 -07:00
GUVWAF
c92474cf36 HopStart should be set for route back to be valid 2024-08-26 20:32:51 +02:00
Ian McEwen
b2acc84717 remove trailing whitespace 2024-08-25 18:27:34 -07:00
Ian McEwen
1981f0e899 Merge pull request #654 from GUVWAF/2way_traceroute
Show two-way traceroute result with SNR if available
2024-08-25 16:54:36 -07:00
Ian McEwen
84dff75399 allow for underscore-only stuff in camel_to_snake, silly as that is 2024-08-24 23:38:48 -07:00
Ian McEwen
df12b8a659 appease the pylint beast 2024-08-24 23:34:28 -07:00
Ian McEwen
eedf42b904 add more fuzzing tests for utility functions 2024-08-24 22:25:18 -07:00
Ian McEwen
e7ed254d9d appease pylint, tests, mypy 2024-08-24 22:00:29 -07:00
Ian McEwen
dfa29bbb7c Add ensureSessionKey to setURL and setFixedPosition calls 2024-08-24 21:50:44 -07:00
Ian McEwen
5a06888cc7 Merge pull request #656 from meshtastic/more-admin
More admin
2024-08-24 21:48:19 -07:00
Jonathan Bennett
a9e2168f1d Refactor to add ensureSessionKey function 2024-08-24 20:18:57 -05:00
Jonathan Bennett
eec745c861 Add missed colon in if statement 2024-08-24 19:29:18 -05:00
Jonathan Bennett
4cc283d004 Add the admin sessionkey_only request 2024-08-24 17:27:42 -05:00
Ian McEwen
bc508ff9e6 cleanup admin stuff a little 2024-08-24 14:15:37 -07:00
Ian McEwen
ff72fc4804 Merge pull request #655 from meshtastic/show-pubkey
Adds admin session_passkey handling for 2.5 remote admin
2024-08-24 14:12:41 -07:00
Jonathan Bennett
78399503c5 Adds handlers to get and set admin session_key for remote admin 2024-08-24 15:32:44 -05:00
GUVWAF
216fd7ddc4 Show two-way traceroute result with SNR if available 2024-08-24 10:07:48 +02:00
Ian McEwen
688693d2fb Merge pull request #653 from meshtastic/show-pubkey
Adds handler function for public key, and adds to nodedb display
2024-08-23 20:41:40 -07:00
Jonathan Bennett
15b5e93563 Correctly format public key and add to node list 2024-08-23 22:21:54 -05:00
Ian McEwen
1bbcc452ae precalculate bit-shifts and don't generate too-large random numbers for packet ID generation 2024-08-23 15:34:25 -07:00
Ben Meadors
1abe00d0b2 Merge pull request #652 from ianmcorvidae/randomized-packet-ids
randomize packet IDs as firmware does
2024-08-23 17:30:07 -05:00
Ian McEwen
c8cf8094c3 randomize packet IDs as firmware does 2024-08-23 13:18:23 -07:00
Ian McEwen
477690edde mark protobufs folder as generated code for github 2024-08-21 22:27:18 -07:00
Ian McEwen
58466f2ab7 Add wiring for security config in node.py 2024-08-21 22:11:45 -07:00
Ian McEwen
bb6f51eb43 protobufs: 2.5 (in progress, untagged) 2024-08-21 21:58:56 -07:00
Ian McEwen
48987c38e2 set alpha version 2024-08-21 21:55:43 -07:00
William Stearns
60de9dddb1 Remove references to BLEClient breaking CI checks 2024-07-09 19:54:01 -04:00
William Stearns
a29ee840f2 Adding mypy typing 2024-06-15 23:22:43 -04:00
75 changed files with 11339 additions and 3581 deletions

1
.gitattributes vendored
View File

@@ -2,3 +2,4 @@
*.{cmd,[cC][mM][dD]} text eol=crlf
*.{bat,[bB][aA][tT]} text eol=crlf
*.{sh,[sS][hH]} text eol=lf
meshtastic/protobuf/* linguist-generated=true

22
.github/CONTRIBUTING.md vendored Normal file
View File

@@ -0,0 +1,22 @@
# Contributing to Meshtastic Python
## Development resources
- [API Documentation](https://python.meshtastic.org/)
- [Meshtastic Python Development](https://meshtastic.org/docs/development/python/)
- [Building Meshtastic Python](https://meshtastic.org/docs/development/python/building/)
- [Using the Meshtastic Python Library](https://meshtastic.org/docs/development/python/library/)
## How to check your code (pytest/pylint) before a PR
- [Pre-requisites](https://meshtastic.org/docs/development/python/building/#pre-requisites)
- also execute `poetry install --all-extras --with dev,powermon` for all optional dependencies
- check your code with github ci actions locally
- You need to have act installed. You can get it at https://nektosact.com/
- on linux: `act -P ubuntu-latest=-self-hosted --matrix "python-version:3.12"`
- on windows:
- linux checks (linux docker): `act --matrix "python-version:3.12"`
- windows checks (windows host): `act -P ubuntu-latest=-self-hosted --matrix "python-version:3.12"`
- or run all locally:
- run `poetry run pylint meshtastic examples/ --ignore-patterns ".*_pb2.pyi?$"`
- run `poetry run mypy meshtastic/`
- run `poetry run pytest`
- more commands see [CI workflow](https://github.com/meshtastic/python/blob/master/.github/workflows/ci.yml)

BIN
.github/meshtastic_logo.png vendored Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

3
.gitignore vendored
View File

@@ -17,4 +17,5 @@ examples/__pycache__
meshtastic.spec
.hypothesis/
coverage.xml
.ipynb_checkpoints
.ipynb_checkpoints
.cursor/

27
.vscode/launch.json vendored
View File

@@ -4,6 +4,7 @@
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "meshtastic BLE",
"type": "debugpy",
@@ -245,6 +246,30 @@
"module": "meshtastic",
"justMyCode": true,
"args": ["--debug", "--nodes"]
}
},
{
"name": "meshtastic nodes table",
"type": "debugpy",
"request": "launch",
"module": "meshtastic",
"justMyCode": true,
"args": ["--nodes"]
},
{
"name": "meshtastic nodes table with show-fields",
"type": "debugpy",
"request": "launch",
"module": "meshtastic",
"justMyCode": true,
"args": ["--nodes", "--show-fields", "AKA,Pubkey,Role,Role,Role,Latitude,Latitude,deviceMetrics.voltage"]
},
{
"name": "meshtastic --export-config",
"type": "debugpy",
"request": "launch",
"module": "meshtastic",
"justMyCode": true,
"args": ["--export-config", "config.json"]
},
]
}

View File

View File

@@ -1,4 +1,10 @@
# Meshtastic Python
<div align="center" markdown="1">
<img src=".github/meshtastic_logo.png" alt="Meshtastic Logo" width="80"/>
<h1 align="center"> Meshtastic Python
</h1>
<p style="font-size:15px;" align="center">A Python library and client for use with Meshtastic devices. </p>
[![codecov](https://codecov.io/gh/meshtastic/python/branch/master/graph/badge.svg?token=TIWPJL73KV)](https://codecov.io/gh/meshtastic/python)
![PyPI - Downloads](https://img.shields.io/pypi/dm/meshtastic)
@@ -7,17 +13,20 @@
[![Fiscal Contributors](https://opencollective.com/meshtastic/tiers/badge.svg?label=Fiscal%20Contributors&color=deeppink)](https://opencollective.com/meshtastic/)
![GPL-3.0](https://img.shields.io/badge/License-GPL%20v3-blue.svg)
</div>
<div align="center">
<a href="https://meshtastic.org/docs/software/python/cli/installation">Getting Started Guide</a>
-
<a href="https://python.meshtastic.org">API Documentation</a>
</div>
## Overview
A Python client for use with Meshtastic devices.
This small library (and example application) provides an easy API for sending and receiving messages over mesh radios.
It also provides access to any of the operations/data available in the device user interface or the Android application.
Events are delivered using a publish-subscribe model, and you can subscribe to only the message types you are interested in.
**[Getting Started Guide](https://meshtastic.org/docs/software/python/cli/installation)**
(Documentation/API Reference is currently offline)
## Call for Contributors
This library and CLI has gone without a consistent maintainer for a while, and there's many improvements that could be made. We're all volunteers here and help is extremely appreciated, whether in implementing your own needs or helping maintain the library and CLI in general.

View File

@@ -6,6 +6,12 @@ set -e
#gsed -i 's/import "\//import ".\//g' ./protobufs/meshtastic/*
#gsed -i 's/package meshtastic;//g' ./protobufs/meshtastic/*
POETRYDIR=$(poetry env info --path)
if [[ -z "${POETRYDIR}" ]]; then
poetry install
fi
# protoc looks for mypy plugin in the python path
source $(poetry env info --path)/bin/activate
@@ -22,6 +28,7 @@ OUTDIR=${TMPDIR}/out
PYIDIR=${TMPDIR}/out
mkdir -p "${OUTDIR}" "${INDIR}" "${PYIDIR}"
cp ./protobufs/meshtastic/*.proto "${INDIR}"
cp ./protobufs/nanopb.proto "${INDIR}"
# OS-X sed is apparently a little different and expects an arg for -i
if [[ $OSTYPE == 'darwin'* ]]; then
@@ -36,6 +43,8 @@ $SEDCMD 's/^package meshtastic;/package meshtastic.protobuf;/' "${INDIR}/"*.prot
# fix the imports to match
$SEDCMD 's/^import "meshtastic\//import "meshtastic\/protobuf\//' "${INDIR}/"*.proto
$SEDCMD 's/^import "nanopb.proto"/import "meshtastic\/protobuf\/nanopb.proto"/' "${INDIR}/"*.proto
# Generate the python files
./nanopb-0.4.8/generator-bin/protoc -I=$TMPDIR/in --python_out "${OUTDIR}" "--mypy_out=${PYIDIR}" $INDIR/*.proto

View File

@@ -4,6 +4,9 @@ owner_short: BOB
channel_url: https://www.meshtastic.org/e/#CgMSAQESCDgBQANIAVAe
canned_messages: Hi|Bye|Yes|No|Ok
ringtone: 24:d=32,o=5,b=565:f6,p,f6,4p,p,f6,p,f6,2p,p,b6,p,b6,p,b6,p,b6,p,b,p,b,p,b,p,b,p,b,p,b,p,b,p,b,1p.,2p.,p
location:
lat: 35.88888
lon: -93.88888

57
examples/waypoint.py Normal file
View File

@@ -0,0 +1,57 @@
"""Program to create and delete waypoint
To run:
python3 examples/waypoint.py --port /dev/ttyUSB0 create 45 test the_desc_2 '2024-12-18T23:05:23' 48.74 7.35
python3 examples/waypoint.py delete 45
"""
import argparse
import datetime
import sys
import meshtastic
import meshtastic.serial_interface
parser = argparse.ArgumentParser(
prog='waypoint',
description='Create and delete Meshtastic waypoint')
parser.add_argument('--port', default=None)
parser.add_argument('--debug', default=False, action='store_true')
subparsers = parser.add_subparsers(dest='cmd')
parser_delete = subparsers.add_parser('delete', help='Delete a waypoint')
parser_delete.add_argument('id', help="id of the waypoint")
parser_create = subparsers.add_parser('create', help='Create a new waypoint')
parser_create.add_argument('id', help="id of the waypoint")
parser_create.add_argument('name', help="name of the waypoint")
parser_create.add_argument('description', help="description of the waypoint")
parser_create.add_argument('icon', help="icon of the waypoint")
parser_create.add_argument('expire', help="expiration date of the waypoint as interpreted by datetime.fromisoformat")
parser_create.add_argument('latitude', help="latitude of the waypoint")
parser_create.add_argument('longitude', help="longitude of the waypoint")
args = parser.parse_args()
print(args)
# By default will try to find a meshtastic device,
# otherwise provide a device path like /dev/ttyUSB0
if args.debug:
d = sys.stderr
else:
d = None
with meshtastic.serial_interface.SerialInterface(args.port, debugOut=d) as iface:
if args.cmd == 'create':
p = iface.sendWaypoint(
waypoint_id=int(args.id),
name=args.name,
description=args.description,
icon=args.icon,
expire=int(datetime.datetime.fromisoformat(args.expire).timestamp()),
latitude=float(args.latitude),
longitude=float(args.longitude),
)
else:
p = iface.deleteWaypoint(int(args.id))
print(p)
# iface.close()

View File

@@ -2,41 +2,45 @@
# A library for the Meshtastic Client API
Primary interfaces: SerialInterface, TCPInterface, BLEInterface
Install with pip: "[pip3 install meshtastic](https://pypi.org/project/meshtastic/)"
Source code on [github](https://github.com/meshtastic/python)
notable properties of interface classes:
- nodes - The database of received nodes. Includes always up-to-date location and username information for each
- `nodes` - The database of received nodes. Includes always up-to-date location and username information for each
node in the mesh. This is a read-only datastructure.
- nodesByNum - like "nodes" but keyed by nodeNum instead of nodeId
- myInfo & metadata - Contain read-only information about the local radio device (software version, hardware version, etc)
- localNode - Pointer to a node object for the local node
- `nodesByNum` - like "nodes" but keyed by nodeNum instead of nodeId. As such, includes "unknown" nodes which haven't seen a User packet yet
- `myInfo` & `metadata` - Contain read-only information about the local radio device (software version, hardware version, etc)
- `localNode` - Pointer to a node object for the local node
notable properties of nodes:
- localConfig - Current radio settings, can be written to the radio with the `writeConfig` method.
- moduleConfig - Current module settings, can be written to the radio with the `writeConfig` method.
- channels - The node's channels, keyed by index.
- `localConfig` - Current radio settings, can be written to the radio with the `writeConfig` method.
- `moduleConfig` - Current module settings, can be written to the radio with the `writeConfig` method.
- `channels` - The node's channels, keyed by index.
# Published PubSub topics
We use a [publish-subscribe](https://pypubsub.readthedocs.io/en/v4.0.3/) model to communicate asynchronous events. Available
topics:
- meshtastic.connection.established - published once we've successfully connected to the radio and downloaded the node DB
- meshtastic.connection.lost - published once we've lost our link to the radio
- meshtastic.receive.text(packet) - delivers a received packet as a dictionary, if you only care about a particular
- `meshtastic.connection.established` - published once we've successfully connected to the radio and downloaded the node DB
- `meshtastic.connection.lost` - published once we've lost our link to the radio
- `meshtastic.receive.text(packet)` - delivers a received packet as a dictionary, if you only care about a particular
type of packet, you should subscribe to the full topic name. If you want to see all packets, simply subscribe to "meshtastic.receive".
- meshtastic.receive.position(packet)
- meshtastic.receive.user(packet)
- meshtastic.receive.data.portnum(packet) (where portnum is an integer or well known PortNum enum)
- meshtastic.node.updated(node = NodeInfo) - published when a node in the DB changes (appears, location changed, username changed, etc...)
- meshtastic.log.line(line) - a raw unparsed log line from the radio
- `meshtastic.receive.position(packet)`
- `meshtastic.receive.user(packet)`
- `meshtastic.receive.data.portnum(packet)` (where portnum is an integer or well known PortNum enum)
- `meshtastic.node.updated(node = NodeInfo)` - published when a node in the DB changes (appears, location changed, username changed, etc...)
- `meshtastic.log.line(line)` - a raw unparsed log line from the radio
- `meshtastic.clientNotification(notification, interface) - a ClientNotification sent from the radio
We receive position, user, or data packets from the mesh. You probably only care about meshtastic.receive.data. The first argument for
that publish will be the packet. Text or binary data packets (from sendData or sendText) will both arrive this way. If you print packet
you'll see the fields in the dictionary. decoded.data.payload will contain the raw bytes that were sent. If the packet was sent with
sendText, decoded.data.text will **also** be populated with the decoded string. For ASCII these two strings will be the same, but for
We receive position, user, or data packets from the mesh. You probably only care about `meshtastic.receive.data`. The first argument for
that publish will be the packet. Text or binary data packets (from `sendData` or `sendText`) will both arrive this way. If you print packet
you'll see the fields in the dictionary. `decoded.data.payload` will contain the raw bytes that were sent. If the packet was sent with
`sendText`, `decoded.data.text` will **also** be populated with the decoded string. For ASCII these two strings will be the same, but for
unicode scripts they can be different.
# Example Usage
@@ -77,7 +81,6 @@ from typing import *
import google.protobuf.json_format
import serial # type: ignore[import-untyped]
from dotmap import DotMap # type: ignore[import-untyped]
from google.protobuf.json_format import MessageToJson
from pubsub import pub # type: ignore[import-untyped]
from tabulate import tabulate
@@ -108,13 +111,13 @@ from . import (
LOCAL_ADDR = "^local"
"""A special ID that means the local node"""
BROADCAST_NUM = 0xFFFFFFFF
BROADCAST_NUM: int = 0xFFFFFFFF
"""if using 8 bit nodenums this will be shortened on the target"""
BROADCAST_ADDR = "^all"
"""A special ID that means broadcast"""
OUR_APP_VERSION = 20300
OUR_APP_VERSION: int = 20300
"""The numeric buildnumber (shared with android apps) specifying the
level of device code we are guaranteed to understand
@@ -126,12 +129,15 @@ NODELESS_WANT_CONFIG_ID = 69420
publishingThread = DeferredExecution("publishing")
logger = logging.getLogger(__name__)
class ResponseHandler(NamedTuple):
"""A pending response callback, waiting for a response to one of our messages"""
# requestId: int - used only as a key
#: a callable to call when a response is received
callback: Callable
#: Whether ACKs and NAKs should be passed to this handler
ackPermitted: bool = False
# FIXME, add timestamp and age out old requests
@@ -139,11 +145,11 @@ class ResponseHandler(NamedTuple):
class KnownProtocol(NamedTuple):
"""Used to automatically decode known protocol payloads"""
#: A descriptive name (e.g. "text", "user", "admin")
name: str
# portnum: int, now a key
# If set, will be called to prase as a protocol buffer
#: If set, will be called to parse as a protocol buffer
protobufFactory: Optional[Callable] = None
# If set, invoked as onReceive(interface, packet)
#: If set, invoked as onReceive(interface, packet)
onReceive: Optional[Callable] = None
@@ -155,31 +161,31 @@ def _onTextReceive(iface, asDict):
#
# Usually btw this problem is caused by apps sending binary data but setting the payload type to
# text.
logging.debug(f"in _onTextReceive() asDict:{asDict}")
logger.debug(f"in _onTextReceive() asDict:{asDict}")
try:
asBytes = asDict["decoded"]["payload"]
asDict["decoded"]["text"] = asBytes.decode("utf-8")
except Exception as ex:
logging.error(f"Malformatted utf8 in text message: {ex}")
logger.error(f"Malformatted utf8 in text message: {ex}")
_receiveInfoUpdate(iface, asDict)
def _onPositionReceive(iface, asDict):
"""Special auto parsing for received messages"""
logging.debug(f"in _onPositionReceive() asDict:{asDict}")
logger.debug(f"in _onPositionReceive() asDict:{asDict}")
if "decoded" in asDict:
if "position" in asDict["decoded"] and "from" in asDict:
p = asDict["decoded"]["position"]
logging.debug(f"p:{p}")
logger.debug(f"p:{p}")
p = iface._fixupPosition(p)
logging.debug(f"after fixup p:{p}")
logger.debug(f"after fixup p:{p}")
# update node DB as needed
iface._getOrCreateByNum(asDict["from"])["position"] = p
def _onNodeInfoReceive(iface, asDict):
"""Special auto parsing for received messages"""
logging.debug(f"in _onNodeInfoReceive() asDict:{asDict}")
logger.debug(f"in _onNodeInfoReceive() asDict:{asDict}")
if "decoded" in asDict:
if "user" in asDict["decoded"] and "from" in asDict:
p = asDict["decoded"]["user"]
@@ -191,6 +197,34 @@ def _onNodeInfoReceive(iface, asDict):
iface.nodes[p["id"]] = n
_receiveInfoUpdate(iface, asDict)
def _onTelemetryReceive(iface, asDict):
"""Automatically update device metrics on received packets"""
logger.debug(f"in _onTelemetryReceive() asDict:{asDict}")
if "from" not in asDict:
return
toUpdate = None
telemetry = asDict.get("decoded", {}).get("telemetry", {})
node = iface._getOrCreateByNum(asDict["from"])
if "deviceMetrics" in telemetry:
toUpdate = "deviceMetrics"
elif "environmentMetrics" in telemetry:
toUpdate = "environmentMetrics"
elif "airQualityMetrics" in telemetry:
toUpdate = "airQualityMetrics"
elif "powerMetrics" in telemetry:
toUpdate = "powerMetrics"
elif "localStats" in telemetry:
toUpdate = "localStats"
else:
return
updateObj = telemetry.get(toUpdate)
newMetrics = node.get(toUpdate, {})
newMetrics.update(updateObj)
logger.debug(f"updating {toUpdate} metrics for {asDict['from']} to {newMetrics}")
node[toUpdate] = newMetrics
def _receiveInfoUpdate(iface, asDict):
if "from" in asDict:
@@ -199,6 +233,12 @@ def _receiveInfoUpdate(iface, asDict):
iface._getOrCreateByNum(asDict["from"])["snr"] = asDict.get("rxSnr")
iface._getOrCreateByNum(asDict["from"])["hopLimit"] = asDict.get("hopLimit")
def _onAdminReceive(iface, asDict):
"""Special auto parsing for received messages"""
logger.debug(f"in _onAdminReceive() asDict:{asDict}")
if "decoded" in asDict and "from" in asDict and "admin" in asDict["decoded"]:
adminMessage = asDict["decoded"]["admin"]["raw"]
iface._getOrCreateByNum(asDict["from"])["adminSessionPassKey"] = adminMessage.session_passkey
"""Well known message payloads can register decoders for automatic protobuf parsing"""
protocols = {
@@ -218,10 +258,12 @@ protocols = {
portnums_pb2.PortNum.NODEINFO_APP: KnownProtocol(
"user", mesh_pb2.User, _onNodeInfoReceive
),
portnums_pb2.PortNum.ADMIN_APP: KnownProtocol("admin", admin_pb2.AdminMessage),
portnums_pb2.PortNum.ADMIN_APP: KnownProtocol(
"admin", admin_pb2.AdminMessage, _onAdminReceive
),
portnums_pb2.PortNum.ROUTING_APP: KnownProtocol("routing", mesh_pb2.Routing),
portnums_pb2.PortNum.TELEMETRY_APP: KnownProtocol(
"telemetry", telemetry_pb2.Telemetry
"telemetry", telemetry_pb2.Telemetry, _onTelemetryReceive
),
portnums_pb2.PortNum.REMOTE_HARDWARE_APP: KnownProtocol(
"remotehw", remote_hardware_pb2.HardwareMessage

View File

File diff suppressed because it is too large Load Diff

View File

@@ -2,6 +2,7 @@
import argparse
import logging
import os
from typing import cast, List
import dash_bootstrap_components as dbc # type: ignore[import-untyped]
@@ -143,8 +144,8 @@ def create_dash(slog_path: str) -> Dash:
"""
app = Dash(external_stylesheets=[dbc.themes.BOOTSTRAP])
dpwr = read_pandas(f"{slog_path}/power.feather")
dslog = read_pandas(f"{slog_path}/slog.feather")
dpwr = read_pandas(os.path.join(slog_path, "power.feather"))
dslog = read_pandas(os.path.join(slog_path, "slog.feather"))
pmon_raises = get_pmon_raises(dslog)
@@ -190,7 +191,7 @@ def main():
parser = create_argparser()
args = parser.parse_args()
if not args.slog:
args.slog = f"{root_dir()}/latest"
args.slog = os.path.join(root_dir(), "latest")
app = create_dash(slog_path=args.slog)
port = 8051

View File

@@ -5,6 +5,7 @@ import atexit
import logging
import struct
import time
import io
from threading import Thread
from typing import List, Optional
@@ -22,6 +23,7 @@ FROMRADIO_UUID = "2c55e69e-4993-11ed-b878-0242ac120002"
FROMNUM_UUID = "ed9da18c-a800-4f66-a670-aa7547e34453"
LEGACY_LOGRADIO_UUID = "6c6fd238-78fa-436b-aacf-15c5be1ef2e2"
LOGRADIO_UUID = "5a3d6e49-06e6-4423-9944-e9de8cdf9547"
logger = logging.getLogger(__name__)
class BLEInterface(MeshInterface):
@@ -30,32 +32,33 @@ class BLEInterface(MeshInterface):
class BLEError(Exception):
"""An exception class for BLE errors."""
def __init__(
def __init__( # pylint: disable=R0917
self,
address: Optional[str],
noProto: bool = False,
debugOut=None,
debugOut: Optional[io.TextIOWrapper]=None,
noNodes: bool = False,
):
timeout: int = 300,
) -> None:
MeshInterface.__init__(
self, debugOut=debugOut, noProto=noProto, noNodes=noNodes
self, debugOut=debugOut, noProto=noProto, noNodes=noNodes, timeout=timeout
)
self.should_read = False
logging.debug("Threads starting")
logger.debug("Threads starting")
self._want_receive = True
self._receiveThread: Optional[Thread] = Thread(
target=self._receiveFromRadioImpl, name="BLEReceive", daemon=True
)
self._receiveThread.start()
logging.debug("Threads running")
logger.debug("Threads running")
self.client: Optional[BLEClient] = None
try:
logging.debug(f"BLE connecting to: {address if address else 'any'}")
logger.debug(f"BLE connecting to: {address if address else 'any'}")
self.client = self.connect(address)
logging.debug("BLE connected")
logger.debug("BLE connected")
except BLEInterface.BLEError as e:
self.close()
raise e
@@ -68,13 +71,13 @@ class BLEInterface(MeshInterface):
if self.client.has_characteristic(LOGRADIO_UUID):
self.client.start_notify(LOGRADIO_UUID, self.log_radio_handler)
logging.debug("Mesh configure starting")
logger.debug("Mesh configure starting")
self._startConfig()
if not self.noProto:
self._waitConnected(timeout=60.0)
self.waitForConfig()
logging.debug("Register FROMNUM notify callback")
logger.debug("Register FROMNUM notify callback")
self.client.start_notify(FROMNUM_UUID, self.from_num_handler)
# We MUST run atexit (if we can) because otherwise (at least on linux) the BLE device is not disconnected
@@ -82,12 +85,23 @@ class BLEInterface(MeshInterface):
# Note: the on disconnected callback will call our self.close which will make us nicely wait for threads to exit
self._exit_handler = atexit.register(self.client.disconnect)
def from_num_handler(self, _, b): # pylint: disable=C0116
def __repr__(self):
rep = f"BLEInterface(address={self.client.address if self.client else None!r}"
if self.debugOut is not None:
rep += f", debugOut={self.debugOut!r}"
if self.noProto:
rep += ", noProto=True"
if self.noNodes:
rep += ", noNodes=True"
rep += ")"
return rep
def from_num_handler(self, _, b: bytes) -> None: # pylint: disable=C0116
"""Handle callbacks for fromnum notify.
Note: this method does not need to be async because it is just setting a bool.
"""
from_num = struct.unpack("<I", bytes(b))[0]
logging.debug(f"FROMNUM notify: {from_num}")
logger.debug(f"FROMNUM notify: {from_num}")
self.should_read = True
async def log_radio_handler(self, _, b): # pylint: disable=C0116
@@ -102,7 +116,7 @@ class BLEInterface(MeshInterface):
)
self._handleLogLine(message)
except google.protobuf.message.DecodeError:
logging.warning("Malformed LogRecord received. Skipping.")
logger.warning("Malformed LogRecord received. Skipping.")
async def legacy_log_radio_handler(self, _, b): # pylint: disable=C0116
log_radio = b.decode("utf-8").replace("\n", "")
@@ -112,7 +126,7 @@ class BLEInterface(MeshInterface):
def scan() -> List[BLEDevice]:
"""Scan for available BLE devices."""
with BLEClient() as client:
logging.info("Scanning for BLE devices (takes 10 seconds)...")
logger.info("Scanning for BLE devices (takes 10 seconds)...")
response = client.discover(
timeout=10, return_adv=True, service_uuids=[SERVICE_UUID]
)
@@ -150,36 +164,43 @@ class BLEInterface(MeshInterface):
)
return addressed_devices[0]
def _sanitize_address(address): # pylint: disable=E0213
def _sanitize_address(self, address: Optional[str]) -> Optional[str]: # pylint: disable=E0213
"Standardize BLE address by removing extraneous characters and lowercasing."
return address.replace("-", "").replace("_", "").replace(":", "").lower()
if address is None:
return None
else:
return address.replace("-", "").replace("_", "").replace(":", "").lower()
def connect(self, address: Optional[str] = None) -> "BLEClient":
"Connect to a device by address."
# Bleak docs recommend always doing a scan before connecting (even if we know addr)
device = self.find_device(address)
client = BLEClient(device.address, disconnected_callback=lambda _: self.close)
client = BLEClient(device.address, disconnected_callback=lambda _: self.close())
client.connect()
client.discover()
return client
def _receiveFromRadioImpl(self):
def _receiveFromRadioImpl(self) -> None:
while self._want_receive:
if self.should_read:
self.should_read = False
retries = 0
retries: int = 0
while self._want_receive:
if self.client is None:
logger.debug(f"BLE client is None, shutting down")
self._want_receive = False
continue
try:
b = bytes(self.client.read_gatt_char(FROMRADIO_UUID))
except BleakDBusError as e:
# Device disconnected probably, so end our read loop immediately
logging.debug(f"Device disconnected, shutting down {e}")
logger.debug(f"Device disconnected, shutting down {e}")
self._want_receive = False
except BleakError as e:
# We were definitely disconnected
if "Not connected" in str(e):
logging.debug(f"Device disconnected, shutting down {e}")
logger.debug(f"Device disconnected, shutting down {e}")
self._want_receive = False
else:
raise BLEInterface.BLEError("Error reading BLE") from e
@@ -189,15 +210,15 @@ class BLEInterface(MeshInterface):
retries += 1
continue
break
logging.debug(f"FROMRADIO read: {b.hex()}")
logger.debug(f"FROMRADIO read: {b.hex()}")
self._handleFromRadio(b)
else:
time.sleep(0.01)
def _sendToRadioImpl(self, toRadio):
b = toRadio.SerializeToString()
def _sendToRadioImpl(self, toRadio) -> None:
b: bytes = toRadio.SerializeToString()
if b and self.client: # we silently ignore writes while we are shutting down
logging.debug(f"TORADIO write: {b.hex()}")
logger.debug(f"TORADIO write: {b.hex()}")
try:
self.client.write_gatt_char(
TORADIO_UUID, b, response=True
@@ -211,14 +232,14 @@ class BLEInterface(MeshInterface):
time.sleep(0.01)
self.should_read = True
def close(self):
def close(self) -> None:
try:
MeshInterface.close(self)
except Exception as e:
logging.error(f"Error closing mesh interface: {e}")
logger.error(f"Error closing mesh interface: {e}")
if self._want_receive:
self.want_receive = False # Tell the thread we want it to stop
self._want_receive = False # Tell the thread we want it to stop
if self._receiveThread:
self._receiveThread.join(
timeout=2
@@ -230,12 +251,13 @@ class BLEInterface(MeshInterface):
self.client.disconnect()
self.client.close()
self.client = None
self._disconnected() # send the disconnected indicator up to clients
class BLEClient:
"""Client for managing connection to a BLE device"""
def __init__(self, address=None, **kwargs):
def __init__(self, address=None, **kwargs) -> None:
self._eventLoop = asyncio.new_event_loop()
self._eventThread = Thread(
target=self._run_event_loop, name="BLEClient", daemon=True
@@ -243,7 +265,7 @@ class BLEClient:
self._eventThread.start()
if not address:
logging.debug("No address provided - only discover method will work.")
logger.debug("No address provided - only discover method will work.")
return
self.bleak_client = BleakClient(address, **kwargs)

View File

File diff suppressed because it is too large Load Diff

View File

@@ -13,6 +13,8 @@ with rather more easily once the code is simplified by this change.
"""
from typing import Any, Optional
def reset():
"""
Restore the namespace to pristine condition.
@@ -33,5 +35,5 @@ args = None
parser = None
channel_index = None
logfile = None
tunnelInstance = None
tunnelInstance: Optional[Any] = None
camel_case = False

View File

@@ -5,9 +5,9 @@ import base64
import logging
import time
from typing import Optional, Union
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 (
Timeout,
camel_to_snake,
@@ -16,8 +16,12 @@ from meshtastic.util import (
pskToString,
stripnl,
message_to_json,
generate_channel_hash,
to_node_num,
flags_to_list,
)
logger = logging.getLogger(__name__)
class Node:
"""A model of a (local or remote) node in the mesh
@@ -25,15 +29,15 @@ class Node:
Includes methods for localConfig, moduleConfig and channels
"""
def __init__(self, iface, nodeNum, noProto=False):
def __init__(self, iface, nodeNum, noProto=False, timeout: int = 300):
"""Constructor"""
self.iface = iface
self.nodeNum = nodeNum
self.localConfig = localonly_pb2.LocalConfig()
self.moduleConfig = localonly_pb2.LocalModuleConfig()
self.channels = None
self._timeout = Timeout(maxSecs=300)
self.partialChannels = None
self._timeout = Timeout(maxSecs=timeout)
self.partialChannels: Optional[List] = None
self.noProto = noProto
self.cannedPluginMessage = None
self.cannedPluginMessageMessages = None
@@ -42,11 +46,40 @@ class Node:
self.gotResponse = None
def __repr__(self):
r = f"Node({self.iface!r}, 0x{self.nodeNum:08x}"
if self.noProto:
r += ", noProto=True"
if self._timeout.expireTimeout != 300:
r += ", timeout={self._timeout.expireTimeout!r}"
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:
"""Check DeviceMetadata.excluded_modules to see if a module is available."""
meta = getattr(self.iface, "metadata", None)
if meta is None:
return True
try:
return (meta.excluded_modules & excluded_bit) == 0
except Exception:
return True
def showChannels(self):
"""Show human readable description of our channels."""
print("Channels:")
if self.channels:
logging.debug(f"self.channels:{self.channels}")
logger.debug(f"self.channels:{self.channels}")
for c in self.channels:
cStr = message_to_json(c.settings)
# don't show disabled channels
@@ -77,17 +110,19 @@ class Node:
self.channels = channels
self._fixupChannels()
def requestChannels(self):
def requestChannels(self, startingIndex: int = 0):
"""Send regular MeshPackets to ask channels."""
logging.debug(f"requestChannels for nodeNum:{self.nodeNum}")
self.channels = None
self.partialChannels = [] # We keep our channels in a temp array until finished
self._requestChannel(0)
logger.debug(f"requestChannels for nodeNum:{self.nodeNum}")
# only initialize if we're starting out fresh
if startingIndex == 0:
self.channels = None
self.partialChannels = [] # We keep our channels in a temp array until finished
self._requestChannel(startingIndex)
def onResponseRequestSettings(self, p):
"""Handle the response packets for requesting settings _requestSettings()"""
logging.debug(f"onResponseRequestSetting() p:{p}")
logger.debug(f"onResponseRequestSetting() p:{p}")
config_values = None
if "routing" in p["decoded"]:
if p["decoded"]["routing"]["errorReason"] != "NONE":
print(f'Error on response: {p["decoded"]["routing"]["errorReason"]}')
@@ -97,13 +132,16 @@ class Node:
print("")
adminMessage = p["decoded"]["admin"]
if "getConfigResponse" in adminMessage:
oneof = "get_config_response"
resp = adminMessage["getConfigResponse"]
field = list(resp.keys())[0]
config_type = self.localConfig.DESCRIPTOR.fields_by_name.get(
camel_to_snake(field)
)
config_values = getattr(self.localConfig, config_type.name)
if config_type is not None:
config_values = getattr(self.localConfig, config_type.name)
elif "getModuleConfigResponse" in adminMessage:
oneof = "get_module_config_response"
resp = adminMessage["getModuleConfigResponse"]
field = list(resp.keys())[0]
config_type = self.moduleConfig.DESCRIPTOR.fields_by_name.get(
@@ -115,9 +153,10 @@ class Node:
"Did not receive a valid response. Make sure to have a shared channel named 'admin'."
)
return
for key, value in resp[field].items():
setattr(config_values, camel_to_snake(key), value)
print(f"{str(camel_to_snake(field))}:\n{str(config_values)}")
if config_values is not None:
raw_config = getattr(getattr(adminMessage['raw'], oneof), camel_to_snake(field))
config_values.CopyFrom(raw_config)
print(f"{str(camel_to_snake(field))}:\n{str(config_values)}")
def requestConfig(self, configType):
"""Request the config from the node via admin message"""
@@ -126,16 +165,18 @@ class Node:
else:
onResponse = self.onResponseRequestSettings
print("Requesting current config from remote node (this can take a while).")
p = admin_pb2.AdminMessage()
if isinstance(configType, int):
p.get_config_request = configType
msgIndex = configType.index
if configType.containing_type.name == "LocalConfig":
p = admin_pb2.AdminMessage()
p.get_config_request = msgIndex
self._sendAdmin(p, wantResponse=True, onResponse=onResponse)
else:
p = admin_pb2.AdminMessage()
p.get_module_config_request = msgIndex
self._sendAdmin(p, wantResponse=True, onResponse=onResponse)
msgIndex = configType.index
if configType.containing_type.name == "LocalConfig":
p.get_config_request = msgIndex
else:
p.get_module_config_request = msgIndex
self._sendAdmin(p, wantResponse=True, onResponse=onResponse)
if onResponse:
self.iface.waitForAckNak()
@@ -170,6 +211,8 @@ class Node:
p.set_config.lora.CopyFrom(self.localConfig.lora)
elif config_name == "bluetooth":
p.set_config.bluetooth.CopyFrom(self.localConfig.bluetooth)
elif config_name == "security":
p.set_config.security.CopyFrom(self.localConfig.security)
elif config_name == "mqtt":
p.set_module_config.mqtt.CopyFrom(self.moduleConfig.mqtt)
elif config_name == "serial":
@@ -205,7 +248,7 @@ class Node:
else:
our_exit(f"Error: No valid config with name {config_name}")
logging.debug(f"Wrote: {config_name}")
logger.debug(f"Wrote: {config_name}")
if self == self.iface.localNode:
onResponse = None
else:
@@ -214,11 +257,11 @@ class Node:
def writeChannel(self, channelIndex, adminIndex=0):
"""Write the current (edited) channel to the device"""
self.ensureSessionKey()
p = admin_pb2.AdminMessage()
p.set_channel.CopyFrom(self.channels[channelIndex])
self._sendAdmin(p, adminIndex=adminIndex)
logging.debug(f"Wrote channel {channelIndex}")
logger.debug(f"Wrote channel {channelIndex}")
def getChannelByChannelIndex(self, channelIndex):
"""Get channel by channelIndex
@@ -279,27 +322,37 @@ class Node:
return c.index
return 0
def setOwner(self, long_name: Optional[str]=None, short_name: Optional[str]=None, is_licensed: bool=False):
def setOwner(self, long_name: Optional[str]=None, short_name: Optional[str]=None, is_licensed: bool=False, is_unmessagable: Optional[bool]=None):
"""Set device owner name"""
logging.debug(f"in setOwner nodeNum:{self.nodeNum}")
logger.debug(f"in setOwner nodeNum:{self.nodeNum}")
self.ensureSessionKey()
p = admin_pb2.AdminMessage()
nChars = 4
if long_name is not None:
long_name = long_name.strip()
# Validate that long_name is not empty or whitespace-only
if not long_name:
our_exit("ERROR: Long Name cannot be empty or contain only whitespace characters")
p.set_owner.long_name = long_name
p.set_owner.is_licensed = is_licensed
if short_name is not None:
short_name = short_name.strip()
# Validate that short_name is not empty or whitespace-only
if not short_name:
our_exit("ERROR: Short Name cannot be empty or contain only whitespace characters")
if len(short_name) > nChars:
short_name = short_name[:nChars]
print(f"Maximum is 4 characters, truncated to {short_name}")
p.set_owner.short_name = short_name
if is_unmessagable is not None:
p.set_owner.is_unmessagable = is_unmessagable
# Note: These debug lines are used in unit tests
logging.debug(f"p.set_owner.long_name:{p.set_owner.long_name}:")
logging.debug(f"p.set_owner.short_name:{p.set_owner.short_name}:")
logging.debug(f"p.set_owner.is_licensed:{p.set_owner.is_licensed}")
logger.debug(f"p.set_owner.long_name:{p.set_owner.long_name}:")
logger.debug(f"p.set_owner.short_name:{p.set_owner.short_name}:")
logger.debug(f"p.set_owner.is_licensed:{p.set_owner.is_licensed}")
logger.debug(f"p.set_owner.is_unmessagable:{p.set_owner.is_unmessagable}:")
# If sending to a remote node, wait for ACK/NAK
if self == self.iface.localNode:
onResponse = None
@@ -326,14 +379,19 @@ class Node:
s = s.replace("=", "").replace("+", "-").replace("/", "_")
return f"https://meshtastic.org/e/#{s}"
def setURL(self, url):
def setURL(self, url: str, addOnly: bool = False):
"""Set mesh network URL"""
if self.localConfig is None:
our_exit("Warning: No Config has been read")
if self.localConfig is None or self.channels is None:
our_exit("Warning: config or channels not loaded")
# URLs are of the form https://meshtastic.org/d/#{base64_channel_set}
# Split on '/#' to find the base64 encoded channel settings
splitURL = url.split("/#")
if addOnly:
splitURL = url.split("/?add=true#")
else:
splitURL = url.split("/#")
if len(splitURL) == 1:
our_exit(f"Warning: Invalid URL '{url}'")
b64 = splitURL[-1]
# We normally strip padding to make for a shorter URL, but the python parser doesn't like
@@ -350,28 +408,45 @@ class Node:
if len(channelSet.settings) == 0:
our_exit("Warning: There were no settings.")
i = 0
for chs in channelSet.settings:
ch = channel_pb2.Channel()
ch.role = (
channel_pb2.Channel.Role.PRIMARY
if i == 0
else channel_pb2.Channel.Role.SECONDARY
)
ch.index = i
ch.settings.CopyFrom(chs)
self.channels[ch.index] = ch
logging.debug(f"Channel i:{i} ch:{ch}")
self.writeChannel(ch.index)
i = i + 1
if addOnly:
# Add new channels with names not already present
# Don't change existing channels
for chs in channelSet.settings:
channelExists = self.getChannelByName(chs.name)
if channelExists or chs.name == "":
print(f"Ignoring existing or empty channel \"{chs.name}\" from add URL")
continue
ch = self.getDisabledChannel()
if not ch:
our_exit("Warning: No free channels were found")
ch.settings.CopyFrom(chs)
ch.role = channel_pb2.Channel.Role.SECONDARY
print(f"Adding new channel '{chs.name}' to device")
self.writeChannel(ch.index)
else:
i = 0
for chs in channelSet.settings:
ch = channel_pb2.Channel()
ch.role = (
channel_pb2.Channel.Role.PRIMARY
if i == 0
else channel_pb2.Channel.Role.SECONDARY
)
ch.index = i
ch.settings.CopyFrom(chs)
self.channels[ch.index] = ch
logger.debug(f"Channel i:{i} ch:{ch}")
self.writeChannel(ch.index)
i = i + 1
p = admin_pb2.AdminMessage()
p.set_config.lora.CopyFrom(channelSet.lora_config)
self.ensureSessionKey()
self._sendAdmin(p)
def onResponseRequestRingtone(self, p):
"""Handle the response packet for requesting ringtone part 1"""
logging.debug(f"onResponseRequestRingtone() p:{p}")
logger.debug(f"onResponseRequestRingtone() p:{p}")
errorFound = False
if "routing" in p["decoded"]:
if p["decoded"]["routing"]["errorReason"] != "NONE":
@@ -384,12 +459,16 @@ class Node:
self.ringtonePart = p["decoded"]["admin"][
"raw"
].get_ringtone_response
logging.debug(f"self.ringtonePart:{self.ringtonePart}")
logger.debug(f"self.ringtonePart:{self.ringtonePart}")
self.gotResponse = True
def get_ringtone(self):
"""Get the ringtone. Concatenate all pieces together and return a single string."""
logging.debug(f"in get_ringtone()")
logger.debug(f"in get_ringtone()")
if not self.module_available(mesh_pb2.EXTNOTIF_CONFIG):
logging.warning("External Notification module not present (excluded by firmware)")
return None
if not self.ringtone:
p1 = admin_pb2.AdminMessage()
p1.get_ringtone_request = True
@@ -400,22 +479,24 @@ class Node:
while self.gotResponse is False:
time.sleep(0.1)
logging.debug(f"self.ringtone:{self.ringtone}")
logger.debug(f"self.ringtone:{self.ringtone}")
self.ringtone = ""
if self.ringtonePart:
self.ringtone += self.ringtonePart
print(f"ringtone:{self.ringtone}")
logging.debug(f"ringtone:{self.ringtone}")
logger.debug(f"ringtone:{self.ringtone}")
return self.ringtone
def set_ringtone(self, ringtone):
"""Set the ringtone. The ringtone length must be less than 230 character."""
if not self.module_available(mesh_pb2.EXTNOTIF_CONFIG):
logging.warning("External Notification module not present (excluded by firmware)")
return None
if len(ringtone) > 230:
our_exit("Warning: The ringtone must be less than 230 characters.")
self.ensureSessionKey()
# split into chunks
chunks = []
chunks_size = 230
@@ -431,7 +512,7 @@ class Node:
if i == 0:
p.set_ringtone_message = chunk
logging.debug(f"Setting ringtone '{chunk}' part {i+1}")
logger.debug(f"Setting ringtone '{chunk}' part {i+1}")
# If sending to a remote node, wait for ACK/NAK
if self == self.iface.localNode:
onResponse = None
@@ -441,7 +522,7 @@ class Node:
def onResponseRequestCannedMessagePluginMessageMessages(self, p):
"""Handle the response packet for requesting canned message plugin message part 1"""
logging.debug(f"onResponseRequestCannedMessagePluginMessageMessages() p:{p}")
logger.debug(f"onResponseRequestCannedMessagePluginMessageMessages() p:{p}")
errorFound = False
if "routing" in p["decoded"]:
if p["decoded"]["routing"]["errorReason"] != "NONE":
@@ -454,14 +535,17 @@ class Node:
self.cannedPluginMessageMessages = p["decoded"]["admin"][
"raw"
].get_canned_message_module_messages_response
logging.debug(
logger.debug(
f"self.cannedPluginMessageMessages:{self.cannedPluginMessageMessages}"
)
self.gotResponse = True
def get_canned_message(self):
"""Get the canned message string. Concatenate all pieces together and return a single string."""
logging.debug(f"in get_canned_message()")
logger.debug(f"in get_canned_message()")
if not self.module_available(mesh_pb2.CANNEDMSG_CONFIG):
logging.warning("Canned Message module not present (excluded by firmware)")
return None
if not self.cannedPluginMessage:
p1 = admin_pb2.AdminMessage()
p1.get_canned_message_module_messages_request = True
@@ -474,7 +558,7 @@ class Node:
while self.gotResponse is False:
time.sleep(0.1)
logging.debug(
logger.debug(
f"self.cannedPluginMessageMessages:{self.cannedPluginMessageMessages}"
)
@@ -482,16 +566,18 @@ class Node:
if self.cannedPluginMessageMessages:
self.cannedPluginMessage += self.cannedPluginMessageMessages
print(f"canned_plugin_message:{self.cannedPluginMessage}")
logging.debug(f"canned_plugin_message:{self.cannedPluginMessage}")
logger.debug(f"canned_plugin_message:{self.cannedPluginMessage}")
return self.cannedPluginMessage
def set_canned_message(self, message):
"""Set the canned message. The canned messages length must be less than 200 character."""
if not self.module_available(mesh_pb2.CANNEDMSG_CONFIG):
logging.warning("Canned Message module not present (excluded by firmware)")
return None
if len(message) > 200:
our_exit("Warning: The canned message must be less than 200 characters.")
self.ensureSessionKey()
# split into chunks
chunks = []
chunks_size = 200
@@ -507,7 +593,7 @@ class Node:
if i == 0:
p.set_canned_message_module_messages = chunk
logging.debug(f"Setting canned message '{chunk}' part {i+1}")
logger.debug(f"Setting canned message '{chunk}' part {i+1}")
# If sending to a remote node, wait for ACK/NAK
if self == self.iface.localNode:
onResponse = None
@@ -518,17 +604,19 @@ class Node:
def exitSimulator(self):
"""Tell a simulator node to exit (this message
is ignored for other nodes)"""
self.ensureSessionKey()
p = admin_pb2.AdminMessage()
p.exit_simulator = True
logging.debug("in exitSimulator()")
logger.debug("in exitSimulator()")
return self._sendAdmin(p)
def reboot(self, secs: int = 10):
"""Tell the node to reboot."""
self.ensureSessionKey()
p = admin_pb2.AdminMessage()
p.reboot_seconds = secs
logging.info(f"Telling node to reboot in {secs} seconds")
logger.info(f"Telling node to reboot in {secs} seconds")
# If sending to a remote node, wait for ACK/NAK
if self == self.iface.localNode:
@@ -539,9 +627,10 @@ class Node:
def beginSettingsTransaction(self):
"""Tell the node to open a transaction to edit settings."""
self.ensureSessionKey()
p = admin_pb2.AdminMessage()
p.begin_edit_settings = True
logging.info(f"Telling open a transaction to edit settings")
logger.info(f"Telling open a transaction to edit settings")
# If sending to a remote node, wait for ACK/NAK
if self == self.iface.localNode:
@@ -552,9 +641,10 @@ class Node:
def commitSettingsTransaction(self):
"""Tell the node to commit the open transaction for editing settings."""
self.ensureSessionKey()
p = admin_pb2.AdminMessage()
p.commit_edit_settings = True
logging.info(f"Telling node to commit open transaction for editing settings")
logger.info(f"Telling node to commit open transaction for editing settings")
# If sending to a remote node, wait for ACK/NAK
if self == self.iface.localNode:
@@ -565,9 +655,10 @@ class Node:
def rebootOTA(self, secs: int = 10):
"""Tell the node to reboot into factory firmware."""
self.ensureSessionKey()
p = admin_pb2.AdminMessage()
p.reboot_ota_seconds = secs
logging.info(f"Telling node to reboot to OTA in {secs} seconds")
logger.info(f"Telling node to reboot to OTA in {secs} seconds")
# If sending to a remote node, wait for ACK/NAK
if self == self.iface.localNode:
@@ -578,9 +669,10 @@ class Node:
def enterDFUMode(self):
"""Tell the node to enter DFU mode (NRF52)."""
self.ensureSessionKey()
p = admin_pb2.AdminMessage()
p.enter_dfu_mode_request = True
logging.info(f"Telling node to enable DFU mode")
logger.info(f"Telling node to enable DFU mode")
# If sending to a remote node, wait for ACK/NAK
if self == self.iface.localNode:
@@ -591,9 +683,10 @@ class Node:
def shutdown(self, secs: int = 10):
"""Tell the node to shutdown."""
self.ensureSessionKey()
p = admin_pb2.AdminMessage()
p.shutdown_seconds = secs
logging.info(f"Telling node to shutdown in {secs} seconds")
logger.info(f"Telling node to shutdown in {secs} seconds")
# If sending to a remote node, wait for ACK/NAK
if self == self.iface.localNode:
@@ -606,18 +699,23 @@ class Node:
"""Get the node's metadata."""
p = admin_pb2.AdminMessage()
p.get_device_metadata_request = True
logging.info(f"Requesting device metadata")
logger.info(f"Requesting device metadata")
self._sendAdmin(
p, wantResponse=True, onResponse=self.onRequestGetMetadata
)
self.iface.waitForAckNak()
def factoryReset(self):
def factoryReset(self, full: bool = False):
"""Tell the node to factory reset."""
self.ensureSessionKey()
p = admin_pb2.AdminMessage()
p.factory_reset = True
logging.info(f"Telling node to factory reset")
if full:
p.factory_reset_device = True
logger.info(f"Telling node to factory reset (full device reset)")
else:
p.factory_reset_config = True
logger.info(f"Telling node to factory reset (config reset)")
# If sending to a remote node, wait for ACK/NAK
if self == self.iface.localNode:
@@ -628,11 +726,8 @@ class Node:
def removeNode(self, nodeId: Union[int, str]):
"""Tell the node to remove a specific node by ID"""
if isinstance(nodeId, str):
if nodeId.startswith("!"):
nodeId = int(nodeId[1:], 16)
else:
nodeId = int(nodeId)
self.ensureSessionKey()
nodeId = to_node_num(nodeId)
p = admin_pb2.AdminMessage()
p.remove_by_nodenum = nodeId
@@ -643,11 +738,68 @@ class Node:
onResponse = self.onAckNak
return self._sendAdmin(p, onResponse=onResponse)
def setFavorite(self, nodeId: Union[int, str]):
"""Tell the node to set the specified node ID to be favorited on the NodeDB on the device"""
self.ensureSessionKey()
nodeId = to_node_num(nodeId)
p = admin_pb2.AdminMessage()
p.set_favorite_node = nodeId
if self == self.iface.localNode:
onResponse = None
else:
onResponse = self.onAckNak
return self._sendAdmin(p, onResponse=onResponse)
def removeFavorite(self, nodeId: Union[int, str]):
"""Tell the node to set the specified node ID to be un-favorited on the NodeDB on the device"""
self.ensureSessionKey()
nodeId = to_node_num(nodeId)
p = admin_pb2.AdminMessage()
p.remove_favorite_node = nodeId
if self == self.iface.localNode:
onResponse = None
else:
onResponse = self.onAckNak
return self._sendAdmin(p, onResponse=onResponse)
def setIgnored(self, nodeId: Union[int, str]):
"""Tell the node to set the specified node ID to be ignored on the NodeDB on the device"""
self.ensureSessionKey()
nodeId = to_node_num(nodeId)
p = admin_pb2.AdminMessage()
p.set_ignored_node = nodeId
if self == self.iface.localNode:
onResponse = None
else:
onResponse = self.onAckNak
return self._sendAdmin(p, onResponse=onResponse)
def removeIgnored(self, nodeId: Union[int, str]):
"""Tell the node to set the specified node ID to be un-ignored on the NodeDB on the device"""
self.ensureSessionKey()
nodeId = to_node_num(nodeId)
p = admin_pb2.AdminMessage()
p.remove_ignored_node = nodeId
if self == self.iface.localNode:
onResponse = None
else:
onResponse = self.onAckNak
return self._sendAdmin(p, onResponse=onResponse)
def resetNodeDb(self):
"""Tell the node to reset its list of nodes."""
self.ensureSessionKey()
p = admin_pb2.AdminMessage()
p.nodedb_reset = True
logging.info(f"Telling node to reset the NodeDB")
logger.info(f"Telling node to reset the NodeDB")
# If sending to a remote node, wait for ACK/NAK
if self == self.iface.localNode:
@@ -658,9 +810,7 @@ class Node:
def setFixedPosition(self, lat: Union[int, float], lon: Union[int, float], alt: int):
"""Tell the node to set fixed position to the provided value and enable the fixed position setting"""
if self != self.iface.localNode:
logging.error("Setting position of remote nodes is not supported.")
return None
self.ensureSessionKey()
p = mesh_pb2.Position()
if isinstance(lat, float) and lat != 0.0:
@@ -678,15 +828,40 @@ class Node:
a = admin_pb2.AdminMessage()
a.set_fixed_position.CopyFrom(p)
return self._sendAdmin(a)
if self == self.iface.localNode:
onResponse = None
else:
onResponse = self.onAckNak
return self._sendAdmin(a, onResponse=onResponse)
def removeFixedPosition(self):
"""Tell the node to remove the fixed position and set the fixed position setting to false"""
self.ensureSessionKey()
p = admin_pb2.AdminMessage()
p.remove_fixed_position = True
logging.info(f"Telling node to remove fixed position")
logger.info(f"Telling node to remove fixed position")
return self._sendAdmin(p)
if self == self.iface.localNode:
onResponse = None
else:
onResponse = self.onAckNak
return self._sendAdmin(p, onResponse=onResponse)
def setTime(self, timeSec: int = 0):
"""Tell the node to set its time to the provided timestamp, or the system's current time if not provided or 0."""
self.ensureSessionKey()
if timeSec == 0:
timeSec = int(time.time())
p = admin_pb2.AdminMessage()
p.set_time_only = timeSec
logger.info(f"Setting node time to {timeSec}")
if self == self.iface.localNode:
onResponse = None
else:
onResponse = self.onAckNak
return self._sendAdmin(p, onResponse=onResponse)
def _fixupChannels(self):
"""Fixup indexes and add disabled channels as needed"""
@@ -712,7 +887,7 @@ class Node:
def onRequestGetMetadata(self, p):
"""Handle the response packet for requesting device metadata getMetadata()"""
logging.debug(f"onRequestGetMetadata() p:{p}")
logger.debug(f"onRequestGetMetadata() p:{p}")
if "routing" in p["decoded"]:
if p["decoded"]["routing"]["errorReason"] != "NONE":
@@ -724,30 +899,42 @@ class Node:
portnums_pb2.PortNum.ROUTING_APP
):
if p["decoded"]["routing"]["errorReason"] != "NONE":
logging.warning(
logger.warning(
f'Metadata request failed, error reason: {p["decoded"]["routing"]["errorReason"]}'
)
self._timeout.expireTime = time.time() # Do not wait any longer
return # Don't try to parse this routing message
logging.debug(f"Retrying metadata request.")
logger.debug(f"Retrying metadata request.")
self.getMetadata()
return
c = p["decoded"]["admin"]["raw"].get_device_metadata_response
self._timeout.reset() # We made forward progress
logging.debug(f"Received metadata {stripnl(c)}")
logger.debug(f"Received metadata {stripnl(c)}")
print(f"\nfirmware_version: {c.firmware_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):
"""Handle the response packet for requesting a channel _requestChannel()"""
logging.debug(f"onResponseRequestChannel() p:{p}")
logger.debug(f"onResponseRequestChannel() p:{p}")
if p["decoded"]["portnum"] == portnums_pb2.PortNum.Name(
portnums_pb2.PortNum.ROUTING_APP
):
if p["decoded"]["routing"]["errorReason"] != "NONE":
logging.warning(
logger.warning(
f'Channel request failed, error reason: {p["decoded"]["routing"]["errorReason"]}'
)
self._timeout.expireTime = time.time() # Do not wait any longer
@@ -755,18 +942,18 @@ class Node:
lastTried = 0
if len(self.partialChannels) > 0:
lastTried = self.partialChannels[-1].index
logging.debug(f"Retrying previous channel request.")
logger.debug(f"Retrying previous channel request.")
self._requestChannel(lastTried)
return
c = p["decoded"]["admin"]["raw"].get_channel_response
self.partialChannels.append(c)
self._timeout.reset() # We made forward progress
logging.debug(f"Received channel {stripnl(c)}")
logger.debug(f"Received channel {stripnl(c)}")
index = c.index
if index >= 8 - 1:
logging.debug("Finished downloading channels")
logger.debug("Finished downloading channels")
self.channels = self.partialChannels
self._fixupChannels()
@@ -801,11 +988,11 @@ class Node:
print(
f"Requesting channel {channelNum} info from remote node (this could take a while)"
)
logging.debug(
logger.debug(
f"Requesting channel {channelNum} info from remote node (this could take a while)"
)
else:
logging.debug(f"Requesting channel {channelNum}")
logger.debug(f"Requesting channel {channelNum}")
return self._sendAdmin(
p, wantResponse=True, onResponse=self.onResponseRequestChannel
@@ -822,7 +1009,7 @@ class Node:
"""Send an admin message to the specified node (or the local node if destNodeNum is zero)"""
if self.noProto:
logging.warning(
logger.warning(
f"Not sending packet because protocol use is disabled by noProto"
)
else:
@@ -830,14 +1017,45 @@ class Node:
adminIndex == 0
): # unless a special channel index was used, we want to use the admin index
adminIndex = self.iface.localNode._getAdminChannelIndex()
logging.debug(f"adminIndex:{adminIndex}")
logger.debug(f"adminIndex:{adminIndex}")
nodeid = to_node_num(self.nodeNum)
if "adminSessionPassKey" in self.iface._getOrCreateByNum(nodeid):
p.session_passkey = self.iface._getOrCreateByNum(nodeid).get("adminSessionPassKey")
return self.iface.sendData(
p,
self.nodeNum,
portNum=portnums_pb2.PortNum.ADMIN_APP,
wantAck=False,
wantAck=True,
wantResponse=wantResponse,
onResponse=onResponse,
channelIndex=adminIndex,
pkiEncrypted=True,
)
def ensureSessionKey(self):
"""If our entry in iface.nodesByNum doesn't already have an adminSessionPassKey, make a request to get one"""
if self.noProto:
logger.warning(
f"Not ensuring session key, because protocol use is disabled by noProto"
)
else:
nodeid = to_node_num(self.nodeNum)
if self.iface._getOrCreateByNum(nodeid).get("adminSessionPassKey") is None:
self.requestConfig(admin_pb2.AdminMessage.SESSIONKEY_CONFIG)
def get_channels_with_hash(self):
"""Return a list of dicts with channel info and hash."""
result = []
if self.channels:
for c in self.channels:
if c.settings and hasattr(c.settings, "name") and hasattr(c.settings, "psk"):
hash_val = generate_channel_hash(c.settings.name, c.settings.psk)
else:
hash_val = None
result.append({
"index": c.index,
"role": channel_pb2.Channel.Role.Name(c.role),
"name": c.settings.name if c.settings and hasattr(c.settings, "name") else "",
"hash": hash_val,
})
return result

View File

File diff suppressed because one or more lines are too long

View File

@@ -12,6 +12,7 @@ import google.protobuf.message
import meshtastic.protobuf.channel_pb2
import meshtastic.protobuf.config_pb2
import meshtastic.protobuf.connection_status_pb2
import meshtastic.protobuf.device_ui_pb2
import meshtastic.protobuf.mesh_pb2
import meshtastic.protobuf.module_config_pb2
import sys
@@ -68,6 +69,18 @@ class AdminMessage(google.protobuf.message.Message):
"""
TODO: REPLACE
"""
SECURITY_CONFIG: AdminMessage._ConfigType.ValueType # 7
"""
TODO: REPLACE
"""
SESSIONKEY_CONFIG: AdminMessage._ConfigType.ValueType # 8
"""
Session key config
"""
DEVICEUI_CONFIG: AdminMessage._ConfigType.ValueType # 9
"""
device-ui config
"""
class ConfigType(_ConfigType, metaclass=_ConfigTypeEnumTypeWrapper):
"""
@@ -102,6 +115,18 @@ class AdminMessage(google.protobuf.message.Message):
"""
TODO: REPLACE
"""
SECURITY_CONFIG: AdminMessage.ConfigType.ValueType # 7
"""
TODO: REPLACE
"""
SESSIONKEY_CONFIG: AdminMessage.ConfigType.ValueType # 8
"""
Session key config
"""
DEVICEUI_CONFIG: AdminMessage.ConfigType.ValueType # 9
"""
device-ui config
"""
class _ModuleConfigType:
ValueType = typing.NewType("ValueType", builtins.int)
@@ -220,6 +245,70 @@ class AdminMessage(google.protobuf.message.Message):
TODO: REPLACE
"""
class _BackupLocation:
ValueType = typing.NewType("ValueType", builtins.int)
V: typing_extensions.TypeAlias = ValueType
class _BackupLocationEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[AdminMessage._BackupLocation.ValueType], builtins.type):
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
FLASH: AdminMessage._BackupLocation.ValueType # 0
"""
Backup to the internal flash
"""
SD: AdminMessage._BackupLocation.ValueType # 1
"""
Backup to the SD card
"""
class BackupLocation(_BackupLocation, metaclass=_BackupLocationEnumTypeWrapper): ...
FLASH: AdminMessage.BackupLocation.ValueType # 0
"""
Backup to the internal flash
"""
SD: AdminMessage.BackupLocation.ValueType # 1
"""
Backup to the SD card
"""
@typing.final
class InputEvent(google.protobuf.message.Message):
"""
Input event message to be sent to the node.
"""
DESCRIPTOR: google.protobuf.descriptor.Descriptor
EVENT_CODE_FIELD_NUMBER: builtins.int
KB_CHAR_FIELD_NUMBER: builtins.int
TOUCH_X_FIELD_NUMBER: builtins.int
TOUCH_Y_FIELD_NUMBER: builtins.int
event_code: builtins.int
"""
The input event code
"""
kb_char: builtins.int
"""
Keyboard character code
"""
touch_x: builtins.int
"""
The touch X coordinate
"""
touch_y: builtins.int
"""
The touch Y coordinate
"""
def __init__(
self,
*,
event_code: builtins.int = ...,
kb_char: builtins.int = ...,
touch_x: builtins.int = ...,
touch_y: builtins.int = ...,
) -> None: ...
def ClearField(self, field_name: typing.Literal["event_code", b"event_code", "kb_char", b"kb_char", "touch_x", b"touch_x", "touch_y", b"touch_y"]) -> None: ...
SESSION_PASSKEY_FIELD_NUMBER: builtins.int
GET_CHANNEL_REQUEST_FIELD_NUMBER: builtins.int
GET_CHANNEL_RESPONSE_FIELD_NUMBER: builtins.int
GET_OWNER_REQUEST_FIELD_NUMBER: builtins.int
@@ -242,6 +331,10 @@ class AdminMessage(google.protobuf.message.Message):
ENTER_DFU_MODE_REQUEST_FIELD_NUMBER: builtins.int
DELETE_FILE_REQUEST_FIELD_NUMBER: builtins.int
SET_SCALE_FIELD_NUMBER: builtins.int
BACKUP_PREFERENCES_FIELD_NUMBER: builtins.int
RESTORE_PREFERENCES_FIELD_NUMBER: builtins.int
REMOVE_BACKUP_PREFERENCES_FIELD_NUMBER: builtins.int
SEND_INPUT_EVENT_FIELD_NUMBER: builtins.int
SET_OWNER_FIELD_NUMBER: builtins.int
SET_CHANNEL_FIELD_NUMBER: builtins.int
SET_CONFIG_FIELD_NUMBER: builtins.int
@@ -253,14 +346,29 @@ class AdminMessage(google.protobuf.message.Message):
REMOVE_FAVORITE_NODE_FIELD_NUMBER: builtins.int
SET_FIXED_POSITION_FIELD_NUMBER: builtins.int
REMOVE_FIXED_POSITION_FIELD_NUMBER: builtins.int
SET_TIME_ONLY_FIELD_NUMBER: builtins.int
GET_UI_CONFIG_REQUEST_FIELD_NUMBER: builtins.int
GET_UI_CONFIG_RESPONSE_FIELD_NUMBER: builtins.int
STORE_UI_CONFIG_FIELD_NUMBER: builtins.int
SET_IGNORED_NODE_FIELD_NUMBER: builtins.int
REMOVE_IGNORED_NODE_FIELD_NUMBER: builtins.int
BEGIN_EDIT_SETTINGS_FIELD_NUMBER: builtins.int
COMMIT_EDIT_SETTINGS_FIELD_NUMBER: builtins.int
ADD_CONTACT_FIELD_NUMBER: builtins.int
KEY_VERIFICATION_FIELD_NUMBER: builtins.int
FACTORY_RESET_DEVICE_FIELD_NUMBER: builtins.int
REBOOT_OTA_SECONDS_FIELD_NUMBER: builtins.int
EXIT_SIMULATOR_FIELD_NUMBER: builtins.int
REBOOT_SECONDS_FIELD_NUMBER: builtins.int
SHUTDOWN_SECONDS_FIELD_NUMBER: builtins.int
FACTORY_RESET_FIELD_NUMBER: builtins.int
FACTORY_RESET_CONFIG_FIELD_NUMBER: builtins.int
NODEDB_RESET_FIELD_NUMBER: builtins.int
session_passkey: builtins.bytes
"""
The node generates this key and sends it with any get_x_response packets.
The client MUST include the same key with any set_x commands. Key expires after 300 seconds.
Prevents replay attacks for admin messages.
"""
get_channel_request: builtins.int
"""
Send the specified channel in the response to this message
@@ -319,6 +427,18 @@ class AdminMessage(google.protobuf.message.Message):
"""
Set zero and offset for scale chips
"""
backup_preferences: global___AdminMessage.BackupLocation.ValueType
"""
Backup the node's preferences
"""
restore_preferences: global___AdminMessage.BackupLocation.ValueType
"""
Restore the node's preferences
"""
remove_backup_preferences: global___AdminMessage.BackupLocation.ValueType
"""
Remove backups of the node's preferences
"""
set_canned_message_module_messages: builtins.str
"""
Set the Canned Message Module messages text.
@@ -343,6 +463,23 @@ class AdminMessage(google.protobuf.message.Message):
"""
Clear fixed position coordinates and then set position.fixed_position = false
"""
set_time_only: builtins.int
"""
Set time only on the node
Convenience method to set the time on the node (as Net quality) without any other position data
"""
get_ui_config_request: builtins.bool
"""
Tell the node to send the stored ui data.
"""
set_ignored_node: builtins.int
"""
Set specified node-num to be ignored on the NodeDB on the device
"""
remove_ignored_node: builtins.int
"""
Set specified node-num to be un-ignored on the NodeDB on the device
"""
begin_edit_settings: builtins.bool
"""
Begins an edit transaction for config, module config, owner, and channel settings changes
@@ -352,6 +489,10 @@ class AdminMessage(google.protobuf.message.Message):
"""
Commits an open transaction for any edits made to config, module config, owner, and channel settings
"""
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.
"""
reboot_ota_seconds: builtins.int
"""
Tell the node to reboot into the OTA Firmware in this many seconds (or <0 to cancel reboot)
@@ -370,13 +511,14 @@ class AdminMessage(google.protobuf.message.Message):
"""
Tell the node to shutdown in this many seconds (or <0 to cancel shutdown)
"""
factory_reset: builtins.int
factory_reset_config: builtins.int
"""
Tell the node to factory reset, all device settings will be returned to factory defaults.
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.
When true, favorites are preserved through reset.
"""
@property
def get_channel_response(self) -> meshtastic.protobuf.channel_pb2.Channel:
@@ -426,6 +568,13 @@ class AdminMessage(google.protobuf.message.Message):
Respond with the mesh's nodes with their available gpio pins for RemoteHardware module use
"""
@property
def send_input_event(self) -> global___AdminMessage.InputEvent:
"""
Send an input event to the node.
This is used to trigger physical input events like button presses, touch events, etc.
"""
@property
def set_owner(self) -> meshtastic.protobuf.mesh_pb2.User:
"""
@@ -460,9 +609,34 @@ class AdminMessage(google.protobuf.message.Message):
Set fixed position data on the node and then set the position.fixed_position = true
"""
@property
def get_ui_config_response(self) -> meshtastic.protobuf.device_ui_pb2.DeviceUIConfig:
"""
Reply stored device ui data.
"""
@property
def store_ui_config(self) -> meshtastic.protobuf.device_ui_pb2.DeviceUIConfig:
"""
Tell the node to store UI data persistently.
"""
@property
def add_contact(self) -> global___SharedContact:
"""
Add a contact (User) to the nodedb
"""
@property
def key_verification(self) -> global___KeyVerificationAdmin:
"""
Initiate or respond to a key verification request
"""
def __init__(
self,
*,
session_passkey: builtins.bytes = ...,
get_channel_request: builtins.int = ...,
get_channel_response: meshtastic.protobuf.channel_pb2.Channel | None = ...,
get_owner_request: builtins.bool = ...,
@@ -485,6 +659,10 @@ class AdminMessage(google.protobuf.message.Message):
enter_dfu_mode_request: builtins.bool = ...,
delete_file_request: builtins.str = ...,
set_scale: builtins.int = ...,
backup_preferences: global___AdminMessage.BackupLocation.ValueType = ...,
restore_preferences: global___AdminMessage.BackupLocation.ValueType = ...,
remove_backup_preferences: global___AdminMessage.BackupLocation.ValueType = ...,
send_input_event: global___AdminMessage.InputEvent | None = ...,
set_owner: meshtastic.protobuf.mesh_pb2.User | None = ...,
set_channel: meshtastic.protobuf.channel_pb2.Channel | None = ...,
set_config: meshtastic.protobuf.config_pb2.Config | None = ...,
@@ -496,18 +674,27 @@ class AdminMessage(google.protobuf.message.Message):
remove_favorite_node: builtins.int = ...,
set_fixed_position: meshtastic.protobuf.mesh_pb2.Position | None = ...,
remove_fixed_position: builtins.bool = ...,
set_time_only: builtins.int = ...,
get_ui_config_request: builtins.bool = ...,
get_ui_config_response: meshtastic.protobuf.device_ui_pb2.DeviceUIConfig | None = ...,
store_ui_config: meshtastic.protobuf.device_ui_pb2.DeviceUIConfig | None = ...,
set_ignored_node: builtins.int = ...,
remove_ignored_node: builtins.int = ...,
begin_edit_settings: builtins.bool = ...,
commit_edit_settings: builtins.bool = ...,
add_contact: global___SharedContact | None = ...,
key_verification: global___KeyVerificationAdmin | None = ...,
factory_reset_device: builtins.int = ...,
reboot_ota_seconds: builtins.int = ...,
exit_simulator: builtins.bool = ...,
reboot_seconds: builtins.int = ...,
shutdown_seconds: builtins.int = ...,
factory_reset: builtins.int = ...,
nodedb_reset: builtins.int = ...,
factory_reset_config: builtins.int = ...,
nodedb_reset: builtins.bool = ...,
) -> None: ...
def HasField(self, field_name: typing.Literal["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", b"factory_reset", "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", "nodedb_reset", b"nodedb_reset", "payload_variant", b"payload_variant", "reboot_ota_seconds", b"reboot_ota_seconds", "reboot_seconds", b"reboot_seconds", "remove_by_nodenum", b"remove_by_nodenum", "remove_favorite_node", b"remove_favorite_node", "remove_fixed_position", b"remove_fixed_position", "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_module_config", b"set_module_config", "set_owner", b"set_owner", "set_ringtone_message", b"set_ringtone_message", "set_scale", b"set_scale", "shutdown_seconds", b"shutdown_seconds"]) -> builtins.bool: ...
def ClearField(self, field_name: typing.Literal["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", b"factory_reset", "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", "nodedb_reset", b"nodedb_reset", "payload_variant", b"payload_variant", "reboot_ota_seconds", b"reboot_ota_seconds", "reboot_seconds", b"reboot_seconds", "remove_by_nodenum", b"remove_by_nodenum", "remove_favorite_node", b"remove_favorite_node", "remove_fixed_position", b"remove_fixed_position", "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_module_config", b"set_module_config", "set_owner", b"set_owner", "set_ringtone_message", b"set_ringtone_message", "set_scale", b"set_scale", "shutdown_seconds", b"shutdown_seconds"]) -> 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", "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", "begin_edit_settings", "commit_edit_settings", "reboot_ota_seconds", "exit_simulator", "reboot_seconds", "shutdown_seconds", "factory_reset", "nodedb_reset"] | 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 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 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: ...
global___AdminMessage = AdminMessage
@@ -576,3 +763,128 @@ class NodeRemoteHardwarePinsResponse(google.protobuf.message.Message):
def ClearField(self, field_name: typing.Literal["node_remote_hardware_pins", b"node_remote_hardware_pins"]) -> None: ...
global___NodeRemoteHardwarePinsResponse = NodeRemoteHardwarePinsResponse
@typing.final
class SharedContact(google.protobuf.message.Message):
DESCRIPTOR: google.protobuf.descriptor.Descriptor
NODE_NUM_FIELD_NUMBER: builtins.int
USER_FIELD_NUMBER: builtins.int
SHOULD_IGNORE_FIELD_NUMBER: builtins.int
MANUALLY_VERIFIED_FIELD_NUMBER: builtins.int
node_num: builtins.int
"""
The node number of the contact
"""
should_ignore: builtins.bool
"""
Add this contact to the blocked / ignored list
"""
manually_verified: builtins.bool
"""
Set the IS_KEY_MANUALLY_VERIFIED bit
"""
@property
def user(self) -> meshtastic.protobuf.mesh_pb2.User:
"""
The User of the contact
"""
def __init__(
self,
*,
node_num: builtins.int = ...,
user: meshtastic.protobuf.mesh_pb2.User | None = ...,
should_ignore: builtins.bool = ...,
manually_verified: builtins.bool = ...,
) -> None: ...
def HasField(self, field_name: typing.Literal["user", b"user"]) -> builtins.bool: ...
def ClearField(self, field_name: typing.Literal["manually_verified", b"manually_verified", "node_num", b"node_num", "should_ignore", b"should_ignore", "user", b"user"]) -> None: ...
global___SharedContact = SharedContact
@typing.final
class KeyVerificationAdmin(google.protobuf.message.Message):
"""
This message is used by a client to initiate or complete a key verification
"""
DESCRIPTOR: google.protobuf.descriptor.Descriptor
class _MessageType:
ValueType = typing.NewType("ValueType", builtins.int)
V: typing_extensions.TypeAlias = ValueType
class _MessageTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[KeyVerificationAdmin._MessageType.ValueType], builtins.type):
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
INITIATE_VERIFICATION: KeyVerificationAdmin._MessageType.ValueType # 0
"""
This is the first stage, where a client initiates
"""
PROVIDE_SECURITY_NUMBER: KeyVerificationAdmin._MessageType.ValueType # 1
"""
After the nonce has been returned over the mesh, the client prompts for the security number
And uses this message to provide it to the node.
"""
DO_VERIFY: KeyVerificationAdmin._MessageType.ValueType # 2
"""
Once the user has compared the verification message, this message notifies the node.
"""
DO_NOT_VERIFY: KeyVerificationAdmin._MessageType.ValueType # 3
"""
This is the cancel path, can be taken at any point
"""
class MessageType(_MessageType, metaclass=_MessageTypeEnumTypeWrapper):
"""
Three stages of this request.
"""
INITIATE_VERIFICATION: KeyVerificationAdmin.MessageType.ValueType # 0
"""
This is the first stage, where a client initiates
"""
PROVIDE_SECURITY_NUMBER: KeyVerificationAdmin.MessageType.ValueType # 1
"""
After the nonce has been returned over the mesh, the client prompts for the security number
And uses this message to provide it to the node.
"""
DO_VERIFY: KeyVerificationAdmin.MessageType.ValueType # 2
"""
Once the user has compared the verification message, this message notifies the node.
"""
DO_NOT_VERIFY: KeyVerificationAdmin.MessageType.ValueType # 3
"""
This is the cancel path, can be taken at any point
"""
MESSAGE_TYPE_FIELD_NUMBER: builtins.int
REMOTE_NODENUM_FIELD_NUMBER: builtins.int
NONCE_FIELD_NUMBER: builtins.int
SECURITY_NUMBER_FIELD_NUMBER: builtins.int
message_type: global___KeyVerificationAdmin.MessageType.ValueType
remote_nodenum: builtins.int
"""
The nodenum we're requesting
"""
nonce: builtins.int
"""
The nonce is used to track the connection
"""
security_number: builtins.int
"""
The 4 digit code generated by the remote node, and communicated outside the mesh
"""
def __init__(
self,
*,
message_type: global___KeyVerificationAdmin.MessageType.ValueType = ...,
remote_nodenum: builtins.int = ...,
nonce: builtins.int = ...,
security_number: builtins.int | None = ...,
) -> None: ...
def HasField(self, field_name: typing.Literal["_security_number", b"_security_number", "security_number", b"security_number"]) -> builtins.bool: ...
def ClearField(self, field_name: typing.Literal["_security_number", b"_security_number", "message_type", b"message_type", "nonce", b"nonce", "remote_nodenum", b"remote_nodenum", "security_number", b"security_number"]) -> None: ...
def WhichOneof(self, oneof_group: typing.Literal["_security_number", b"_security_number"]) -> typing.Literal["security_number"] | None: ...
global___KeyVerificationAdmin = KeyVerificationAdmin

View File

@@ -15,14 +15,14 @@ from meshtastic.protobuf import channel_pb2 as meshtastic_dot_protobuf_dot_chann
from meshtastic.protobuf import config_pb2 as meshtastic_dot_protobuf_dot_config__pb2
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n!meshtastic/protobuf/apponly.proto\x12\x13meshtastic.protobuf\x1a!meshtastic/protobuf/channel.proto\x1a meshtastic/protobuf/config.proto\"\x81\x01\n\nChannelSet\x12\x36\n\x08settings\x18\x01 \x03(\x0b\x32$.meshtastic.protobuf.ChannelSettings\x12;\n\x0blora_config\x18\x02 \x01(\x0b\x32&.meshtastic.protobuf.Config.LoRaConfigBb\n\x13\x63om.geeksville.meshB\rAppOnlyProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n!meshtastic/protobuf/apponly.proto\x12\x13meshtastic.protobuf\x1a!meshtastic/protobuf/channel.proto\x1a meshtastic/protobuf/config.proto\"\x81\x01\n\nChannelSet\x12\x36\n\x08settings\x18\x01 \x03(\x0b\x32$.meshtastic.protobuf.ChannelSettings\x12;\n\x0blora_config\x18\x02 \x01(\x0b\x32&.meshtastic.protobuf.Config.LoRaConfigBc\n\x14org.meshtastic.protoB\rAppOnlyProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.apponly_pb2', _globals)
if _descriptor._USE_C_DESCRIPTORS == False:
DESCRIPTOR._options = None
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\rAppOnlyProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\rAppOnlyProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
_globals['_CHANNELSET']._serialized_start=128
_globals['_CHANNELSET']._serialized_end=257
# @@protoc_insertion_point(module_scope)

View File

@@ -13,28 +13,28 @@ _sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1emeshtastic/protobuf/atak.proto\x12\x13meshtastic.protobuf\"\x93\x02\n\tTAKPacket\x12\x15\n\ris_compressed\x18\x01 \x01(\x08\x12-\n\x07\x63ontact\x18\x02 \x01(\x0b\x32\x1c.meshtastic.protobuf.Contact\x12)\n\x05group\x18\x03 \x01(\x0b\x32\x1a.meshtastic.protobuf.Group\x12+\n\x06status\x18\x04 \x01(\x0b\x32\x1b.meshtastic.protobuf.Status\x12\'\n\x03pli\x18\x05 \x01(\x0b\x32\x18.meshtastic.protobuf.PLIH\x00\x12,\n\x04\x63hat\x18\x06 \x01(\x0b\x32\x1c.meshtastic.protobuf.GeoChatH\x00\x42\x11\n\x0fpayload_variant\"\\\n\x07GeoChat\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\x0f\n\x02to\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0bto_callsign\x18\x03 \x01(\tH\x01\x88\x01\x01\x42\x05\n\x03_toB\x0e\n\x0c_to_callsign\"_\n\x05Group\x12-\n\x04role\x18\x01 \x01(\x0e\x32\x1f.meshtastic.protobuf.MemberRole\x12\'\n\x04team\x18\x02 \x01(\x0e\x32\x19.meshtastic.protobuf.Team\"\x19\n\x06Status\x12\x0f\n\x07\x62\x61ttery\x18\x01 \x01(\r\"4\n\x07\x43ontact\x12\x10\n\x08\x63\x61llsign\x18\x01 \x01(\t\x12\x17\n\x0f\x64\x65vice_callsign\x18\x02 \x01(\t\"_\n\x03PLI\x12\x12\n\nlatitude_i\x18\x01 \x01(\x0f\x12\x13\n\x0blongitude_i\x18\x02 \x01(\x0f\x12\x10\n\x08\x61ltitude\x18\x03 \x01(\x05\x12\r\n\x05speed\x18\x04 \x01(\r\x12\x0e\n\x06\x63ourse\x18\x05 \x01(\r*\xc0\x01\n\x04Team\x12\x14\n\x10Unspecifed_Color\x10\x00\x12\t\n\x05White\x10\x01\x12\n\n\x06Yellow\x10\x02\x12\n\n\x06Orange\x10\x03\x12\x0b\n\x07Magenta\x10\x04\x12\x07\n\x03Red\x10\x05\x12\n\n\x06Maroon\x10\x06\x12\n\n\x06Purple\x10\x07\x12\r\n\tDark_Blue\x10\x08\x12\x08\n\x04\x42lue\x10\t\x12\x08\n\x04\x43yan\x10\n\x12\x08\n\x04Teal\x10\x0b\x12\t\n\x05Green\x10\x0c\x12\x0e\n\nDark_Green\x10\r\x12\t\n\x05\x42rown\x10\x0e*\x7f\n\nMemberRole\x12\x0e\n\nUnspecifed\x10\x00\x12\x0e\n\nTeamMember\x10\x01\x12\x0c\n\x08TeamLead\x10\x02\x12\x06\n\x02HQ\x10\x03\x12\n\n\x06Sniper\x10\x04\x12\t\n\x05Medic\x10\x05\x12\x13\n\x0f\x46orwardObserver\x10\x06\x12\x07\n\x03RTO\x10\x07\x12\x06\n\x02K9\x10\x08\x42_\n\x13\x63om.geeksville.meshB\nATAKProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1emeshtastic/protobuf/atak.proto\x12\x13meshtastic.protobuf\"\xa5\x02\n\tTAKPacket\x12\x15\n\ris_compressed\x18\x01 \x01(\x08\x12-\n\x07\x63ontact\x18\x02 \x01(\x0b\x32\x1c.meshtastic.protobuf.Contact\x12)\n\x05group\x18\x03 \x01(\x0b\x32\x1a.meshtastic.protobuf.Group\x12+\n\x06status\x18\x04 \x01(\x0b\x32\x1b.meshtastic.protobuf.Status\x12\'\n\x03pli\x18\x05 \x01(\x0b\x32\x18.meshtastic.protobuf.PLIH\x00\x12,\n\x04\x63hat\x18\x06 \x01(\x0b\x32\x1c.meshtastic.protobuf.GeoChatH\x00\x12\x10\n\x06\x64\x65tail\x18\x07 \x01(\x0cH\x00\x42\x11\n\x0fpayload_variant\"\\\n\x07GeoChat\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\x0f\n\x02to\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0bto_callsign\x18\x03 \x01(\tH\x01\x88\x01\x01\x42\x05\n\x03_toB\x0e\n\x0c_to_callsign\"_\n\x05Group\x12-\n\x04role\x18\x01 \x01(\x0e\x32\x1f.meshtastic.protobuf.MemberRole\x12\'\n\x04team\x18\x02 \x01(\x0e\x32\x19.meshtastic.protobuf.Team\"\x19\n\x06Status\x12\x0f\n\x07\x62\x61ttery\x18\x01 \x01(\r\"4\n\x07\x43ontact\x12\x10\n\x08\x63\x61llsign\x18\x01 \x01(\t\x12\x17\n\x0f\x64\x65vice_callsign\x18\x02 \x01(\t\"_\n\x03PLI\x12\x12\n\nlatitude_i\x18\x01 \x01(\x0f\x12\x13\n\x0blongitude_i\x18\x02 \x01(\x0f\x12\x10\n\x08\x61ltitude\x18\x03 \x01(\x05\x12\r\n\x05speed\x18\x04 \x01(\r\x12\x0e\n\x06\x63ourse\x18\x05 \x01(\r*\xc0\x01\n\x04Team\x12\x14\n\x10Unspecifed_Color\x10\x00\x12\t\n\x05White\x10\x01\x12\n\n\x06Yellow\x10\x02\x12\n\n\x06Orange\x10\x03\x12\x0b\n\x07Magenta\x10\x04\x12\x07\n\x03Red\x10\x05\x12\n\n\x06Maroon\x10\x06\x12\n\n\x06Purple\x10\x07\x12\r\n\tDark_Blue\x10\x08\x12\x08\n\x04\x42lue\x10\t\x12\x08\n\x04\x43yan\x10\n\x12\x08\n\x04Teal\x10\x0b\x12\t\n\x05Green\x10\x0c\x12\x0e\n\nDark_Green\x10\r\x12\t\n\x05\x42rown\x10\x0e*\x7f\n\nMemberRole\x12\x0e\n\nUnspecifed\x10\x00\x12\x0e\n\nTeamMember\x10\x01\x12\x0c\n\x08TeamLead\x10\x02\x12\x06\n\x02HQ\x10\x03\x12\n\n\x06Sniper\x10\x04\x12\t\n\x05Medic\x10\x05\x12\x13\n\x0f\x46orwardObserver\x10\x06\x12\x07\n\x03RTO\x10\x07\x12\x06\n\x02K9\x10\x08\x42`\n\x14org.meshtastic.protoB\nATAKProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.atak_pb2', _globals)
if _descriptor._USE_C_DESCRIPTORS == False:
DESCRIPTOR._options = None
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\nATAKProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
_globals['_TEAM']._serialized_start=703
_globals['_TEAM']._serialized_end=895
_globals['_MEMBERROLE']._serialized_start=897
_globals['_MEMBERROLE']._serialized_end=1024
DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\nATAKProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
_globals['_TEAM']._serialized_start=721
_globals['_TEAM']._serialized_end=913
_globals['_MEMBERROLE']._serialized_start=915
_globals['_MEMBERROLE']._serialized_end=1042
_globals['_TAKPACKET']._serialized_start=56
_globals['_TAKPACKET']._serialized_end=331
_globals['_GEOCHAT']._serialized_start=333
_globals['_GEOCHAT']._serialized_end=425
_globals['_GROUP']._serialized_start=427
_globals['_GROUP']._serialized_end=522
_globals['_STATUS']._serialized_start=524
_globals['_STATUS']._serialized_end=549
_globals['_CONTACT']._serialized_start=551
_globals['_CONTACT']._serialized_end=603
_globals['_PLI']._serialized_start=605
_globals['_PLI']._serialized_end=700
_globals['_TAKPACKET']._serialized_end=349
_globals['_GEOCHAT']._serialized_start=351
_globals['_GEOCHAT']._serialized_end=443
_globals['_GROUP']._serialized_start=445
_globals['_GROUP']._serialized_end=540
_globals['_STATUS']._serialized_start=542
_globals['_STATUS']._serialized_end=567
_globals['_CONTACT']._serialized_start=569
_globals['_CONTACT']._serialized_end=621
_globals['_PLI']._serialized_start=623
_globals['_PLI']._serialized_end=718
# @@protoc_insertion_point(module_scope)

View File

@@ -248,10 +248,16 @@ class TAKPacket(google.protobuf.message.Message):
STATUS_FIELD_NUMBER: builtins.int
PLI_FIELD_NUMBER: builtins.int
CHAT_FIELD_NUMBER: builtins.int
DETAIL_FIELD_NUMBER: builtins.int
is_compressed: builtins.bool
"""
Are the payloads strings compressed for LoRA transport?
"""
detail: builtins.bytes
"""
Generic CoT detail XML
May be compressed / truncated by the sender (EUD)
"""
@property
def contact(self) -> global___Contact:
"""
@@ -291,10 +297,11 @@ class TAKPacket(google.protobuf.message.Message):
status: global___Status | None = ...,
pli: global___PLI | None = ...,
chat: global___GeoChat | None = ...,
detail: builtins.bytes = ...,
) -> None: ...
def HasField(self, field_name: typing.Literal["chat", b"chat", "contact", b"contact", "group", b"group", "payload_variant", b"payload_variant", "pli", b"pli", "status", b"status"]) -> builtins.bool: ...
def ClearField(self, field_name: typing.Literal["chat", b"chat", "contact", b"contact", "group", b"group", "is_compressed", b"is_compressed", "payload_variant", b"payload_variant", "pli", b"pli", "status", b"status"]) -> None: ...
def WhichOneof(self, oneof_group: typing.Literal["payload_variant", b"payload_variant"]) -> typing.Literal["pli", "chat"] | None: ...
def HasField(self, field_name: typing.Literal["chat", b"chat", "contact", b"contact", "detail", b"detail", "group", b"group", "payload_variant", b"payload_variant", "pli", b"pli", "status", b"status"]) -> builtins.bool: ...
def ClearField(self, field_name: typing.Literal["chat", b"chat", "contact", b"contact", "detail", b"detail", "group", b"group", "is_compressed", b"is_compressed", "payload_variant", b"payload_variant", "pli", b"pli", "status", b"status"]) -> None: ...
def WhichOneof(self, oneof_group: typing.Literal["payload_variant", b"payload_variant"]) -> typing.Literal["pli", "chat", "detail"] | None: ...
global___TAKPacket = TAKPacket

View File

@@ -13,14 +13,14 @@ _sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n(meshtastic/protobuf/cannedmessages.proto\x12\x13meshtastic.protobuf\"-\n\x19\x43\x61nnedMessageModuleConfig\x12\x10\n\x08messages\x18\x01 \x01(\tBn\n\x13\x63om.geeksville.meshB\x19\x43\x61nnedMessageConfigProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n(meshtastic/protobuf/cannedmessages.proto\x12\x13meshtastic.protobuf\"-\n\x19\x43\x61nnedMessageModuleConfig\x12\x10\n\x08messages\x18\x01 \x01(\tBo\n\x14org.meshtastic.protoB\x19\x43\x61nnedMessageConfigProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.cannedmessages_pb2', _globals)
if _descriptor._USE_C_DESCRIPTORS == False:
DESCRIPTOR._options = None
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\031CannedMessageConfigProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\031CannedMessageConfigProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
_globals['_CANNEDMESSAGEMODULECONFIG']._serialized_start=65
_globals['_CANNEDMESSAGEMODULECONFIG']._serialized_end=110
# @@protoc_insertion_point(module_scope)

View File

@@ -13,22 +13,22 @@ _sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n!meshtastic/protobuf/channel.proto\x12\x13meshtastic.protobuf\"\xc1\x01\n\x0f\x43hannelSettings\x12\x17\n\x0b\x63hannel_num\x18\x01 \x01(\rB\x02\x18\x01\x12\x0b\n\x03psk\x18\x02 \x01(\x0c\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\n\n\x02id\x18\x04 \x01(\x07\x12\x16\n\x0euplink_enabled\x18\x05 \x01(\x08\x12\x18\n\x10\x64ownlink_enabled\x18\x06 \x01(\x08\x12<\n\x0fmodule_settings\x18\x07 \x01(\x0b\x32#.meshtastic.protobuf.ModuleSettings\"E\n\x0eModuleSettings\x12\x1a\n\x12position_precision\x18\x01 \x01(\r\x12\x17\n\x0fis_client_muted\x18\x02 \x01(\x08\"\xb3\x01\n\x07\x43hannel\x12\r\n\x05index\x18\x01 \x01(\x05\x12\x36\n\x08settings\x18\x02 \x01(\x0b\x32$.meshtastic.protobuf.ChannelSettings\x12/\n\x04role\x18\x03 \x01(\x0e\x32!.meshtastic.protobuf.Channel.Role\"0\n\x04Role\x12\x0c\n\x08\x44ISABLED\x10\x00\x12\x0b\n\x07PRIMARY\x10\x01\x12\r\n\tSECONDARY\x10\x02\x42\x62\n\x13\x63om.geeksville.meshB\rChannelProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n!meshtastic/protobuf/channel.proto\x12\x13meshtastic.protobuf\"\xc1\x01\n\x0f\x43hannelSettings\x12\x17\n\x0b\x63hannel_num\x18\x01 \x01(\rB\x02\x18\x01\x12\x0b\n\x03psk\x18\x02 \x01(\x0c\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\n\n\x02id\x18\x04 \x01(\x07\x12\x16\n\x0euplink_enabled\x18\x05 \x01(\x08\x12\x18\n\x10\x64ownlink_enabled\x18\x06 \x01(\x08\x12<\n\x0fmodule_settings\x18\x07 \x01(\x0b\x32#.meshtastic.protobuf.ModuleSettings\">\n\x0eModuleSettings\x12\x1a\n\x12position_precision\x18\x01 \x01(\r\x12\x10\n\x08is_muted\x18\x02 \x01(\x08\"\xb3\x01\n\x07\x43hannel\x12\r\n\x05index\x18\x01 \x01(\x05\x12\x36\n\x08settings\x18\x02 \x01(\x0b\x32$.meshtastic.protobuf.ChannelSettings\x12/\n\x04role\x18\x03 \x01(\x0e\x32!.meshtastic.protobuf.Channel.Role\"0\n\x04Role\x12\x0c\n\x08\x44ISABLED\x10\x00\x12\x0b\n\x07PRIMARY\x10\x01\x12\r\n\tSECONDARY\x10\x02\x42\x63\n\x14org.meshtastic.protoB\rChannelProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.channel_pb2', _globals)
if _descriptor._USE_C_DESCRIPTORS == False:
DESCRIPTOR._options = None
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\rChannelProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\rChannelProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
_CHANNELSETTINGS.fields_by_name['channel_num']._options = None
_CHANNELSETTINGS.fields_by_name['channel_num']._serialized_options = b'\030\001'
_globals['_CHANNELSETTINGS']._serialized_start=59
_globals['_CHANNELSETTINGS']._serialized_end=252
_globals['_MODULESETTINGS']._serialized_start=254
_globals['_MODULESETTINGS']._serialized_end=323
_globals['_CHANNEL']._serialized_start=326
_globals['_CHANNEL']._serialized_end=505
_globals['_CHANNEL_ROLE']._serialized_start=457
_globals['_CHANNEL_ROLE']._serialized_end=505
_globals['_MODULESETTINGS']._serialized_end=316
_globals['_CHANNEL']._serialized_start=319
_globals['_CHANNEL']._serialized_end=498
_globals['_CHANNEL_ROLE']._serialized_start=450
_globals['_CHANNEL_ROLE']._serialized_end=498
# @@protoc_insertion_point(module_scope)

View File

@@ -127,23 +127,23 @@ class ModuleSettings(google.protobuf.message.Message):
DESCRIPTOR: google.protobuf.descriptor.Descriptor
POSITION_PRECISION_FIELD_NUMBER: builtins.int
IS_CLIENT_MUTED_FIELD_NUMBER: builtins.int
IS_MUTED_FIELD_NUMBER: builtins.int
position_precision: builtins.int
"""
Bits of precision for the location sent in position packets.
"""
is_client_muted: builtins.bool
is_muted: builtins.bool
"""
Controls whether or not the phone / clients should mute the current channel
Controls whether or not the client / device should mute the current channel
Useful for noisy public channels you don't necessarily want to disable
"""
def __init__(
self,
*,
position_precision: builtins.int = ...,
is_client_muted: builtins.bool = ...,
is_muted: builtins.bool = ...,
) -> None: ...
def ClearField(self, field_name: typing.Literal["is_client_muted", b"is_client_muted", "position_precision", b"position_precision"]) -> None: ...
def ClearField(self, field_name: typing.Literal["is_muted", b"is_muted", "position_precision", b"position_precision"]) -> None: ...
global___ModuleSettings = ModuleSettings

View File

@@ -12,16 +12,17 @@ _sym_db = _symbol_database.Default()
from meshtastic.protobuf import localonly_pb2 as meshtastic_dot_protobuf_dot_localonly__pb2
from meshtastic.protobuf import mesh_pb2 as meshtastic_dot_protobuf_dot_mesh__pb2
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n$meshtastic/protobuf/clientonly.proto\x12\x13meshtastic.protobuf\x1a#meshtastic/protobuf/localonly.proto\"\x9f\x02\n\rDeviceProfile\x12\x16\n\tlong_name\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x17\n\nshort_name\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0b\x63hannel_url\x18\x03 \x01(\tH\x02\x88\x01\x01\x12\x35\n\x06\x63onfig\x18\x04 \x01(\x0b\x32 .meshtastic.protobuf.LocalConfigH\x03\x88\x01\x01\x12\x42\n\rmodule_config\x18\x05 \x01(\x0b\x32&.meshtastic.protobuf.LocalModuleConfigH\x04\x88\x01\x01\x42\x0c\n\n_long_nameB\r\n\x0b_short_nameB\x0e\n\x0c_channel_urlB\t\n\x07_configB\x10\n\x0e_module_configBe\n\x13\x63om.geeksville.meshB\x10\x43lientOnlyProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n$meshtastic/protobuf/clientonly.proto\x12\x13meshtastic.protobuf\x1a#meshtastic/protobuf/localonly.proto\x1a\x1emeshtastic/protobuf/mesh.proto\"\xc4\x03\n\rDeviceProfile\x12\x16\n\tlong_name\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x17\n\nshort_name\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0b\x63hannel_url\x18\x03 \x01(\tH\x02\x88\x01\x01\x12\x35\n\x06\x63onfig\x18\x04 \x01(\x0b\x32 .meshtastic.protobuf.LocalConfigH\x03\x88\x01\x01\x12\x42\n\rmodule_config\x18\x05 \x01(\x0b\x32&.meshtastic.protobuf.LocalModuleConfigH\x04\x88\x01\x01\x12:\n\x0e\x66ixed_position\x18\x06 \x01(\x0b\x32\x1d.meshtastic.protobuf.PositionH\x05\x88\x01\x01\x12\x15\n\x08ringtone\x18\x07 \x01(\tH\x06\x88\x01\x01\x12\x1c\n\x0f\x63\x61nned_messages\x18\x08 \x01(\tH\x07\x88\x01\x01\x42\x0c\n\n_long_nameB\r\n\x0b_short_nameB\x0e\n\x0c_channel_urlB\t\n\x07_configB\x10\n\x0e_module_configB\x11\n\x0f_fixed_positionB\x0b\n\t_ringtoneB\x12\n\x10_canned_messagesBf\n\x14org.meshtastic.protoB\x10\x43lientOnlyProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.clientonly_pb2', _globals)
if _descriptor._USE_C_DESCRIPTORS == False:
DESCRIPTOR._options = None
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\020ClientOnlyProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
_globals['_DEVICEPROFILE']._serialized_start=99
_globals['_DEVICEPROFILE']._serialized_end=386
DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\020ClientOnlyProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
_globals['_DEVICEPROFILE']._serialized_start=131
_globals['_DEVICEPROFILE']._serialized_end=583
# @@protoc_insertion_point(module_scope)

View File

@@ -7,6 +7,7 @@ import builtins
import google.protobuf.descriptor
import google.protobuf.message
import meshtastic.protobuf.localonly_pb2
import meshtastic.protobuf.mesh_pb2
import typing
DESCRIPTOR: google.protobuf.descriptor.FileDescriptor
@@ -25,6 +26,9 @@ class DeviceProfile(google.protobuf.message.Message):
CHANNEL_URL_FIELD_NUMBER: builtins.int
CONFIG_FIELD_NUMBER: builtins.int
MODULE_CONFIG_FIELD_NUMBER: builtins.int
FIXED_POSITION_FIELD_NUMBER: builtins.int
RINGTONE_FIELD_NUMBER: builtins.int
CANNED_MESSAGES_FIELD_NUMBER: builtins.int
long_name: builtins.str
"""
Long name for the node
@@ -37,6 +41,14 @@ class DeviceProfile(google.protobuf.message.Message):
"""
The url of the channels from our node
"""
ringtone: builtins.str
"""
Ringtone for ExternalNotification
"""
canned_messages: builtins.str
"""
Predefined messages for CannedMessage
"""
@property
def config(self) -> meshtastic.protobuf.localonly_pb2.LocalConfig:
"""
@@ -49,6 +61,12 @@ class DeviceProfile(google.protobuf.message.Message):
The ModuleConfig of the node
"""
@property
def fixed_position(self) -> meshtastic.protobuf.mesh_pb2.Position:
"""
Fixed position data
"""
def __init__(
self,
*,
@@ -57,18 +75,27 @@ class DeviceProfile(google.protobuf.message.Message):
channel_url: builtins.str | None = ...,
config: meshtastic.protobuf.localonly_pb2.LocalConfig | None = ...,
module_config: meshtastic.protobuf.localonly_pb2.LocalModuleConfig | None = ...,
fixed_position: meshtastic.protobuf.mesh_pb2.Position | None = ...,
ringtone: builtins.str | None = ...,
canned_messages: builtins.str | None = ...,
) -> None: ...
def HasField(self, field_name: typing.Literal["_channel_url", b"_channel_url", "_config", b"_config", "_long_name", b"_long_name", "_module_config", b"_module_config", "_short_name", b"_short_name", "channel_url", b"channel_url", "config", b"config", "long_name", b"long_name", "module_config", b"module_config", "short_name", b"short_name"]) -> builtins.bool: ...
def ClearField(self, field_name: typing.Literal["_channel_url", b"_channel_url", "_config", b"_config", "_long_name", b"_long_name", "_module_config", b"_module_config", "_short_name", b"_short_name", "channel_url", b"channel_url", "config", b"config", "long_name", b"long_name", "module_config", b"module_config", "short_name", b"short_name"]) -> None: ...
def HasField(self, field_name: typing.Literal["_canned_messages", b"_canned_messages", "_channel_url", b"_channel_url", "_config", b"_config", "_fixed_position", b"_fixed_position", "_long_name", b"_long_name", "_module_config", b"_module_config", "_ringtone", b"_ringtone", "_short_name", b"_short_name", "canned_messages", b"canned_messages", "channel_url", b"channel_url", "config", b"config", "fixed_position", b"fixed_position", "long_name", b"long_name", "module_config", b"module_config", "ringtone", b"ringtone", "short_name", b"short_name"]) -> builtins.bool: ...
def ClearField(self, field_name: typing.Literal["_canned_messages", b"_canned_messages", "_channel_url", b"_channel_url", "_config", b"_config", "_fixed_position", b"_fixed_position", "_long_name", b"_long_name", "_module_config", b"_module_config", "_ringtone", b"_ringtone", "_short_name", b"_short_name", "canned_messages", b"canned_messages", "channel_url", b"channel_url", "config", b"config", "fixed_position", b"fixed_position", "long_name", b"long_name", "module_config", b"module_config", "ringtone", b"ringtone", "short_name", b"short_name"]) -> None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_canned_messages", b"_canned_messages"]) -> typing.Literal["canned_messages"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_channel_url", b"_channel_url"]) -> typing.Literal["channel_url"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_config", b"_config"]) -> typing.Literal["config"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_fixed_position", b"_fixed_position"]) -> typing.Literal["fixed_position"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_long_name", b"_long_name"]) -> typing.Literal["long_name"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_module_config", b"_module_config"]) -> typing.Literal["module_config"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_ringtone", b"_ringtone"]) -> typing.Literal["ringtone"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_short_name", b"_short_name"]) -> typing.Literal["short_name"] | None: ...
global___DeviceProfile = DeviceProfile

View File

File diff suppressed because one or more lines are too long

View File

@@ -9,6 +9,7 @@ import google.protobuf.descriptor
import google.protobuf.internal.containers
import google.protobuf.internal.enum_type_wrapper
import google.protobuf.message
import meshtastic.protobuf.device_ui_pb2
import sys
import typing
@@ -63,6 +64,7 @@ class Config(google.protobuf.message.Message):
Description: Infrastructure node for extending network coverage by relaying messages with minimal overhead. Not visible in Nodes list.
Technical Details: Mesh packets will simply be rebroadcasted over this node. Nodes configured with this role will not originate NodeInfo, Position, Telemetry
or any other packet type. They will simply rebroadcast any mesh packets on the same frequency, channel num, spread factor, and coding rate.
Deprecated in v2.7.11 because it creates "holes" in the mesh rebroadcast chain.
"""
TRACKER: Config.DeviceConfig._Role.ValueType # 5
"""
@@ -107,6 +109,21 @@ class Config(google.protobuf.message.Message):
and automatic TAK PLI (position location information) broadcasts.
Uses position module configuration to determine TAK PLI broadcast interval.
"""
ROUTER_LATE: Config.DeviceConfig._Role.ValueType # 11
"""
Description: Will always rebroadcast packets, but will do so after all other modes.
Technical Details: Used for router nodes that are intended to provide additional coverage
in areas not already covered by other routers, or to bridge around problematic terrain,
but should not be given priority over other routers in order to avoid unnecessaraily
consuming hops.
"""
CLIENT_BASE: Config.DeviceConfig._Role.ValueType # 12
"""
Description: Treats packets from or to favorited nodes as ROUTER, and all other packets as CLIENT.
Technical Details: Used for stronger attic/roof nodes to distribute messages more widely
from weaker, indoor, or less-well-positioned nodes. Recommended for users with multiple nodes
where one CLIENT_BASE acts as a more powerful base station, such as an attic/roof node.
"""
class Role(_Role, metaclass=_RoleEnumTypeWrapper):
"""
@@ -139,6 +156,7 @@ class Config(google.protobuf.message.Message):
Description: Infrastructure node for extending network coverage by relaying messages with minimal overhead. Not visible in Nodes list.
Technical Details: Mesh packets will simply be rebroadcasted over this node. Nodes configured with this role will not originate NodeInfo, Position, Telemetry
or any other packet type. They will simply rebroadcast any mesh packets on the same frequency, channel num, spread factor, and coding rate.
Deprecated in v2.7.11 because it creates "holes" in the mesh rebroadcast chain.
"""
TRACKER: Config.DeviceConfig.Role.ValueType # 5
"""
@@ -183,6 +201,21 @@ class Config(google.protobuf.message.Message):
and automatic TAK PLI (position location information) broadcasts.
Uses position module configuration to determine TAK PLI broadcast interval.
"""
ROUTER_LATE: Config.DeviceConfig.Role.ValueType # 11
"""
Description: Will always rebroadcast packets, but will do so after all other modes.
Technical Details: Used for router nodes that are intended to provide additional coverage
in areas not already covered by other routers, or to bridge around problematic terrain,
but should not be given priority over other routers in order to avoid unnecessaraily
consuming hops.
"""
CLIENT_BASE: Config.DeviceConfig.Role.ValueType # 12
"""
Description: Treats packets from or to favorited nodes as ROUTER, and all other packets as CLIENT.
Technical Details: Used for stronger attic/roof nodes to distribute messages more widely
from weaker, indoor, or less-well-positioned nodes. Recommended for users with multiple nodes
where one CLIENT_BASE acts as a more powerful base station, such as an attic/roof node.
"""
class _RebroadcastMode:
ValueType = typing.NewType("ValueType", builtins.int)
@@ -210,6 +243,15 @@ class Config(google.protobuf.message.Message):
Ignores observed messages from foreign meshes like LOCAL_ONLY,
but takes it step further by also ignoring messages from nodenums not in the node's known list (NodeDB)
"""
NONE: Config.DeviceConfig._RebroadcastMode.ValueType # 4
"""
Only permitted for SENSOR, TRACKER and TAK_TRACKER roles, this will inhibit all rebroadcasts, not unlike CLIENT_MUTE role.
"""
CORE_PORTNUMS_ONLY: Config.DeviceConfig._RebroadcastMode.ValueType # 5
"""
Ignores packets from non-standard portnums such as: TAK, RangeTest, PaxCounter, etc.
Only rebroadcasts packets with standard portnums: NodeInfo, Text, Position, Telemetry, and Routing.
"""
class RebroadcastMode(_RebroadcastMode, metaclass=_RebroadcastModeEnumTypeWrapper):
"""
@@ -236,10 +278,85 @@ class Config(google.protobuf.message.Message):
Ignores observed messages from foreign meshes like LOCAL_ONLY,
but takes it step further by also ignoring messages from nodenums not in the node's known list (NodeDB)
"""
NONE: Config.DeviceConfig.RebroadcastMode.ValueType # 4
"""
Only permitted for SENSOR, TRACKER and TAK_TRACKER roles, this will inhibit all rebroadcasts, not unlike CLIENT_MUTE role.
"""
CORE_PORTNUMS_ONLY: Config.DeviceConfig.RebroadcastMode.ValueType # 5
"""
Ignores packets from non-standard portnums such as: TAK, RangeTest, PaxCounter, etc.
Only rebroadcasts packets with standard portnums: NodeInfo, Text, Position, Telemetry, and Routing.
"""
class _BuzzerMode:
ValueType = typing.NewType("ValueType", builtins.int)
V: typing_extensions.TypeAlias = ValueType
class _BuzzerModeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[Config.DeviceConfig._BuzzerMode.ValueType], builtins.type):
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
ALL_ENABLED: Config.DeviceConfig._BuzzerMode.ValueType # 0
"""
Default behavior.
Buzzer is enabled for all audio feedback including button presses and alerts.
"""
DISABLED: Config.DeviceConfig._BuzzerMode.ValueType # 1
"""
Disabled.
All buzzer audio feedback is disabled.
"""
NOTIFICATIONS_ONLY: Config.DeviceConfig._BuzzerMode.ValueType # 2
"""
Notifications Only.
Buzzer is enabled only for notifications and alerts, but not for button presses.
External notification config determines the specifics of the notification behavior.
"""
SYSTEM_ONLY: Config.DeviceConfig._BuzzerMode.ValueType # 3
"""
Non-notification system buzzer tones only.
Buzzer is enabled only for non-notification tones such as button presses, startup, shutdown, but not for alerts.
"""
DIRECT_MSG_ONLY: Config.DeviceConfig._BuzzerMode.ValueType # 4
"""
Direct Message notifications only.
Buzzer is enabled only for direct messages and alerts, but not for button presses.
External notification config determines the specifics of the notification behavior.
"""
class BuzzerMode(_BuzzerMode, metaclass=_BuzzerModeEnumTypeWrapper):
"""
Defines buzzer behavior for audio feedback
"""
ALL_ENABLED: Config.DeviceConfig.BuzzerMode.ValueType # 0
"""
Default behavior.
Buzzer is enabled for all audio feedback including button presses and alerts.
"""
DISABLED: Config.DeviceConfig.BuzzerMode.ValueType # 1
"""
Disabled.
All buzzer audio feedback is disabled.
"""
NOTIFICATIONS_ONLY: Config.DeviceConfig.BuzzerMode.ValueType # 2
"""
Notifications Only.
Buzzer is enabled only for notifications and alerts, but not for button presses.
External notification config determines the specifics of the notification behavior.
"""
SYSTEM_ONLY: Config.DeviceConfig.BuzzerMode.ValueType # 3
"""
Non-notification system buzzer tones only.
Buzzer is enabled only for non-notification tones such as button presses, startup, shutdown, but not for alerts.
"""
DIRECT_MSG_ONLY: Config.DeviceConfig.BuzzerMode.ValueType # 4
"""
Direct Message notifications only.
Buzzer is enabled only for direct messages and alerts, but not for button presses.
External notification config determines the specifics of the notification behavior.
"""
ROLE_FIELD_NUMBER: builtins.int
SERIAL_ENABLED_FIELD_NUMBER: builtins.int
DEBUG_LOG_ENABLED_FIELD_NUMBER: builtins.int
BUTTON_GPIO_FIELD_NUMBER: builtins.int
BUZZER_GPIO_FIELD_NUMBER: builtins.int
REBROADCAST_MODE_FIELD_NUMBER: builtins.int
@@ -249,6 +366,7 @@ class Config(google.protobuf.message.Message):
DISABLE_TRIPLE_CLICK_FIELD_NUMBER: builtins.int
TZDEF_FIELD_NUMBER: builtins.int
LED_HEARTBEAT_DISABLED_FIELD_NUMBER: builtins.int
BUZZER_MODE_FIELD_NUMBER: builtins.int
role: global___Config.DeviceConfig.Role.ValueType
"""
Sets the role of node
@@ -256,11 +374,7 @@ class Config(google.protobuf.message.Message):
serial_enabled: builtins.bool
"""
Disabling this will disable the SerialConsole by not initilizing the StreamAPI
"""
debug_log_enabled: builtins.bool
"""
By default we turn off logging as soon as an API client connects (to keep shared serial link quiet).
Set this to true to leave the debug log outputting even when API is active.
Moved to SecurityConfig
"""
button_gpio: builtins.int
"""
@@ -289,6 +403,7 @@ class Config(google.protobuf.message.Message):
"""
If true, device is considered to be "managed" by a mesh administrator
Clients should then limit available configuration and administrative options inside the user interface
Moved to SecurityConfig
"""
disable_triple_click: builtins.bool
"""
@@ -302,12 +417,16 @@ class Config(google.protobuf.message.Message):
"""
If true, disable the default blinking LED (LED_PIN) behavior on the device
"""
buzzer_mode: global___Config.DeviceConfig.BuzzerMode.ValueType
"""
Controls buzzer behavior for audio feedback
Defaults to ENABLED
"""
def __init__(
self,
*,
role: global___Config.DeviceConfig.Role.ValueType = ...,
serial_enabled: builtins.bool = ...,
debug_log_enabled: builtins.bool = ...,
button_gpio: builtins.int = ...,
buzzer_gpio: builtins.int = ...,
rebroadcast_mode: global___Config.DeviceConfig.RebroadcastMode.ValueType = ...,
@@ -317,8 +436,9 @@ class Config(google.protobuf.message.Message):
disable_triple_click: builtins.bool = ...,
tzdef: builtins.str = ...,
led_heartbeat_disabled: builtins.bool = ...,
buzzer_mode: global___Config.DeviceConfig.BuzzerMode.ValueType = ...,
) -> None: ...
def ClearField(self, field_name: typing.Literal["button_gpio", b"button_gpio", "buzzer_gpio", b"buzzer_gpio", "debug_log_enabled", b"debug_log_enabled", "disable_triple_click", b"disable_triple_click", "double_tap_as_button_press", b"double_tap_as_button_press", "is_managed", b"is_managed", "led_heartbeat_disabled", b"led_heartbeat_disabled", "node_info_broadcast_secs", b"node_info_broadcast_secs", "rebroadcast_mode", b"rebroadcast_mode", "role", b"role", "serial_enabled", b"serial_enabled", "tzdef", b"tzdef"]) -> None: ...
def ClearField(self, field_name: typing.Literal["button_gpio", b"button_gpio", "buzzer_gpio", b"buzzer_gpio", "buzzer_mode", b"buzzer_mode", "disable_triple_click", b"disable_triple_click", "double_tap_as_button_press", b"double_tap_as_button_press", "is_managed", b"is_managed", "led_heartbeat_disabled", b"led_heartbeat_disabled", "node_info_broadcast_secs", b"node_info_broadcast_secs", "rebroadcast_mode", b"rebroadcast_mode", "role", b"role", "serial_enabled", b"serial_enabled", "tzdef", b"tzdef"]) -> None: ...
@typing.final
class PositionConfig(google.protobuf.message.Message):
@@ -585,7 +705,7 @@ class Config(google.protobuf.message.Message):
POWERMON_ENABLES_FIELD_NUMBER: builtins.int
is_power_saving: builtins.bool
"""
Description: Will sleep everything as much as possible, for the tracker and sensor role this will also include the lora radio.
Description: Will sleep everything as much as possible, for the tracker and sensor role this will also include the lora radio.
Don't use this setting if you want to use your device with the phone apps or are using a device without a user button.
Technical Details: Works for ESP32 devices and NRF52 devices in the Sensor or Tracker roles
"""
@@ -679,6 +799,35 @@ class Config(google.protobuf.message.Message):
use static ip address
"""
class _ProtocolFlags:
ValueType = typing.NewType("ValueType", builtins.int)
V: typing_extensions.TypeAlias = ValueType
class _ProtocolFlagsEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[Config.NetworkConfig._ProtocolFlags.ValueType], builtins.type):
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
NO_BROADCAST: Config.NetworkConfig._ProtocolFlags.ValueType # 0
"""
Do not broadcast packets over any network protocol
"""
UDP_BROADCAST: Config.NetworkConfig._ProtocolFlags.ValueType # 1
"""
Enable broadcasting packets via UDP over the local network
"""
class ProtocolFlags(_ProtocolFlags, metaclass=_ProtocolFlagsEnumTypeWrapper):
"""
Available flags auxiliary network protocols
"""
NO_BROADCAST: Config.NetworkConfig.ProtocolFlags.ValueType # 0
"""
Do not broadcast packets over any network protocol
"""
UDP_BROADCAST: Config.NetworkConfig.ProtocolFlags.ValueType # 1
"""
Enable broadcasting packets via UDP over the local network
"""
@typing.final
class IpV4Config(google.protobuf.message.Message):
DESCRIPTOR: google.protobuf.descriptor.Descriptor
@@ -721,6 +870,8 @@ class Config(google.protobuf.message.Message):
ADDRESS_MODE_FIELD_NUMBER: builtins.int
IPV4_CONFIG_FIELD_NUMBER: builtins.int
RSYSLOG_SERVER_FIELD_NUMBER: builtins.int
ENABLED_PROTOCOLS_FIELD_NUMBER: builtins.int
IPV6_ENABLED_FIELD_NUMBER: builtins.int
wifi_enabled: builtins.bool
"""
Enable WiFi (disables Bluetooth)
@@ -736,7 +887,7 @@ class Config(google.protobuf.message.Message):
"""
ntp_server: builtins.str
"""
NTP server to use if WiFi is conneced, defaults to `0.pool.ntp.org`
NTP server to use if WiFi is conneced, defaults to `meshtastic.pool.ntp.org`
"""
eth_enabled: builtins.bool
"""
@@ -750,6 +901,14 @@ class Config(google.protobuf.message.Message):
"""
rsyslog Server and Port
"""
enabled_protocols: builtins.int
"""
Flags for enabling/disabling network protocols
"""
ipv6_enabled: builtins.bool
"""
Enable/Disable ipv6 support
"""
@property
def ipv4_config(self) -> global___Config.NetworkConfig.IpV4Config:
"""
@@ -767,9 +926,11 @@ class Config(google.protobuf.message.Message):
address_mode: global___Config.NetworkConfig.AddressMode.ValueType = ...,
ipv4_config: global___Config.NetworkConfig.IpV4Config | None = ...,
rsyslog_server: builtins.str = ...,
enabled_protocols: builtins.int = ...,
ipv6_enabled: builtins.bool = ...,
) -> None: ...
def HasField(self, field_name: typing.Literal["ipv4_config", b"ipv4_config"]) -> builtins.bool: ...
def ClearField(self, field_name: typing.Literal["address_mode", b"address_mode", "eth_enabled", b"eth_enabled", "ipv4_config", b"ipv4_config", "ntp_server", b"ntp_server", "rsyslog_server", b"rsyslog_server", "wifi_enabled", b"wifi_enabled", "wifi_psk", b"wifi_psk", "wifi_ssid", b"wifi_ssid"]) -> None: ...
def ClearField(self, field_name: typing.Literal["address_mode", b"address_mode", "enabled_protocols", b"enabled_protocols", "eth_enabled", b"eth_enabled", "ipv4_config", b"ipv4_config", "ipv6_enabled", b"ipv6_enabled", "ntp_server", b"ntp_server", "rsyslog_server", b"rsyslog_server", "wifi_enabled", b"wifi_enabled", "wifi_psk", b"wifi_psk", "wifi_ssid", b"wifi_ssid"]) -> None: ...
@typing.final
class DisplayConfig(google.protobuf.message.Message):
@@ -779,80 +940,20 @@ class Config(google.protobuf.message.Message):
DESCRIPTOR: google.protobuf.descriptor.Descriptor
class _GpsCoordinateFormat:
class _DeprecatedGpsCoordinateFormat:
ValueType = typing.NewType("ValueType", builtins.int)
V: typing_extensions.TypeAlias = ValueType
class _GpsCoordinateFormatEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[Config.DisplayConfig._GpsCoordinateFormat.ValueType], builtins.type):
class _DeprecatedGpsCoordinateFormatEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[Config.DisplayConfig._DeprecatedGpsCoordinateFormat.ValueType], builtins.type):
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
DEC: Config.DisplayConfig._GpsCoordinateFormat.ValueType # 0
UNUSED: Config.DisplayConfig._DeprecatedGpsCoordinateFormat.ValueType # 0
class DeprecatedGpsCoordinateFormat(_DeprecatedGpsCoordinateFormat, metaclass=_DeprecatedGpsCoordinateFormatEnumTypeWrapper):
"""
GPS coordinates are displayed in the normal decimal degrees format:
DD.DDDDDD DDD.DDDDDD
"""
DMS: Config.DisplayConfig._GpsCoordinateFormat.ValueType # 1
"""
GPS coordinates are displayed in the degrees minutes seconds format:
DD°MM'SS"C DDD°MM'SS"C, where C is the compass point representing the locations quadrant
"""
UTM: Config.DisplayConfig._GpsCoordinateFormat.ValueType # 2
"""
Universal Transverse Mercator format:
ZZB EEEEEE NNNNNNN, where Z is zone, B is band, E is easting, N is northing
"""
MGRS: Config.DisplayConfig._GpsCoordinateFormat.ValueType # 3
"""
Military Grid Reference System format:
ZZB CD EEEEE NNNNN, where Z is zone, B is band, C is the east 100k square, D is the north 100k square,
E is easting, N is northing
"""
OLC: Config.DisplayConfig._GpsCoordinateFormat.ValueType # 4
"""
Open Location Code (aka Plus Codes).
"""
OSGR: Config.DisplayConfig._GpsCoordinateFormat.ValueType # 5
"""
Ordnance Survey Grid Reference (the National Grid System of the UK).
Format: AB EEEEE NNNNN, where A is the east 100k square, B is the north 100k square,
E is the easting, N is the northing
Deprecated in 2.7.4: Unused
"""
class GpsCoordinateFormat(_GpsCoordinateFormat, metaclass=_GpsCoordinateFormatEnumTypeWrapper):
"""
How the GPS coordinates are displayed on the OLED screen.
"""
DEC: Config.DisplayConfig.GpsCoordinateFormat.ValueType # 0
"""
GPS coordinates are displayed in the normal decimal degrees format:
DD.DDDDDD DDD.DDDDDD
"""
DMS: Config.DisplayConfig.GpsCoordinateFormat.ValueType # 1
"""
GPS coordinates are displayed in the degrees minutes seconds format:
DD°MM'SS"C DDD°MM'SS"C, where C is the compass point representing the locations quadrant
"""
UTM: Config.DisplayConfig.GpsCoordinateFormat.ValueType # 2
"""
Universal Transverse Mercator format:
ZZB EEEEEE NNNNNNN, where Z is zone, B is band, E is easting, N is northing
"""
MGRS: Config.DisplayConfig.GpsCoordinateFormat.ValueType # 3
"""
Military Grid Reference System format:
ZZB CD EEEEE NNNNN, where Z is zone, B is band, C is the east 100k square, D is the north 100k square,
E is easting, N is northing
"""
OLC: Config.DisplayConfig.GpsCoordinateFormat.ValueType # 4
"""
Open Location Code (aka Plus Codes).
"""
OSGR: Config.DisplayConfig.GpsCoordinateFormat.ValueType # 5
"""
Ordnance Survey Grid Reference (the National Grid System of the UK).
Format: AB EEEEE NNNNN, where A is the east 100k square, B is the north 100k square,
E is the easting, N is the northing
"""
UNUSED: Config.DisplayConfig.DeprecatedGpsCoordinateFormat.ValueType # 0
class _DisplayUnits:
ValueType = typing.NewType("ValueType", builtins.int)
@@ -891,18 +992,22 @@ class Config(google.protobuf.message.Message):
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
OLED_AUTO: Config.DisplayConfig._OledType.ValueType # 0
"""
Default / Auto
Default / Autodetect
"""
OLED_SSD1306: Config.DisplayConfig._OledType.ValueType # 1
"""
Default / Auto
Default / Autodetect
"""
OLED_SH1106: Config.DisplayConfig._OledType.ValueType # 2
"""
Default / Auto
Default / Autodetect
"""
OLED_SH1107: Config.DisplayConfig._OledType.ValueType # 3
"""
Can not be auto detected but set by proto. Used for 128x64 screens
"""
OLED_SH1107_128_128: Config.DisplayConfig._OledType.ValueType # 4
"""
Can not be auto detected but set by proto. Used for 128x128 screens
"""
@@ -913,18 +1018,22 @@ class Config(google.protobuf.message.Message):
OLED_AUTO: Config.DisplayConfig.OledType.ValueType # 0
"""
Default / Auto
Default / Autodetect
"""
OLED_SSD1306: Config.DisplayConfig.OledType.ValueType # 1
"""
Default / Auto
Default / Autodetect
"""
OLED_SH1106: Config.DisplayConfig.OledType.ValueType # 2
"""
Default / Auto
Default / Autodetect
"""
OLED_SH1107: Config.DisplayConfig.OledType.ValueType # 3
"""
Can not be auto detected but set by proto. Used for 128x64 screens
"""
OLED_SH1107_128_128: Config.DisplayConfig.OledType.ValueType # 4
"""
Can not be auto detected but set by proto. Used for 128x128 screens
"""
@@ -1053,13 +1162,16 @@ class Config(google.protobuf.message.Message):
HEADING_BOLD_FIELD_NUMBER: builtins.int
WAKE_ON_TAP_OR_MOTION_FIELD_NUMBER: builtins.int
COMPASS_ORIENTATION_FIELD_NUMBER: builtins.int
USE_12H_CLOCK_FIELD_NUMBER: builtins.int
USE_LONG_NODE_NAME_FIELD_NUMBER: builtins.int
screen_on_secs: builtins.int
"""
Number of seconds the screen stays on after pressing the user button or receiving a message
0 for default of one minute MAXUINT for always on
"""
gps_format: global___Config.DisplayConfig.GpsCoordinateFormat.ValueType
gps_format: global___Config.DisplayConfig.DeprecatedGpsCoordinateFormat.ValueType
"""
Deprecated in 2.7.4: Unused
How the GPS coordinates are formatted on the OLED screen.
"""
auto_screen_carousel_secs: builtins.int
@@ -1100,11 +1212,21 @@ class Config(google.protobuf.message.Message):
"""
Indicates how to rotate or invert the compass output to accurate display on the display.
"""
use_12h_clock: builtins.bool
"""
If false (default), the device will display the time in 24-hour format on screen.
If true, the device will display the time in 12-hour format on screen.
"""
use_long_node_name: builtins.bool
"""
If false (default), the device will use short names for various display screens.
If true, node names will show in long format
"""
def __init__(
self,
*,
screen_on_secs: builtins.int = ...,
gps_format: global___Config.DisplayConfig.GpsCoordinateFormat.ValueType = ...,
gps_format: global___Config.DisplayConfig.DeprecatedGpsCoordinateFormat.ValueType = ...,
auto_screen_carousel_secs: builtins.int = ...,
compass_north_top: builtins.bool = ...,
flip_screen: builtins.bool = ...,
@@ -1114,8 +1236,10 @@ class Config(google.protobuf.message.Message):
heading_bold: builtins.bool = ...,
wake_on_tap_or_motion: builtins.bool = ...,
compass_orientation: global___Config.DisplayConfig.CompassOrientation.ValueType = ...,
use_12h_clock: builtins.bool = ...,
use_long_node_name: builtins.bool = ...,
) -> None: ...
def ClearField(self, field_name: typing.Literal["auto_screen_carousel_secs", b"auto_screen_carousel_secs", "compass_north_top", b"compass_north_top", "compass_orientation", b"compass_orientation", "displaymode", b"displaymode", "flip_screen", b"flip_screen", "gps_format", b"gps_format", "heading_bold", b"heading_bold", "oled", b"oled", "screen_on_secs", b"screen_on_secs", "units", b"units", "wake_on_tap_or_motion", b"wake_on_tap_or_motion"]) -> None: ...
def ClearField(self, field_name: typing.Literal["auto_screen_carousel_secs", b"auto_screen_carousel_secs", "compass_north_top", b"compass_north_top", "compass_orientation", b"compass_orientation", "displaymode", b"displaymode", "flip_screen", b"flip_screen", "gps_format", b"gps_format", "heading_bold", b"heading_bold", "oled", b"oled", "screen_on_secs", b"screen_on_secs", "units", b"units", "use_12h_clock", b"use_12h_clock", "use_long_node_name", b"use_long_node_name", "wake_on_tap_or_motion", b"wake_on_tap_or_motion"]) -> None: ...
@typing.final
class LoRaConfig(google.protobuf.message.Message):
@@ -1207,6 +1331,38 @@ class Config(google.protobuf.message.Message):
"""
Singapore 923mhz
"""
PH_433: Config.LoRaConfig._RegionCode.ValueType # 19
"""
Philippines 433mhz
"""
PH_868: Config.LoRaConfig._RegionCode.ValueType # 20
"""
Philippines 868mhz
"""
PH_915: Config.LoRaConfig._RegionCode.ValueType # 21
"""
Philippines 915mhz
"""
ANZ_433: Config.LoRaConfig._RegionCode.ValueType # 22
"""
Australia / New Zealand 433MHz
"""
KZ_433: Config.LoRaConfig._RegionCode.ValueType # 23
"""
Kazakhstan 433MHz
"""
KZ_863: Config.LoRaConfig._RegionCode.ValueType # 24
"""
Kazakhstan 863MHz
"""
NP_865: Config.LoRaConfig._RegionCode.ValueType # 25
"""
Nepal 865MHz
"""
BR_902: Config.LoRaConfig._RegionCode.ValueType # 26
"""
Brazil 902MHz
"""
class RegionCode(_RegionCode, metaclass=_RegionCodeEnumTypeWrapper): ...
UNSET: Config.LoRaConfig.RegionCode.ValueType # 0
@@ -1285,6 +1441,38 @@ class Config(google.protobuf.message.Message):
"""
Singapore 923mhz
"""
PH_433: Config.LoRaConfig.RegionCode.ValueType # 19
"""
Philippines 433mhz
"""
PH_868: Config.LoRaConfig.RegionCode.ValueType # 20
"""
Philippines 868mhz
"""
PH_915: Config.LoRaConfig.RegionCode.ValueType # 21
"""
Philippines 915mhz
"""
ANZ_433: Config.LoRaConfig.RegionCode.ValueType # 22
"""
Australia / New Zealand 433MHz
"""
KZ_433: Config.LoRaConfig.RegionCode.ValueType # 23
"""
Kazakhstan 433MHz
"""
KZ_863: Config.LoRaConfig.RegionCode.ValueType # 24
"""
Kazakhstan 863MHz
"""
NP_865: Config.LoRaConfig.RegionCode.ValueType # 25
"""
Nepal 865MHz
"""
BR_902: Config.LoRaConfig.RegionCode.ValueType # 26
"""
Brazil 902MHz
"""
class _ModemPreset:
ValueType = typing.NewType("ValueType", builtins.int)
@@ -1303,6 +1491,7 @@ class Config(google.protobuf.message.Message):
VERY_LONG_SLOW: Config.LoRaConfig._ModemPreset.ValueType # 2
"""
Very Long Range - Slow
Deprecated in 2.5: Works only with txco and is unusably slow
"""
MEDIUM_SLOW: Config.LoRaConfig._ModemPreset.ValueType # 3
"""
@@ -1324,6 +1513,12 @@ class Config(google.protobuf.message.Message):
"""
Long Range - Moderately Fast
"""
SHORT_TURBO: Config.LoRaConfig._ModemPreset.ValueType # 8
"""
Short Range - Turbo
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.
"""
class ModemPreset(_ModemPreset, metaclass=_ModemPresetEnumTypeWrapper):
"""
@@ -1342,6 +1537,7 @@ class Config(google.protobuf.message.Message):
VERY_LONG_SLOW: Config.LoRaConfig.ModemPreset.ValueType # 2
"""
Very Long Range - Slow
Deprecated in 2.5: Works only with txco and is unusably slow
"""
MEDIUM_SLOW: Config.LoRaConfig.ModemPreset.ValueType # 3
"""
@@ -1363,6 +1559,12 @@ class Config(google.protobuf.message.Message):
"""
Long Range - Moderately Fast
"""
SHORT_TURBO: Config.LoRaConfig.ModemPreset.ValueType # 8
"""
Short Range - Turbo
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.
"""
USE_PRESET_FIELD_NUMBER: builtins.int
MODEM_PRESET_FIELD_NUMBER: builtins.int
@@ -1381,6 +1583,7 @@ class Config(google.protobuf.message.Message):
PA_FAN_DISABLED_FIELD_NUMBER: builtins.int
IGNORE_INCOMING_FIELD_NUMBER: builtins.int
IGNORE_MQTT_FIELD_NUMBER: builtins.int
CONFIG_OK_TO_MQTT_FIELD_NUMBER: builtins.int
use_preset: builtins.bool
"""
When enabled, the `modem_preset` fields will be adhered to, else the `bandwidth`/`spread_factor`/`coding_rate`
@@ -1474,6 +1677,10 @@ class Config(google.protobuf.message.Message):
"""
If true, the device will not process any packets received via LoRa that passed via MQTT anywhere on the path towards it.
"""
config_ok_to_mqtt: builtins.bool
"""
Sets the ok_to_mqtt bit on outgoing packets
"""
@property
def ignore_incoming(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.int]:
"""
@@ -1502,8 +1709,9 @@ class Config(google.protobuf.message.Message):
pa_fan_disabled: builtins.bool = ...,
ignore_incoming: collections.abc.Iterable[builtins.int] | None = ...,
ignore_mqtt: builtins.bool = ...,
config_ok_to_mqtt: builtins.bool = ...,
) -> None: ...
def ClearField(self, field_name: typing.Literal["bandwidth", b"bandwidth", "channel_num", b"channel_num", "coding_rate", b"coding_rate", "frequency_offset", b"frequency_offset", "hop_limit", b"hop_limit", "ignore_incoming", b"ignore_incoming", "ignore_mqtt", b"ignore_mqtt", "modem_preset", b"modem_preset", "override_duty_cycle", b"override_duty_cycle", "override_frequency", b"override_frequency", "pa_fan_disabled", b"pa_fan_disabled", "region", b"region", "spread_factor", b"spread_factor", "sx126x_rx_boosted_gain", b"sx126x_rx_boosted_gain", "tx_enabled", b"tx_enabled", "tx_power", b"tx_power", "use_preset", b"use_preset"]) -> None: ...
def ClearField(self, field_name: typing.Literal["bandwidth", b"bandwidth", "channel_num", b"channel_num", "coding_rate", b"coding_rate", "config_ok_to_mqtt", b"config_ok_to_mqtt", "frequency_offset", b"frequency_offset", "hop_limit", b"hop_limit", "ignore_incoming", b"ignore_incoming", "ignore_mqtt", b"ignore_mqtt", "modem_preset", b"modem_preset", "override_duty_cycle", b"override_duty_cycle", "override_frequency", b"override_frequency", "pa_fan_disabled", b"pa_fan_disabled", "region", b"region", "spread_factor", b"spread_factor", "sx126x_rx_boosted_gain", b"sx126x_rx_boosted_gain", "tx_enabled", b"tx_enabled", "tx_power", b"tx_power", "use_preset", b"use_preset"]) -> None: ...
@typing.final
class BluetoothConfig(google.protobuf.message.Message):
@@ -1545,7 +1753,6 @@ class Config(google.protobuf.message.Message):
ENABLED_FIELD_NUMBER: builtins.int
MODE_FIELD_NUMBER: builtins.int
FIXED_PIN_FIELD_NUMBER: builtins.int
DEVICE_LOGGING_ENABLED_FIELD_NUMBER: builtins.int
enabled: builtins.bool
"""
Enable Bluetooth on the device
@@ -1558,19 +1765,84 @@ class Config(google.protobuf.message.Message):
"""
Specified PIN for PairingMode.FixedPin
"""
device_logging_enabled: builtins.bool
"""
Enables device (serial style logs) over Bluetooth
"""
def __init__(
self,
*,
enabled: builtins.bool = ...,
mode: global___Config.BluetoothConfig.PairingMode.ValueType = ...,
fixed_pin: builtins.int = ...,
device_logging_enabled: builtins.bool = ...,
) -> None: ...
def ClearField(self, field_name: typing.Literal["device_logging_enabled", b"device_logging_enabled", "enabled", b"enabled", "fixed_pin", b"fixed_pin", "mode", b"mode"]) -> None: ...
def ClearField(self, field_name: typing.Literal["enabled", b"enabled", "fixed_pin", b"fixed_pin", "mode", b"mode"]) -> None: ...
@typing.final
class SecurityConfig(google.protobuf.message.Message):
DESCRIPTOR: google.protobuf.descriptor.Descriptor
PUBLIC_KEY_FIELD_NUMBER: builtins.int
PRIVATE_KEY_FIELD_NUMBER: builtins.int
ADMIN_KEY_FIELD_NUMBER: builtins.int
IS_MANAGED_FIELD_NUMBER: builtins.int
SERIAL_ENABLED_FIELD_NUMBER: builtins.int
DEBUG_LOG_API_ENABLED_FIELD_NUMBER: builtins.int
ADMIN_CHANNEL_ENABLED_FIELD_NUMBER: builtins.int
public_key: builtins.bytes
"""
The public key of the user's device.
Sent out to other nodes on the mesh to allow them to compute a shared secret key.
"""
private_key: builtins.bytes
"""
The private key of the device.
Used to create a shared key with a remote device.
"""
is_managed: builtins.bool
"""
If true, device is considered to be "managed" by a mesh administrator via admin messages
Device is managed by a mesh administrator.
"""
serial_enabled: builtins.bool
"""
Serial Console over the Stream API."
"""
debug_log_api_enabled: builtins.bool
"""
By default we turn off logging as soon as an API client connects (to keep shared serial link quiet).
Output live debug logging over serial or bluetooth is set to true.
"""
admin_channel_enabled: builtins.bool
"""
Allow incoming device control over the insecure legacy admin channel.
"""
@property
def admin_key(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.bytes]:
"""
The public key authorized to send admin messages to this node.
"""
def __init__(
self,
*,
public_key: builtins.bytes = ...,
private_key: builtins.bytes = ...,
admin_key: collections.abc.Iterable[builtins.bytes] | None = ...,
is_managed: builtins.bool = ...,
serial_enabled: builtins.bool = ...,
debug_log_api_enabled: builtins.bool = ...,
admin_channel_enabled: builtins.bool = ...,
) -> None: ...
def ClearField(self, field_name: typing.Literal["admin_channel_enabled", b"admin_channel_enabled", "admin_key", b"admin_key", "debug_log_api_enabled", b"debug_log_api_enabled", "is_managed", b"is_managed", "private_key", b"private_key", "public_key", b"public_key", "serial_enabled", b"serial_enabled"]) -> None: ...
@typing.final
class SessionkeyConfig(google.protobuf.message.Message):
"""
Blank config request, strictly for getting the session key
"""
DESCRIPTOR: google.protobuf.descriptor.Descriptor
def __init__(
self,
) -> None: ...
DEVICE_FIELD_NUMBER: builtins.int
POSITION_FIELD_NUMBER: builtins.int
@@ -1579,6 +1851,9 @@ class Config(google.protobuf.message.Message):
DISPLAY_FIELD_NUMBER: builtins.int
LORA_FIELD_NUMBER: builtins.int
BLUETOOTH_FIELD_NUMBER: builtins.int
SECURITY_FIELD_NUMBER: builtins.int
SESSIONKEY_FIELD_NUMBER: builtins.int
DEVICE_UI_FIELD_NUMBER: builtins.int
@property
def device(self) -> global___Config.DeviceConfig: ...
@property
@@ -1593,6 +1868,12 @@ class Config(google.protobuf.message.Message):
def lora(self) -> global___Config.LoRaConfig: ...
@property
def bluetooth(self) -> global___Config.BluetoothConfig: ...
@property
def security(self) -> global___Config.SecurityConfig: ...
@property
def sessionkey(self) -> global___Config.SessionkeyConfig: ...
@property
def device_ui(self) -> meshtastic.protobuf.device_ui_pb2.DeviceUIConfig: ...
def __init__(
self,
*,
@@ -1603,9 +1884,12 @@ class Config(google.protobuf.message.Message):
display: global___Config.DisplayConfig | None = ...,
lora: global___Config.LoRaConfig | None = ...,
bluetooth: global___Config.BluetoothConfig | None = ...,
security: global___Config.SecurityConfig | None = ...,
sessionkey: global___Config.SessionkeyConfig | None = ...,
device_ui: meshtastic.protobuf.device_ui_pb2.DeviceUIConfig | None = ...,
) -> None: ...
def HasField(self, field_name: typing.Literal["bluetooth", b"bluetooth", "device", b"device", "display", b"display", "lora", b"lora", "network", b"network", "payload_variant", b"payload_variant", "position", b"position", "power", b"power"]) -> builtins.bool: ...
def ClearField(self, field_name: typing.Literal["bluetooth", b"bluetooth", "device", b"device", "display", b"display", "lora", b"lora", "network", b"network", "payload_variant", b"payload_variant", "position", b"position", "power", b"power"]) -> None: ...
def WhichOneof(self, oneof_group: typing.Literal["payload_variant", b"payload_variant"]) -> typing.Literal["device", "position", "power", "network", "display", "lora", "bluetooth"] | None: ...
def HasField(self, field_name: typing.Literal["bluetooth", b"bluetooth", "device", b"device", "device_ui", b"device_ui", "display", b"display", "lora", b"lora", "network", b"network", "payload_variant", b"payload_variant", "position", b"position", "power", b"power", "security", b"security", "sessionkey", b"sessionkey"]) -> builtins.bool: ...
def ClearField(self, field_name: typing.Literal["bluetooth", b"bluetooth", "device", b"device", "device_ui", b"device_ui", "display", b"display", "lora", b"lora", "network", b"network", "payload_variant", b"payload_variant", "position", b"position", "power", b"power", "security", b"security", "sessionkey", b"sessionkey"]) -> None: ...
def WhichOneof(self, oneof_group: typing.Literal["payload_variant", b"payload_variant"]) -> typing.Literal["device", "position", "power", "network", "display", "lora", "bluetooth", "security", "sessionkey", "device_ui"] | None: ...
global___Config = Config

View File

@@ -13,14 +13,14 @@ _sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n+meshtastic/protobuf/connection_status.proto\x12\x13meshtastic.protobuf\"\xd5\x02\n\x16\x44\x65viceConnectionStatus\x12<\n\x04wifi\x18\x01 \x01(\x0b\x32).meshtastic.protobuf.WifiConnectionStatusH\x00\x88\x01\x01\x12\x44\n\x08\x65thernet\x18\x02 \x01(\x0b\x32-.meshtastic.protobuf.EthernetConnectionStatusH\x01\x88\x01\x01\x12\x46\n\tbluetooth\x18\x03 \x01(\x0b\x32..meshtastic.protobuf.BluetoothConnectionStatusH\x02\x88\x01\x01\x12@\n\x06serial\x18\x04 \x01(\x0b\x32+.meshtastic.protobuf.SerialConnectionStatusH\x03\x88\x01\x01\x42\x07\n\x05_wifiB\x0b\n\t_ethernetB\x0c\n\n_bluetoothB\t\n\x07_serial\"p\n\x14WifiConnectionStatus\x12<\n\x06status\x18\x01 \x01(\x0b\x32,.meshtastic.protobuf.NetworkConnectionStatus\x12\x0c\n\x04ssid\x18\x02 \x01(\t\x12\x0c\n\x04rssi\x18\x03 \x01(\x05\"X\n\x18\x45thernetConnectionStatus\x12<\n\x06status\x18\x01 \x01(\x0b\x32,.meshtastic.protobuf.NetworkConnectionStatus\"{\n\x17NetworkConnectionStatus\x12\x12\n\nip_address\x18\x01 \x01(\x07\x12\x14\n\x0cis_connected\x18\x02 \x01(\x08\x12\x19\n\x11is_mqtt_connected\x18\x03 \x01(\x08\x12\x1b\n\x13is_syslog_connected\x18\x04 \x01(\x08\"L\n\x19\x42luetoothConnectionStatus\x12\x0b\n\x03pin\x18\x01 \x01(\r\x12\x0c\n\x04rssi\x18\x02 \x01(\x05\x12\x14\n\x0cis_connected\x18\x03 \x01(\x08\"<\n\x16SerialConnectionStatus\x12\x0c\n\x04\x62\x61ud\x18\x01 \x01(\r\x12\x14\n\x0cis_connected\x18\x02 \x01(\x08\x42\x65\n\x13\x63om.geeksville.meshB\x10\x43onnStatusProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n+meshtastic/protobuf/connection_status.proto\x12\x13meshtastic.protobuf\"\xd5\x02\n\x16\x44\x65viceConnectionStatus\x12<\n\x04wifi\x18\x01 \x01(\x0b\x32).meshtastic.protobuf.WifiConnectionStatusH\x00\x88\x01\x01\x12\x44\n\x08\x65thernet\x18\x02 \x01(\x0b\x32-.meshtastic.protobuf.EthernetConnectionStatusH\x01\x88\x01\x01\x12\x46\n\tbluetooth\x18\x03 \x01(\x0b\x32..meshtastic.protobuf.BluetoothConnectionStatusH\x02\x88\x01\x01\x12@\n\x06serial\x18\x04 \x01(\x0b\x32+.meshtastic.protobuf.SerialConnectionStatusH\x03\x88\x01\x01\x42\x07\n\x05_wifiB\x0b\n\t_ethernetB\x0c\n\n_bluetoothB\t\n\x07_serial\"p\n\x14WifiConnectionStatus\x12<\n\x06status\x18\x01 \x01(\x0b\x32,.meshtastic.protobuf.NetworkConnectionStatus\x12\x0c\n\x04ssid\x18\x02 \x01(\t\x12\x0c\n\x04rssi\x18\x03 \x01(\x05\"X\n\x18\x45thernetConnectionStatus\x12<\n\x06status\x18\x01 \x01(\x0b\x32,.meshtastic.protobuf.NetworkConnectionStatus\"{\n\x17NetworkConnectionStatus\x12\x12\n\nip_address\x18\x01 \x01(\x07\x12\x14\n\x0cis_connected\x18\x02 \x01(\x08\x12\x19\n\x11is_mqtt_connected\x18\x03 \x01(\x08\x12\x1b\n\x13is_syslog_connected\x18\x04 \x01(\x08\"L\n\x19\x42luetoothConnectionStatus\x12\x0b\n\x03pin\x18\x01 \x01(\r\x12\x0c\n\x04rssi\x18\x02 \x01(\x05\x12\x14\n\x0cis_connected\x18\x03 \x01(\x08\"<\n\x16SerialConnectionStatus\x12\x0c\n\x04\x62\x61ud\x18\x01 \x01(\r\x12\x14\n\x0cis_connected\x18\x02 \x01(\x08\x42\x66\n\x14org.meshtastic.protoB\x10\x43onnStatusProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.connection_status_pb2', _globals)
if _descriptor._USE_C_DESCRIPTORS == False:
DESCRIPTOR._options = None
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\020ConnStatusProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\020ConnStatusProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
_globals['_DEVICECONNECTIONSTATUS']._serialized_start=69
_globals['_DEVICECONNECTIONSTATUS']._serialized_end=410
_globals['_WIFICONNECTIONSTATUS']._serialized_start=412

42
meshtastic/protobuf/device_ui_pb2.py generated Normal file
View File

@@ -0,0 +1,42 @@
# -*- coding: utf-8 -*-
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: meshtastic/protobuf/device_ui.proto
"""Generated protocol buffer code."""
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pool as _descriptor_pool
from google.protobuf import symbol_database as _symbol_database
from google.protobuf.internal import builder as _builder
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n#meshtastic/protobuf/device_ui.proto\x12\x13meshtastic.protobuf\"\xff\x05\n\x0e\x44\x65viceUIConfig\x12\x0f\n\x07version\x18\x01 \x01(\r\x12\x19\n\x11screen_brightness\x18\x02 \x01(\r\x12\x16\n\x0escreen_timeout\x18\x03 \x01(\r\x12\x13\n\x0bscreen_lock\x18\x04 \x01(\x08\x12\x15\n\rsettings_lock\x18\x05 \x01(\x08\x12\x10\n\x08pin_code\x18\x06 \x01(\r\x12)\n\x05theme\x18\x07 \x01(\x0e\x32\x1a.meshtastic.protobuf.Theme\x12\x15\n\ralert_enabled\x18\x08 \x01(\x08\x12\x16\n\x0e\x62\x61nner_enabled\x18\t \x01(\x08\x12\x14\n\x0cring_tone_id\x18\n \x01(\r\x12/\n\x08language\x18\x0b \x01(\x0e\x32\x1d.meshtastic.protobuf.Language\x12\x34\n\x0bnode_filter\x18\x0c \x01(\x0b\x32\x1f.meshtastic.protobuf.NodeFilter\x12:\n\x0enode_highlight\x18\r \x01(\x0b\x32\".meshtastic.protobuf.NodeHighlight\x12\x18\n\x10\x63\x61libration_data\x18\x0e \x01(\x0c\x12*\n\x08map_data\x18\x0f \x01(\x0b\x32\x18.meshtastic.protobuf.Map\x12\x36\n\x0c\x63ompass_mode\x18\x10 \x01(\x0e\x32 .meshtastic.protobuf.CompassMode\x12\x18\n\x10screen_rgb_color\x18\x11 \x01(\r\x12\x1b\n\x13is_clockface_analog\x18\x12 \x01(\x08\x12K\n\ngps_format\x18\x13 \x01(\x0e\x32\x37.meshtastic.protobuf.DeviceUIConfig.GpsCoordinateFormat\"V\n\x13GpsCoordinateFormat\x12\x07\n\x03\x44\x45\x43\x10\x00\x12\x07\n\x03\x44MS\x10\x01\x12\x07\n\x03UTM\x10\x02\x12\x08\n\x04MGRS\x10\x03\x12\x07\n\x03OLC\x10\x04\x12\x08\n\x04OSGR\x10\x05\x12\x07\n\x03MLS\x10\x06\"\xa7\x01\n\nNodeFilter\x12\x16\n\x0eunknown_switch\x18\x01 \x01(\x08\x12\x16\n\x0eoffline_switch\x18\x02 \x01(\x08\x12\x19\n\x11public_key_switch\x18\x03 \x01(\x08\x12\x11\n\thops_away\x18\x04 \x01(\x05\x12\x17\n\x0fposition_switch\x18\x05 \x01(\x08\x12\x11\n\tnode_name\x18\x06 \x01(\t\x12\x0f\n\x07\x63hannel\x18\x07 \x01(\x05\"~\n\rNodeHighlight\x12\x13\n\x0b\x63hat_switch\x18\x01 \x01(\x08\x12\x17\n\x0fposition_switch\x18\x02 \x01(\x08\x12\x18\n\x10telemetry_switch\x18\x03 \x01(\x08\x12\x12\n\niaq_switch\x18\x04 \x01(\x08\x12\x11\n\tnode_name\x18\x05 \x01(\t\"=\n\x08GeoPoint\x12\x0c\n\x04zoom\x18\x01 \x01(\x05\x12\x10\n\x08latitude\x18\x02 \x01(\x05\x12\x11\n\tlongitude\x18\x03 \x01(\x05\"U\n\x03Map\x12+\n\x04home\x18\x01 \x01(\x0b\x32\x1d.meshtastic.protobuf.GeoPoint\x12\r\n\x05style\x18\x02 \x01(\t\x12\x12\n\nfollow_gps\x18\x03 \x01(\x08*>\n\x0b\x43ompassMode\x12\x0b\n\x07\x44YNAMIC\x10\x00\x12\x0e\n\nFIXED_RING\x10\x01\x12\x12\n\x0e\x46REEZE_HEADING\x10\x02*%\n\x05Theme\x12\x08\n\x04\x44\x41RK\x10\x00\x12\t\n\x05LIGHT\x10\x01\x12\x07\n\x03RED\x10\x02*\xc0\x02\n\x08Language\x12\x0b\n\x07\x45NGLISH\x10\x00\x12\n\n\x06\x46RENCH\x10\x01\x12\n\n\x06GERMAN\x10\x02\x12\x0b\n\x07ITALIAN\x10\x03\x12\x0e\n\nPORTUGUESE\x10\x04\x12\x0b\n\x07SPANISH\x10\x05\x12\x0b\n\x07SWEDISH\x10\x06\x12\x0b\n\x07\x46INNISH\x10\x07\x12\n\n\x06POLISH\x10\x08\x12\x0b\n\x07TURKISH\x10\t\x12\x0b\n\x07SERBIAN\x10\n\x12\x0b\n\x07RUSSIAN\x10\x0b\x12\t\n\x05\x44UTCH\x10\x0c\x12\t\n\x05GREEK\x10\r\x12\r\n\tNORWEGIAN\x10\x0e\x12\r\n\tSLOVENIAN\x10\x0f\x12\r\n\tUKRAINIAN\x10\x10\x12\r\n\tBULGARIAN\x10\x11\x12\t\n\x05\x43ZECH\x10\x12\x12\n\n\x06\x44\x41NISH\x10\x13\x12\x16\n\x12SIMPLIFIED_CHINESE\x10\x1e\x12\x17\n\x13TRADITIONAL_CHINESE\x10\x1f\x42\x64\n\x14org.meshtastic.protoB\x0e\x44\x65viceUIProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.device_ui_pb2', _globals)
if _descriptor._USE_C_DESCRIPTORS == False:
DESCRIPTOR._options = None
DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\016DeviceUIProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
_globals['_COMPASSMODE']._serialized_start=1278
_globals['_COMPASSMODE']._serialized_end=1340
_globals['_THEME']._serialized_start=1342
_globals['_THEME']._serialized_end=1379
_globals['_LANGUAGE']._serialized_start=1382
_globals['_LANGUAGE']._serialized_end=1702
_globals['_DEVICEUICONFIG']._serialized_start=61
_globals['_DEVICEUICONFIG']._serialized_end=828
_globals['_DEVICEUICONFIG_GPSCOORDINATEFORMAT']._serialized_start=742
_globals['_DEVICEUICONFIG_GPSCOORDINATEFORMAT']._serialized_end=828
_globals['_NODEFILTER']._serialized_start=831
_globals['_NODEFILTER']._serialized_end=998
_globals['_NODEHIGHLIGHT']._serialized_start=1000
_globals['_NODEHIGHLIGHT']._serialized_end=1126
_globals['_GEOPOINT']._serialized_start=1128
_globals['_GEOPOINT']._serialized_end=1189
_globals['_MAP']._serialized_start=1191
_globals['_MAP']._serialized_end=1276
# @@protoc_insertion_point(module_scope)

649
meshtastic/protobuf/device_ui_pb2.pyi generated Normal file
View File

@@ -0,0 +1,649 @@
"""
@generated by mypy-protobuf. Do not edit manually!
isort:skip_file
"""
import builtins
import google.protobuf.descriptor
import google.protobuf.internal.enum_type_wrapper
import google.protobuf.message
import sys
import typing
if sys.version_info >= (3, 10):
import typing as typing_extensions
else:
import typing_extensions
DESCRIPTOR: google.protobuf.descriptor.FileDescriptor
class _CompassMode:
ValueType = typing.NewType("ValueType", builtins.int)
V: typing_extensions.TypeAlias = ValueType
class _CompassModeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_CompassMode.ValueType], builtins.type):
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
DYNAMIC: _CompassMode.ValueType # 0
"""
Compass with dynamic ring and heading
"""
FIXED_RING: _CompassMode.ValueType # 1
"""
Compass with fixed ring and heading
"""
FREEZE_HEADING: _CompassMode.ValueType # 2
"""
Compass with heading and freeze option
"""
class CompassMode(_CompassMode, metaclass=_CompassModeEnumTypeWrapper): ...
DYNAMIC: CompassMode.ValueType # 0
"""
Compass with dynamic ring and heading
"""
FIXED_RING: CompassMode.ValueType # 1
"""
Compass with fixed ring and heading
"""
FREEZE_HEADING: CompassMode.ValueType # 2
"""
Compass with heading and freeze option
"""
global___CompassMode = CompassMode
class _Theme:
ValueType = typing.NewType("ValueType", builtins.int)
V: typing_extensions.TypeAlias = ValueType
class _ThemeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_Theme.ValueType], builtins.type):
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
DARK: _Theme.ValueType # 0
"""
Dark
"""
LIGHT: _Theme.ValueType # 1
"""
Light
"""
RED: _Theme.ValueType # 2
"""
Red
"""
class Theme(_Theme, metaclass=_ThemeEnumTypeWrapper): ...
DARK: Theme.ValueType # 0
"""
Dark
"""
LIGHT: Theme.ValueType # 1
"""
Light
"""
RED: Theme.ValueType # 2
"""
Red
"""
global___Theme = Theme
class _Language:
ValueType = typing.NewType("ValueType", builtins.int)
V: typing_extensions.TypeAlias = ValueType
class _LanguageEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_Language.ValueType], builtins.type):
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
ENGLISH: _Language.ValueType # 0
"""
English
"""
FRENCH: _Language.ValueType # 1
"""
French
"""
GERMAN: _Language.ValueType # 2
"""
German
"""
ITALIAN: _Language.ValueType # 3
"""
Italian
"""
PORTUGUESE: _Language.ValueType # 4
"""
Portuguese
"""
SPANISH: _Language.ValueType # 5
"""
Spanish
"""
SWEDISH: _Language.ValueType # 6
"""
Swedish
"""
FINNISH: _Language.ValueType # 7
"""
Finnish
"""
POLISH: _Language.ValueType # 8
"""
Polish
"""
TURKISH: _Language.ValueType # 9
"""
Turkish
"""
SERBIAN: _Language.ValueType # 10
"""
Serbian
"""
RUSSIAN: _Language.ValueType # 11
"""
Russian
"""
DUTCH: _Language.ValueType # 12
"""
Dutch
"""
GREEK: _Language.ValueType # 13
"""
Greek
"""
NORWEGIAN: _Language.ValueType # 14
"""
Norwegian
"""
SLOVENIAN: _Language.ValueType # 15
"""
Slovenian
"""
UKRAINIAN: _Language.ValueType # 16
"""
Ukrainian
"""
BULGARIAN: _Language.ValueType # 17
"""
Bulgarian
"""
CZECH: _Language.ValueType # 18
"""
Czech
"""
DANISH: _Language.ValueType # 19
"""
Danish
"""
SIMPLIFIED_CHINESE: _Language.ValueType # 30
"""
Simplified Chinese (experimental)
"""
TRADITIONAL_CHINESE: _Language.ValueType # 31
"""
Traditional Chinese (experimental)
"""
class Language(_Language, metaclass=_LanguageEnumTypeWrapper):
"""
Localization
"""
ENGLISH: Language.ValueType # 0
"""
English
"""
FRENCH: Language.ValueType # 1
"""
French
"""
GERMAN: Language.ValueType # 2
"""
German
"""
ITALIAN: Language.ValueType # 3
"""
Italian
"""
PORTUGUESE: Language.ValueType # 4
"""
Portuguese
"""
SPANISH: Language.ValueType # 5
"""
Spanish
"""
SWEDISH: Language.ValueType # 6
"""
Swedish
"""
FINNISH: Language.ValueType # 7
"""
Finnish
"""
POLISH: Language.ValueType # 8
"""
Polish
"""
TURKISH: Language.ValueType # 9
"""
Turkish
"""
SERBIAN: Language.ValueType # 10
"""
Serbian
"""
RUSSIAN: Language.ValueType # 11
"""
Russian
"""
DUTCH: Language.ValueType # 12
"""
Dutch
"""
GREEK: Language.ValueType # 13
"""
Greek
"""
NORWEGIAN: Language.ValueType # 14
"""
Norwegian
"""
SLOVENIAN: Language.ValueType # 15
"""
Slovenian
"""
UKRAINIAN: Language.ValueType # 16
"""
Ukrainian
"""
BULGARIAN: Language.ValueType # 17
"""
Bulgarian
"""
CZECH: Language.ValueType # 18
"""
Czech
"""
DANISH: Language.ValueType # 19
"""
Danish
"""
SIMPLIFIED_CHINESE: Language.ValueType # 30
"""
Simplified Chinese (experimental)
"""
TRADITIONAL_CHINESE: Language.ValueType # 31
"""
Traditional Chinese (experimental)
"""
global___Language = Language
@typing.final
class DeviceUIConfig(google.protobuf.message.Message):
"""
Protobuf structures for device-ui persistency
"""
DESCRIPTOR: google.protobuf.descriptor.Descriptor
class _GpsCoordinateFormat:
ValueType = typing.NewType("ValueType", builtins.int)
V: typing_extensions.TypeAlias = ValueType
class _GpsCoordinateFormatEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[DeviceUIConfig._GpsCoordinateFormat.ValueType], builtins.type):
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
DEC: DeviceUIConfig._GpsCoordinateFormat.ValueType # 0
"""
GPS coordinates are displayed in the normal decimal degrees format:
DD.DDDDDD DDD.DDDDDD
"""
DMS: DeviceUIConfig._GpsCoordinateFormat.ValueType # 1
"""
GPS coordinates are displayed in the degrees minutes seconds format:
DD°MM'SS"C DDD°MM'SS"C, where C is the compass point representing the locations quadrant
"""
UTM: DeviceUIConfig._GpsCoordinateFormat.ValueType # 2
"""
Universal Transverse Mercator format:
ZZB EEEEEE NNNNNNN, where Z is zone, B is band, E is easting, N is northing
"""
MGRS: DeviceUIConfig._GpsCoordinateFormat.ValueType # 3
"""
Military Grid Reference System format:
ZZB CD EEEEE NNNNN, where Z is zone, B is band, C is the east 100k square, D is the north 100k square,
E is easting, N is northing
"""
OLC: DeviceUIConfig._GpsCoordinateFormat.ValueType # 4
"""
Open Location Code (aka Plus Codes).
"""
OSGR: DeviceUIConfig._GpsCoordinateFormat.ValueType # 5
"""
Ordnance Survey Grid Reference (the National Grid System of the UK).
Format: AB EEEEE NNNNN, where A is the east 100k square, B is the north 100k square,
E is the easting, N is the northing
"""
MLS: DeviceUIConfig._GpsCoordinateFormat.ValueType # 6
"""
Maidenhead Locator System
Described here: https://en.wikipedia.org/wiki/Maidenhead_Locator_System
"""
class GpsCoordinateFormat(_GpsCoordinateFormat, metaclass=_GpsCoordinateFormatEnumTypeWrapper):
"""
How the GPS coordinates are displayed on the OLED screen.
"""
DEC: DeviceUIConfig.GpsCoordinateFormat.ValueType # 0
"""
GPS coordinates are displayed in the normal decimal degrees format:
DD.DDDDDD DDD.DDDDDD
"""
DMS: DeviceUIConfig.GpsCoordinateFormat.ValueType # 1
"""
GPS coordinates are displayed in the degrees minutes seconds format:
DD°MM'SS"C DDD°MM'SS"C, where C is the compass point representing the locations quadrant
"""
UTM: DeviceUIConfig.GpsCoordinateFormat.ValueType # 2
"""
Universal Transverse Mercator format:
ZZB EEEEEE NNNNNNN, where Z is zone, B is band, E is easting, N is northing
"""
MGRS: DeviceUIConfig.GpsCoordinateFormat.ValueType # 3
"""
Military Grid Reference System format:
ZZB CD EEEEE NNNNN, where Z is zone, B is band, C is the east 100k square, D is the north 100k square,
E is easting, N is northing
"""
OLC: DeviceUIConfig.GpsCoordinateFormat.ValueType # 4
"""
Open Location Code (aka Plus Codes).
"""
OSGR: DeviceUIConfig.GpsCoordinateFormat.ValueType # 5
"""
Ordnance Survey Grid Reference (the National Grid System of the UK).
Format: AB EEEEE NNNNN, where A is the east 100k square, B is the north 100k square,
E is the easting, N is the northing
"""
MLS: DeviceUIConfig.GpsCoordinateFormat.ValueType # 6
"""
Maidenhead Locator System
Described here: https://en.wikipedia.org/wiki/Maidenhead_Locator_System
"""
VERSION_FIELD_NUMBER: builtins.int
SCREEN_BRIGHTNESS_FIELD_NUMBER: builtins.int
SCREEN_TIMEOUT_FIELD_NUMBER: builtins.int
SCREEN_LOCK_FIELD_NUMBER: builtins.int
SETTINGS_LOCK_FIELD_NUMBER: builtins.int
PIN_CODE_FIELD_NUMBER: builtins.int
THEME_FIELD_NUMBER: builtins.int
ALERT_ENABLED_FIELD_NUMBER: builtins.int
BANNER_ENABLED_FIELD_NUMBER: builtins.int
RING_TONE_ID_FIELD_NUMBER: builtins.int
LANGUAGE_FIELD_NUMBER: builtins.int
NODE_FILTER_FIELD_NUMBER: builtins.int
NODE_HIGHLIGHT_FIELD_NUMBER: builtins.int
CALIBRATION_DATA_FIELD_NUMBER: builtins.int
MAP_DATA_FIELD_NUMBER: builtins.int
COMPASS_MODE_FIELD_NUMBER: builtins.int
SCREEN_RGB_COLOR_FIELD_NUMBER: builtins.int
IS_CLOCKFACE_ANALOG_FIELD_NUMBER: builtins.int
GPS_FORMAT_FIELD_NUMBER: builtins.int
version: builtins.int
"""
A version integer used to invalidate saved files when we make incompatible changes.
"""
screen_brightness: builtins.int
"""
TFT display brightness 1..255
"""
screen_timeout: builtins.int
"""
Screen timeout 0..900
"""
screen_lock: builtins.bool
"""
Screen/Settings lock enabled
"""
settings_lock: builtins.bool
pin_code: builtins.int
theme: global___Theme.ValueType
"""
Color theme
"""
alert_enabled: builtins.bool
"""
Audible message, banner and ring tone
"""
banner_enabled: builtins.bool
ring_tone_id: builtins.int
language: global___Language.ValueType
"""
Localization
"""
calibration_data: builtins.bytes
"""
8 integers for screen calibration data
"""
compass_mode: global___CompassMode.ValueType
"""
Compass mode
"""
screen_rgb_color: builtins.int
"""
RGB color for BaseUI
0xRRGGBB format, e.g. 0xFF0000 for red
"""
is_clockface_analog: builtins.bool
"""
Clockface analog style
true for analog clockface, false for digital clockface
"""
gps_format: global___DeviceUIConfig.GpsCoordinateFormat.ValueType
"""
How the GPS coordinates are formatted on the OLED screen.
"""
@property
def node_filter(self) -> global___NodeFilter:
"""
Node list filter
"""
@property
def node_highlight(self) -> global___NodeHighlight:
"""
Node list highlightening
"""
@property
def map_data(self) -> global___Map:
"""
Map related data
"""
def __init__(
self,
*,
version: builtins.int = ...,
screen_brightness: builtins.int = ...,
screen_timeout: builtins.int = ...,
screen_lock: builtins.bool = ...,
settings_lock: builtins.bool = ...,
pin_code: builtins.int = ...,
theme: global___Theme.ValueType = ...,
alert_enabled: builtins.bool = ...,
banner_enabled: builtins.bool = ...,
ring_tone_id: builtins.int = ...,
language: global___Language.ValueType = ...,
node_filter: global___NodeFilter | None = ...,
node_highlight: global___NodeHighlight | None = ...,
calibration_data: builtins.bytes = ...,
map_data: global___Map | None = ...,
compass_mode: global___CompassMode.ValueType = ...,
screen_rgb_color: builtins.int = ...,
is_clockface_analog: builtins.bool = ...,
gps_format: global___DeviceUIConfig.GpsCoordinateFormat.ValueType = ...,
) -> None: ...
def HasField(self, field_name: typing.Literal["map_data", b"map_data", "node_filter", b"node_filter", "node_highlight", b"node_highlight"]) -> builtins.bool: ...
def ClearField(self, field_name: typing.Literal["alert_enabled", b"alert_enabled", "banner_enabled", b"banner_enabled", "calibration_data", b"calibration_data", "compass_mode", b"compass_mode", "gps_format", b"gps_format", "is_clockface_analog", b"is_clockface_analog", "language", b"language", "map_data", b"map_data", "node_filter", b"node_filter", "node_highlight", b"node_highlight", "pin_code", b"pin_code", "ring_tone_id", b"ring_tone_id", "screen_brightness", b"screen_brightness", "screen_lock", b"screen_lock", "screen_rgb_color", b"screen_rgb_color", "screen_timeout", b"screen_timeout", "settings_lock", b"settings_lock", "theme", b"theme", "version", b"version"]) -> None: ...
global___DeviceUIConfig = DeviceUIConfig
@typing.final
class NodeFilter(google.protobuf.message.Message):
DESCRIPTOR: google.protobuf.descriptor.Descriptor
UNKNOWN_SWITCH_FIELD_NUMBER: builtins.int
OFFLINE_SWITCH_FIELD_NUMBER: builtins.int
PUBLIC_KEY_SWITCH_FIELD_NUMBER: builtins.int
HOPS_AWAY_FIELD_NUMBER: builtins.int
POSITION_SWITCH_FIELD_NUMBER: builtins.int
NODE_NAME_FIELD_NUMBER: builtins.int
CHANNEL_FIELD_NUMBER: builtins.int
unknown_switch: builtins.bool
"""
Filter unknown nodes
"""
offline_switch: builtins.bool
"""
Filter offline nodes
"""
public_key_switch: builtins.bool
"""
Filter nodes w/o public key
"""
hops_away: builtins.int
"""
Filter based on hops away
"""
position_switch: builtins.bool
"""
Filter nodes w/o position
"""
node_name: builtins.str
"""
Filter nodes by matching name string
"""
channel: builtins.int
"""
Filter based on channel
"""
def __init__(
self,
*,
unknown_switch: builtins.bool = ...,
offline_switch: builtins.bool = ...,
public_key_switch: builtins.bool = ...,
hops_away: builtins.int = ...,
position_switch: builtins.bool = ...,
node_name: builtins.str = ...,
channel: builtins.int = ...,
) -> None: ...
def ClearField(self, field_name: typing.Literal["channel", b"channel", "hops_away", b"hops_away", "node_name", b"node_name", "offline_switch", b"offline_switch", "position_switch", b"position_switch", "public_key_switch", b"public_key_switch", "unknown_switch", b"unknown_switch"]) -> None: ...
global___NodeFilter = NodeFilter
@typing.final
class NodeHighlight(google.protobuf.message.Message):
DESCRIPTOR: google.protobuf.descriptor.Descriptor
CHAT_SWITCH_FIELD_NUMBER: builtins.int
POSITION_SWITCH_FIELD_NUMBER: builtins.int
TELEMETRY_SWITCH_FIELD_NUMBER: builtins.int
IAQ_SWITCH_FIELD_NUMBER: builtins.int
NODE_NAME_FIELD_NUMBER: builtins.int
chat_switch: builtins.bool
"""
Hightlight nodes w/ active chat
"""
position_switch: builtins.bool
"""
Highlight nodes w/ position
"""
telemetry_switch: builtins.bool
"""
Highlight nodes w/ telemetry data
"""
iaq_switch: builtins.bool
"""
Highlight nodes w/ iaq data
"""
node_name: builtins.str
"""
Highlight nodes by matching name string
"""
def __init__(
self,
*,
chat_switch: builtins.bool = ...,
position_switch: builtins.bool = ...,
telemetry_switch: builtins.bool = ...,
iaq_switch: builtins.bool = ...,
node_name: builtins.str = ...,
) -> None: ...
def ClearField(self, field_name: typing.Literal["chat_switch", b"chat_switch", "iaq_switch", b"iaq_switch", "node_name", b"node_name", "position_switch", b"position_switch", "telemetry_switch", b"telemetry_switch"]) -> None: ...
global___NodeHighlight = NodeHighlight
@typing.final
class GeoPoint(google.protobuf.message.Message):
DESCRIPTOR: google.protobuf.descriptor.Descriptor
ZOOM_FIELD_NUMBER: builtins.int
LATITUDE_FIELD_NUMBER: builtins.int
LONGITUDE_FIELD_NUMBER: builtins.int
zoom: builtins.int
"""
Zoom level
"""
latitude: builtins.int
"""
Coordinate: latitude
"""
longitude: builtins.int
"""
Coordinate: longitude
"""
def __init__(
self,
*,
zoom: builtins.int = ...,
latitude: builtins.int = ...,
longitude: builtins.int = ...,
) -> None: ...
def ClearField(self, field_name: typing.Literal["latitude", b"latitude", "longitude", b"longitude", "zoom", b"zoom"]) -> None: ...
global___GeoPoint = GeoPoint
@typing.final
class Map(google.protobuf.message.Message):
DESCRIPTOR: google.protobuf.descriptor.Descriptor
HOME_FIELD_NUMBER: builtins.int
STYLE_FIELD_NUMBER: builtins.int
FOLLOW_GPS_FIELD_NUMBER: builtins.int
style: builtins.str
"""
Map tile style
"""
follow_gps: builtins.bool
"""
Map scroll follows GPS
"""
@property
def home(self) -> global___GeoPoint:
"""
Home coordinates
"""
def __init__(
self,
*,
home: global___GeoPoint | None = ...,
style: builtins.str = ...,
follow_gps: builtins.bool = ...,
) -> None: ...
def HasField(self, field_name: typing.Literal["home", b"home"]) -> builtins.bool: ...
def ClearField(self, field_name: typing.Literal["follow_gps", b"follow_gps", "home", b"home", "style", b"style"]) -> None: ...
global___Map = Map

View File

@@ -12,34 +12,41 @@ _sym_db = _symbol_database.Default()
from meshtastic.protobuf import channel_pb2 as meshtastic_dot_protobuf_dot_channel__pb2
from meshtastic.protobuf import config_pb2 as meshtastic_dot_protobuf_dot_config__pb2
from meshtastic.protobuf import localonly_pb2 as meshtastic_dot_protobuf_dot_localonly__pb2
from meshtastic.protobuf import mesh_pb2 as meshtastic_dot_protobuf_dot_mesh__pb2
from meshtastic.protobuf import telemetry_pb2 as meshtastic_dot_protobuf_dot_telemetry__pb2
import nanopb_pb2 as nanopb__pb2
from meshtastic.protobuf import nanopb_pb2 as meshtastic_dot_protobuf_dot_nanopb__pb2
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n$meshtastic/protobuf/deviceonly.proto\x12\x13meshtastic.protobuf\x1a!meshtastic/protobuf/channel.proto\x1a#meshtastic/protobuf/localonly.proto\x1a\x1emeshtastic/protobuf/mesh.proto\x1a#meshtastic/protobuf/telemetry.proto\x1a\x0cnanopb.proto\"\x99\x01\n\x0cPositionLite\x12\x12\n\nlatitude_i\x18\x01 \x01(\x0f\x12\x13\n\x0blongitude_i\x18\x02 \x01(\x0f\x12\x10\n\x08\x61ltitude\x18\x03 \x01(\x05\x12\x0c\n\x04time\x18\x04 \x01(\x07\x12@\n\x0flocation_source\x18\x05 \x01(\x0e\x32\'.meshtastic.protobuf.Position.LocSource\"\xa1\x02\n\x0cNodeInfoLite\x12\x0b\n\x03num\x18\x01 \x01(\r\x12\'\n\x04user\x18\x02 \x01(\x0b\x32\x19.meshtastic.protobuf.User\x12\x33\n\x08position\x18\x03 \x01(\x0b\x32!.meshtastic.protobuf.PositionLite\x12\x0b\n\x03snr\x18\x04 \x01(\x02\x12\x12\n\nlast_heard\x18\x05 \x01(\x07\x12:\n\x0e\x64\x65vice_metrics\x18\x06 \x01(\x0b\x32\".meshtastic.protobuf.DeviceMetrics\x12\x0f\n\x07\x63hannel\x18\x07 \x01(\r\x12\x10\n\x08via_mqtt\x18\x08 \x01(\x08\x12\x11\n\thops_away\x18\t \x01(\r\x12\x13\n\x0bis_favorite\x18\n \x01(\x08\"\x82\x04\n\x0b\x44\x65viceState\x12\x30\n\x07my_node\x18\x02 \x01(\x0b\x32\x1f.meshtastic.protobuf.MyNodeInfo\x12(\n\x05owner\x18\x03 \x01(\x0b\x32\x19.meshtastic.protobuf.User\x12\x36\n\rreceive_queue\x18\x05 \x03(\x0b\x32\x1f.meshtastic.protobuf.MeshPacket\x12\x0f\n\x07version\x18\x08 \x01(\r\x12\x38\n\x0frx_text_message\x18\x07 \x01(\x0b\x32\x1f.meshtastic.protobuf.MeshPacket\x12\x13\n\x07no_save\x18\t \x01(\x08\x42\x02\x18\x01\x12\x15\n\rdid_gps_reset\x18\x0b \x01(\x08\x12\x34\n\x0brx_waypoint\x18\x0c \x01(\x0b\x32\x1f.meshtastic.protobuf.MeshPacket\x12M\n\x19node_remote_hardware_pins\x18\r \x03(\x0b\x32*.meshtastic.protobuf.NodeRemoteHardwarePin\x12\x63\n\x0cnode_db_lite\x18\x0e \x03(\x0b\x32!.meshtastic.protobuf.NodeInfoLiteB*\x92?\'\x92\x01$std::vector<meshtastic_NodeInfoLite>\"N\n\x0b\x43hannelFile\x12.\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x1c.meshtastic.protobuf.Channel\x12\x0f\n\x07version\x18\x02 \x01(\r\"\xb2\x02\n\x08OEMStore\x12\x16\n\x0eoem_icon_width\x18\x01 \x01(\r\x12\x17\n\x0foem_icon_height\x18\x02 \x01(\r\x12\x15\n\roem_icon_bits\x18\x03 \x01(\x0c\x12\x32\n\x08oem_font\x18\x04 \x01(\x0e\x32 .meshtastic.protobuf.ScreenFonts\x12\x10\n\x08oem_text\x18\x05 \x01(\t\x12\x13\n\x0boem_aes_key\x18\x06 \x01(\x0c\x12:\n\x10oem_local_config\x18\x07 \x01(\x0b\x32 .meshtastic.protobuf.LocalConfig\x12G\n\x17oem_local_module_config\x18\x08 \x01(\x0b\x32&.meshtastic.protobuf.LocalModuleConfig*>\n\x0bScreenFonts\x12\x0e\n\nFONT_SMALL\x10\x00\x12\x0f\n\x0b\x46ONT_MEDIUM\x10\x01\x12\x0e\n\nFONT_LARGE\x10\x02\x42m\n\x13\x63om.geeksville.meshB\nDeviceOnlyZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x92?\x0b\xc2\x01\x08<vector>b\x06proto3')
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n$meshtastic/protobuf/deviceonly.proto\x12\x13meshtastic.protobuf\x1a!meshtastic/protobuf/channel.proto\x1a meshtastic/protobuf/config.proto\x1a#meshtastic/protobuf/localonly.proto\x1a\x1emeshtastic/protobuf/mesh.proto\x1a#meshtastic/protobuf/telemetry.proto\x1a meshtastic/protobuf/nanopb.proto\"\x99\x01\n\x0cPositionLite\x12\x12\n\nlatitude_i\x18\x01 \x01(\x0f\x12\x13\n\x0blongitude_i\x18\x02 \x01(\x0f\x12\x10\n\x08\x61ltitude\x18\x03 \x01(\x05\x12\x0c\n\x04time\x18\x04 \x01(\x07\x12@\n\x0flocation_source\x18\x05 \x01(\x0e\x32\'.meshtastic.protobuf.Position.LocSource\"\x94\x02\n\x08UserLite\x12\x13\n\x07macaddr\x18\x01 \x01(\x0c\x42\x02\x18\x01\x12\x11\n\tlong_name\x18\x02 \x01(\t\x12\x12\n\nshort_name\x18\x03 \x01(\t\x12\x34\n\x08hw_model\x18\x04 \x01(\x0e\x32\".meshtastic.protobuf.HardwareModel\x12\x13\n\x0bis_licensed\x18\x05 \x01(\x08\x12;\n\x04role\x18\x06 \x01(\x0e\x32-.meshtastic.protobuf.Config.DeviceConfig.Role\x12\x12\n\npublic_key\x18\x07 \x01(\x0c\x12\x1c\n\x0fis_unmessagable\x18\t \x01(\x08H\x00\x88\x01\x01\x42\x12\n\x10_is_unmessagable\"\xf0\x02\n\x0cNodeInfoLite\x12\x0b\n\x03num\x18\x01 \x01(\r\x12+\n\x04user\x18\x02 \x01(\x0b\x32\x1d.meshtastic.protobuf.UserLite\x12\x33\n\x08position\x18\x03 \x01(\x0b\x32!.meshtastic.protobuf.PositionLite\x12\x0b\n\x03snr\x18\x04 \x01(\x02\x12\x12\n\nlast_heard\x18\x05 \x01(\x07\x12:\n\x0e\x64\x65vice_metrics\x18\x06 \x01(\x0b\x32\".meshtastic.protobuf.DeviceMetrics\x12\x0f\n\x07\x63hannel\x18\x07 \x01(\r\x12\x10\n\x08via_mqtt\x18\x08 \x01(\x08\x12\x16\n\thops_away\x18\t \x01(\rH\x00\x88\x01\x01\x12\x13\n\x0bis_favorite\x18\n \x01(\x08\x12\x12\n\nis_ignored\x18\x0b \x01(\x08\x12\x10\n\x08next_hop\x18\x0c \x01(\r\x12\x10\n\x08\x62itfield\x18\r \x01(\rB\x0c\n\n_hops_away\"\xa1\x03\n\x0b\x44\x65viceState\x12\x30\n\x07my_node\x18\x02 \x01(\x0b\x32\x1f.meshtastic.protobuf.MyNodeInfo\x12(\n\x05owner\x18\x03 \x01(\x0b\x32\x19.meshtastic.protobuf.User\x12\x36\n\rreceive_queue\x18\x05 \x03(\x0b\x32\x1f.meshtastic.protobuf.MeshPacket\x12\x0f\n\x07version\x18\x08 \x01(\r\x12\x38\n\x0frx_text_message\x18\x07 \x01(\x0b\x32\x1f.meshtastic.protobuf.MeshPacket\x12\x13\n\x07no_save\x18\t \x01(\x08\x42\x02\x18\x01\x12\x19\n\rdid_gps_reset\x18\x0b \x01(\x08\x42\x02\x18\x01\x12\x34\n\x0brx_waypoint\x18\x0c \x01(\x0b\x32\x1f.meshtastic.protobuf.MeshPacket\x12M\n\x19node_remote_hardware_pins\x18\r \x03(\x0b\x32*.meshtastic.protobuf.NodeRemoteHardwarePin\"}\n\x0cNodeDatabase\x12\x0f\n\x07version\x18\x01 \x01(\r\x12\\\n\x05nodes\x18\x02 \x03(\x0b\x32!.meshtastic.protobuf.NodeInfoLiteB*\x92?\'\x92\x01$std::vector<meshtastic_NodeInfoLite>\"N\n\x0b\x43hannelFile\x12.\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x1c.meshtastic.protobuf.Channel\x12\x0f\n\x07version\x18\x02 \x01(\r\"\x86\x02\n\x11\x42\x61\x63kupPreferences\x12\x0f\n\x07version\x18\x01 \x01(\r\x12\x11\n\ttimestamp\x18\x02 \x01(\x07\x12\x30\n\x06\x63onfig\x18\x03 \x01(\x0b\x32 .meshtastic.protobuf.LocalConfig\x12=\n\rmodule_config\x18\x04 \x01(\x0b\x32&.meshtastic.protobuf.LocalModuleConfig\x12\x32\n\x08\x63hannels\x18\x05 \x01(\x0b\x32 .meshtastic.protobuf.ChannelFile\x12(\n\x05owner\x18\x06 \x01(\x0b\x32\x19.meshtastic.protobuf.UserBn\n\x14org.meshtastic.protoB\nDeviceOnlyZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x92?\x0b\xc2\x01\x08<vector>b\x06proto3')
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.deviceonly_pb2', _globals)
if _descriptor._USE_C_DESCRIPTORS == False:
DESCRIPTOR._options = None
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\nDeviceOnlyZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000\222?\013\302\001\010<vector>'
DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\nDeviceOnlyZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000\222?\013\302\001\010<vector>'
_USERLITE.fields_by_name['macaddr']._options = None
_USERLITE.fields_by_name['macaddr']._serialized_options = b'\030\001'
_DEVICESTATE.fields_by_name['no_save']._options = None
_DEVICESTATE.fields_by_name['no_save']._serialized_options = b'\030\001'
_DEVICESTATE.fields_by_name['node_db_lite']._options = None
_DEVICESTATE.fields_by_name['node_db_lite']._serialized_options = b'\222?\'\222\001$std::vector<meshtastic_NodeInfoLite>'
_globals['_SCREENFONTS']._serialized_start=1570
_globals['_SCREENFONTS']._serialized_end=1632
_globals['_POSITIONLITE']._serialized_start=217
_globals['_POSITIONLITE']._serialized_end=370
_globals['_NODEINFOLITE']._serialized_start=373
_globals['_NODEINFOLITE']._serialized_end=662
_globals['_DEVICESTATE']._serialized_start=665
_globals['_DEVICESTATE']._serialized_end=1179
_globals['_CHANNELFILE']._serialized_start=1181
_globals['_CHANNELFILE']._serialized_end=1259
_globals['_OEMSTORE']._serialized_start=1262
_globals['_OEMSTORE']._serialized_end=1568
_DEVICESTATE.fields_by_name['did_gps_reset']._options = None
_DEVICESTATE.fields_by_name['did_gps_reset']._serialized_options = b'\030\001'
_NODEDATABASE.fields_by_name['nodes']._options = None
_NODEDATABASE.fields_by_name['nodes']._serialized_options = b'\222?\'\222\001$std::vector<meshtastic_NodeInfoLite>'
_globals['_POSITIONLITE']._serialized_start=271
_globals['_POSITIONLITE']._serialized_end=424
_globals['_USERLITE']._serialized_start=427
_globals['_USERLITE']._serialized_end=703
_globals['_NODEINFOLITE']._serialized_start=706
_globals['_NODEINFOLITE']._serialized_end=1074
_globals['_DEVICESTATE']._serialized_start=1077
_globals['_DEVICESTATE']._serialized_end=1494
_globals['_NODEDATABASE']._serialized_start=1496
_globals['_NODEDATABASE']._serialized_end=1621
_globals['_CHANNELFILE']._serialized_start=1623
_globals['_CHANNELFILE']._serialized_end=1701
_globals['_BACKUPPREFERENCES']._serialized_start=1704
_globals['_BACKUPPREFERENCES']._serialized_end=1966
# @@protoc_insertion_point(module_scope)

View File

@@ -7,60 +7,16 @@ import builtins
import collections.abc
import google.protobuf.descriptor
import google.protobuf.internal.containers
import google.protobuf.internal.enum_type_wrapper
import google.protobuf.message
import meshtastic.protobuf.channel_pb2
import meshtastic.protobuf.config_pb2
import meshtastic.protobuf.localonly_pb2
import meshtastic.protobuf.mesh_pb2
import meshtastic.protobuf.telemetry_pb2
import sys
import typing
if sys.version_info >= (3, 10):
import typing as typing_extensions
else:
import typing_extensions
DESCRIPTOR: google.protobuf.descriptor.FileDescriptor
class _ScreenFonts:
ValueType = typing.NewType("ValueType", builtins.int)
V: typing_extensions.TypeAlias = ValueType
class _ScreenFontsEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_ScreenFonts.ValueType], builtins.type):
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
FONT_SMALL: _ScreenFonts.ValueType # 0
"""
TODO: REPLACE
"""
FONT_MEDIUM: _ScreenFonts.ValueType # 1
"""
TODO: REPLACE
"""
FONT_LARGE: _ScreenFonts.ValueType # 2
"""
TODO: REPLACE
"""
class ScreenFonts(_ScreenFonts, metaclass=_ScreenFontsEnumTypeWrapper):
"""
Font sizes for the device screen
"""
FONT_SMALL: ScreenFonts.ValueType # 0
"""
TODO: REPLACE
"""
FONT_MEDIUM: ScreenFonts.ValueType # 1
"""
TODO: REPLACE
"""
FONT_LARGE: ScreenFonts.ValueType # 2
"""
TODO: REPLACE
"""
global___ScreenFonts = ScreenFonts
@typing.final
class PositionLite(google.protobuf.message.Message):
"""
@@ -112,6 +68,75 @@ class PositionLite(google.protobuf.message.Message):
global___PositionLite = PositionLite
@typing.final
class UserLite(google.protobuf.message.Message):
DESCRIPTOR: google.protobuf.descriptor.Descriptor
MACADDR_FIELD_NUMBER: builtins.int
LONG_NAME_FIELD_NUMBER: builtins.int
SHORT_NAME_FIELD_NUMBER: builtins.int
HW_MODEL_FIELD_NUMBER: builtins.int
IS_LICENSED_FIELD_NUMBER: builtins.int
ROLE_FIELD_NUMBER: builtins.int
PUBLIC_KEY_FIELD_NUMBER: builtins.int
IS_UNMESSAGABLE_FIELD_NUMBER: builtins.int
macaddr: builtins.bytes
"""
This is the addr of the radio.
"""
long_name: builtins.str
"""
A full name for this user, i.e. "Kevin Hester"
"""
short_name: builtins.str
"""
A VERY short name, ideally two characters.
Suitable for a tiny OLED screen
"""
hw_model: meshtastic.protobuf.mesh_pb2.HardwareModel.ValueType
"""
TBEAM, HELTEC, etc...
Starting in 1.2.11 moved to hw_model enum in the NodeInfo object.
Apps will still need the string here for older builds
(so OTA update can find the right image), but if the enum is available it will be used instead.
"""
is_licensed: builtins.bool
"""
In some regions Ham radio operators have different bandwidth limitations than others.
If this user is a licensed operator, set this flag.
Also, "long_name" should be their licence number.
"""
role: meshtastic.protobuf.config_pb2.Config.DeviceConfig.Role.ValueType
"""
Indicates that the user's role in the mesh
"""
public_key: builtins.bytes
"""
The public key of the user's device.
This is sent out to other nodes on the mesh to allow them to compute a shared secret key.
"""
is_unmessagable: builtins.bool
"""
Whether or not the node can be messaged
"""
def __init__(
self,
*,
macaddr: builtins.bytes = ...,
long_name: builtins.str = ...,
short_name: builtins.str = ...,
hw_model: meshtastic.protobuf.mesh_pb2.HardwareModel.ValueType = ...,
is_licensed: builtins.bool = ...,
role: meshtastic.protobuf.config_pb2.Config.DeviceConfig.Role.ValueType = ...,
public_key: builtins.bytes = ...,
is_unmessagable: builtins.bool | None = ...,
) -> None: ...
def HasField(self, field_name: typing.Literal["_is_unmessagable", b"_is_unmessagable", "is_unmessagable", b"is_unmessagable"]) -> builtins.bool: ...
def ClearField(self, field_name: typing.Literal["_is_unmessagable", b"_is_unmessagable", "hw_model", b"hw_model", "is_licensed", b"is_licensed", "is_unmessagable", b"is_unmessagable", "long_name", b"long_name", "macaddr", b"macaddr", "public_key", b"public_key", "role", b"role", "short_name", b"short_name"]) -> None: ...
def WhichOneof(self, oneof_group: typing.Literal["_is_unmessagable", b"_is_unmessagable"]) -> typing.Literal["is_unmessagable"] | None: ...
global___UserLite = UserLite
@typing.final
class NodeInfoLite(google.protobuf.message.Message):
DESCRIPTOR: google.protobuf.descriptor.Descriptor
@@ -126,6 +151,9 @@ class NodeInfoLite(google.protobuf.message.Message):
VIA_MQTT_FIELD_NUMBER: builtins.int
HOPS_AWAY_FIELD_NUMBER: builtins.int
IS_FAVORITE_FIELD_NUMBER: builtins.int
IS_IGNORED_FIELD_NUMBER: builtins.int
NEXT_HOP_FIELD_NUMBER: builtins.int
BITFIELD_FIELD_NUMBER: builtins.int
num: builtins.int
"""
The node number
@@ -149,15 +177,29 @@ class NodeInfoLite(google.protobuf.message.Message):
"""
hops_away: builtins.int
"""
Number of hops away from us this node is (0 if adjacent)
Number of hops away from us this node is (0 if direct neighbor)
"""
is_favorite: builtins.bool
"""
True if node is in our favorites list
Persists between NodeDB internal clean ups
"""
is_ignored: builtins.bool
"""
True if node is in our ignored list
Persists between NodeDB internal clean ups
"""
next_hop: builtins.int
"""
Last byte of the node number of the node that should be used as the next hop to reach this node.
"""
bitfield: builtins.int
"""
Bitfield for storing booleans.
LSB 0 is_key_manually_verified
"""
@property
def user(self) -> meshtastic.protobuf.mesh_pb2.User:
def user(self) -> global___UserLite:
"""
The user info for this node
"""
@@ -179,18 +221,22 @@ class NodeInfoLite(google.protobuf.message.Message):
self,
*,
num: builtins.int = ...,
user: meshtastic.protobuf.mesh_pb2.User | None = ...,
user: global___UserLite | None = ...,
position: global___PositionLite | None = ...,
snr: builtins.float = ...,
last_heard: builtins.int = ...,
device_metrics: meshtastic.protobuf.telemetry_pb2.DeviceMetrics | None = ...,
channel: builtins.int = ...,
via_mqtt: builtins.bool = ...,
hops_away: builtins.int = ...,
hops_away: builtins.int | None = ...,
is_favorite: builtins.bool = ...,
is_ignored: builtins.bool = ...,
next_hop: builtins.int = ...,
bitfield: builtins.int = ...,
) -> None: ...
def HasField(self, field_name: typing.Literal["device_metrics", b"device_metrics", "position", b"position", "user", b"user"]) -> builtins.bool: ...
def ClearField(self, field_name: typing.Literal["channel", b"channel", "device_metrics", b"device_metrics", "hops_away", b"hops_away", "is_favorite", b"is_favorite", "last_heard", b"last_heard", "num", b"num", "position", b"position", "snr", b"snr", "user", b"user", "via_mqtt", b"via_mqtt"]) -> None: ...
def HasField(self, field_name: typing.Literal["_hops_away", b"_hops_away", "device_metrics", b"device_metrics", "hops_away", b"hops_away", "position", b"position", "user", b"user"]) -> builtins.bool: ...
def ClearField(self, field_name: typing.Literal["_hops_away", b"_hops_away", "bitfield", b"bitfield", "channel", b"channel", "device_metrics", b"device_metrics", "hops_away", b"hops_away", "is_favorite", b"is_favorite", "is_ignored", b"is_ignored", "last_heard", b"last_heard", "next_hop", b"next_hop", "num", b"num", "position", b"position", "snr", b"snr", "user", b"user", "via_mqtt", b"via_mqtt"]) -> None: ...
def WhichOneof(self, oneof_group: typing.Literal["_hops_away", b"_hops_away"]) -> typing.Literal["hops_away"] | None: ...
global___NodeInfoLite = NodeInfoLite
@@ -215,7 +261,6 @@ class DeviceState(google.protobuf.message.Message):
DID_GPS_RESET_FIELD_NUMBER: builtins.int
RX_WAYPOINT_FIELD_NUMBER: builtins.int
NODE_REMOTE_HARDWARE_PINS_FIELD_NUMBER: builtins.int
NODE_DB_LITE_FIELD_NUMBER: builtins.int
version: builtins.int
"""
A version integer used to invalidate old save files when we make
@@ -230,7 +275,8 @@ class DeviceState(google.protobuf.message.Message):
"""
did_gps_reset: builtins.bool
"""
Some GPS receivers seem to have bogus settings from the factory, so we always do one factory reset.
Previously used to manage GPS factory resets.
Deprecated in 2.5.23
"""
@property
def my_node(self) -> meshtastic.protobuf.mesh_pb2.MyNodeInfo:
@@ -272,12 +318,6 @@ class DeviceState(google.protobuf.message.Message):
The mesh's nodes with their available gpio pins for RemoteHardware module
"""
@property
def node_db_lite(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___NodeInfoLite]:
"""
New lite version of NodeDB to decrease memory footprint
"""
def __init__(
self,
*,
@@ -290,13 +330,40 @@ class DeviceState(google.protobuf.message.Message):
did_gps_reset: builtins.bool = ...,
rx_waypoint: meshtastic.protobuf.mesh_pb2.MeshPacket | None = ...,
node_remote_hardware_pins: collections.abc.Iterable[meshtastic.protobuf.mesh_pb2.NodeRemoteHardwarePin] | None = ...,
node_db_lite: collections.abc.Iterable[global___NodeInfoLite] | None = ...,
) -> None: ...
def HasField(self, field_name: typing.Literal["my_node", b"my_node", "owner", b"owner", "rx_text_message", b"rx_text_message", "rx_waypoint", b"rx_waypoint"]) -> builtins.bool: ...
def ClearField(self, field_name: typing.Literal["did_gps_reset", b"did_gps_reset", "my_node", b"my_node", "no_save", b"no_save", "node_db_lite", b"node_db_lite", "node_remote_hardware_pins", b"node_remote_hardware_pins", "owner", b"owner", "receive_queue", b"receive_queue", "rx_text_message", b"rx_text_message", "rx_waypoint", b"rx_waypoint", "version", b"version"]) -> None: ...
def ClearField(self, field_name: typing.Literal["did_gps_reset", b"did_gps_reset", "my_node", b"my_node", "no_save", b"no_save", "node_remote_hardware_pins", b"node_remote_hardware_pins", "owner", b"owner", "receive_queue", b"receive_queue", "rx_text_message", b"rx_text_message", "rx_waypoint", b"rx_waypoint", "version", b"version"]) -> None: ...
global___DeviceState = DeviceState
@typing.final
class NodeDatabase(google.protobuf.message.Message):
DESCRIPTOR: google.protobuf.descriptor.Descriptor
VERSION_FIELD_NUMBER: builtins.int
NODES_FIELD_NUMBER: builtins.int
version: builtins.int
"""
A version integer used to invalidate old save files when we make
incompatible changes This integer is set at build time and is private to
NodeDB.cpp in the device code.
"""
@property
def nodes(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___NodeInfoLite]:
"""
New lite version of NodeDB to decrease memory footprint
"""
def __init__(
self,
*,
version: builtins.int = ...,
nodes: collections.abc.Iterable[global___NodeInfoLite] | None = ...,
) -> None: ...
def ClearField(self, field_name: typing.Literal["nodes", b"nodes", "version", b"version"]) -> None: ...
global___NodeDatabase = NodeDatabase
@typing.final
class ChannelFile(google.protobuf.message.Message):
"""
@@ -330,71 +397,62 @@ class ChannelFile(google.protobuf.message.Message):
global___ChannelFile = ChannelFile
@typing.final
class OEMStore(google.protobuf.message.Message):
class BackupPreferences(google.protobuf.message.Message):
"""
This can be used for customizing the firmware distribution. If populated,
show a secondary bootup screen with custom logo and text for 2.5 seconds.
The on-disk backup of the node's preferences
"""
DESCRIPTOR: google.protobuf.descriptor.Descriptor
OEM_ICON_WIDTH_FIELD_NUMBER: builtins.int
OEM_ICON_HEIGHT_FIELD_NUMBER: builtins.int
OEM_ICON_BITS_FIELD_NUMBER: builtins.int
OEM_FONT_FIELD_NUMBER: builtins.int
OEM_TEXT_FIELD_NUMBER: builtins.int
OEM_AES_KEY_FIELD_NUMBER: builtins.int
OEM_LOCAL_CONFIG_FIELD_NUMBER: builtins.int
OEM_LOCAL_MODULE_CONFIG_FIELD_NUMBER: builtins.int
oem_icon_width: builtins.int
VERSION_FIELD_NUMBER: builtins.int
TIMESTAMP_FIELD_NUMBER: builtins.int
CONFIG_FIELD_NUMBER: builtins.int
MODULE_CONFIG_FIELD_NUMBER: builtins.int
CHANNELS_FIELD_NUMBER: builtins.int
OWNER_FIELD_NUMBER: builtins.int
version: builtins.int
"""
The Logo width in Px
The version of the backup
"""
oem_icon_height: builtins.int
timestamp: builtins.int
"""
The Logo height in Px
"""
oem_icon_bits: builtins.bytes
"""
The Logo in XBM bytechar format
"""
oem_font: global___ScreenFonts.ValueType
"""
Use this font for the OEM text.
"""
oem_text: builtins.str
"""
Use this font for the OEM text.
"""
oem_aes_key: builtins.bytes
"""
The default device encryption key, 16 or 32 byte
The timestamp of the backup (if node has time)
"""
@property
def oem_local_config(self) -> meshtastic.protobuf.localonly_pb2.LocalConfig:
def config(self) -> meshtastic.protobuf.localonly_pb2.LocalConfig:
"""
A Preset LocalConfig to apply during factory reset
The node's configuration
"""
@property
def oem_local_module_config(self) -> meshtastic.protobuf.localonly_pb2.LocalModuleConfig:
def module_config(self) -> meshtastic.protobuf.localonly_pb2.LocalModuleConfig:
"""
A Preset LocalModuleConfig to apply during factory reset
The node's module configuration
"""
@property
def channels(self) -> global___ChannelFile:
"""
The node's channels
"""
@property
def owner(self) -> meshtastic.protobuf.mesh_pb2.User:
"""
The node's user (owner) information
"""
def __init__(
self,
*,
oem_icon_width: builtins.int = ...,
oem_icon_height: builtins.int = ...,
oem_icon_bits: builtins.bytes = ...,
oem_font: global___ScreenFonts.ValueType = ...,
oem_text: builtins.str = ...,
oem_aes_key: builtins.bytes = ...,
oem_local_config: meshtastic.protobuf.localonly_pb2.LocalConfig | None = ...,
oem_local_module_config: meshtastic.protobuf.localonly_pb2.LocalModuleConfig | None = ...,
version: builtins.int = ...,
timestamp: builtins.int = ...,
config: meshtastic.protobuf.localonly_pb2.LocalConfig | None = ...,
module_config: meshtastic.protobuf.localonly_pb2.LocalModuleConfig | None = ...,
channels: global___ChannelFile | None = ...,
owner: meshtastic.protobuf.mesh_pb2.User | None = ...,
) -> None: ...
def HasField(self, field_name: typing.Literal["oem_local_config", b"oem_local_config", "oem_local_module_config", b"oem_local_module_config"]) -> builtins.bool: ...
def ClearField(self, field_name: typing.Literal["oem_aes_key", b"oem_aes_key", "oem_font", b"oem_font", "oem_icon_bits", b"oem_icon_bits", "oem_icon_height", b"oem_icon_height", "oem_icon_width", b"oem_icon_width", "oem_local_config", b"oem_local_config", "oem_local_module_config", b"oem_local_module_config", "oem_text", b"oem_text"]) -> None: ...
def HasField(self, field_name: typing.Literal["channels", b"channels", "config", b"config", "module_config", b"module_config", "owner", b"owner"]) -> builtins.bool: ...
def ClearField(self, field_name: typing.Literal["channels", b"channels", "config", b"config", "module_config", b"module_config", "owner", b"owner", "timestamp", b"timestamp", "version", b"version"]) -> None: ...
global___OEMStore = OEMStore
global___BackupPreferences = BackupPreferences

30
meshtastic/protobuf/interdevice_pb2.py generated Normal file
View File

@@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: meshtastic/protobuf/interdevice.proto
"""Generated protocol buffer code."""
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pool as _descriptor_pool
from google.protobuf import symbol_database as _symbol_database
from google.protobuf.internal import builder as _builder
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n%meshtastic/protobuf/interdevice.proto\x12\x13meshtastic.protobuf\"s\n\nSensorData\x12.\n\x04type\x18\x01 \x01(\x0e\x32 .meshtastic.protobuf.MessageType\x12\x15\n\x0b\x66loat_value\x18\x02 \x01(\x02H\x00\x12\x16\n\x0cuint32_value\x18\x03 \x01(\rH\x00\x42\x06\n\x04\x64\x61ta\"_\n\x12InterdeviceMessage\x12\x0e\n\x04nmea\x18\x01 \x01(\tH\x00\x12\x31\n\x06sensor\x18\x02 \x01(\x0b\x32\x1f.meshtastic.protobuf.SensorDataH\x00\x42\x06\n\x04\x64\x61ta*\xd5\x01\n\x0bMessageType\x12\x07\n\x03\x41\x43K\x10\x00\x12\x15\n\x10\x43OLLECT_INTERVAL\x10\xa0\x01\x12\x0c\n\x07\x42\x45\x45P_ON\x10\xa1\x01\x12\r\n\x08\x42\x45\x45P_OFF\x10\xa2\x01\x12\r\n\x08SHUTDOWN\x10\xa3\x01\x12\r\n\x08POWER_ON\x10\xa4\x01\x12\x0f\n\nSCD41_TEMP\x10\xb0\x01\x12\x13\n\x0eSCD41_HUMIDITY\x10\xb1\x01\x12\x0e\n\tSCD41_CO2\x10\xb2\x01\x12\x0f\n\nAHT20_TEMP\x10\xb3\x01\x12\x13\n\x0e\x41HT20_HUMIDITY\x10\xb4\x01\x12\x0f\n\nTVOC_INDEX\x10\xb5\x01\x42g\n\x14org.meshtastic.protoB\x11InterdeviceProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.interdevice_pb2', _globals)
if _descriptor._USE_C_DESCRIPTORS == False:
DESCRIPTOR._options = None
DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\021InterdeviceProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
_globals['_MESSAGETYPE']._serialized_start=277
_globals['_MESSAGETYPE']._serialized_end=490
_globals['_SENSORDATA']._serialized_start=62
_globals['_SENSORDATA']._serialized_end=177
_globals['_INTERDEVICEMESSAGE']._serialized_start=179
_globals['_INTERDEVICEMESSAGE']._serialized_end=274
# @@protoc_insertion_point(module_scope)

105
meshtastic/protobuf/interdevice_pb2.pyi generated Normal file
View File

@@ -0,0 +1,105 @@
"""
@generated by mypy-protobuf. Do not edit manually!
isort:skip_file
"""
import builtins
import google.protobuf.descriptor
import google.protobuf.internal.enum_type_wrapper
import google.protobuf.message
import sys
import typing
if sys.version_info >= (3, 10):
import typing as typing_extensions
else:
import typing_extensions
DESCRIPTOR: google.protobuf.descriptor.FileDescriptor
class _MessageType:
ValueType = typing.NewType("ValueType", builtins.int)
V: typing_extensions.TypeAlias = ValueType
class _MessageTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_MessageType.ValueType], builtins.type):
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
ACK: _MessageType.ValueType # 0
COLLECT_INTERVAL: _MessageType.ValueType # 160
"""in ms"""
BEEP_ON: _MessageType.ValueType # 161
"""duration ms"""
BEEP_OFF: _MessageType.ValueType # 162
"""cancel prematurely"""
SHUTDOWN: _MessageType.ValueType # 163
POWER_ON: _MessageType.ValueType # 164
SCD41_TEMP: _MessageType.ValueType # 176
SCD41_HUMIDITY: _MessageType.ValueType # 177
SCD41_CO2: _MessageType.ValueType # 178
AHT20_TEMP: _MessageType.ValueType # 179
AHT20_HUMIDITY: _MessageType.ValueType # 180
TVOC_INDEX: _MessageType.ValueType # 181
class MessageType(_MessageType, metaclass=_MessageTypeEnumTypeWrapper):
"""encapsulate up to 1k of NMEA string data"""
ACK: MessageType.ValueType # 0
COLLECT_INTERVAL: MessageType.ValueType # 160
"""in ms"""
BEEP_ON: MessageType.ValueType # 161
"""duration ms"""
BEEP_OFF: MessageType.ValueType # 162
"""cancel prematurely"""
SHUTDOWN: MessageType.ValueType # 163
POWER_ON: MessageType.ValueType # 164
SCD41_TEMP: MessageType.ValueType # 176
SCD41_HUMIDITY: MessageType.ValueType # 177
SCD41_CO2: MessageType.ValueType # 178
AHT20_TEMP: MessageType.ValueType # 179
AHT20_HUMIDITY: MessageType.ValueType # 180
TVOC_INDEX: MessageType.ValueType # 181
global___MessageType = MessageType
@typing.final
class SensorData(google.protobuf.message.Message):
DESCRIPTOR: google.protobuf.descriptor.Descriptor
TYPE_FIELD_NUMBER: builtins.int
FLOAT_VALUE_FIELD_NUMBER: builtins.int
UINT32_VALUE_FIELD_NUMBER: builtins.int
type: global___MessageType.ValueType
"""The message type"""
float_value: builtins.float
uint32_value: builtins.int
def __init__(
self,
*,
type: global___MessageType.ValueType = ...,
float_value: builtins.float = ...,
uint32_value: builtins.int = ...,
) -> None: ...
def HasField(self, field_name: typing.Literal["data", b"data", "float_value", b"float_value", "uint32_value", b"uint32_value"]) -> builtins.bool: ...
def ClearField(self, field_name: typing.Literal["data", b"data", "float_value", b"float_value", "type", b"type", "uint32_value", b"uint32_value"]) -> None: ...
def WhichOneof(self, oneof_group: typing.Literal["data", b"data"]) -> typing.Literal["float_value", "uint32_value"] | None: ...
global___SensorData = SensorData
@typing.final
class InterdeviceMessage(google.protobuf.message.Message):
DESCRIPTOR: google.protobuf.descriptor.Descriptor
NMEA_FIELD_NUMBER: builtins.int
SENSOR_FIELD_NUMBER: builtins.int
nmea: builtins.str
@property
def sensor(self) -> global___SensorData: ...
def __init__(
self,
*,
nmea: builtins.str = ...,
sensor: global___SensorData | None = ...,
) -> None: ...
def HasField(self, field_name: typing.Literal["data", b"data", "nmea", b"nmea", "sensor", b"sensor"]) -> builtins.bool: ...
def ClearField(self, field_name: typing.Literal["data", b"data", "nmea", b"nmea", "sensor", b"sensor"]) -> None: ...
def WhichOneof(self, oneof_group: typing.Literal["data", b"data"]) -> typing.Literal["nmea", "sensor"] | None: ...
global___InterdeviceMessage = InterdeviceMessage

View File

@@ -15,16 +15,16 @@ from meshtastic.protobuf import config_pb2 as meshtastic_dot_protobuf_dot_config
from meshtastic.protobuf import module_config_pb2 as meshtastic_dot_protobuf_dot_module__config__pb2
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n#meshtastic/protobuf/localonly.proto\x12\x13meshtastic.protobuf\x1a meshtastic/protobuf/config.proto\x1a\'meshtastic/protobuf/module_config.proto\"\xbc\x03\n\x0bLocalConfig\x12\x38\n\x06\x64\x65vice\x18\x01 \x01(\x0b\x32(.meshtastic.protobuf.Config.DeviceConfig\x12<\n\x08position\x18\x02 \x01(\x0b\x32*.meshtastic.protobuf.Config.PositionConfig\x12\x36\n\x05power\x18\x03 \x01(\x0b\x32\'.meshtastic.protobuf.Config.PowerConfig\x12:\n\x07network\x18\x04 \x01(\x0b\x32).meshtastic.protobuf.Config.NetworkConfig\x12:\n\x07\x64isplay\x18\x05 \x01(\x0b\x32).meshtastic.protobuf.Config.DisplayConfig\x12\x34\n\x04lora\x18\x06 \x01(\x0b\x32&.meshtastic.protobuf.Config.LoRaConfig\x12>\n\tbluetooth\x18\x07 \x01(\x0b\x32+.meshtastic.protobuf.Config.BluetoothConfig\x12\x0f\n\x07version\x18\x08 \x01(\r\"\xf0\x07\n\x11LocalModuleConfig\x12:\n\x04mqtt\x18\x01 \x01(\x0b\x32,.meshtastic.protobuf.ModuleConfig.MQTTConfig\x12>\n\x06serial\x18\x02 \x01(\x0b\x32..meshtastic.protobuf.ModuleConfig.SerialConfig\x12[\n\x15\x65xternal_notification\x18\x03 \x01(\x0b\x32<.meshtastic.protobuf.ModuleConfig.ExternalNotificationConfig\x12K\n\rstore_forward\x18\x04 \x01(\x0b\x32\x34.meshtastic.protobuf.ModuleConfig.StoreForwardConfig\x12\x45\n\nrange_test\x18\x05 \x01(\x0b\x32\x31.meshtastic.protobuf.ModuleConfig.RangeTestConfig\x12\x44\n\ttelemetry\x18\x06 \x01(\x0b\x32\x31.meshtastic.protobuf.ModuleConfig.TelemetryConfig\x12M\n\x0e\x63\x61nned_message\x18\x07 \x01(\x0b\x32\x35.meshtastic.protobuf.ModuleConfig.CannedMessageConfig\x12<\n\x05\x61udio\x18\t \x01(\x0b\x32-.meshtastic.protobuf.ModuleConfig.AudioConfig\x12O\n\x0fremote_hardware\x18\n \x01(\x0b\x32\x36.meshtastic.protobuf.ModuleConfig.RemoteHardwareConfig\x12K\n\rneighbor_info\x18\x0b \x01(\x0b\x32\x34.meshtastic.protobuf.ModuleConfig.NeighborInfoConfig\x12Q\n\x10\x61mbient_lighting\x18\x0c \x01(\x0b\x32\x37.meshtastic.protobuf.ModuleConfig.AmbientLightingConfig\x12Q\n\x10\x64\x65tection_sensor\x18\r \x01(\x0b\x32\x37.meshtastic.protobuf.ModuleConfig.DetectionSensorConfig\x12\x46\n\npaxcounter\x18\x0e \x01(\x0b\x32\x32.meshtastic.protobuf.ModuleConfig.PaxcounterConfig\x12\x0f\n\x07version\x18\x08 \x01(\rBd\n\x13\x63om.geeksville.meshB\x0fLocalOnlyProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n#meshtastic/protobuf/localonly.proto\x12\x13meshtastic.protobuf\x1a meshtastic/protobuf/config.proto\x1a\'meshtastic/protobuf/module_config.proto\"\xfa\x03\n\x0bLocalConfig\x12\x38\n\x06\x64\x65vice\x18\x01 \x01(\x0b\x32(.meshtastic.protobuf.Config.DeviceConfig\x12<\n\x08position\x18\x02 \x01(\x0b\x32*.meshtastic.protobuf.Config.PositionConfig\x12\x36\n\x05power\x18\x03 \x01(\x0b\x32\'.meshtastic.protobuf.Config.PowerConfig\x12:\n\x07network\x18\x04 \x01(\x0b\x32).meshtastic.protobuf.Config.NetworkConfig\x12:\n\x07\x64isplay\x18\x05 \x01(\x0b\x32).meshtastic.protobuf.Config.DisplayConfig\x12\x34\n\x04lora\x18\x06 \x01(\x0b\x32&.meshtastic.protobuf.Config.LoRaConfig\x12>\n\tbluetooth\x18\x07 \x01(\x0b\x32+.meshtastic.protobuf.Config.BluetoothConfig\x12\x0f\n\x07version\x18\x08 \x01(\r\x12<\n\x08security\x18\t \x01(\x0b\x32*.meshtastic.protobuf.Config.SecurityConfig\"\xf0\x07\n\x11LocalModuleConfig\x12:\n\x04mqtt\x18\x01 \x01(\x0b\x32,.meshtastic.protobuf.ModuleConfig.MQTTConfig\x12>\n\x06serial\x18\x02 \x01(\x0b\x32..meshtastic.protobuf.ModuleConfig.SerialConfig\x12[\n\x15\x65xternal_notification\x18\x03 \x01(\x0b\x32<.meshtastic.protobuf.ModuleConfig.ExternalNotificationConfig\x12K\n\rstore_forward\x18\x04 \x01(\x0b\x32\x34.meshtastic.protobuf.ModuleConfig.StoreForwardConfig\x12\x45\n\nrange_test\x18\x05 \x01(\x0b\x32\x31.meshtastic.protobuf.ModuleConfig.RangeTestConfig\x12\x44\n\ttelemetry\x18\x06 \x01(\x0b\x32\x31.meshtastic.protobuf.ModuleConfig.TelemetryConfig\x12M\n\x0e\x63\x61nned_message\x18\x07 \x01(\x0b\x32\x35.meshtastic.protobuf.ModuleConfig.CannedMessageConfig\x12<\n\x05\x61udio\x18\t \x01(\x0b\x32-.meshtastic.protobuf.ModuleConfig.AudioConfig\x12O\n\x0fremote_hardware\x18\n \x01(\x0b\x32\x36.meshtastic.protobuf.ModuleConfig.RemoteHardwareConfig\x12K\n\rneighbor_info\x18\x0b \x01(\x0b\x32\x34.meshtastic.protobuf.ModuleConfig.NeighborInfoConfig\x12Q\n\x10\x61mbient_lighting\x18\x0c \x01(\x0b\x32\x37.meshtastic.protobuf.ModuleConfig.AmbientLightingConfig\x12Q\n\x10\x64\x65tection_sensor\x18\r \x01(\x0b\x32\x37.meshtastic.protobuf.ModuleConfig.DetectionSensorConfig\x12\x46\n\npaxcounter\x18\x0e \x01(\x0b\x32\x32.meshtastic.protobuf.ModuleConfig.PaxcounterConfig\x12\x0f\n\x07version\x18\x08 \x01(\rBe\n\x14org.meshtastic.protoB\x0fLocalOnlyProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.localonly_pb2', _globals)
if _descriptor._USE_C_DESCRIPTORS == False:
DESCRIPTOR._options = None
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\017LocalOnlyProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\017LocalOnlyProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
_globals['_LOCALCONFIG']._serialized_start=136
_globals['_LOCALCONFIG']._serialized_end=580
_globals['_LOCALMODULECONFIG']._serialized_start=583
_globals['_LOCALMODULECONFIG']._serialized_end=1591
_globals['_LOCALCONFIG']._serialized_end=642
_globals['_LOCALMODULECONFIG']._serialized_start=645
_globals['_LOCALMODULECONFIG']._serialized_end=1653
# @@protoc_insertion_point(module_scope)

View File

@@ -29,6 +29,7 @@ class LocalConfig(google.protobuf.message.Message):
LORA_FIELD_NUMBER: builtins.int
BLUETOOTH_FIELD_NUMBER: builtins.int
VERSION_FIELD_NUMBER: builtins.int
SECURITY_FIELD_NUMBER: builtins.int
version: builtins.int
"""
A version integer used to invalidate old save files when we make
@@ -77,6 +78,12 @@ class LocalConfig(google.protobuf.message.Message):
The part of the config that is specific to the Bluetooth settings
"""
@property
def security(self) -> meshtastic.protobuf.config_pb2.Config.SecurityConfig:
"""
The part of the config that is specific to Security settings
"""
def __init__(
self,
*,
@@ -88,9 +95,10 @@ class LocalConfig(google.protobuf.message.Message):
lora: meshtastic.protobuf.config_pb2.Config.LoRaConfig | None = ...,
bluetooth: meshtastic.protobuf.config_pb2.Config.BluetoothConfig | None = ...,
version: builtins.int = ...,
security: meshtastic.protobuf.config_pb2.Config.SecurityConfig | None = ...,
) -> None: ...
def HasField(self, field_name: typing.Literal["bluetooth", b"bluetooth", "device", b"device", "display", b"display", "lora", b"lora", "network", b"network", "position", b"position", "power", b"power"]) -> builtins.bool: ...
def ClearField(self, field_name: typing.Literal["bluetooth", b"bluetooth", "device", b"device", "display", b"display", "lora", b"lora", "network", b"network", "position", b"position", "power", b"power", "version", b"version"]) -> None: ...
def HasField(self, field_name: typing.Literal["bluetooth", b"bluetooth", "device", b"device", "display", b"display", "lora", b"lora", "network", b"network", "position", b"position", "power", b"power", "security", b"security"]) -> builtins.bool: ...
def ClearField(self, field_name: typing.Literal["bluetooth", b"bluetooth", "device", b"device", "display", b"display", "lora", b"lora", "network", b"network", "position", b"position", "power", b"power", "security", b"security", "version", b"version"]) -> None: ...
global___LocalConfig = LocalConfig

View File

File diff suppressed because one or more lines are too long

View File

File diff suppressed because it is too large Load Diff

View File

File diff suppressed because one or more lines are too long

View File

@@ -165,6 +165,7 @@ class ModuleConfig(google.protobuf.message.Message):
PUBLISH_INTERVAL_SECS_FIELD_NUMBER: builtins.int
POSITION_PRECISION_FIELD_NUMBER: builtins.int
SHOULD_REPORT_LOCATION_FIELD_NUMBER: builtins.int
publish_interval_secs: builtins.int
"""
How often we should report our info to the map (in seconds)
@@ -173,13 +174,18 @@ class ModuleConfig(google.protobuf.message.Message):
"""
Bits of precision for the location sent (default of 32 is full precision).
"""
should_report_location: builtins.bool
"""
Whether we have opted-in to report our location to the map
"""
def __init__(
self,
*,
publish_interval_secs: builtins.int = ...,
position_precision: builtins.int = ...,
should_report_location: builtins.bool = ...,
) -> None: ...
def ClearField(self, field_name: typing.Literal["position_precision", b"position_precision", "publish_interval_secs", b"publish_interval_secs"]) -> None: ...
def ClearField(self, field_name: typing.Literal["position_precision", b"position_precision", "publish_interval_secs", b"publish_interval_secs", "should_report_location", b"should_report_location"]) -> None: ...
@typing.final
class RemoteHardwareConfig(google.protobuf.message.Message):
@@ -225,6 +231,7 @@ class ModuleConfig(google.protobuf.message.Message):
ENABLED_FIELD_NUMBER: builtins.int
UPDATE_INTERVAL_FIELD_NUMBER: builtins.int
TRANSMIT_OVER_LORA_FIELD_NUMBER: builtins.int
enabled: builtins.bool
"""
Whether the Module is enabled
@@ -232,15 +239,21 @@ class ModuleConfig(google.protobuf.message.Message):
update_interval: builtins.int
"""
Interval in seconds of how often we should try to send our
Neighbor Info to the mesh
Neighbor Info (minimum is 14400, i.e., 4 hours)
"""
transmit_over_lora: builtins.bool
"""
Whether in addition to sending it to MQTT and the PhoneAPI, our NeighborInfo should be transmitted over LoRa.
Note that this is not available on a channel with default key and name.
"""
def __init__(
self,
*,
enabled: builtins.bool = ...,
update_interval: builtins.int = ...,
transmit_over_lora: builtins.bool = ...,
) -> None: ...
def ClearField(self, field_name: typing.Literal["enabled", b"enabled", "update_interval", b"update_interval"]) -> None: ...
def ClearField(self, field_name: typing.Literal["enabled", b"enabled", "transmit_over_lora", b"transmit_over_lora", "update_interval", b"update_interval"]) -> None: ...
@typing.final
class DetectionSensorConfig(google.protobuf.message.Message):
@@ -250,13 +263,54 @@ class ModuleConfig(google.protobuf.message.Message):
DESCRIPTOR: google.protobuf.descriptor.Descriptor
class _TriggerType:
ValueType = typing.NewType("ValueType", builtins.int)
V: typing_extensions.TypeAlias = ValueType
class _TriggerTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[ModuleConfig.DetectionSensorConfig._TriggerType.ValueType], builtins.type):
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
LOGIC_LOW: ModuleConfig.DetectionSensorConfig._TriggerType.ValueType # 0
"""Event is triggered if pin is low"""
LOGIC_HIGH: ModuleConfig.DetectionSensorConfig._TriggerType.ValueType # 1
"""Event is triggered if pin is high"""
FALLING_EDGE: ModuleConfig.DetectionSensorConfig._TriggerType.ValueType # 2
"""Event is triggered when pin goes high to low"""
RISING_EDGE: ModuleConfig.DetectionSensorConfig._TriggerType.ValueType # 3
"""Event is triggered when pin goes low to high"""
EITHER_EDGE_ACTIVE_LOW: ModuleConfig.DetectionSensorConfig._TriggerType.ValueType # 4
"""Event is triggered on every pin state change, low is considered to be
"active"
"""
EITHER_EDGE_ACTIVE_HIGH: ModuleConfig.DetectionSensorConfig._TriggerType.ValueType # 5
"""Event is triggered on every pin state change, high is considered to be
"active"
"""
class TriggerType(_TriggerType, metaclass=_TriggerTypeEnumTypeWrapper): ...
LOGIC_LOW: ModuleConfig.DetectionSensorConfig.TriggerType.ValueType # 0
"""Event is triggered if pin is low"""
LOGIC_HIGH: ModuleConfig.DetectionSensorConfig.TriggerType.ValueType # 1
"""Event is triggered if pin is high"""
FALLING_EDGE: ModuleConfig.DetectionSensorConfig.TriggerType.ValueType # 2
"""Event is triggered when pin goes high to low"""
RISING_EDGE: ModuleConfig.DetectionSensorConfig.TriggerType.ValueType # 3
"""Event is triggered when pin goes low to high"""
EITHER_EDGE_ACTIVE_LOW: ModuleConfig.DetectionSensorConfig.TriggerType.ValueType # 4
"""Event is triggered on every pin state change, low is considered to be
"active"
"""
EITHER_EDGE_ACTIVE_HIGH: ModuleConfig.DetectionSensorConfig.TriggerType.ValueType # 5
"""Event is triggered on every pin state change, high is considered to be
"active"
"""
ENABLED_FIELD_NUMBER: builtins.int
MINIMUM_BROADCAST_SECS_FIELD_NUMBER: builtins.int
STATE_BROADCAST_SECS_FIELD_NUMBER: builtins.int
SEND_BELL_FIELD_NUMBER: builtins.int
NAME_FIELD_NUMBER: builtins.int
MONITOR_PIN_FIELD_NUMBER: builtins.int
DETECTION_TRIGGERED_HIGH_FIELD_NUMBER: builtins.int
DETECTION_TRIGGER_TYPE_FIELD_NUMBER: builtins.int
USE_PULLUP_FIELD_NUMBER: builtins.int
enabled: builtins.bool
"""
@@ -264,13 +318,15 @@ class ModuleConfig(google.protobuf.message.Message):
"""
minimum_broadcast_secs: builtins.int
"""
Interval in seconds of how often we can send a message to the mesh when a state change is detected
Interval in seconds of how often we can send a message to the mesh when a
trigger event is detected
"""
state_broadcast_secs: builtins.int
"""
Interval in seconds of how often we should send a message to the mesh with the current state regardless of changes
When set to 0, only state changes will be broadcasted
Works as a sort of status heartbeat for peace of mind
Interval in seconds of how often we should send a message to the mesh
with the current state regardless of trigger events When set to 0, only
trigger events will be broadcasted Works as a sort of status heartbeat
for peace of mind
"""
send_bell: builtins.bool
"""
@@ -287,10 +343,9 @@ class ModuleConfig(google.protobuf.message.Message):
"""
GPIO pin to monitor for state changes
"""
detection_triggered_high: builtins.bool
detection_trigger_type: global___ModuleConfig.DetectionSensorConfig.TriggerType.ValueType
"""
Whether or not the GPIO pin state detection is triggered on HIGH (1)
Otherwise LOW (0)
The type of trigger event to be used
"""
use_pullup: builtins.bool
"""
@@ -306,10 +361,10 @@ class ModuleConfig(google.protobuf.message.Message):
send_bell: builtins.bool = ...,
name: builtins.str = ...,
monitor_pin: builtins.int = ...,
detection_triggered_high: builtins.bool = ...,
detection_trigger_type: global___ModuleConfig.DetectionSensorConfig.TriggerType.ValueType = ...,
use_pullup: builtins.bool = ...,
) -> None: ...
def ClearField(self, field_name: typing.Literal["detection_triggered_high", b"detection_triggered_high", "enabled", b"enabled", "minimum_broadcast_secs", b"minimum_broadcast_secs", "monitor_pin", b"monitor_pin", "name", b"name", "send_bell", b"send_bell", "state_broadcast_secs", b"state_broadcast_secs", "use_pullup", b"use_pullup"]) -> None: ...
def ClearField(self, field_name: typing.Literal["detection_trigger_type", b"detection_trigger_type", "enabled", b"enabled", "minimum_broadcast_secs", b"minimum_broadcast_secs", "monitor_pin", b"monitor_pin", "name", b"name", "send_bell", b"send_bell", "state_broadcast_secs", b"state_broadcast_secs", "use_pullup", b"use_pullup"]) -> None: ...
@typing.final
class AudioConfig(google.protobuf.message.Message):
@@ -505,6 +560,14 @@ class ModuleConfig(google.protobuf.message.Message):
"""NMEA messages specifically tailored for CalTopo"""
WS85: ModuleConfig.SerialConfig._Serial_Mode.ValueType # 6
"""Ecowitt WS85 weather station"""
VE_DIRECT: ModuleConfig.SerialConfig._Serial_Mode.ValueType # 7
"""VE.Direct is a serial protocol used by Victron Energy products
https://beta.ivc.no/wiki/index.php/Victron_VE_Direct_DIY_Cable
"""
MS_CONFIG: ModuleConfig.SerialConfig._Serial_Mode.ValueType # 8
"""Used to configure and view some parameters of MeshSolar.
https://heltec.org/project/meshsolar/
"""
class Serial_Mode(_Serial_Mode, metaclass=_Serial_ModeEnumTypeWrapper):
"""
@@ -520,6 +583,14 @@ class ModuleConfig(google.protobuf.message.Message):
"""NMEA messages specifically tailored for CalTopo"""
WS85: ModuleConfig.SerialConfig.Serial_Mode.ValueType # 6
"""Ecowitt WS85 weather station"""
VE_DIRECT: ModuleConfig.SerialConfig.Serial_Mode.ValueType # 7
"""VE.Direct is a serial protocol used by Victron Energy products
https://beta.ivc.no/wiki/index.php/Victron_VE_Direct_DIY_Cable
"""
MS_CONFIG: ModuleConfig.SerialConfig.Serial_Mode.ValueType # 8
"""Used to configure and view some parameters of MeshSolar.
https://heltec.org/project/meshsolar/
"""
ENABLED_FIELD_NUMBER: builtins.int
ECHO_FIELD_NUMBER: builtins.int
@@ -753,6 +824,7 @@ class ModuleConfig(google.protobuf.message.Message):
ENABLED_FIELD_NUMBER: builtins.int
SENDER_FIELD_NUMBER: builtins.int
SAVE_FIELD_NUMBER: builtins.int
CLEAR_ON_REBOOT_FIELD_NUMBER: builtins.int
enabled: builtins.bool
"""
Enable the Range Test Module
@@ -766,14 +838,20 @@ class ModuleConfig(google.protobuf.message.Message):
Bool value indicating that this node should save a RangeTest.csv file.
ESP32 Only
"""
clear_on_reboot: builtins.bool
"""
Bool indicating that the node should cleanup / destroy it's RangeTest.csv file.
ESP32 Only
"""
def __init__(
self,
*,
enabled: builtins.bool = ...,
sender: builtins.int = ...,
save: builtins.bool = ...,
clear_on_reboot: builtins.bool = ...,
) -> None: ...
def ClearField(self, field_name: typing.Literal["enabled", b"enabled", "save", b"save", "sender", b"sender"]) -> None: ...
def ClearField(self, field_name: typing.Literal["clear_on_reboot", b"clear_on_reboot", "enabled", b"enabled", "save", b"save", "sender", b"sender"]) -> None: ...
@typing.final
class TelemetryConfig(google.protobuf.message.Message):
@@ -793,6 +871,10 @@ class ModuleConfig(google.protobuf.message.Message):
POWER_MEASUREMENT_ENABLED_FIELD_NUMBER: builtins.int
POWER_UPDATE_INTERVAL_FIELD_NUMBER: builtins.int
POWER_SCREEN_ENABLED_FIELD_NUMBER: builtins.int
HEALTH_MEASUREMENT_ENABLED_FIELD_NUMBER: builtins.int
HEALTH_UPDATE_INTERVAL_FIELD_NUMBER: builtins.int
HEALTH_SCREEN_ENABLED_FIELD_NUMBER: builtins.int
DEVICE_TELEMETRY_ENABLED_FIELD_NUMBER: builtins.int
device_update_interval: builtins.int
"""
Interval in seconds of how often we should try to send our
@@ -828,18 +910,35 @@ class ModuleConfig(google.protobuf.message.Message):
"""
power_measurement_enabled: builtins.bool
"""
Interval in seconds of how often we should try to send our
air quality metrics to the mesh
Enable/disable Power metrics
"""
power_update_interval: builtins.int
"""
Interval in seconds of how often we should try to send our
air quality metrics to the mesh
power metrics to the mesh
"""
power_screen_enabled: builtins.bool
"""
Enable/Disable the power measurement module on-device display
"""
health_measurement_enabled: builtins.bool
"""
Preferences for the (Health) Telemetry Module
Enable/Disable the telemetry measurement module measurement collection
"""
health_update_interval: builtins.int
"""
Interval in seconds of how often we should try to send our
air quality metrics to the mesh
health metrics to the mesh
"""
health_screen_enabled: builtins.bool
"""
Enable/Disable the health telemetry module on-device display
"""
device_telemetry_enabled: builtins.bool
"""
Enable/Disable the device telemetry module to send metrics to the mesh
Note: We will still send telemtry to the connected phone / client every minute over the API
"""
def __init__(
self,
@@ -854,13 +953,17 @@ class ModuleConfig(google.protobuf.message.Message):
power_measurement_enabled: builtins.bool = ...,
power_update_interval: builtins.int = ...,
power_screen_enabled: builtins.bool = ...,
health_measurement_enabled: builtins.bool = ...,
health_update_interval: builtins.int = ...,
health_screen_enabled: builtins.bool = ...,
device_telemetry_enabled: builtins.bool = ...,
) -> None: ...
def ClearField(self, field_name: typing.Literal["air_quality_enabled", b"air_quality_enabled", "air_quality_interval", b"air_quality_interval", "device_update_interval", b"device_update_interval", "environment_display_fahrenheit", b"environment_display_fahrenheit", "environment_measurement_enabled", b"environment_measurement_enabled", "environment_screen_enabled", b"environment_screen_enabled", "environment_update_interval", b"environment_update_interval", "power_measurement_enabled", b"power_measurement_enabled", "power_screen_enabled", b"power_screen_enabled", "power_update_interval", b"power_update_interval"]) -> None: ...
def ClearField(self, field_name: typing.Literal["air_quality_enabled", b"air_quality_enabled", "air_quality_interval", b"air_quality_interval", "device_telemetry_enabled", b"device_telemetry_enabled", "device_update_interval", b"device_update_interval", "environment_display_fahrenheit", b"environment_display_fahrenheit", "environment_measurement_enabled", b"environment_measurement_enabled", "environment_screen_enabled", b"environment_screen_enabled", "environment_update_interval", b"environment_update_interval", "health_measurement_enabled", b"health_measurement_enabled", "health_screen_enabled", b"health_screen_enabled", "health_update_interval", b"health_update_interval", "power_measurement_enabled", b"power_measurement_enabled", "power_screen_enabled", b"power_screen_enabled", "power_update_interval", b"power_update_interval"]) -> None: ...
@typing.final
class CannedMessageConfig(google.protobuf.message.Message):
"""
TODO: REPLACE
Canned Messages Module Config
"""
DESCRIPTOR: google.protobuf.descriptor.Descriptor
@@ -992,7 +1095,7 @@ class ModuleConfig(google.protobuf.message.Message):
allow_input_source: builtins.str
"""
Input event origin accepted by the canned message module.
Can be e.g. "rotEnc1", "upDownEnc1" or keyword "_any"
Can be e.g. "rotEnc1", "upDownEnc1", "scanAndSelect", "cardkb", "serialkb", or keyword "_any"
"""
send_bell: builtins.bool
"""

View File

@@ -15,16 +15,16 @@ from meshtastic.protobuf import config_pb2 as meshtastic_dot_protobuf_dot_config
from meshtastic.protobuf import mesh_pb2 as meshtastic_dot_protobuf_dot_mesh__pb2
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1emeshtastic/protobuf/mqtt.proto\x12\x13meshtastic.protobuf\x1a meshtastic/protobuf/config.proto\x1a\x1emeshtastic/protobuf/mesh.proto\"j\n\x0fServiceEnvelope\x12/\n\x06packet\x18\x01 \x01(\x0b\x32\x1f.meshtastic.protobuf.MeshPacket\x12\x12\n\nchannel_id\x18\x02 \x01(\t\x12\x12\n\ngateway_id\x18\x03 \x01(\t\"\xe0\x03\n\tMapReport\x12\x11\n\tlong_name\x18\x01 \x01(\t\x12\x12\n\nshort_name\x18\x02 \x01(\t\x12;\n\x04role\x18\x03 \x01(\x0e\x32-.meshtastic.protobuf.Config.DeviceConfig.Role\x12\x34\n\x08hw_model\x18\x04 \x01(\x0e\x32\".meshtastic.protobuf.HardwareModel\x12\x18\n\x10\x66irmware_version\x18\x05 \x01(\t\x12\x41\n\x06region\x18\x06 \x01(\x0e\x32\x31.meshtastic.protobuf.Config.LoRaConfig.RegionCode\x12H\n\x0cmodem_preset\x18\x07 \x01(\x0e\x32\x32.meshtastic.protobuf.Config.LoRaConfig.ModemPreset\x12\x1b\n\x13has_default_channel\x18\x08 \x01(\x08\x12\x12\n\nlatitude_i\x18\t \x01(\x0f\x12\x13\n\x0blongitude_i\x18\n \x01(\x0f\x12\x10\n\x08\x61ltitude\x18\x0b \x01(\x05\x12\x1a\n\x12position_precision\x18\x0c \x01(\r\x12\x1e\n\x16num_online_local_nodes\x18\r \x01(\rB_\n\x13\x63om.geeksville.meshB\nMQTTProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1emeshtastic/protobuf/mqtt.proto\x12\x13meshtastic.protobuf\x1a meshtastic/protobuf/config.proto\x1a\x1emeshtastic/protobuf/mesh.proto\"j\n\x0fServiceEnvelope\x12/\n\x06packet\x18\x01 \x01(\x0b\x32\x1f.meshtastic.protobuf.MeshPacket\x12\x12\n\nchannel_id\x18\x02 \x01(\t\x12\x12\n\ngateway_id\x18\x03 \x01(\t\"\x83\x04\n\tMapReport\x12\x11\n\tlong_name\x18\x01 \x01(\t\x12\x12\n\nshort_name\x18\x02 \x01(\t\x12;\n\x04role\x18\x03 \x01(\x0e\x32-.meshtastic.protobuf.Config.DeviceConfig.Role\x12\x34\n\x08hw_model\x18\x04 \x01(\x0e\x32\".meshtastic.protobuf.HardwareModel\x12\x18\n\x10\x66irmware_version\x18\x05 \x01(\t\x12\x41\n\x06region\x18\x06 \x01(\x0e\x32\x31.meshtastic.protobuf.Config.LoRaConfig.RegionCode\x12H\n\x0cmodem_preset\x18\x07 \x01(\x0e\x32\x32.meshtastic.protobuf.Config.LoRaConfig.ModemPreset\x12\x1b\n\x13has_default_channel\x18\x08 \x01(\x08\x12\x12\n\nlatitude_i\x18\t \x01(\x0f\x12\x13\n\x0blongitude_i\x18\n \x01(\x0f\x12\x10\n\x08\x61ltitude\x18\x0b \x01(\x05\x12\x1a\n\x12position_precision\x18\x0c \x01(\r\x12\x1e\n\x16num_online_local_nodes\x18\r \x01(\r\x12!\n\x19has_opted_report_location\x18\x0e \x01(\x08\x42`\n\x14org.meshtastic.protoB\nMQTTProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.mqtt_pb2', _globals)
if _descriptor._USE_C_DESCRIPTORS == False:
DESCRIPTOR._options = None
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\nMQTTProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\nMQTTProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
_globals['_SERVICEENVELOPE']._serialized_start=121
_globals['_SERVICEENVELOPE']._serialized_end=227
_globals['_MAPREPORT']._serialized_start=230
_globals['_MAPREPORT']._serialized_end=710
_globals['_MAPREPORT']._serialized_end=745
# @@protoc_insertion_point(module_scope)

View File

@@ -72,6 +72,7 @@ class MapReport(google.protobuf.message.Message):
ALTITUDE_FIELD_NUMBER: builtins.int
POSITION_PRECISION_FIELD_NUMBER: builtins.int
NUM_ONLINE_LOCAL_NODES_FIELD_NUMBER: builtins.int
HAS_OPTED_REPORT_LOCATION_FIELD_NUMBER: builtins.int
long_name: builtins.str
"""
A full name for this user, i.e. "Kevin Hester"
@@ -126,6 +127,11 @@ class MapReport(google.protobuf.message.Message):
"""
Number of online nodes (heard in the last 2 hours) this node has in its list that were received locally (not via MQTT)
"""
has_opted_report_location: builtins.bool
"""
User has opted in to share their location (map report) with the mqtt server
Controlled by map_report.should_report_location
"""
def __init__(
self,
*,
@@ -142,7 +148,8 @@ class MapReport(google.protobuf.message.Message):
altitude: builtins.int = ...,
position_precision: builtins.int = ...,
num_online_local_nodes: builtins.int = ...,
has_opted_report_location: builtins.bool = ...,
) -> None: ...
def ClearField(self, field_name: typing.Literal["altitude", b"altitude", "firmware_version", b"firmware_version", "has_default_channel", b"has_default_channel", "hw_model", b"hw_model", "latitude_i", b"latitude_i", "long_name", b"long_name", "longitude_i", b"longitude_i", "modem_preset", b"modem_preset", "num_online_local_nodes", b"num_online_local_nodes", "position_precision", b"position_precision", "region", b"region", "role", b"role", "short_name", b"short_name"]) -> None: ...
def ClearField(self, field_name: typing.Literal["altitude", b"altitude", "firmware_version", b"firmware_version", "has_default_channel", b"has_default_channel", "has_opted_report_location", b"has_opted_report_location", "hw_model", b"hw_model", "latitude_i", b"latitude_i", "long_name", b"long_name", "longitude_i", b"longitude_i", "modem_preset", b"modem_preset", "num_online_local_nodes", b"num_online_local_nodes", "position_precision", b"position_precision", "region", b"region", "role", b"role", "short_name", b"short_name"]) -> None: ...
global___MapReport = MapReport

35
meshtastic/protobuf/nanopb_pb2.py generated Normal file
View File

@@ -0,0 +1,35 @@
# -*- coding: utf-8 -*-
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: meshtastic/protobuf/nanopb.proto
"""Generated protocol buffer code."""
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pool as _descriptor_pool
from google.protobuf import symbol_database as _symbol_database
from google.protobuf.internal import builder as _builder
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
from google.protobuf import descriptor_pb2 as google_dot_protobuf_dot_descriptor__pb2
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n meshtastic/protobuf/nanopb.proto\x1a google/protobuf/descriptor.proto\"\xa4\x07\n\rNanoPBOptions\x12\x10\n\x08max_size\x18\x01 \x01(\x05\x12\x12\n\nmax_length\x18\x0e \x01(\x05\x12\x11\n\tmax_count\x18\x02 \x01(\x05\x12&\n\x08int_size\x18\x07 \x01(\x0e\x32\x08.IntSize:\nIS_DEFAULT\x12$\n\x04type\x18\x03 \x01(\x0e\x32\n.FieldType:\nFT_DEFAULT\x12\x18\n\nlong_names\x18\x04 \x01(\x08:\x04true\x12\x1c\n\rpacked_struct\x18\x05 \x01(\x08:\x05\x66\x61lse\x12\x1a\n\x0bpacked_enum\x18\n \x01(\x08:\x05\x66\x61lse\x12\x1b\n\x0cskip_message\x18\x06 \x01(\x08:\x05\x66\x61lse\x12\x18\n\tno_unions\x18\x08 \x01(\x08:\x05\x66\x61lse\x12\r\n\x05msgid\x18\t \x01(\r\x12\x1e\n\x0f\x61nonymous_oneof\x18\x0b \x01(\x08:\x05\x66\x61lse\x12\x15\n\x06proto3\x18\x0c \x01(\x08:\x05\x66\x61lse\x12#\n\x14proto3_singular_msgs\x18\x15 \x01(\x08:\x05\x66\x61lse\x12\x1d\n\x0e\x65num_to_string\x18\r \x01(\x08:\x05\x66\x61lse\x12\x1b\n\x0c\x66ixed_length\x18\x0f \x01(\x08:\x05\x66\x61lse\x12\x1a\n\x0b\x66ixed_count\x18\x10 \x01(\x08:\x05\x66\x61lse\x12\x1e\n\x0fsubmsg_callback\x18\x16 \x01(\x08:\x05\x66\x61lse\x12/\n\x0cmangle_names\x18\x11 \x01(\x0e\x32\x11.TypenameMangling:\x06M_NONE\x12(\n\x11\x63\x61llback_datatype\x18\x12 \x01(\t:\rpb_callback_t\x12\x34\n\x11\x63\x61llback_function\x18\x13 \x01(\t:\x19pb_default_field_callback\x12\x30\n\x0e\x64\x65scriptorsize\x18\x14 \x01(\x0e\x32\x0f.DescriptorSize:\x07\x44S_AUTO\x12\x1a\n\x0b\x64\x65\x66\x61ult_has\x18\x17 \x01(\x08:\x05\x66\x61lse\x12\x0f\n\x07include\x18\x18 \x03(\t\x12\x0f\n\x07\x65xclude\x18\x1a \x03(\t\x12\x0f\n\x07package\x18\x19 \x01(\t\x12\x41\n\rtype_override\x18\x1b \x01(\x0e\x32*.google.protobuf.FieldDescriptorProto.Type\x12\x19\n\x0bsort_by_tag\x18\x1c \x01(\x08:\x04true\x12.\n\rfallback_type\x18\x1d \x01(\x0e\x32\n.FieldType:\x0b\x46T_CALLBACK*i\n\tFieldType\x12\x0e\n\nFT_DEFAULT\x10\x00\x12\x0f\n\x0b\x46T_CALLBACK\x10\x01\x12\x0e\n\nFT_POINTER\x10\x04\x12\r\n\tFT_STATIC\x10\x02\x12\r\n\tFT_IGNORE\x10\x03\x12\r\n\tFT_INLINE\x10\x05*D\n\x07IntSize\x12\x0e\n\nIS_DEFAULT\x10\x00\x12\x08\n\x04IS_8\x10\x08\x12\t\n\x05IS_16\x10\x10\x12\t\n\x05IS_32\x10 \x12\t\n\x05IS_64\x10@*Z\n\x10TypenameMangling\x12\n\n\x06M_NONE\x10\x00\x12\x13\n\x0fM_STRIP_PACKAGE\x10\x01\x12\r\n\tM_FLATTEN\x10\x02\x12\x16\n\x12M_PACKAGE_INITIALS\x10\x03*E\n\x0e\x44\x65scriptorSize\x12\x0b\n\x07\x44S_AUTO\x10\x00\x12\x08\n\x04\x44S_1\x10\x01\x12\x08\n\x04\x44S_2\x10\x02\x12\x08\n\x04\x44S_4\x10\x04\x12\x08\n\x04\x44S_8\x10\x08:E\n\x0enanopb_fileopt\x12\x1c.google.protobuf.FileOptions\x18\xf2\x07 \x01(\x0b\x32\x0e.NanoPBOptions:G\n\rnanopb_msgopt\x12\x1f.google.protobuf.MessageOptions\x18\xf2\x07 \x01(\x0b\x32\x0e.NanoPBOptions:E\n\x0enanopb_enumopt\x12\x1c.google.protobuf.EnumOptions\x18\xf2\x07 \x01(\x0b\x32\x0e.NanoPBOptions:>\n\x06nanopb\x12\x1d.google.protobuf.FieldOptions\x18\xf2\x07 \x01(\x0b\x32\x0e.NanoPBOptionsB>\n\x18\x66i.kapsi.koti.jpa.nanopbZ\"github.com/meshtastic/go/generated')
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.nanopb_pb2', _globals)
if _descriptor._USE_C_DESCRIPTORS == False:
DESCRIPTOR._options = None
DESCRIPTOR._serialized_options = b'\n\030fi.kapsi.koti.jpa.nanopbZ\"github.com/meshtastic/go/generated'
_globals['_FIELDTYPE']._serialized_start=1005
_globals['_FIELDTYPE']._serialized_end=1110
_globals['_INTSIZE']._serialized_start=1112
_globals['_INTSIZE']._serialized_end=1180
_globals['_TYPENAMEMANGLING']._serialized_start=1182
_globals['_TYPENAMEMANGLING']._serialized_end=1272
_globals['_DESCRIPTORSIZE']._serialized_start=1274
_globals['_DESCRIPTORSIZE']._serialized_end=1343
_globals['_NANOPBOPTIONS']._serialized_start=71
_globals['_NANOPBOPTIONS']._serialized_end=1003
# @@protoc_insertion_point(module_scope)

324
meshtastic/protobuf/nanopb_pb2.pyi generated Normal file
View File

@@ -0,0 +1,324 @@
"""
@generated by mypy-protobuf. Do not edit manually!
isort:skip_file
Custom options for defining:
- Maximum size of string/bytes
- Maximum number of elements in array
These are used by nanopb to generate statically allocable structures
for memory-limited environments.
"""
import builtins
import collections.abc
import google.protobuf.descriptor
import google.protobuf.descriptor_pb2
import google.protobuf.internal.containers
import google.protobuf.internal.enum_type_wrapper
import google.protobuf.internal.extension_dict
import google.protobuf.message
import sys
import typing
if sys.version_info >= (3, 10):
import typing as typing_extensions
else:
import typing_extensions
DESCRIPTOR: google.protobuf.descriptor.FileDescriptor
class _FieldType:
ValueType = typing.NewType("ValueType", builtins.int)
V: typing_extensions.TypeAlias = ValueType
class _FieldTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_FieldType.ValueType], builtins.type):
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
FT_DEFAULT: _FieldType.ValueType # 0
"""Automatically decide field type, generate static field if possible."""
FT_CALLBACK: _FieldType.ValueType # 1
"""Always generate a callback field."""
FT_POINTER: _FieldType.ValueType # 4
"""Always generate a dynamically allocated field."""
FT_STATIC: _FieldType.ValueType # 2
"""Generate a static field or raise an exception if not possible."""
FT_IGNORE: _FieldType.ValueType # 3
"""Ignore the field completely."""
FT_INLINE: _FieldType.ValueType # 5
"""Legacy option, use the separate 'fixed_length' option instead"""
class FieldType(_FieldType, metaclass=_FieldTypeEnumTypeWrapper): ...
FT_DEFAULT: FieldType.ValueType # 0
"""Automatically decide field type, generate static field if possible."""
FT_CALLBACK: FieldType.ValueType # 1
"""Always generate a callback field."""
FT_POINTER: FieldType.ValueType # 4
"""Always generate a dynamically allocated field."""
FT_STATIC: FieldType.ValueType # 2
"""Generate a static field or raise an exception if not possible."""
FT_IGNORE: FieldType.ValueType # 3
"""Ignore the field completely."""
FT_INLINE: FieldType.ValueType # 5
"""Legacy option, use the separate 'fixed_length' option instead"""
global___FieldType = FieldType
class _IntSize:
ValueType = typing.NewType("ValueType", builtins.int)
V: typing_extensions.TypeAlias = ValueType
class _IntSizeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_IntSize.ValueType], builtins.type):
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
IS_DEFAULT: _IntSize.ValueType # 0
"""Default, 32/64bit based on type in .proto"""
IS_8: _IntSize.ValueType # 8
IS_16: _IntSize.ValueType # 16
IS_32: _IntSize.ValueType # 32
IS_64: _IntSize.ValueType # 64
class IntSize(_IntSize, metaclass=_IntSizeEnumTypeWrapper): ...
IS_DEFAULT: IntSize.ValueType # 0
"""Default, 32/64bit based on type in .proto"""
IS_8: IntSize.ValueType # 8
IS_16: IntSize.ValueType # 16
IS_32: IntSize.ValueType # 32
IS_64: IntSize.ValueType # 64
global___IntSize = IntSize
class _TypenameMangling:
ValueType = typing.NewType("ValueType", builtins.int)
V: typing_extensions.TypeAlias = ValueType
class _TypenameManglingEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_TypenameMangling.ValueType], builtins.type):
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
M_NONE: _TypenameMangling.ValueType # 0
"""Default, no typename mangling"""
M_STRIP_PACKAGE: _TypenameMangling.ValueType # 1
"""Strip current package name"""
M_FLATTEN: _TypenameMangling.ValueType # 2
"""Only use last path component"""
M_PACKAGE_INITIALS: _TypenameMangling.ValueType # 3
"""Replace the package name by the initials"""
class TypenameMangling(_TypenameMangling, metaclass=_TypenameManglingEnumTypeWrapper): ...
M_NONE: TypenameMangling.ValueType # 0
"""Default, no typename mangling"""
M_STRIP_PACKAGE: TypenameMangling.ValueType # 1
"""Strip current package name"""
M_FLATTEN: TypenameMangling.ValueType # 2
"""Only use last path component"""
M_PACKAGE_INITIALS: TypenameMangling.ValueType # 3
"""Replace the package name by the initials"""
global___TypenameMangling = TypenameMangling
class _DescriptorSize:
ValueType = typing.NewType("ValueType", builtins.int)
V: typing_extensions.TypeAlias = ValueType
class _DescriptorSizeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_DescriptorSize.ValueType], builtins.type):
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor
DS_AUTO: _DescriptorSize.ValueType # 0
"""Select minimal size based on field type"""
DS_1: _DescriptorSize.ValueType # 1
"""1 word; up to 15 byte fields, no arrays"""
DS_2: _DescriptorSize.ValueType # 2
"""2 words; up to 4095 byte fields, 4095 entry arrays"""
DS_4: _DescriptorSize.ValueType # 4
"""4 words; up to 2^32-1 byte fields, 2^16-1 entry arrays"""
DS_8: _DescriptorSize.ValueType # 8
"""8 words; up to 2^32-1 entry arrays"""
class DescriptorSize(_DescriptorSize, metaclass=_DescriptorSizeEnumTypeWrapper): ...
DS_AUTO: DescriptorSize.ValueType # 0
"""Select minimal size based on field type"""
DS_1: DescriptorSize.ValueType # 1
"""1 word; up to 15 byte fields, no arrays"""
DS_2: DescriptorSize.ValueType # 2
"""2 words; up to 4095 byte fields, 4095 entry arrays"""
DS_4: DescriptorSize.ValueType # 4
"""4 words; up to 2^32-1 byte fields, 2^16-1 entry arrays"""
DS_8: DescriptorSize.ValueType # 8
"""8 words; up to 2^32-1 entry arrays"""
global___DescriptorSize = DescriptorSize
@typing.final
class NanoPBOptions(google.protobuf.message.Message):
"""This is the inner options message, which basically defines options for
a field. When it is used in message or file scope, it applies to all
fields.
"""
DESCRIPTOR: google.protobuf.descriptor.Descriptor
MAX_SIZE_FIELD_NUMBER: builtins.int
MAX_LENGTH_FIELD_NUMBER: builtins.int
MAX_COUNT_FIELD_NUMBER: builtins.int
INT_SIZE_FIELD_NUMBER: builtins.int
TYPE_FIELD_NUMBER: builtins.int
LONG_NAMES_FIELD_NUMBER: builtins.int
PACKED_STRUCT_FIELD_NUMBER: builtins.int
PACKED_ENUM_FIELD_NUMBER: builtins.int
SKIP_MESSAGE_FIELD_NUMBER: builtins.int
NO_UNIONS_FIELD_NUMBER: builtins.int
MSGID_FIELD_NUMBER: builtins.int
ANONYMOUS_ONEOF_FIELD_NUMBER: builtins.int
PROTO3_FIELD_NUMBER: builtins.int
PROTO3_SINGULAR_MSGS_FIELD_NUMBER: builtins.int
ENUM_TO_STRING_FIELD_NUMBER: builtins.int
FIXED_LENGTH_FIELD_NUMBER: builtins.int
FIXED_COUNT_FIELD_NUMBER: builtins.int
SUBMSG_CALLBACK_FIELD_NUMBER: builtins.int
MANGLE_NAMES_FIELD_NUMBER: builtins.int
CALLBACK_DATATYPE_FIELD_NUMBER: builtins.int
CALLBACK_FUNCTION_FIELD_NUMBER: builtins.int
DESCRIPTORSIZE_FIELD_NUMBER: builtins.int
DEFAULT_HAS_FIELD_NUMBER: builtins.int
INCLUDE_FIELD_NUMBER: builtins.int
EXCLUDE_FIELD_NUMBER: builtins.int
PACKAGE_FIELD_NUMBER: builtins.int
TYPE_OVERRIDE_FIELD_NUMBER: builtins.int
SORT_BY_TAG_FIELD_NUMBER: builtins.int
FALLBACK_TYPE_FIELD_NUMBER: builtins.int
max_size: builtins.int
"""Allocated size for 'bytes' and 'string' fields.
For string fields, this should include the space for null terminator.
"""
max_length: builtins.int
"""Maximum length for 'string' fields. Setting this is equivalent
to setting max_size to a value of length+1.
"""
max_count: builtins.int
"""Allocated number of entries in arrays ('repeated' fields)"""
int_size: global___IntSize.ValueType
"""Size of integer fields. Can save some memory if you don't need
full 32 bits for the value.
"""
type: global___FieldType.ValueType
"""Force type of field (callback or static allocation)"""
long_names: builtins.bool
"""Use long names for enums, i.e. EnumName_EnumValue."""
packed_struct: builtins.bool
"""Add 'packed' attribute to generated structs.
Note: this cannot be used on CPUs that break on unaligned
accesses to variables.
"""
packed_enum: builtins.bool
"""Add 'packed' attribute to generated enums."""
skip_message: builtins.bool
"""Skip this message"""
no_unions: builtins.bool
"""Generate oneof fields as normal optional fields instead of union."""
msgid: builtins.int
"""integer type tag for a message"""
anonymous_oneof: builtins.bool
"""decode oneof as anonymous union"""
proto3: builtins.bool
"""Proto3 singular field does not generate a "has_" flag"""
proto3_singular_msgs: builtins.bool
"""Force proto3 messages to have no "has_" flag.
This was default behavior until nanopb-0.4.0.
"""
enum_to_string: builtins.bool
"""Generate an enum->string mapping function (can take up lots of space)."""
fixed_length: builtins.bool
"""Generate bytes arrays with fixed length"""
fixed_count: builtins.bool
"""Generate repeated field with fixed count"""
submsg_callback: builtins.bool
"""Generate message-level callback that is called before decoding submessages.
This can be used to set callback fields for submsgs inside oneofs.
"""
mangle_names: global___TypenameMangling.ValueType
"""Shorten or remove package names from type names.
This option applies only on the file level.
"""
callback_datatype: builtins.str
"""Data type for storage associated with callback fields."""
callback_function: builtins.str
"""Callback function used for encoding and decoding.
Prior to nanopb-0.4.0, the callback was specified in per-field pb_callback_t
structure. This is still supported, but does not work inside e.g. oneof or pointer
fields. Instead, a new method allows specifying a per-message callback that
will be called for all callback fields in a message type.
"""
descriptorsize: global___DescriptorSize.ValueType
"""Select the size of field descriptors. This option has to be defined
for the whole message, not per-field. Usually automatic selection is
ok, but if it results in compilation errors you can increase the field
size here.
"""
default_has: builtins.bool
"""Set default value for has_ fields."""
package: builtins.str
"""Package name that applies only for nanopb."""
type_override: google.protobuf.descriptor_pb2.FieldDescriptorProto.Type.ValueType
"""Override type of the field in generated C code. Only to be used with related field types"""
sort_by_tag: builtins.bool
"""Due to historical reasons, nanopb orders fields in structs by their tag number
instead of the order in .proto. Set this to false to keep the .proto order.
The default value will probably change to false in nanopb-0.5.0.
"""
fallback_type: global___FieldType.ValueType
"""Set the FT_DEFAULT field conversion strategy.
A field that can become a static member of a c struct (e.g. int, bool, etc)
will be a a static field.
Fields with dynamic length are converted to either a pointer or a callback.
"""
@property
def include(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]:
"""Extra files to include in generated `.pb.h`"""
@property
def exclude(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]:
"""Automatic includes to exclude from generated `.pb.h`
Same as nanopb_generator.py command line flag -x.
"""
def __init__(
self,
*,
max_size: builtins.int | None = ...,
max_length: builtins.int | None = ...,
max_count: builtins.int | None = ...,
int_size: global___IntSize.ValueType | None = ...,
type: global___FieldType.ValueType | None = ...,
long_names: builtins.bool | None = ...,
packed_struct: builtins.bool | None = ...,
packed_enum: builtins.bool | None = ...,
skip_message: builtins.bool | None = ...,
no_unions: builtins.bool | None = ...,
msgid: builtins.int | None = ...,
anonymous_oneof: builtins.bool | None = ...,
proto3: builtins.bool | None = ...,
proto3_singular_msgs: builtins.bool | None = ...,
enum_to_string: builtins.bool | None = ...,
fixed_length: builtins.bool | None = ...,
fixed_count: builtins.bool | None = ...,
submsg_callback: builtins.bool | None = ...,
mangle_names: global___TypenameMangling.ValueType | None = ...,
callback_datatype: builtins.str | None = ...,
callback_function: builtins.str | None = ...,
descriptorsize: global___DescriptorSize.ValueType | None = ...,
default_has: builtins.bool | None = ...,
include: collections.abc.Iterable[builtins.str] | None = ...,
exclude: collections.abc.Iterable[builtins.str] | None = ...,
package: builtins.str | None = ...,
type_override: google.protobuf.descriptor_pb2.FieldDescriptorProto.Type.ValueType | None = ...,
sort_by_tag: builtins.bool | None = ...,
fallback_type: global___FieldType.ValueType | None = ...,
) -> None: ...
def HasField(self, field_name: typing.Literal["anonymous_oneof", b"anonymous_oneof", "callback_datatype", b"callback_datatype", "callback_function", b"callback_function", "default_has", b"default_has", "descriptorsize", b"descriptorsize", "enum_to_string", b"enum_to_string", "fallback_type", b"fallback_type", "fixed_count", b"fixed_count", "fixed_length", b"fixed_length", "int_size", b"int_size", "long_names", b"long_names", "mangle_names", b"mangle_names", "max_count", b"max_count", "max_length", b"max_length", "max_size", b"max_size", "msgid", b"msgid", "no_unions", b"no_unions", "package", b"package", "packed_enum", b"packed_enum", "packed_struct", b"packed_struct", "proto3", b"proto3", "proto3_singular_msgs", b"proto3_singular_msgs", "skip_message", b"skip_message", "sort_by_tag", b"sort_by_tag", "submsg_callback", b"submsg_callback", "type", b"type", "type_override", b"type_override"]) -> builtins.bool: ...
def ClearField(self, field_name: typing.Literal["anonymous_oneof", b"anonymous_oneof", "callback_datatype", b"callback_datatype", "callback_function", b"callback_function", "default_has", b"default_has", "descriptorsize", b"descriptorsize", "enum_to_string", b"enum_to_string", "exclude", b"exclude", "fallback_type", b"fallback_type", "fixed_count", b"fixed_count", "fixed_length", b"fixed_length", "include", b"include", "int_size", b"int_size", "long_names", b"long_names", "mangle_names", b"mangle_names", "max_count", b"max_count", "max_length", b"max_length", "max_size", b"max_size", "msgid", b"msgid", "no_unions", b"no_unions", "package", b"package", "packed_enum", b"packed_enum", "packed_struct", b"packed_struct", "proto3", b"proto3", "proto3_singular_msgs", b"proto3_singular_msgs", "skip_message", b"skip_message", "sort_by_tag", b"sort_by_tag", "submsg_callback", b"submsg_callback", "type", b"type", "type_override", b"type_override"]) -> None: ...
global___NanoPBOptions = NanoPBOptions
NANOPB_FILEOPT_FIELD_NUMBER: builtins.int
NANOPB_MSGOPT_FIELD_NUMBER: builtins.int
NANOPB_ENUMOPT_FIELD_NUMBER: builtins.int
NANOPB_FIELD_NUMBER: builtins.int
nanopb_fileopt: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[google.protobuf.descriptor_pb2.FileOptions, global___NanoPBOptions]
nanopb_msgopt: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[google.protobuf.descriptor_pb2.MessageOptions, global___NanoPBOptions]
nanopb_enumopt: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[google.protobuf.descriptor_pb2.EnumOptions, global___NanoPBOptions]
nanopb: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[google.protobuf.descriptor_pb2.FieldOptions, global___NanoPBOptions]

View File

@@ -13,14 +13,14 @@ _sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\"meshtastic/protobuf/paxcount.proto\x12\x13meshtastic.protobuf\"5\n\x08Paxcount\x12\x0c\n\x04wifi\x18\x01 \x01(\r\x12\x0b\n\x03\x62le\x18\x02 \x01(\r\x12\x0e\n\x06uptime\x18\x03 \x01(\rBc\n\x13\x63om.geeksville.meshB\x0ePaxcountProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\"meshtastic/protobuf/paxcount.proto\x12\x13meshtastic.protobuf\"5\n\x08Paxcount\x12\x0c\n\x04wifi\x18\x01 \x01(\r\x12\x0b\n\x03\x62le\x18\x02 \x01(\r\x12\x0e\n\x06uptime\x18\x03 \x01(\rBd\n\x14org.meshtastic.protoB\x0ePaxcountProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.paxcount_pb2', _globals)
if _descriptor._USE_C_DESCRIPTORS == False:
DESCRIPTOR._options = None
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\016PaxcountProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\016PaxcountProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
_globals['_PAXCOUNT']._serialized_start=59
_globals['_PAXCOUNT']._serialized_end=112
# @@protoc_insertion_point(module_scope)

View File

@@ -13,14 +13,14 @@ _sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\"meshtastic/protobuf/portnums.proto\x12\x13meshtastic.protobuf*\xa2\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\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\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\x13\x63om.geeksville.meshB\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*\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')
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.portnums_pb2', _globals)
if _descriptor._USE_C_DESCRIPTORS == False:
DESCRIPTOR._options = None
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\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_end=606
_globals['_PORTNUM']._serialized_end=690
# @@protoc_insertion_point(module_scope)

View File

@@ -93,6 +93,14 @@ class _PortNumEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTy
Same as Text Message but originating from Detection Sensor Module.
NOTE: This portnum traffic is not sent to the public MQTT starting at firmware version 2.2.9
"""
ALERT_APP: _PortNum.ValueType # 11
"""
Same as Text Message but used for critical alerts.
"""
KEY_VERIFICATION_APP: _PortNum.ValueType # 12
"""
Module/port for handling key verification requests.
"""
REPLY_APP: _PortNum.ValueType # 32
"""
Provides a 'ping' service that replies to any packet it receives.
@@ -154,7 +162,7 @@ class _PortNumEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTy
TRACEROUTE_APP: _PortNum.ValueType # 70
"""
Provides a traceroute functionality to show the route a packet towards
a certain destination would take on the mesh.
a certain destination would take on the mesh. Contains a RouteDiscovery message as payload.
ENCODING: Protobuf
"""
NEIGHBORINFO_APP: _PortNum.ValueType # 71
@@ -175,6 +183,17 @@ class _PortNumEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTy
"""
PowerStress based monitoring support (for automated power consumption testing)
"""
RETICULUM_TUNNEL_APP: _PortNum.ValueType # 76
"""
Reticulum Network Stack Tunnel App
ENCODING: Fragmented RNS Packet. Handled by Meshtastic RNS interface
"""
CAYENNE_APP: _PortNum.ValueType # 77
"""
App for transporting Cayenne Low Power Payload, popular for LoRaWAN sensor nodes. Offers ability to send
arbitrary telemetry over meshtastic that is not covered by telemetry.proto
ENCODING: CayenneLLP
"""
PRIVATE_APP: _PortNum.ValueType # 256
"""
Private applications should use portnums >= 256.
@@ -278,6 +297,14 @@ DETECTION_SENSOR_APP: PortNum.ValueType # 10
Same as Text Message but originating from Detection Sensor Module.
NOTE: This portnum traffic is not sent to the public MQTT starting at firmware version 2.2.9
"""
ALERT_APP: PortNum.ValueType # 11
"""
Same as Text Message but used for critical alerts.
"""
KEY_VERIFICATION_APP: PortNum.ValueType # 12
"""
Module/port for handling key verification requests.
"""
REPLY_APP: PortNum.ValueType # 32
"""
Provides a 'ping' service that replies to any packet it receives.
@@ -339,7 +366,7 @@ ENCODING: Protobuf (?)
TRACEROUTE_APP: PortNum.ValueType # 70
"""
Provides a traceroute functionality to show the route a packet towards
a certain destination would take on the mesh.
a certain destination would take on the mesh. Contains a RouteDiscovery message as payload.
ENCODING: Protobuf
"""
NEIGHBORINFO_APP: PortNum.ValueType # 71
@@ -360,6 +387,17 @@ POWERSTRESS_APP: PortNum.ValueType # 74
"""
PowerStress based monitoring support (for automated power consumption testing)
"""
RETICULUM_TUNNEL_APP: PortNum.ValueType # 76
"""
Reticulum Network Stack Tunnel App
ENCODING: Fragmented RNS Packet. Handled by Meshtastic RNS interface
"""
CAYENNE_APP: PortNum.ValueType # 77
"""
App for transporting Cayenne Low Power Payload, popular for LoRaWAN sensor nodes. Offers ability to send
arbitrary telemetry over meshtastic that is not covered by telemetry.proto
ENCODING: CayenneLLP
"""
PRIVATE_APP: PortNum.ValueType # 256
"""
Private applications should use portnums >= 256.

View File

@@ -13,14 +13,14 @@ _sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\"meshtastic/protobuf/powermon.proto\x12\x13meshtastic.protobuf\"\xe0\x01\n\x08PowerMon\"\xd3\x01\n\x05State\x12\x08\n\x04None\x10\x00\x12\x11\n\rCPU_DeepSleep\x10\x01\x12\x12\n\x0e\x43PU_LightSleep\x10\x02\x12\x0c\n\x08Vext1_On\x10\x04\x12\r\n\tLora_RXOn\x10\x08\x12\r\n\tLora_TXOn\x10\x10\x12\x11\n\rLora_RXActive\x10 \x12\t\n\x05\x42T_On\x10@\x12\x0b\n\x06LED_On\x10\x80\x01\x12\x0e\n\tScreen_On\x10\x80\x02\x12\x13\n\x0eScreen_Drawing\x10\x80\x04\x12\x0c\n\x07Wifi_On\x10\x80\x08\x12\x0f\n\nGPS_Active\x10\x80\x10\"\x88\x03\n\x12PowerStressMessage\x12;\n\x03\x63md\x18\x01 \x01(\x0e\x32..meshtastic.protobuf.PowerStressMessage.Opcode\x12\x13\n\x0bnum_seconds\x18\x02 \x01(\x02\"\x9f\x02\n\x06Opcode\x12\t\n\x05UNSET\x10\x00\x12\x0e\n\nPRINT_INFO\x10\x01\x12\x0f\n\x0b\x46ORCE_QUIET\x10\x02\x12\r\n\tEND_QUIET\x10\x03\x12\r\n\tSCREEN_ON\x10\x10\x12\x0e\n\nSCREEN_OFF\x10\x11\x12\x0c\n\x08\x43PU_IDLE\x10 \x12\x11\n\rCPU_DEEPSLEEP\x10!\x12\x0e\n\nCPU_FULLON\x10\"\x12\n\n\x06LED_ON\x10\x30\x12\x0b\n\x07LED_OFF\x10\x31\x12\x0c\n\x08LORA_OFF\x10@\x12\x0b\n\x07LORA_TX\x10\x41\x12\x0b\n\x07LORA_RX\x10\x42\x12\n\n\x06\x42T_OFF\x10P\x12\t\n\x05\x42T_ON\x10Q\x12\x0c\n\x08WIFI_OFF\x10`\x12\x0b\n\x07WIFI_ON\x10\x61\x12\x0b\n\x07GPS_OFF\x10p\x12\n\n\x06GPS_ON\x10qBc\n\x13\x63om.geeksville.meshB\x0ePowerMonProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\"meshtastic/protobuf/powermon.proto\x12\x13meshtastic.protobuf\"\xe0\x01\n\x08PowerMon\"\xd3\x01\n\x05State\x12\x08\n\x04None\x10\x00\x12\x11\n\rCPU_DeepSleep\x10\x01\x12\x12\n\x0e\x43PU_LightSleep\x10\x02\x12\x0c\n\x08Vext1_On\x10\x04\x12\r\n\tLora_RXOn\x10\x08\x12\r\n\tLora_TXOn\x10\x10\x12\x11\n\rLora_RXActive\x10 \x12\t\n\x05\x42T_On\x10@\x12\x0b\n\x06LED_On\x10\x80\x01\x12\x0e\n\tScreen_On\x10\x80\x02\x12\x13\n\x0eScreen_Drawing\x10\x80\x04\x12\x0c\n\x07Wifi_On\x10\x80\x08\x12\x0f\n\nGPS_Active\x10\x80\x10\"\x88\x03\n\x12PowerStressMessage\x12;\n\x03\x63md\x18\x01 \x01(\x0e\x32..meshtastic.protobuf.PowerStressMessage.Opcode\x12\x13\n\x0bnum_seconds\x18\x02 \x01(\x02\"\x9f\x02\n\x06Opcode\x12\t\n\x05UNSET\x10\x00\x12\x0e\n\nPRINT_INFO\x10\x01\x12\x0f\n\x0b\x46ORCE_QUIET\x10\x02\x12\r\n\tEND_QUIET\x10\x03\x12\r\n\tSCREEN_ON\x10\x10\x12\x0e\n\nSCREEN_OFF\x10\x11\x12\x0c\n\x08\x43PU_IDLE\x10 \x12\x11\n\rCPU_DEEPSLEEP\x10!\x12\x0e\n\nCPU_FULLON\x10\"\x12\n\n\x06LED_ON\x10\x30\x12\x0b\n\x07LED_OFF\x10\x31\x12\x0c\n\x08LORA_OFF\x10@\x12\x0b\n\x07LORA_TX\x10\x41\x12\x0b\n\x07LORA_RX\x10\x42\x12\n\n\x06\x42T_OFF\x10P\x12\t\n\x05\x42T_ON\x10Q\x12\x0c\n\x08WIFI_OFF\x10`\x12\x0b\n\x07WIFI_ON\x10\x61\x12\x0b\n\x07GPS_OFF\x10p\x12\n\n\x06GPS_ON\x10qBd\n\x14org.meshtastic.protoB\x0ePowerMonProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.powermon_pb2', _globals)
if _descriptor._USE_C_DESCRIPTORS == False:
DESCRIPTOR._options = None
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\016PowerMonProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\016PowerMonProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
_globals['_POWERMON']._serialized_start=60
_globals['_POWERMON']._serialized_end=284
_globals['_POWERMON_STATE']._serialized_start=73

View File

@@ -13,14 +13,14 @@ _sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n)meshtastic/protobuf/remote_hardware.proto\x12\x13meshtastic.protobuf\"\xdf\x01\n\x0fHardwareMessage\x12\x37\n\x04type\x18\x01 \x01(\x0e\x32).meshtastic.protobuf.HardwareMessage.Type\x12\x11\n\tgpio_mask\x18\x02 \x01(\x04\x12\x12\n\ngpio_value\x18\x03 \x01(\x04\"l\n\x04Type\x12\t\n\x05UNSET\x10\x00\x12\x0f\n\x0bWRITE_GPIOS\x10\x01\x12\x0f\n\x0bWATCH_GPIOS\x10\x02\x12\x11\n\rGPIOS_CHANGED\x10\x03\x12\x0e\n\nREAD_GPIOS\x10\x04\x12\x14\n\x10READ_GPIOS_REPLY\x10\x05\x42\x63\n\x13\x63om.geeksville.meshB\x0eRemoteHardwareZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n)meshtastic/protobuf/remote_hardware.proto\x12\x13meshtastic.protobuf\"\xdf\x01\n\x0fHardwareMessage\x12\x37\n\x04type\x18\x01 \x01(\x0e\x32).meshtastic.protobuf.HardwareMessage.Type\x12\x11\n\tgpio_mask\x18\x02 \x01(\x04\x12\x12\n\ngpio_value\x18\x03 \x01(\x04\"l\n\x04Type\x12\t\n\x05UNSET\x10\x00\x12\x0f\n\x0bWRITE_GPIOS\x10\x01\x12\x0f\n\x0bWATCH_GPIOS\x10\x02\x12\x11\n\rGPIOS_CHANGED\x10\x03\x12\x0e\n\nREAD_GPIOS\x10\x04\x12\x14\n\x10READ_GPIOS_REPLY\x10\x05\x42\x64\n\x14org.meshtastic.protoB\x0eRemoteHardwareZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.remote_hardware_pb2', _globals)
if _descriptor._USE_C_DESCRIPTORS == False:
DESCRIPTOR._options = None
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\016RemoteHardwareZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\016RemoteHardwareZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
_globals['_HARDWAREMESSAGE']._serialized_start=67
_globals['_HARDWAREMESSAGE']._serialized_end=290
_globals['_HARDWAREMESSAGE_TYPE']._serialized_start=182

View File

@@ -13,14 +13,14 @@ _sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1fmeshtastic/protobuf/rtttl.proto\x12\x13meshtastic.protobuf\"\x1f\n\x0bRTTTLConfig\x12\x10\n\x08ringtone\x18\x01 \x01(\tBf\n\x13\x63om.geeksville.meshB\x11RTTTLConfigProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1fmeshtastic/protobuf/rtttl.proto\x12\x13meshtastic.protobuf\"\x1f\n\x0bRTTTLConfig\x12\x10\n\x08ringtone\x18\x01 \x01(\tBg\n\x14org.meshtastic.protoB\x11RTTTLConfigProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.rtttl_pb2', _globals)
if _descriptor._USE_C_DESCRIPTORS == False:
DESCRIPTOR._options = None
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\021RTTTLConfigProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\021RTTTLConfigProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
_globals['_RTTTLCONFIG']._serialized_start=56
_globals['_RTTTLCONFIG']._serialized_end=87
# @@protoc_insertion_point(module_scope)

View File

@@ -13,14 +13,14 @@ _sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n&meshtastic/protobuf/storeforward.proto\x12\x13meshtastic.protobuf\"\xc0\x07\n\x0fStoreAndForward\x12@\n\x02rr\x18\x01 \x01(\x0e\x32\x34.meshtastic.protobuf.StoreAndForward.RequestResponse\x12@\n\x05stats\x18\x02 \x01(\x0b\x32/.meshtastic.protobuf.StoreAndForward.StatisticsH\x00\x12?\n\x07history\x18\x03 \x01(\x0b\x32,.meshtastic.protobuf.StoreAndForward.HistoryH\x00\x12\x43\n\theartbeat\x18\x04 \x01(\x0b\x32..meshtastic.protobuf.StoreAndForward.HeartbeatH\x00\x12\x0e\n\x04text\x18\x05 \x01(\x0cH\x00\x1a\xcd\x01\n\nStatistics\x12\x16\n\x0emessages_total\x18\x01 \x01(\r\x12\x16\n\x0emessages_saved\x18\x02 \x01(\r\x12\x14\n\x0cmessages_max\x18\x03 \x01(\r\x12\x0f\n\x07up_time\x18\x04 \x01(\r\x12\x10\n\x08requests\x18\x05 \x01(\r\x12\x18\n\x10requests_history\x18\x06 \x01(\r\x12\x11\n\theartbeat\x18\x07 \x01(\x08\x12\x12\n\nreturn_max\x18\x08 \x01(\r\x12\x15\n\rreturn_window\x18\t \x01(\r\x1aI\n\x07History\x12\x18\n\x10history_messages\x18\x01 \x01(\r\x12\x0e\n\x06window\x18\x02 \x01(\r\x12\x14\n\x0clast_request\x18\x03 \x01(\r\x1a.\n\tHeartbeat\x12\x0e\n\x06period\x18\x01 \x01(\r\x12\x11\n\tsecondary\x18\x02 \x01(\r\"\xbc\x02\n\x0fRequestResponse\x12\t\n\x05UNSET\x10\x00\x12\x10\n\x0cROUTER_ERROR\x10\x01\x12\x14\n\x10ROUTER_HEARTBEAT\x10\x02\x12\x0f\n\x0bROUTER_PING\x10\x03\x12\x0f\n\x0bROUTER_PONG\x10\x04\x12\x0f\n\x0bROUTER_BUSY\x10\x05\x12\x12\n\x0eROUTER_HISTORY\x10\x06\x12\x10\n\x0cROUTER_STATS\x10\x07\x12\x16\n\x12ROUTER_TEXT_DIRECT\x10\x08\x12\x19\n\x15ROUTER_TEXT_BROADCAST\x10\t\x12\x10\n\x0c\x43LIENT_ERROR\x10@\x12\x12\n\x0e\x43LIENT_HISTORY\x10\x41\x12\x10\n\x0c\x43LIENT_STATS\x10\x42\x12\x0f\n\x0b\x43LIENT_PING\x10\x43\x12\x0f\n\x0b\x43LIENT_PONG\x10\x44\x12\x10\n\x0c\x43LIENT_ABORT\x10jB\t\n\x07variantBj\n\x13\x63om.geeksville.meshB\x15StoreAndForwardProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n&meshtastic/protobuf/storeforward.proto\x12\x13meshtastic.protobuf\"\xc0\x07\n\x0fStoreAndForward\x12@\n\x02rr\x18\x01 \x01(\x0e\x32\x34.meshtastic.protobuf.StoreAndForward.RequestResponse\x12@\n\x05stats\x18\x02 \x01(\x0b\x32/.meshtastic.protobuf.StoreAndForward.StatisticsH\x00\x12?\n\x07history\x18\x03 \x01(\x0b\x32,.meshtastic.protobuf.StoreAndForward.HistoryH\x00\x12\x43\n\theartbeat\x18\x04 \x01(\x0b\x32..meshtastic.protobuf.StoreAndForward.HeartbeatH\x00\x12\x0e\n\x04text\x18\x05 \x01(\x0cH\x00\x1a\xcd\x01\n\nStatistics\x12\x16\n\x0emessages_total\x18\x01 \x01(\r\x12\x16\n\x0emessages_saved\x18\x02 \x01(\r\x12\x14\n\x0cmessages_max\x18\x03 \x01(\r\x12\x0f\n\x07up_time\x18\x04 \x01(\r\x12\x10\n\x08requests\x18\x05 \x01(\r\x12\x18\n\x10requests_history\x18\x06 \x01(\r\x12\x11\n\theartbeat\x18\x07 \x01(\x08\x12\x12\n\nreturn_max\x18\x08 \x01(\r\x12\x15\n\rreturn_window\x18\t \x01(\r\x1aI\n\x07History\x12\x18\n\x10history_messages\x18\x01 \x01(\r\x12\x0e\n\x06window\x18\x02 \x01(\r\x12\x14\n\x0clast_request\x18\x03 \x01(\r\x1a.\n\tHeartbeat\x12\x0e\n\x06period\x18\x01 \x01(\r\x12\x11\n\tsecondary\x18\x02 \x01(\r\"\xbc\x02\n\x0fRequestResponse\x12\t\n\x05UNSET\x10\x00\x12\x10\n\x0cROUTER_ERROR\x10\x01\x12\x14\n\x10ROUTER_HEARTBEAT\x10\x02\x12\x0f\n\x0bROUTER_PING\x10\x03\x12\x0f\n\x0bROUTER_PONG\x10\x04\x12\x0f\n\x0bROUTER_BUSY\x10\x05\x12\x12\n\x0eROUTER_HISTORY\x10\x06\x12\x10\n\x0cROUTER_STATS\x10\x07\x12\x16\n\x12ROUTER_TEXT_DIRECT\x10\x08\x12\x19\n\x15ROUTER_TEXT_BROADCAST\x10\t\x12\x10\n\x0c\x43LIENT_ERROR\x10@\x12\x12\n\x0e\x43LIENT_HISTORY\x10\x41\x12\x10\n\x0c\x43LIENT_STATS\x10\x42\x12\x0f\n\x0b\x43LIENT_PING\x10\x43\x12\x0f\n\x0b\x43LIENT_PONG\x10\x44\x12\x10\n\x0c\x43LIENT_ABORT\x10jB\t\n\x07variantBk\n\x14org.meshtastic.protoB\x15StoreAndForwardProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.storeforward_pb2', _globals)
if _descriptor._USE_C_DESCRIPTORS == False:
DESCRIPTOR._options = None
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\025StoreAndForwardProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\025StoreAndForwardProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
_globals['_STOREANDFORWARD']._serialized_start=64
_globals['_STOREANDFORWARD']._serialized_end=1024
_globals['_STOREANDFORWARD_STATISTICS']._serialized_start=366

View File

File diff suppressed because one or more lines are too long

View File

@@ -127,6 +127,86 @@ class _TelemetrySensorTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wra
"""
NAU7802 Scale Chip or compatible
"""
BMP3XX: _TelemetrySensorType.ValueType # 26
"""
BMP3XX High accuracy temperature and pressure
"""
ICM20948: _TelemetrySensorType.ValueType # 27
"""
ICM-20948 9-Axis digital motion processor
"""
MAX17048: _TelemetrySensorType.ValueType # 28
"""
MAX17048 1S lipo battery sensor (voltage, state of charge, time to go)
"""
CUSTOM_SENSOR: _TelemetrySensorType.ValueType # 29
"""
Custom I2C sensor implementation based on https://github.com/meshtastic/i2c-sensor
"""
MAX30102: _TelemetrySensorType.ValueType # 30
"""
MAX30102 Pulse Oximeter and Heart-Rate Sensor
"""
MLX90614: _TelemetrySensorType.ValueType # 31
"""
MLX90614 non-contact IR temperature sensor
"""
SCD4X: _TelemetrySensorType.ValueType # 32
"""
SCD40/SCD41 CO2, humidity, temperature sensor
"""
RADSENS: _TelemetrySensorType.ValueType # 33
"""
ClimateGuard RadSens, radiation, Geiger-Muller Tube
"""
INA226: _TelemetrySensorType.ValueType # 34
"""
High accuracy current and voltage
"""
DFROBOT_RAIN: _TelemetrySensorType.ValueType # 35
"""
DFRobot Gravity tipping bucket rain gauge
"""
DPS310: _TelemetrySensorType.ValueType # 36
"""
Infineon DPS310 High accuracy pressure and temperature
"""
RAK12035: _TelemetrySensorType.ValueType # 37
"""
RAKWireless RAK12035 Soil Moisture Sensor Module
"""
MAX17261: _TelemetrySensorType.ValueType # 38
"""
MAX17261 lipo battery gauge
"""
PCT2075: _TelemetrySensorType.ValueType # 39
"""
PCT2075 Temperature Sensor
"""
ADS1X15: _TelemetrySensorType.ValueType # 40
"""
ADS1X15 ADC
"""
ADS1X15_ALT: _TelemetrySensorType.ValueType # 41
"""
ADS1X15 ADC_ALT
"""
SFA30: _TelemetrySensorType.ValueType # 42
"""
Sensirion SFA30 Formaldehyde sensor
"""
SEN5X: _TelemetrySensorType.ValueType # 43
"""
SEN5X PM SENSORS
"""
TSL2561: _TelemetrySensorType.ValueType # 44
"""
TSL2561 light sensor
"""
BH1750: _TelemetrySensorType.ValueType # 45
"""
BH1750 light sensor
"""
class TelemetrySensorType(_TelemetrySensorType, metaclass=_TelemetrySensorTypeEnumTypeWrapper):
"""
@@ -237,6 +317,86 @@ NAU7802: TelemetrySensorType.ValueType # 25
"""
NAU7802 Scale Chip or compatible
"""
BMP3XX: TelemetrySensorType.ValueType # 26
"""
BMP3XX High accuracy temperature and pressure
"""
ICM20948: TelemetrySensorType.ValueType # 27
"""
ICM-20948 9-Axis digital motion processor
"""
MAX17048: TelemetrySensorType.ValueType # 28
"""
MAX17048 1S lipo battery sensor (voltage, state of charge, time to go)
"""
CUSTOM_SENSOR: TelemetrySensorType.ValueType # 29
"""
Custom I2C sensor implementation based on https://github.com/meshtastic/i2c-sensor
"""
MAX30102: TelemetrySensorType.ValueType # 30
"""
MAX30102 Pulse Oximeter and Heart-Rate Sensor
"""
MLX90614: TelemetrySensorType.ValueType # 31
"""
MLX90614 non-contact IR temperature sensor
"""
SCD4X: TelemetrySensorType.ValueType # 32
"""
SCD40/SCD41 CO2, humidity, temperature sensor
"""
RADSENS: TelemetrySensorType.ValueType # 33
"""
ClimateGuard RadSens, radiation, Geiger-Muller Tube
"""
INA226: TelemetrySensorType.ValueType # 34
"""
High accuracy current and voltage
"""
DFROBOT_RAIN: TelemetrySensorType.ValueType # 35
"""
DFRobot Gravity tipping bucket rain gauge
"""
DPS310: TelemetrySensorType.ValueType # 36
"""
Infineon DPS310 High accuracy pressure and temperature
"""
RAK12035: TelemetrySensorType.ValueType # 37
"""
RAKWireless RAK12035 Soil Moisture Sensor Module
"""
MAX17261: TelemetrySensorType.ValueType # 38
"""
MAX17261 lipo battery gauge
"""
PCT2075: TelemetrySensorType.ValueType # 39
"""
PCT2075 Temperature Sensor
"""
ADS1X15: TelemetrySensorType.ValueType # 40
"""
ADS1X15 ADC
"""
ADS1X15_ALT: TelemetrySensorType.ValueType # 41
"""
ADS1X15 ADC_ALT
"""
SFA30: TelemetrySensorType.ValueType # 42
"""
Sensirion SFA30 Formaldehyde sensor
"""
SEN5X: TelemetrySensorType.ValueType # 43
"""
SEN5X PM SENSORS
"""
TSL2561: TelemetrySensorType.ValueType # 44
"""
TSL2561 light sensor
"""
BH1750: TelemetrySensorType.ValueType # 45
"""
BH1750 light sensor
"""
global___TelemetrySensorType = TelemetrySensorType
@typing.final
@@ -275,13 +435,24 @@ class DeviceMetrics(google.protobuf.message.Message):
def __init__(
self,
*,
battery_level: builtins.int = ...,
voltage: builtins.float = ...,
channel_utilization: builtins.float = ...,
air_util_tx: builtins.float = ...,
uptime_seconds: builtins.int = ...,
battery_level: builtins.int | None = ...,
voltage: builtins.float | None = ...,
channel_utilization: builtins.float | None = ...,
air_util_tx: builtins.float | None = ...,
uptime_seconds: builtins.int | None = ...,
) -> None: ...
def ClearField(self, field_name: typing.Literal["air_util_tx", b"air_util_tx", "battery_level", b"battery_level", "channel_utilization", b"channel_utilization", "uptime_seconds", b"uptime_seconds", "voltage", b"voltage"]) -> None: ...
def HasField(self, field_name: typing.Literal["_air_util_tx", b"_air_util_tx", "_battery_level", b"_battery_level", "_channel_utilization", b"_channel_utilization", "_uptime_seconds", b"_uptime_seconds", "_voltage", b"_voltage", "air_util_tx", b"air_util_tx", "battery_level", b"battery_level", "channel_utilization", b"channel_utilization", "uptime_seconds", b"uptime_seconds", "voltage", b"voltage"]) -> builtins.bool: ...
def ClearField(self, field_name: typing.Literal["_air_util_tx", b"_air_util_tx", "_battery_level", b"_battery_level", "_channel_utilization", b"_channel_utilization", "_uptime_seconds", b"_uptime_seconds", "_voltage", b"_voltage", "air_util_tx", b"air_util_tx", "battery_level", b"battery_level", "channel_utilization", b"channel_utilization", "uptime_seconds", b"uptime_seconds", "voltage", b"voltage"]) -> None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_air_util_tx", b"_air_util_tx"]) -> typing.Literal["air_util_tx"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_battery_level", b"_battery_level"]) -> typing.Literal["battery_level"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_channel_utilization", b"_channel_utilization"]) -> typing.Literal["channel_utilization"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_uptime_seconds", b"_uptime_seconds"]) -> typing.Literal["uptime_seconds"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_voltage", b"_voltage"]) -> typing.Literal["voltage"] | None: ...
global___DeviceMetrics = DeviceMetrics
@@ -310,6 +481,11 @@ class EnvironmentMetrics(google.protobuf.message.Message):
WEIGHT_FIELD_NUMBER: builtins.int
WIND_GUST_FIELD_NUMBER: builtins.int
WIND_LULL_FIELD_NUMBER: builtins.int
RADIATION_FIELD_NUMBER: builtins.int
RAINFALL_1H_FIELD_NUMBER: builtins.int
RAINFALL_24H_FIELD_NUMBER: builtins.int
SOIL_MOISTURE_FIELD_NUMBER: builtins.int
SOIL_TEMPERATURE_FIELD_NUMBER: builtins.int
temperature: builtins.float
"""
Temperature measured
@@ -380,28 +556,98 @@ class EnvironmentMetrics(google.protobuf.message.Message):
"""
Wind lull in m/s
"""
radiation: builtins.float
"""
Radiation in µR/h
"""
rainfall_1h: builtins.float
"""
Rainfall in the last hour in mm
"""
rainfall_24h: builtins.float
"""
Rainfall in the last 24 hours in mm
"""
soil_moisture: builtins.int
"""
Soil moisture measured (% 1-100)
"""
soil_temperature: builtins.float
"""
Soil temperature measured (*C)
"""
def __init__(
self,
*,
temperature: builtins.float = ...,
relative_humidity: builtins.float = ...,
barometric_pressure: builtins.float = ...,
gas_resistance: builtins.float = ...,
voltage: builtins.float = ...,
current: builtins.float = ...,
iaq: builtins.int = ...,
distance: builtins.float = ...,
lux: builtins.float = ...,
white_lux: builtins.float = ...,
ir_lux: builtins.float = ...,
uv_lux: builtins.float = ...,
wind_direction: builtins.int = ...,
wind_speed: builtins.float = ...,
weight: builtins.float = ...,
wind_gust: builtins.float = ...,
wind_lull: builtins.float = ...,
temperature: builtins.float | None = ...,
relative_humidity: builtins.float | None = ...,
barometric_pressure: builtins.float | None = ...,
gas_resistance: builtins.float | None = ...,
voltage: builtins.float | None = ...,
current: builtins.float | None = ...,
iaq: builtins.int | None = ...,
distance: builtins.float | None = ...,
lux: builtins.float | None = ...,
white_lux: builtins.float | None = ...,
ir_lux: builtins.float | None = ...,
uv_lux: builtins.float | None = ...,
wind_direction: builtins.int | None = ...,
wind_speed: builtins.float | None = ...,
weight: builtins.float | None = ...,
wind_gust: builtins.float | None = ...,
wind_lull: builtins.float | None = ...,
radiation: builtins.float | None = ...,
rainfall_1h: builtins.float | None = ...,
rainfall_24h: builtins.float | None = ...,
soil_moisture: builtins.int | None = ...,
soil_temperature: builtins.float | None = ...,
) -> None: ...
def ClearField(self, field_name: typing.Literal["barometric_pressure", b"barometric_pressure", "current", b"current", "distance", b"distance", "gas_resistance", b"gas_resistance", "iaq", b"iaq", "ir_lux", b"ir_lux", "lux", b"lux", "relative_humidity", b"relative_humidity", "temperature", b"temperature", "uv_lux", b"uv_lux", "voltage", b"voltage", "weight", b"weight", "white_lux", b"white_lux", "wind_direction", b"wind_direction", "wind_gust", b"wind_gust", "wind_lull", b"wind_lull", "wind_speed", b"wind_speed"]) -> None: ...
def HasField(self, field_name: typing.Literal["_barometric_pressure", b"_barometric_pressure", "_current", b"_current", "_distance", b"_distance", "_gas_resistance", b"_gas_resistance", "_iaq", b"_iaq", "_ir_lux", b"_ir_lux", "_lux", b"_lux", "_radiation", b"_radiation", "_rainfall_1h", b"_rainfall_1h", "_rainfall_24h", b"_rainfall_24h", "_relative_humidity", b"_relative_humidity", "_soil_moisture", b"_soil_moisture", "_soil_temperature", b"_soil_temperature", "_temperature", b"_temperature", "_uv_lux", b"_uv_lux", "_voltage", b"_voltage", "_weight", b"_weight", "_white_lux", b"_white_lux", "_wind_direction", b"_wind_direction", "_wind_gust", b"_wind_gust", "_wind_lull", b"_wind_lull", "_wind_speed", b"_wind_speed", "barometric_pressure", b"barometric_pressure", "current", b"current", "distance", b"distance", "gas_resistance", b"gas_resistance", "iaq", b"iaq", "ir_lux", b"ir_lux", "lux", b"lux", "radiation", b"radiation", "rainfall_1h", b"rainfall_1h", "rainfall_24h", b"rainfall_24h", "relative_humidity", b"relative_humidity", "soil_moisture", b"soil_moisture", "soil_temperature", b"soil_temperature", "temperature", b"temperature", "uv_lux", b"uv_lux", "voltage", b"voltage", "weight", b"weight", "white_lux", b"white_lux", "wind_direction", b"wind_direction", "wind_gust", b"wind_gust", "wind_lull", b"wind_lull", "wind_speed", b"wind_speed"]) -> builtins.bool: ...
def ClearField(self, field_name: typing.Literal["_barometric_pressure", b"_barometric_pressure", "_current", b"_current", "_distance", b"_distance", "_gas_resistance", b"_gas_resistance", "_iaq", b"_iaq", "_ir_lux", b"_ir_lux", "_lux", b"_lux", "_radiation", b"_radiation", "_rainfall_1h", b"_rainfall_1h", "_rainfall_24h", b"_rainfall_24h", "_relative_humidity", b"_relative_humidity", "_soil_moisture", b"_soil_moisture", "_soil_temperature", b"_soil_temperature", "_temperature", b"_temperature", "_uv_lux", b"_uv_lux", "_voltage", b"_voltage", "_weight", b"_weight", "_white_lux", b"_white_lux", "_wind_direction", b"_wind_direction", "_wind_gust", b"_wind_gust", "_wind_lull", b"_wind_lull", "_wind_speed", b"_wind_speed", "barometric_pressure", b"barometric_pressure", "current", b"current", "distance", b"distance", "gas_resistance", b"gas_resistance", "iaq", b"iaq", "ir_lux", b"ir_lux", "lux", b"lux", "radiation", b"radiation", "rainfall_1h", b"rainfall_1h", "rainfall_24h", b"rainfall_24h", "relative_humidity", b"relative_humidity", "soil_moisture", b"soil_moisture", "soil_temperature", b"soil_temperature", "temperature", b"temperature", "uv_lux", b"uv_lux", "voltage", b"voltage", "weight", b"weight", "white_lux", b"white_lux", "wind_direction", b"wind_direction", "wind_gust", b"wind_gust", "wind_lull", b"wind_lull", "wind_speed", b"wind_speed"]) -> None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_barometric_pressure", b"_barometric_pressure"]) -> typing.Literal["barometric_pressure"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_current", b"_current"]) -> typing.Literal["current"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_distance", b"_distance"]) -> typing.Literal["distance"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_gas_resistance", b"_gas_resistance"]) -> typing.Literal["gas_resistance"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_iaq", b"_iaq"]) -> typing.Literal["iaq"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_ir_lux", b"_ir_lux"]) -> typing.Literal["ir_lux"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_lux", b"_lux"]) -> typing.Literal["lux"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_radiation", b"_radiation"]) -> typing.Literal["radiation"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_rainfall_1h", b"_rainfall_1h"]) -> typing.Literal["rainfall_1h"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_rainfall_24h", b"_rainfall_24h"]) -> typing.Literal["rainfall_24h"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_relative_humidity", b"_relative_humidity"]) -> typing.Literal["relative_humidity"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_soil_moisture", b"_soil_moisture"]) -> typing.Literal["soil_moisture"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_soil_temperature", b"_soil_temperature"]) -> typing.Literal["soil_temperature"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_temperature", b"_temperature"]) -> typing.Literal["temperature"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_uv_lux", b"_uv_lux"]) -> typing.Literal["uv_lux"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_voltage", b"_voltage"]) -> typing.Literal["voltage"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_weight", b"_weight"]) -> typing.Literal["weight"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_white_lux", b"_white_lux"]) -> typing.Literal["white_lux"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_wind_direction", b"_wind_direction"]) -> typing.Literal["wind_direction"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_wind_gust", b"_wind_gust"]) -> typing.Literal["wind_gust"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_wind_lull", b"_wind_lull"]) -> typing.Literal["wind_lull"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_wind_speed", b"_wind_speed"]) -> typing.Literal["wind_speed"] | None: ...
global___EnvironmentMetrics = EnvironmentMetrics
@@ -419,6 +665,16 @@ class PowerMetrics(google.protobuf.message.Message):
CH2_CURRENT_FIELD_NUMBER: builtins.int
CH3_VOLTAGE_FIELD_NUMBER: builtins.int
CH3_CURRENT_FIELD_NUMBER: builtins.int
CH4_VOLTAGE_FIELD_NUMBER: builtins.int
CH4_CURRENT_FIELD_NUMBER: builtins.int
CH5_VOLTAGE_FIELD_NUMBER: builtins.int
CH5_CURRENT_FIELD_NUMBER: builtins.int
CH6_VOLTAGE_FIELD_NUMBER: builtins.int
CH6_CURRENT_FIELD_NUMBER: builtins.int
CH7_VOLTAGE_FIELD_NUMBER: builtins.int
CH7_CURRENT_FIELD_NUMBER: builtins.int
CH8_VOLTAGE_FIELD_NUMBER: builtins.int
CH8_CURRENT_FIELD_NUMBER: builtins.int
ch1_voltage: builtins.float
"""
Voltage (Ch1)
@@ -443,17 +699,100 @@ class PowerMetrics(google.protobuf.message.Message):
"""
Current (Ch3)
"""
ch4_voltage: builtins.float
"""
Voltage (Ch4)
"""
ch4_current: builtins.float
"""
Current (Ch4)
"""
ch5_voltage: builtins.float
"""
Voltage (Ch5)
"""
ch5_current: builtins.float
"""
Current (Ch5)
"""
ch6_voltage: builtins.float
"""
Voltage (Ch6)
"""
ch6_current: builtins.float
"""
Current (Ch6)
"""
ch7_voltage: builtins.float
"""
Voltage (Ch7)
"""
ch7_current: builtins.float
"""
Current (Ch7)
"""
ch8_voltage: builtins.float
"""
Voltage (Ch8)
"""
ch8_current: builtins.float
"""
Current (Ch8)
"""
def __init__(
self,
*,
ch1_voltage: builtins.float = ...,
ch1_current: builtins.float = ...,
ch2_voltage: builtins.float = ...,
ch2_current: builtins.float = ...,
ch3_voltage: builtins.float = ...,
ch3_current: builtins.float = ...,
ch1_voltage: builtins.float | None = ...,
ch1_current: builtins.float | None = ...,
ch2_voltage: builtins.float | None = ...,
ch2_current: builtins.float | None = ...,
ch3_voltage: builtins.float | None = ...,
ch3_current: builtins.float | None = ...,
ch4_voltage: builtins.float | None = ...,
ch4_current: builtins.float | None = ...,
ch5_voltage: builtins.float | None = ...,
ch5_current: builtins.float | None = ...,
ch6_voltage: builtins.float | None = ...,
ch6_current: builtins.float | None = ...,
ch7_voltage: builtins.float | None = ...,
ch7_current: builtins.float | None = ...,
ch8_voltage: builtins.float | None = ...,
ch8_current: builtins.float | None = ...,
) -> None: ...
def ClearField(self, field_name: typing.Literal["ch1_current", b"ch1_current", "ch1_voltage", b"ch1_voltage", "ch2_current", b"ch2_current", "ch2_voltage", b"ch2_voltage", "ch3_current", b"ch3_current", "ch3_voltage", b"ch3_voltage"]) -> None: ...
def HasField(self, field_name: typing.Literal["_ch1_current", b"_ch1_current", "_ch1_voltage", b"_ch1_voltage", "_ch2_current", b"_ch2_current", "_ch2_voltage", b"_ch2_voltage", "_ch3_current", b"_ch3_current", "_ch3_voltage", b"_ch3_voltage", "_ch4_current", b"_ch4_current", "_ch4_voltage", b"_ch4_voltage", "_ch5_current", b"_ch5_current", "_ch5_voltage", b"_ch5_voltage", "_ch6_current", b"_ch6_current", "_ch6_voltage", b"_ch6_voltage", "_ch7_current", b"_ch7_current", "_ch7_voltage", b"_ch7_voltage", "_ch8_current", b"_ch8_current", "_ch8_voltage", b"_ch8_voltage", "ch1_current", b"ch1_current", "ch1_voltage", b"ch1_voltage", "ch2_current", b"ch2_current", "ch2_voltage", b"ch2_voltage", "ch3_current", b"ch3_current", "ch3_voltage", b"ch3_voltage", "ch4_current", b"ch4_current", "ch4_voltage", b"ch4_voltage", "ch5_current", b"ch5_current", "ch5_voltage", b"ch5_voltage", "ch6_current", b"ch6_current", "ch6_voltage", b"ch6_voltage", "ch7_current", b"ch7_current", "ch7_voltage", b"ch7_voltage", "ch8_current", b"ch8_current", "ch8_voltage", b"ch8_voltage"]) -> builtins.bool: ...
def ClearField(self, field_name: typing.Literal["_ch1_current", b"_ch1_current", "_ch1_voltage", b"_ch1_voltage", "_ch2_current", b"_ch2_current", "_ch2_voltage", b"_ch2_voltage", "_ch3_current", b"_ch3_current", "_ch3_voltage", b"_ch3_voltage", "_ch4_current", b"_ch4_current", "_ch4_voltage", b"_ch4_voltage", "_ch5_current", b"_ch5_current", "_ch5_voltage", b"_ch5_voltage", "_ch6_current", b"_ch6_current", "_ch6_voltage", b"_ch6_voltage", "_ch7_current", b"_ch7_current", "_ch7_voltage", b"_ch7_voltage", "_ch8_current", b"_ch8_current", "_ch8_voltage", b"_ch8_voltage", "ch1_current", b"ch1_current", "ch1_voltage", b"ch1_voltage", "ch2_current", b"ch2_current", "ch2_voltage", b"ch2_voltage", "ch3_current", b"ch3_current", "ch3_voltage", b"ch3_voltage", "ch4_current", b"ch4_current", "ch4_voltage", b"ch4_voltage", "ch5_current", b"ch5_current", "ch5_voltage", b"ch5_voltage", "ch6_current", b"ch6_current", "ch6_voltage", b"ch6_voltage", "ch7_current", b"ch7_current", "ch7_voltage", b"ch7_voltage", "ch8_current", b"ch8_current", "ch8_voltage", b"ch8_voltage"]) -> None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_ch1_current", b"_ch1_current"]) -> typing.Literal["ch1_current"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_ch1_voltage", b"_ch1_voltage"]) -> typing.Literal["ch1_voltage"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_ch2_current", b"_ch2_current"]) -> typing.Literal["ch2_current"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_ch2_voltage", b"_ch2_voltage"]) -> typing.Literal["ch2_voltage"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_ch3_current", b"_ch3_current"]) -> typing.Literal["ch3_current"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_ch3_voltage", b"_ch3_voltage"]) -> typing.Literal["ch3_voltage"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_ch4_current", b"_ch4_current"]) -> typing.Literal["ch4_current"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_ch4_voltage", b"_ch4_voltage"]) -> typing.Literal["ch4_voltage"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_ch5_current", b"_ch5_current"]) -> typing.Literal["ch5_current"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_ch5_voltage", b"_ch5_voltage"]) -> typing.Literal["ch5_voltage"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_ch6_current", b"_ch6_current"]) -> typing.Literal["ch6_current"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_ch6_voltage", b"_ch6_voltage"]) -> typing.Literal["ch6_voltage"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_ch7_current", b"_ch7_current"]) -> typing.Literal["ch7_current"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_ch7_voltage", b"_ch7_voltage"]) -> typing.Literal["ch7_voltage"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_ch8_current", b"_ch8_current"]) -> typing.Literal["ch8_current"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_ch8_voltage", b"_ch8_voltage"]) -> typing.Literal["ch8_voltage"] | None: ...
global___PowerMetrics = PowerMetrics
@@ -477,74 +816,424 @@ class AirQualityMetrics(google.protobuf.message.Message):
PARTICLES_25UM_FIELD_NUMBER: builtins.int
PARTICLES_50UM_FIELD_NUMBER: builtins.int
PARTICLES_100UM_FIELD_NUMBER: builtins.int
CO2_FIELD_NUMBER: builtins.int
CO2_TEMPERATURE_FIELD_NUMBER: builtins.int
CO2_HUMIDITY_FIELD_NUMBER: builtins.int
FORM_FORMALDEHYDE_FIELD_NUMBER: builtins.int
FORM_HUMIDITY_FIELD_NUMBER: builtins.int
FORM_TEMPERATURE_FIELD_NUMBER: builtins.int
PM40_STANDARD_FIELD_NUMBER: builtins.int
PARTICLES_40UM_FIELD_NUMBER: builtins.int
PM_TEMPERATURE_FIELD_NUMBER: builtins.int
PM_HUMIDITY_FIELD_NUMBER: builtins.int
PM_VOC_IDX_FIELD_NUMBER: builtins.int
PM_NOX_IDX_FIELD_NUMBER: builtins.int
PARTICLES_TPS_FIELD_NUMBER: builtins.int
pm10_standard: builtins.int
"""
Concentration Units Standard PM1.0
Concentration Units Standard PM1.0 in ug/m3
"""
pm25_standard: builtins.int
"""
Concentration Units Standard PM2.5
Concentration Units Standard PM2.5 in ug/m3
"""
pm100_standard: builtins.int
"""
Concentration Units Standard PM10.0
Concentration Units Standard PM10.0 in ug/m3
"""
pm10_environmental: builtins.int
"""
Concentration Units Environmental PM1.0
Concentration Units Environmental PM1.0 in ug/m3
"""
pm25_environmental: builtins.int
"""
Concentration Units Environmental PM2.5
Concentration Units Environmental PM2.5 in ug/m3
"""
pm100_environmental: builtins.int
"""
Concentration Units Environmental PM10.0
Concentration Units Environmental PM10.0 in ug/m3
"""
particles_03um: builtins.int
"""
0.3um Particle Count
0.3um Particle Count in #/0.1l
"""
particles_05um: builtins.int
"""
0.5um Particle Count
0.5um Particle Count in #/0.1l
"""
particles_10um: builtins.int
"""
1.0um Particle Count
1.0um Particle Count in #/0.1l
"""
particles_25um: builtins.int
"""
2.5um Particle Count
2.5um Particle Count in #/0.1l
"""
particles_50um: builtins.int
"""
5.0um Particle Count
5.0um Particle Count in #/0.1l
"""
particles_100um: builtins.int
"""
10.0um Particle Count
10.0um Particle Count in #/0.1l
"""
co2: builtins.int
"""
CO2 concentration in ppm
"""
co2_temperature: builtins.float
"""
CO2 sensor temperature in degC
"""
co2_humidity: builtins.float
"""
CO2 sensor relative humidity in %
"""
form_formaldehyde: builtins.float
"""
Formaldehyde sensor formaldehyde concentration in ppb
"""
form_humidity: builtins.float
"""
Formaldehyde sensor relative humidity in %RH
"""
form_temperature: builtins.float
"""
Formaldehyde sensor temperature in degrees Celsius
"""
pm40_standard: builtins.int
"""
Concentration Units Standard PM4.0 in ug/m3
"""
particles_40um: builtins.int
"""
4.0um Particle Count in #/0.1l
"""
pm_temperature: builtins.float
"""
PM Sensor Temperature
"""
pm_humidity: builtins.float
"""
PM Sensor humidity
"""
pm_voc_idx: builtins.float
"""
PM Sensor VOC Index
"""
pm_nox_idx: builtins.float
"""
PM Sensor NOx Index
"""
particles_tps: builtins.float
"""
Typical Particle Size in um
"""
def __init__(
self,
*,
pm10_standard: builtins.int = ...,
pm25_standard: builtins.int = ...,
pm100_standard: builtins.int = ...,
pm10_environmental: builtins.int = ...,
pm25_environmental: builtins.int = ...,
pm100_environmental: builtins.int = ...,
particles_03um: builtins.int = ...,
particles_05um: builtins.int = ...,
particles_10um: builtins.int = ...,
particles_25um: builtins.int = ...,
particles_50um: builtins.int = ...,
particles_100um: builtins.int = ...,
pm10_standard: builtins.int | None = ...,
pm25_standard: builtins.int | None = ...,
pm100_standard: builtins.int | None = ...,
pm10_environmental: builtins.int | None = ...,
pm25_environmental: builtins.int | None = ...,
pm100_environmental: builtins.int | None = ...,
particles_03um: builtins.int | None = ...,
particles_05um: builtins.int | None = ...,
particles_10um: builtins.int | None = ...,
particles_25um: builtins.int | None = ...,
particles_50um: builtins.int | None = ...,
particles_100um: builtins.int | None = ...,
co2: builtins.int | None = ...,
co2_temperature: builtins.float | None = ...,
co2_humidity: builtins.float | None = ...,
form_formaldehyde: builtins.float | None = ...,
form_humidity: builtins.float | None = ...,
form_temperature: builtins.float | None = ...,
pm40_standard: builtins.int | None = ...,
particles_40um: builtins.int | None = ...,
pm_temperature: builtins.float | None = ...,
pm_humidity: builtins.float | None = ...,
pm_voc_idx: builtins.float | None = ...,
pm_nox_idx: builtins.float | None = ...,
particles_tps: builtins.float | None = ...,
) -> None: ...
def ClearField(self, field_name: typing.Literal["particles_03um", b"particles_03um", "particles_05um", b"particles_05um", "particles_100um", b"particles_100um", "particles_10um", b"particles_10um", "particles_25um", b"particles_25um", "particles_50um", b"particles_50um", "pm100_environmental", b"pm100_environmental", "pm100_standard", b"pm100_standard", "pm10_environmental", b"pm10_environmental", "pm10_standard", b"pm10_standard", "pm25_environmental", b"pm25_environmental", "pm25_standard", b"pm25_standard"]) -> None: ...
def HasField(self, field_name: typing.Literal["_co2", b"_co2", "_co2_humidity", b"_co2_humidity", "_co2_temperature", b"_co2_temperature", "_form_formaldehyde", b"_form_formaldehyde", "_form_humidity", b"_form_humidity", "_form_temperature", b"_form_temperature", "_particles_03um", b"_particles_03um", "_particles_05um", b"_particles_05um", "_particles_100um", b"_particles_100um", "_particles_10um", b"_particles_10um", "_particles_25um", b"_particles_25um", "_particles_40um", b"_particles_40um", "_particles_50um", b"_particles_50um", "_particles_tps", b"_particles_tps", "_pm100_environmental", b"_pm100_environmental", "_pm100_standard", b"_pm100_standard", "_pm10_environmental", b"_pm10_environmental", "_pm10_standard", b"_pm10_standard", "_pm25_environmental", b"_pm25_environmental", "_pm25_standard", b"_pm25_standard", "_pm40_standard", b"_pm40_standard", "_pm_humidity", b"_pm_humidity", "_pm_nox_idx", b"_pm_nox_idx", "_pm_temperature", b"_pm_temperature", "_pm_voc_idx", b"_pm_voc_idx", "co2", b"co2", "co2_humidity", b"co2_humidity", "co2_temperature", b"co2_temperature", "form_formaldehyde", b"form_formaldehyde", "form_humidity", b"form_humidity", "form_temperature", b"form_temperature", "particles_03um", b"particles_03um", "particles_05um", b"particles_05um", "particles_100um", b"particles_100um", "particles_10um", b"particles_10um", "particles_25um", b"particles_25um", "particles_40um", b"particles_40um", "particles_50um", b"particles_50um", "particles_tps", b"particles_tps", "pm100_environmental", b"pm100_environmental", "pm100_standard", b"pm100_standard", "pm10_environmental", b"pm10_environmental", "pm10_standard", b"pm10_standard", "pm25_environmental", b"pm25_environmental", "pm25_standard", b"pm25_standard", "pm40_standard", b"pm40_standard", "pm_humidity", b"pm_humidity", "pm_nox_idx", b"pm_nox_idx", "pm_temperature", b"pm_temperature", "pm_voc_idx", b"pm_voc_idx"]) -> builtins.bool: ...
def ClearField(self, field_name: typing.Literal["_co2", b"_co2", "_co2_humidity", b"_co2_humidity", "_co2_temperature", b"_co2_temperature", "_form_formaldehyde", b"_form_formaldehyde", "_form_humidity", b"_form_humidity", "_form_temperature", b"_form_temperature", "_particles_03um", b"_particles_03um", "_particles_05um", b"_particles_05um", "_particles_100um", b"_particles_100um", "_particles_10um", b"_particles_10um", "_particles_25um", b"_particles_25um", "_particles_40um", b"_particles_40um", "_particles_50um", b"_particles_50um", "_particles_tps", b"_particles_tps", "_pm100_environmental", b"_pm100_environmental", "_pm100_standard", b"_pm100_standard", "_pm10_environmental", b"_pm10_environmental", "_pm10_standard", b"_pm10_standard", "_pm25_environmental", b"_pm25_environmental", "_pm25_standard", b"_pm25_standard", "_pm40_standard", b"_pm40_standard", "_pm_humidity", b"_pm_humidity", "_pm_nox_idx", b"_pm_nox_idx", "_pm_temperature", b"_pm_temperature", "_pm_voc_idx", b"_pm_voc_idx", "co2", b"co2", "co2_humidity", b"co2_humidity", "co2_temperature", b"co2_temperature", "form_formaldehyde", b"form_formaldehyde", "form_humidity", b"form_humidity", "form_temperature", b"form_temperature", "particles_03um", b"particles_03um", "particles_05um", b"particles_05um", "particles_100um", b"particles_100um", "particles_10um", b"particles_10um", "particles_25um", b"particles_25um", "particles_40um", b"particles_40um", "particles_50um", b"particles_50um", "particles_tps", b"particles_tps", "pm100_environmental", b"pm100_environmental", "pm100_standard", b"pm100_standard", "pm10_environmental", b"pm10_environmental", "pm10_standard", b"pm10_standard", "pm25_environmental", b"pm25_environmental", "pm25_standard", b"pm25_standard", "pm40_standard", b"pm40_standard", "pm_humidity", b"pm_humidity", "pm_nox_idx", b"pm_nox_idx", "pm_temperature", b"pm_temperature", "pm_voc_idx", b"pm_voc_idx"]) -> None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_co2", b"_co2"]) -> typing.Literal["co2"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_co2_humidity", b"_co2_humidity"]) -> typing.Literal["co2_humidity"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_co2_temperature", b"_co2_temperature"]) -> typing.Literal["co2_temperature"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_form_formaldehyde", b"_form_formaldehyde"]) -> typing.Literal["form_formaldehyde"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_form_humidity", b"_form_humidity"]) -> typing.Literal["form_humidity"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_form_temperature", b"_form_temperature"]) -> typing.Literal["form_temperature"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_particles_03um", b"_particles_03um"]) -> typing.Literal["particles_03um"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_particles_05um", b"_particles_05um"]) -> typing.Literal["particles_05um"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_particles_100um", b"_particles_100um"]) -> typing.Literal["particles_100um"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_particles_10um", b"_particles_10um"]) -> typing.Literal["particles_10um"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_particles_25um", b"_particles_25um"]) -> typing.Literal["particles_25um"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_particles_40um", b"_particles_40um"]) -> typing.Literal["particles_40um"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_particles_50um", b"_particles_50um"]) -> typing.Literal["particles_50um"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_particles_tps", b"_particles_tps"]) -> typing.Literal["particles_tps"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_pm100_environmental", b"_pm100_environmental"]) -> typing.Literal["pm100_environmental"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_pm100_standard", b"_pm100_standard"]) -> typing.Literal["pm100_standard"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_pm10_environmental", b"_pm10_environmental"]) -> typing.Literal["pm10_environmental"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_pm10_standard", b"_pm10_standard"]) -> typing.Literal["pm10_standard"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_pm25_environmental", b"_pm25_environmental"]) -> typing.Literal["pm25_environmental"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_pm25_standard", b"_pm25_standard"]) -> typing.Literal["pm25_standard"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_pm40_standard", b"_pm40_standard"]) -> typing.Literal["pm40_standard"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_pm_humidity", b"_pm_humidity"]) -> typing.Literal["pm_humidity"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_pm_nox_idx", b"_pm_nox_idx"]) -> typing.Literal["pm_nox_idx"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_pm_temperature", b"_pm_temperature"]) -> typing.Literal["pm_temperature"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_pm_voc_idx", b"_pm_voc_idx"]) -> typing.Literal["pm_voc_idx"] | None: ...
global___AirQualityMetrics = AirQualityMetrics
@typing.final
class LocalStats(google.protobuf.message.Message):
"""
Local device mesh statistics
"""
DESCRIPTOR: google.protobuf.descriptor.Descriptor
UPTIME_SECONDS_FIELD_NUMBER: builtins.int
CHANNEL_UTILIZATION_FIELD_NUMBER: builtins.int
AIR_UTIL_TX_FIELD_NUMBER: builtins.int
NUM_PACKETS_TX_FIELD_NUMBER: builtins.int
NUM_PACKETS_RX_FIELD_NUMBER: builtins.int
NUM_PACKETS_RX_BAD_FIELD_NUMBER: builtins.int
NUM_ONLINE_NODES_FIELD_NUMBER: builtins.int
NUM_TOTAL_NODES_FIELD_NUMBER: builtins.int
NUM_RX_DUPE_FIELD_NUMBER: builtins.int
NUM_TX_RELAY_FIELD_NUMBER: builtins.int
NUM_TX_RELAY_CANCELED_FIELD_NUMBER: builtins.int
HEAP_TOTAL_BYTES_FIELD_NUMBER: builtins.int
HEAP_FREE_BYTES_FIELD_NUMBER: builtins.int
NUM_TX_DROPPED_FIELD_NUMBER: builtins.int
uptime_seconds: builtins.int
"""
How long the device has been running since the last reboot (in seconds)
"""
channel_utilization: builtins.float
"""
Utilization for the current channel, including well formed TX, RX and malformed RX (aka noise).
"""
air_util_tx: builtins.float
"""
Percent of airtime for transmission used within the last hour.
"""
num_packets_tx: builtins.int
"""
Number of packets sent
"""
num_packets_rx: builtins.int
"""
Number of packets received (both good and bad)
"""
num_packets_rx_bad: builtins.int
"""
Number of packets received that are malformed or violate the protocol
"""
num_online_nodes: builtins.int
"""
Number of nodes online (in the past 2 hours)
"""
num_total_nodes: builtins.int
"""
Number of nodes total
"""
num_rx_dupe: builtins.int
"""
Number of received packets that were duplicates (due to multiple nodes relaying).
If this number is high, there are nodes in the mesh relaying packets when it's unnecessary, for example due to the ROUTER/REPEATER role.
"""
num_tx_relay: builtins.int
"""
Number of packets we transmitted that were a relay for others (not originating from ourselves).
"""
num_tx_relay_canceled: builtins.int
"""
Number of times we canceled a packet to be relayed, because someone else did it before us.
This will always be zero for ROUTERs/REPEATERs. If this number is high, some other node(s) is/are relaying faster than you.
"""
heap_total_bytes: builtins.int
"""
Number of bytes used in the heap
"""
heap_free_bytes: builtins.int
"""
Number of bytes free in the heap
"""
num_tx_dropped: builtins.int
"""
Number of packets that were dropped because the transmit queue was full.
"""
def __init__(
self,
*,
uptime_seconds: builtins.int = ...,
channel_utilization: builtins.float = ...,
air_util_tx: builtins.float = ...,
num_packets_tx: builtins.int = ...,
num_packets_rx: builtins.int = ...,
num_packets_rx_bad: builtins.int = ...,
num_online_nodes: builtins.int = ...,
num_total_nodes: builtins.int = ...,
num_rx_dupe: builtins.int = ...,
num_tx_relay: builtins.int = ...,
num_tx_relay_canceled: builtins.int = ...,
heap_total_bytes: builtins.int = ...,
heap_free_bytes: builtins.int = ...,
num_tx_dropped: builtins.int = ...,
) -> None: ...
def ClearField(self, field_name: typing.Literal["air_util_tx", b"air_util_tx", "channel_utilization", b"channel_utilization", "heap_free_bytes", b"heap_free_bytes", "heap_total_bytes", b"heap_total_bytes", "num_online_nodes", b"num_online_nodes", "num_packets_rx", b"num_packets_rx", "num_packets_rx_bad", b"num_packets_rx_bad", "num_packets_tx", b"num_packets_tx", "num_rx_dupe", b"num_rx_dupe", "num_total_nodes", b"num_total_nodes", "num_tx_dropped", b"num_tx_dropped", "num_tx_relay", b"num_tx_relay", "num_tx_relay_canceled", b"num_tx_relay_canceled", "uptime_seconds", b"uptime_seconds"]) -> None: ...
global___LocalStats = LocalStats
@typing.final
class HealthMetrics(google.protobuf.message.Message):
"""
Health telemetry metrics
"""
DESCRIPTOR: google.protobuf.descriptor.Descriptor
HEART_BPM_FIELD_NUMBER: builtins.int
SPO2_FIELD_NUMBER: builtins.int
TEMPERATURE_FIELD_NUMBER: builtins.int
heart_bpm: builtins.int
"""
Heart rate (beats per minute)
"""
spO2: builtins.int
"""
SpO2 (blood oxygen saturation) level
"""
temperature: builtins.float
"""
Body temperature in degrees Celsius
"""
def __init__(
self,
*,
heart_bpm: builtins.int | None = ...,
spO2: builtins.int | None = ...,
temperature: builtins.float | None = ...,
) -> None: ...
def HasField(self, field_name: typing.Literal["_heart_bpm", b"_heart_bpm", "_spO2", b"_spO2", "_temperature", b"_temperature", "heart_bpm", b"heart_bpm", "spO2", b"spO2", "temperature", b"temperature"]) -> builtins.bool: ...
def ClearField(self, field_name: typing.Literal["_heart_bpm", b"_heart_bpm", "_spO2", b"_spO2", "_temperature", b"_temperature", "heart_bpm", b"heart_bpm", "spO2", b"spO2", "temperature", b"temperature"]) -> None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_heart_bpm", b"_heart_bpm"]) -> typing.Literal["heart_bpm"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_spO2", b"_spO2"]) -> typing.Literal["spO2"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_temperature", b"_temperature"]) -> typing.Literal["temperature"] | None: ...
global___HealthMetrics = HealthMetrics
@typing.final
class HostMetrics(google.protobuf.message.Message):
"""
Linux host metrics
"""
DESCRIPTOR: google.protobuf.descriptor.Descriptor
UPTIME_SECONDS_FIELD_NUMBER: builtins.int
FREEMEM_BYTES_FIELD_NUMBER: builtins.int
DISKFREE1_BYTES_FIELD_NUMBER: builtins.int
DISKFREE2_BYTES_FIELD_NUMBER: builtins.int
DISKFREE3_BYTES_FIELD_NUMBER: builtins.int
LOAD1_FIELD_NUMBER: builtins.int
LOAD5_FIELD_NUMBER: builtins.int
LOAD15_FIELD_NUMBER: builtins.int
USER_STRING_FIELD_NUMBER: builtins.int
uptime_seconds: builtins.int
"""
Host system uptime
"""
freemem_bytes: builtins.int
"""
Host system free memory
"""
diskfree1_bytes: builtins.int
"""
Host system disk space free for /
"""
diskfree2_bytes: builtins.int
"""
Secondary system disk space free
"""
diskfree3_bytes: builtins.int
"""
Tertiary disk space free
"""
load1: builtins.int
"""
Host system one minute load in 1/100ths
"""
load5: builtins.int
"""
Host system five minute load in 1/100ths
"""
load15: builtins.int
"""
Host system fifteen minute load in 1/100ths
"""
user_string: builtins.str
"""
Optional User-provided string for arbitrary host system information
that doesn't make sense as a dedicated entry.
"""
def __init__(
self,
*,
uptime_seconds: builtins.int = ...,
freemem_bytes: builtins.int = ...,
diskfree1_bytes: builtins.int = ...,
diskfree2_bytes: builtins.int | None = ...,
diskfree3_bytes: builtins.int | None = ...,
load1: builtins.int = ...,
load5: builtins.int = ...,
load15: builtins.int = ...,
user_string: builtins.str | None = ...,
) -> None: ...
def HasField(self, field_name: typing.Literal["_diskfree2_bytes", b"_diskfree2_bytes", "_diskfree3_bytes", b"_diskfree3_bytes", "_user_string", b"_user_string", "diskfree2_bytes", b"diskfree2_bytes", "diskfree3_bytes", b"diskfree3_bytes", "user_string", b"user_string"]) -> builtins.bool: ...
def ClearField(self, field_name: typing.Literal["_diskfree2_bytes", b"_diskfree2_bytes", "_diskfree3_bytes", b"_diskfree3_bytes", "_user_string", b"_user_string", "diskfree1_bytes", b"diskfree1_bytes", "diskfree2_bytes", b"diskfree2_bytes", "diskfree3_bytes", b"diskfree3_bytes", "freemem_bytes", b"freemem_bytes", "load1", b"load1", "load15", b"load15", "load5", b"load5", "uptime_seconds", b"uptime_seconds", "user_string", b"user_string"]) -> None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_diskfree2_bytes", b"_diskfree2_bytes"]) -> typing.Literal["diskfree2_bytes"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_diskfree3_bytes", b"_diskfree3_bytes"]) -> typing.Literal["diskfree3_bytes"] | None: ...
@typing.overload
def WhichOneof(self, oneof_group: typing.Literal["_user_string", b"_user_string"]) -> typing.Literal["user_string"] | None: ...
global___HostMetrics = HostMetrics
@typing.final
class Telemetry(google.protobuf.message.Message):
"""
@@ -558,6 +1247,9 @@ class Telemetry(google.protobuf.message.Message):
ENVIRONMENT_METRICS_FIELD_NUMBER: builtins.int
AIR_QUALITY_METRICS_FIELD_NUMBER: builtins.int
POWER_METRICS_FIELD_NUMBER: builtins.int
LOCAL_STATS_FIELD_NUMBER: builtins.int
HEALTH_METRICS_FIELD_NUMBER: builtins.int
HOST_METRICS_FIELD_NUMBER: builtins.int
time: builtins.int
"""
Seconds since 1970 - or 0 for unknown/unset
@@ -586,6 +1278,24 @@ class Telemetry(google.protobuf.message.Message):
Power Metrics
"""
@property
def local_stats(self) -> global___LocalStats:
"""
Local device mesh statistics
"""
@property
def health_metrics(self) -> global___HealthMetrics:
"""
Health telemetry metrics
"""
@property
def host_metrics(self) -> global___HostMetrics:
"""
Linux host metrics
"""
def __init__(
self,
*,
@@ -594,10 +1304,13 @@ class Telemetry(google.protobuf.message.Message):
environment_metrics: global___EnvironmentMetrics | None = ...,
air_quality_metrics: global___AirQualityMetrics | None = ...,
power_metrics: global___PowerMetrics | None = ...,
local_stats: global___LocalStats | None = ...,
health_metrics: global___HealthMetrics | None = ...,
host_metrics: global___HostMetrics | None = ...,
) -> None: ...
def HasField(self, field_name: typing.Literal["air_quality_metrics", b"air_quality_metrics", "device_metrics", b"device_metrics", "environment_metrics", b"environment_metrics", "power_metrics", b"power_metrics", "variant", b"variant"]) -> builtins.bool: ...
def ClearField(self, field_name: typing.Literal["air_quality_metrics", b"air_quality_metrics", "device_metrics", b"device_metrics", "environment_metrics", b"environment_metrics", "power_metrics", b"power_metrics", "time", b"time", "variant", b"variant"]) -> None: ...
def WhichOneof(self, oneof_group: typing.Literal["variant", b"variant"]) -> typing.Literal["device_metrics", "environment_metrics", "air_quality_metrics", "power_metrics"] | None: ...
def HasField(self, field_name: typing.Literal["air_quality_metrics", b"air_quality_metrics", "device_metrics", b"device_metrics", "environment_metrics", b"environment_metrics", "health_metrics", b"health_metrics", "host_metrics", b"host_metrics", "local_stats", b"local_stats", "power_metrics", b"power_metrics", "variant", b"variant"]) -> builtins.bool: ...
def ClearField(self, field_name: typing.Literal["air_quality_metrics", b"air_quality_metrics", "device_metrics", b"device_metrics", "environment_metrics", b"environment_metrics", "health_metrics", b"health_metrics", "host_metrics", b"host_metrics", "local_stats", b"local_stats", "power_metrics", b"power_metrics", "time", b"time", "variant", b"variant"]) -> None: ...
def WhichOneof(self, oneof_group: typing.Literal["variant", b"variant"]) -> typing.Literal["device_metrics", "environment_metrics", "air_quality_metrics", "power_metrics", "local_stats", "health_metrics", "host_metrics"] | None: ...
global___Telemetry = Telemetry

View File

@@ -13,14 +13,14 @@ _sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n meshtastic/protobuf/xmodem.proto\x12\x13meshtastic.protobuf\"\xbf\x01\n\x06XModem\x12\x34\n\x07\x63ontrol\x18\x01 \x01(\x0e\x32#.meshtastic.protobuf.XModem.Control\x12\x0b\n\x03seq\x18\x02 \x01(\r\x12\r\n\x05\x63rc16\x18\x03 \x01(\r\x12\x0e\n\x06\x62uffer\x18\x04 \x01(\x0c\"S\n\x07\x43ontrol\x12\x07\n\x03NUL\x10\x00\x12\x07\n\x03SOH\x10\x01\x12\x07\n\x03STX\x10\x02\x12\x07\n\x03\x45OT\x10\x04\x12\x07\n\x03\x41\x43K\x10\x06\x12\x07\n\x03NAK\x10\x15\x12\x07\n\x03\x43\x41N\x10\x18\x12\t\n\x05\x43TRLZ\x10\x1a\x42\x61\n\x13\x63om.geeksville.meshB\x0cXmodemProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n meshtastic/protobuf/xmodem.proto\x12\x13meshtastic.protobuf\"\xbf\x01\n\x06XModem\x12\x34\n\x07\x63ontrol\x18\x01 \x01(\x0e\x32#.meshtastic.protobuf.XModem.Control\x12\x0b\n\x03seq\x18\x02 \x01(\r\x12\r\n\x05\x63rc16\x18\x03 \x01(\r\x12\x0e\n\x06\x62uffer\x18\x04 \x01(\x0c\"S\n\x07\x43ontrol\x12\x07\n\x03NUL\x10\x00\x12\x07\n\x03SOH\x10\x01\x12\x07\n\x03STX\x10\x02\x12\x07\n\x03\x45OT\x10\x04\x12\x07\n\x03\x41\x43K\x10\x06\x12\x07\n\x03NAK\x10\x15\x12\x07\n\x03\x43\x41N\x10\x18\x12\t\n\x05\x43TRLZ\x10\x1a\x42\x62\n\x14org.meshtastic.protoB\x0cXmodemProtosZ\"github.com/meshtastic/go/generated\xaa\x02\x14Meshtastic.Protobufs\xba\x02\x00\x62\x06proto3')
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'meshtastic.protobuf.xmodem_pb2', _globals)
if _descriptor._USE_C_DESCRIPTORS == False:
DESCRIPTOR._options = None
DESCRIPTOR._serialized_options = b'\n\023com.geeksville.meshB\014XmodemProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
DESCRIPTOR._serialized_options = b'\n\024org.meshtastic.protoB\014XmodemProtosZ\"github.com/meshtastic/go/generated\252\002\024Meshtastic.Protobufs\272\002\000'
_globals['_XMODEM']._serialized_start=58
_globals['_XMODEM']._serialized_end=249
_globals['_XMODEM_CONTROL']._serialized_start=166

View File

@@ -7,10 +7,11 @@ from pubsub import pub # type: ignore[import-untyped]
from meshtastic.protobuf import portnums_pb2, remote_hardware_pb2
from meshtastic.util import our_exit
logger = logging.getLogger(__name__)
def onGPIOreceive(packet, interface):
def onGPIOreceive(packet, interface) -> None:
"""Callback for received GPIO responses"""
logging.debug(f"packet:{packet} interface:{interface}")
logger.debug(f"packet:{packet} interface:{interface}")
gpioValue = 0
hw = packet["decoded"]["remotehw"]
if "gpioValue" in hw:
@@ -37,7 +38,7 @@ class RemoteHardwareClient:
code for how you can connect to your own custom meshtastic services
"""
def __init__(self, iface):
def __init__(self, iface) -> None:
"""
Constructor
@@ -76,7 +77,7 @@ class RemoteHardwareClient:
Write the specified vals bits to the device GPIOs. Only bits in mask that
are 1 will be changed
"""
logging.debug(f"writeGPIOs nodeid:{nodeid} mask:{mask} vals:{vals}")
logger.debug(f"writeGPIOs nodeid:{nodeid} mask:{mask} vals:{vals}")
r = remote_hardware_pb2.HardwareMessage()
r.type = remote_hardware_pb2.HardwareMessage.Type.WRITE_GPIOS
r.gpio_mask = mask
@@ -85,7 +86,7 @@ class RemoteHardwareClient:
def readGPIOs(self, nodeid, mask, onResponse=None):
"""Read the specified bits from GPIO inputs on the device"""
logging.debug(f"readGPIOs nodeid:{nodeid} mask:{mask}")
logger.debug(f"readGPIOs nodeid:{nodeid} mask:{mask}")
r = remote_hardware_pb2.HardwareMessage()
r.type = remote_hardware_pb2.HardwareMessage.Type.READ_GPIOS
r.gpio_mask = mask
@@ -93,7 +94,7 @@ class RemoteHardwareClient:
def watchGPIOs(self, nodeid, mask):
"""Watch the specified bits from GPIO inputs on the device for changes"""
logging.debug(f"watchGPIOs nodeid:{nodeid} mask:{mask}")
logger.debug(f"watchGPIOs nodeid:{nodeid} mask:{mask}")
r = remote_hardware_pb2.HardwareMessage()
r.type = remote_hardware_pb2.HardwareMessage.Type.WATCH_GPIOS
r.gpio_mask = mask

View File

@@ -1,76 +1,103 @@
""" Serial interface class
"""
# pylint: disable=R0917
import logging
import platform
import sys
import time
from io import TextIOWrapper
from typing import Optional
from typing import List, Optional
import serial # type: ignore[import-untyped]
import meshtastic.util
from meshtastic.stream_interface import StreamInterface
if platform.system() != "Windows":
import termios
logger = logging.getLogger(__name__)
class SerialInterface(StreamInterface):
"""Interface class for meshtastic devices over a serial link"""
def __init__(self, devPath: Optional[str]=None, debugOut=None, noProto=False, connectNow=True, noNodes: bool=False):
def __init__(
self,
devPath: Optional[str] = None,
debugOut=None,
noProto: bool = False,
connectNow: bool = True,
noNodes: bool = False,
timeout: int = 300
) -> None:
"""Constructor, opens a connection to a specified serial port, or if unspecified try to
find one Meshtastic device by probing
Keyword Arguments:
devPath {string} -- A filepath to a device, i.e. /dev/ttyUSB0 (default: {None})
debugOut {stream} -- If a stream is provided, any debug serial output from the device will be emitted to that stream. (default: {None})
timeout -- How long to wait for replies (default: 300 seconds)
"""
self.noProto = noProto
self.devPath: Optional[str] = devPath
if self.devPath is None:
ports = meshtastic.util.findPorts(True)
logging.debug(f"ports:{ports}")
ports: List[str] = meshtastic.util.findPorts(True)
logger.debug(f"ports:{ports}")
if len(ports) == 0:
print("No Serial Meshtastic device detected, attempting TCP connection on localhost.")
return
elif len(ports) > 1:
message = "Warning: Multiple serial ports were detected so one serial port must be specified with the '--port'.\n"
message: str = "Warning: Multiple serial ports were detected so one serial port must be specified with the '--port'.\n"
message += f" Ports detected:{ports}"
meshtastic.util.our_exit(message)
else:
self.devPath = ports[0]
logging.debug(f"Connecting to {self.devPath}")
logger.debug(f"Connecting to {self.devPath}")
# first we need to set the HUPCL so the device will not reboot based on RTS and/or DTR
# see https://github.com/pyserial/pyserial/issues/124
if platform.system() != "Windows":
if sys.platform != "win32":
with open(self.devPath, encoding="utf8") as f:
attrs = termios.tcgetattr(f)
attrs[2] = attrs[2] & ~termios.HUPCL
termios.tcsetattr(f, termios.TCSAFLUSH, attrs)
f.close()
self._set_hupcl_with_termios(f)
time.sleep(0.1)
self.stream = serial.Serial(
self.devPath, 115200, exclusive=True, timeout=0.5, write_timeout=0
)
self.stream.flush()
self.stream.flush() # type: ignore[attr-defined]
time.sleep(0.1)
StreamInterface.__init__(
self, debugOut=debugOut, noProto=noProto, connectNow=connectNow, noNodes=noNodes
self, debugOut=debugOut, noProto=noProto, connectNow=connectNow, noNodes=noNodes, timeout=timeout
)
def close(self):
def _set_hupcl_with_termios(self, f: TextIOWrapper):
"""first we need to set the HUPCL so the device will not reboot based on RTS and/or DTR
see https://github.com/pyserial/pyserial/issues/124
"""
if sys.platform == "win32":
return
import termios # pylint: disable=C0415,E0401
attrs = termios.tcgetattr(f)
attrs[2] = attrs[2] & ~termios.HUPCL
termios.tcsetattr(f, termios.TCSAFLUSH, attrs)
def __repr__(self):
rep = f"SerialInterface(devPath={self.devPath!r}"
if hasattr(self, 'debugOut') and self.debugOut is not None:
rep += f", debugOut={self.debugOut!r}"
if self.noProto:
rep += ", noProto=True"
if hasattr(self, 'noNodes') and self.noNodes:
rep += ", noNodes=True"
rep += ")"
return rep
def close(self) -> None:
"""Close a connection to the device"""
if self.stream: # Stream can be null if we were already closed
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()
time.sleep(0.1)
logging.debug("Closing Serial stream")
logger.debug("Closing Serial stream")
StreamInterface.close(self)

View File

@@ -10,6 +10,7 @@ import time
from dataclasses import dataclass
from datetime import datetime
from functools import reduce
from pathlib import Path
from typing import Optional, List, Tuple
import parse # type: ignore[import-untyped]
@@ -22,6 +23,7 @@ from meshtastic.powermon import PowerMeter
from .arrow import FeatherWriter
logger = logging.getLogger(__name__)
def root_dir() -> str:
"""Return the root directory for slog files."""
@@ -29,9 +31,9 @@ def root_dir() -> str:
app_name = "meshtastic"
app_author = "meshtastic"
app_dir = platformdirs.user_data_dir(app_name, app_author)
dir_name = f"{app_dir}/slogs"
os.makedirs(dir_name, exist_ok=True)
return dir_name
dir_name = Path(app_dir, "slogs")
dir_name.mkdir(exist_ok=True, parents=True)
return str(dir_name)
@dataclass(init=False)
@@ -144,7 +146,7 @@ class StructuredLogger:
self.power_logger = power_logger
# Setup the arrow writer (and its schema)
self.writer = FeatherWriter(f"{dir_path}/slog")
self.writer = FeatherWriter(os.path.join(dir_path, "slog"))
all_fields = reduce(
(lambda x, y: x + y), map(lambda x: x.fields, log_defs.values())
)
@@ -164,7 +166,7 @@ class StructuredLogger:
self.raw_file: Optional[
io.TextIOWrapper
] = open( # pylint: disable=consider-using-with
f"{dir_path}/raw.txt", "w", encoding="utf8"
os.path.join(dir_path, "raw.txt"), "w", encoding="utf8"
)
# We need a closure here because the subscription API is very strict about exact arg matching
@@ -197,7 +199,7 @@ class StructuredLogger:
if m:
src = m.group(1)
args = m.group(2)
logging.debug(f"SLog {src}, args: {args}")
logger.debug(f"SLog {src}, args: {args}")
d = log_defs.get(src)
if d:
@@ -219,9 +221,9 @@ class StructuredLogger:
# If the last field is an empty string, remove it
del di[last_field[0]]
else:
logging.warning(f"Failed to parse slog {line} with {d.format}")
logger.warning(f"Failed to parse slog {line} with {d.format}")
else:
logging.warning(f"Unknown Structured Log: {line}")
logger.warning(f"Unknown Structured Log: {line}")
# Store our structured log record
if di or self.include_raw:
@@ -256,23 +258,28 @@ class LogSet:
if not dir_name:
app_dir = root_dir()
dir_name = f"{app_dir}/{datetime.now().strftime('%Y%m%d-%H%M%S')}"
os.makedirs(dir_name, exist_ok=True)
app_time_dir = Path(app_dir, datetime.now().strftime('%Y%m%d-%H%M%S'))
app_time_dir.mkdir(exist_ok=True)
dir_name = str(app_time_dir)
# Also make a 'latest' directory that always points to the most recent logs
latest_dir = Path(app_dir, "latest")
latest_dir.unlink(missing_ok=True)
# symlink might fail on some platforms, if it does fail silently
if os.path.exists(f"{app_dir}/latest"):
os.unlink(f"{app_dir}/latest")
os.symlink(dir_name, f"{app_dir}/latest", target_is_directory=True)
try:
latest_dir.symlink_to(dir_name, target_is_directory=True)
except OSError:
pass
self.dir_name = dir_name
logging.info(f"Writing slogs to {dir_name}")
logger.info(f"Writing slogs to {dir_name}")
self.power_logger: Optional[PowerLogger] = (
None
if not power_meter
else PowerLogger(power_meter, f"{self.dir_name}/power")
else PowerLogger(power_meter, os.path.join(self.dir_name, "power"))
)
self.slog_logger: Optional[StructuredLogger] = StructuredLogger(
@@ -286,7 +293,7 @@ class LogSet:
"""Close the log set."""
if self.slog_logger:
logging.info(f"Closing slogs in {self.dir_name}")
logger.info(f"Closing slogs in {self.dir_name}")
atexit.unregister(
self.atexit_handler
) # docs say it will silently ignore if not found

View File

@@ -1,10 +1,13 @@
"""Stream Interface base class
"""
import io
import logging
import threading
import time
import traceback
from typing import Optional, cast
import serial # type: ignore[import-untyped]
from meshtastic.mesh_interface import MeshInterface
@@ -14,17 +17,26 @@ START1 = 0x94
START2 = 0xC3
HEADER_LEN = 4
MAX_TO_FROM_RADIO_SIZE = 512
logger = logging.getLogger(__name__)
class StreamInterface(MeshInterface):
"""Interface class for meshtastic devices over a stream link (serial, TCP, etc)"""
def __init__(self, debugOut=None, noProto=False, connectNow=True, noNodes=False):
def __init__( # pylint: disable=R0917
self,
debugOut: Optional[io.TextIOWrapper] = None,
noProto: bool = False,
connectNow: bool = True,
noNodes: bool = False,
timeout: int = 300
) -> None:
"""Constructor, opens a connection to self.stream
Keyword Arguments:
debugOut {stream} -- If a stream is provided, any debug serial output from the
device will be emitted to that stream. (default: {None})
timeout -- How long to wait for replies (default: 300 seconds)
Raises:
Exception: [description]
@@ -35,6 +47,7 @@ class StreamInterface(MeshInterface):
raise Exception( # pylint: disable=W0719
"StreamInterface is now abstract (to update existing code create SerialInterface instead)"
)
self.stream: Optional[serial.Serial] # only serial uses this, TCPInterface overrides the relevant methods instead
self._rxBuf = bytes() # empty
self._wantExit = False
@@ -44,7 +57,7 @@ class StreamInterface(MeshInterface):
# FIXME, figure out why daemon=True causes reader thread to exit too early
self._rxThread = threading.Thread(target=self.__reader, args=(), daemon=True, name="stream reader")
MeshInterface.__init__(self, debugOut=debugOut, noProto=noProto, noNodes=noNodes)
MeshInterface.__init__(self, debugOut=debugOut, noProto=noProto, noNodes=noNodes, timeout=timeout)
# Start the reader thread after superclass constructor completes init
if connectNow:
@@ -52,7 +65,7 @@ class StreamInterface(MeshInterface):
if not noProto:
self.waitForConfig()
def connect(self):
def connect(self) -> None:
"""Connect to our radio
Normally this is called automatically by the constructor, but if you
@@ -63,7 +76,7 @@ class StreamInterface(MeshInterface):
# if the reading statemachine was parsing a bad packet make sure
# we write enough start bytes to force it to resync (we don't use START1
# because we want to ensure it is looking for START1)
p = bytearray([START2] * 32)
p: bytes = bytearray([START2] * 32)
self._writeBytes(p)
time.sleep(0.1) # wait 100ms to give device time to start running
@@ -74,11 +87,11 @@ class StreamInterface(MeshInterface):
if not self.noProto: # Wait for the db download if using the protocol
self._waitConnected()
def _disconnected(self):
def _disconnected(self) -> None:
"""We override the superclass implementation to close our port"""
MeshInterface._disconnected(self)
logging.debug("Closing our port")
logger.debug("Closing our port")
# pylint: disable=E0203
if not self.stream is None:
# pylint: disable=E0203
@@ -86,7 +99,7 @@ class StreamInterface(MeshInterface):
# pylint: disable=W0201
self.stream = None
def _writeBytes(self, b):
def _writeBytes(self, b: bytes) -> None:
"""Write an array of bytes to our stream and flush"""
if self.stream: # ignore writes when stream is closed
self.stream.write(b)
@@ -98,26 +111,26 @@ class StreamInterface(MeshInterface):
# we sleep here to give the TBeam a chance to work
time.sleep(0.1)
def _readBytes(self, length):
def _readBytes(self, length) -> Optional[bytes]:
"""Read an array of bytes from our stream"""
if self.stream:
return self.stream.read(length)
else:
return None
def _sendToRadioImpl(self, toRadio):
def _sendToRadioImpl(self, toRadio) -> None:
"""Send a ToRadio protobuf to the device"""
logging.debug(f"Sending: {stripnl(toRadio)}")
b = toRadio.SerializeToString()
bufLen = len(b)
logger.debug(f"Sending: {stripnl(toRadio)}")
b: bytes = toRadio.SerializeToString()
bufLen: int = len(b)
# We convert into a string, because the TCP code doesn't work with byte arrays
header = bytes([START1, START2, (bufLen >> 8) & 0xFF, bufLen & 0xFF])
logging.debug(f"sending header:{header} b:{b}")
header: bytes = bytes([START1, START2, (bufLen >> 8) & 0xFF, bufLen & 0xFF])
logger.debug(f"sending header:{header!r} b:{b!r}")
self._writeBytes(header + b)
def close(self):
def close(self) -> None:
"""Close a connection to the device"""
logging.debug("Closing stream")
logger.debug("Closing stream")
MeshInterface.close(self)
# pyserial cancel_read doesn't seem to work, therefore we ask the
# reader thread to close things for us
@@ -142,21 +155,21 @@ class StreamInterface(MeshInterface):
else:
self.cur_log_line += utf
def __reader(self):
def __reader(self) -> None:
"""The reader thread that reads bytes from our stream"""
logging.debug("in __reader()")
logger.debug("in __reader()")
empty = bytes()
try:
while not self._wantExit:
# logging.debug("reading character")
b = self._readBytes(1)
# logging.debug("In reader loop")
# logging.debug(f"read returned {b}")
if len(b) > 0:
c = b[0]
# logging.debug(f'c:{c}')
ptr = len(self._rxBuf)
# logger.debug("reading character")
b: Optional[bytes] = self._readBytes(1)
# logger.debug("In reader loop")
# logger.debug(f"read returned {b}")
if b is not None and len(cast(bytes, b)) > 0:
c: int = b[0]
# logger.debug(f'c:{c}')
ptr: int = len(self._rxBuf)
# Assume we want to append this byte, fixme use bytearray instead
self._rxBuf = self._rxBuf + b
@@ -172,7 +185,7 @@ class StreamInterface(MeshInterface):
if c != START2:
self._rxBuf = empty # failed to find start2
elif ptr >= HEADER_LEN - 1: # we've at least got a header
# logging.debug('at least we received a header')
# logger.debug('at least we received a header')
# big endian length follows header
packetlen = (self._rxBuf[2] << 8) + self._rxBuf[3]
@@ -188,32 +201,32 @@ class StreamInterface(MeshInterface):
try:
self._handleFromRadio(self._rxBuf[HEADER_LEN:])
except Exception as ex:
logging.error(
logger.error(
f"Error while handling message from radio {ex}"
)
traceback.print_exc()
self._rxBuf = empty
else:
# logging.debug(f"timeout")
# logger.debug(f"timeout")
pass
except serial.SerialException as ex:
if (
not self._wantExit
): # We might intentionally get an exception during shutdown
logging.warning(
logger.warning(
f"Meshtastic serial port disconnected, disconnecting... {ex}"
)
except OSError as ex:
if (
not self._wantExit
): # We might intentionally get an exception during shutdown
logging.error(
logger.error(
f"Unexpected OSError, terminating meshtastic reader... {ex}"
)
except Exception as ex:
logging.error(
logger.error(
f"Unexpected exception, terminating meshtastic reader... {ex}"
)
finally:
logging.debug("reader is exiting")
logger.debug("reader is exiting")
self._disconnected()

View File

@@ -1,6 +1,7 @@
""" Supported Meshtastic Devices - This is a class and collection of Meshtastic devices.
It is used for auto detection as to which device might be connected.
"""
# pylint: disable=R0917
# Goal is to detect which device and port to use from the supported devices
# without installing any libraries that are not currently in the python meshtastic library
@@ -206,6 +207,30 @@ nano_g1 = SupportedDevice(
usb_product_id_in_hex="55d4",
)
seeed_xiao_s3 = SupportedDevice(
name = "Seeed Xiao ESP32-S3",
version = "",
for_firmware="seeed-xiao-esp32s3",
baseport_on_linux="ttyACM",
baseport_on_mac="cu.usbmodem",
usb_vendor_id_in_hex="2886",
usb_product_id_in_hex="0059",
)
tdeck = SupportedDevice(
name="T-Deck",
version="",
for_firmware="t-deck", # Confirmed firmware identifier
device_class="esp32",
baseport_on_linux="ttyACM",
baseport_on_mac="cu.usbmodem",
baseport_on_windows="COM",
usb_vendor_id_in_hex="303a", # Espressif Systems (VERIFIED)
usb_product_id_in_hex="1001", # VERIFIED from actual device
)
supported_devices = [
tbeam_v0_7,
tbeam_v1_1,
@@ -225,4 +250,6 @@ supported_devices = [
rak4631_19003,
rak11200,
nano_g1,
seeed_xiao_s3,
tdeck, # T-Deck support added
]

View File

@@ -1,11 +1,16 @@
"""TCPInterface class for interfacing with http endpoint
"""
# pylint: disable=R0917
import contextlib
import logging
import socket
import time
from typing import Optional
from meshtastic.stream_interface import StreamInterface
DEFAULT_TCP_PORT = 4403
logger = logging.getLogger(__name__)
class TCPInterface(StreamInterface):
"""Interface class for meshtastic devices over a TCP link"""
@@ -14,64 +19,99 @@ class TCPInterface(StreamInterface):
self,
hostname: str,
debugOut=None,
noProto=False,
connectNow=True,
portNumber=4403,
noProto: bool=False,
connectNow: bool=True,
portNumber: int=DEFAULT_TCP_PORT,
noNodes:bool=False,
timeout: int = 300,
):
"""Constructor, opens a connection to a specified IP address/hostname
Keyword Arguments:
hostname {string} -- Hostname/IP address of the device to connect to
timeout -- How long to wait for replies (default: 300 seconds)
"""
self.stream = None
self.hostname = hostname
self.portNumber = portNumber
self.hostname: str = hostname
self.portNumber: int = portNumber
self.socket: Optional[socket.socket] = None
if connectNow:
logging.debug(f"Connecting to {hostname}") # type: ignore[str-bytes-safe]
server_address = (hostname, portNumber)
sock = socket.create_connection(server_address)
self.socket: Optional[socket.socket] = sock
self.myConnect()
else:
self.socket = None
StreamInterface.__init__(
self, debugOut=debugOut, noProto=noProto, connectNow=connectNow, noNodes=noNodes
)
super().__init__(debugOut=debugOut, noProto=noProto, connectNow=connectNow, noNodes=noNodes, timeout=timeout)
def _socket_shutdown(self):
def __repr__(self):
rep = f"TCPInterface({self.hostname!r}"
if self.debugOut is not None:
rep += f", debugOut={self.debugOut!r}"
if self.noProto:
rep += ", noProto=True"
if self.socket is None:
rep += ", connectNow=False"
if self.portNumber != DEFAULT_TCP_PORT:
rep += f", portNumber={self.portNumber!r}"
if self.noNodes:
rep += ", noNodes=True"
rep += ")"
return rep
def _socket_shutdown(self) -> None:
"""Shutdown the socket.
Note: Broke out this line so the exception could be unit tested.
"""
self.socket.shutdown(socket.SHUT_RDWR)
if self.socket is not None:
self.socket.shutdown(socket.SHUT_RDWR)
def myConnect(self):
def myConnect(self) -> None:
"""Connect to socket"""
logger.debug(f"Connecting to {self.hostname}") # type: ignore[str-bytes-safe]
server_address = (self.hostname, self.portNumber)
sock = socket.create_connection(server_address)
self.socket = sock
self.socket = socket.create_connection(server_address)
def close(self):
def close(self) -> None:
"""Close a connection to the device"""
logging.debug("Closing TCP stream")
StreamInterface.close(self)
logger.debug("Closing TCP stream")
super().close()
# Sometimes the socket read might be blocked in the reader thread.
# Therefore we force the shutdown by closing the socket here
self._wantExit = True
if not self.socket is None:
try:
if self.socket is not None:
with contextlib.suppress(Exception): # Ignore errors in shutdown, because we might have a race with the server
self._socket_shutdown()
except:
pass # Ignore errors in shutdown, because we might have a race with the server
self.socket.close()
def _writeBytes(self, b):
"""Write an array of bytes to our stream and flush"""
self.socket.send(b)
self.socket = None
def _readBytes(self, length):
def _writeBytes(self, b: bytes) -> None:
"""Write an array of bytes to our stream and flush"""
if self.socket is not None:
self.socket.send(b)
def _readBytes(self, length) -> Optional[bytes]:
"""Read an array of bytes from our stream"""
return self.socket.recv(length)
if self.socket is not None:
data = self.socket.recv(length)
# empty byte indicates a disconnected socket,
# we need to handle it to avoid an infinite loop reading from null socket
if data == b'':
logger.debug("dead socket, re-connecting")
# cleanup and reconnect socket without breaking reader thread
with contextlib.suppress(Exception):
self._socket_shutdown()
self.socket.close()
self.socket = None
time.sleep(1)
self.myConnect()
self._startConfig()
return None
return data
# no socket, break reader thread
self._wantExit = True
return None

View File

@@ -5,6 +5,9 @@ import logging
import sys
import time
import traceback
import io
from typing import List, Optional
from dotmap import DotMap # type: ignore[import-untyped]
from pubsub import pub # type: ignore[import-untyped]
@@ -15,19 +18,20 @@ from meshtastic.serial_interface import SerialInterface
from meshtastic.tcp_interface import TCPInterface
"""The interfaces we are using for our tests"""
interfaces = None
interfaces: List = []
"""A list of all packets we received while the current test was running"""
receivedPackets = None
receivedPackets: Optional[List] = None
testsRunning = False
testsRunning: bool = False
testNumber = 0
testNumber: int = 0
sendingInterface = None
logger = logging.getLogger(__name__)
def onReceive(packet, interface):
def onReceive(packet, interface) -> None:
"""Callback invoked when a packet arrives"""
if sendingInterface == interface:
pass
@@ -42,20 +46,20 @@ def onReceive(packet, interface):
receivedPackets.append(p)
def onNode(node):
def onNode(node) -> None:
"""Callback invoked when the node DB changes"""
print(f"Node changed: {node}")
def subscribe():
def subscribe() -> None:
"""Subscribe to the topics the user probably wants to see, prints output to stdout"""
pub.subscribe(onNode, "meshtastic.node")
def testSend(
fromInterface, toInterface, isBroadcast=False, asBinary=False, wantAck=False
):
fromInterface, toInterface, isBroadcast: bool=False, asBinary: bool=False, wantAck: bool=False
) -> bool:
"""
Sends one test packet between two nodes and then returns success or failure
@@ -76,7 +80,7 @@ def testSend(
else:
toNode = toInterface.myInfo.my_node_num
logging.debug(f"Sending test wantAck={wantAck} packet from {fromNode} to {toNode}")
logger.debug(f"Sending test wantAck={wantAck} packet from {fromNode} to {toNode}")
# pylint: disable=W0603
global sendingInterface
sendingInterface = fromInterface
@@ -93,43 +97,43 @@ def testSend(
return False # Failed to send
def runTests(numTests=50, wantAck=False, maxFailures=0):
def runTests(numTests: int=50, wantAck: bool=False, maxFailures: int=0) -> bool:
"""Run the tests."""
logging.info(f"Running {numTests} tests with wantAck={wantAck}")
numFail = 0
numSuccess = 0
logger.info(f"Running {numTests} tests with wantAck={wantAck}")
numFail: int = 0
numSuccess: int = 0
for _ in range(numTests):
# pylint: disable=W0603
global testNumber
testNumber = testNumber + 1
isBroadcast = True
isBroadcast:bool = True
# asBinary=(i % 2 == 0)
success = testSend(
interfaces[0], interfaces[1], isBroadcast, asBinary=False, wantAck=wantAck
)
if not success:
numFail = numFail + 1
logging.error(
logger.error(
f"Test {testNumber} failed, expected packet not received ({numFail} failures so far)"
)
else:
numSuccess = numSuccess + 1
logging.info(
logger.info(
f"Test {testNumber} succeeded {numSuccess} successes {numFail} failures so far"
)
time.sleep(1)
if numFail > maxFailures:
logging.error("Too many failures! Test failed!")
logger.error("Too many failures! Test failed!")
return False
return True
def testThread(numTests=50):
def testThread(numTests=50) -> bool:
"""Test thread"""
logging.info("Found devices, starting tests...")
result = runTests(numTests, wantAck=True)
logger.info("Found devices, starting tests...")
result: bool = runTests(numTests, wantAck=True)
if result:
# Run another test
# Allow a few dropped packets
@@ -137,25 +141,25 @@ def testThread(numTests=50):
return result
def onConnection(topic=pub.AUTO_TOPIC):
def onConnection(topic=pub.AUTO_TOPIC) -> None:
"""Callback invoked when we connect/disconnect from a radio"""
print(f"Connection changed: {topic.getName()}")
def openDebugLog(portName):
def openDebugLog(portName) -> io.TextIOWrapper:
"""Open the debug log file"""
debugname = "log" + portName.replace("/", "_")
logging.info(f"Writing serial debugging to {debugname}")
logger.info(f"Writing serial debugging to {debugname}")
return open(debugname, "w+", buffering=1, encoding="utf8")
def testAll(numTests=5):
def testAll(numTests: int=5) -> bool:
"""
Run a series of tests using devices we can find.
This is called from the cli with the "--test" option.
"""
ports = meshtastic.util.findPorts(True)
ports: List[str] = meshtastic.util.findPorts(True)
if len(ports) < 2:
meshtastic.util.our_exit(
"Warning: Must have at least two devices connected to USB."
@@ -174,8 +178,8 @@ def testAll(numTests=5):
)
)
logging.info("Ports opened, starting test")
result = testThread(numTests)
logger.info("Ports opened, starting test")
result: bool = testThread(numTests)
for i in interfaces:
i.close()
@@ -183,7 +187,7 @@ def testAll(numTests=5):
return result
def testSimulator():
def testSimulator() -> None:
"""
Assume that someone has launched meshtastic-native as a simulated node.
Talk to that node over TCP, do some operations and if they are successful
@@ -193,14 +197,14 @@ def testSimulator():
python3 -c 'from meshtastic.test import testSimulator; testSimulator()'
"""
logging.basicConfig(level=logging.DEBUG)
logging.info("Connecting to simulator on localhost!")
logger.info("Connecting to simulator on localhost!")
try:
iface = TCPInterface("localhost")
iface: meshtastic.tcp_interface.TCPInterface = TCPInterface("localhost")
iface.showInfo()
iface.localNode.showInfo()
iface.localNode.exitSimulator()
iface.close()
logging.info("Integration test successful!")
logger.info("Integration test successful!")
except:
print("Error while testing simulator:", sys.exc_info()[0])
traceback.print_exc()

View File

@@ -6,7 +6,11 @@ import sys
import pytest
from meshtastic.analysis.__main__ import main
try:
# Depends upon matplotlib & other packages in poetry's analysis group, not installed by default
from meshtastic.analysis.__main__ import main
except ImportError:
pytest.skip("Can't import meshtastic.analysis", allow_module_level=True)
@pytest.mark.unit

View File

@@ -1,5 +1,5 @@
"""Meshtastic unit tests for __main__.py"""
# pylint: disable=C0302,W0613
# pylint: disable=C0302,W0613,R0917
import logging
import os
@@ -18,6 +18,7 @@ from meshtastic.__main__ import (
onNode,
onReceive,
tunnelMain,
set_missing_flags_false,
)
from meshtastic import mt_config
@@ -34,7 +35,6 @@ from ..tcp_interface import TCPInterface
# from ..remote_hardware import onGPIOreceive
# from ..config_pb2 import Config
@pytest.mark.unit
@pytest.mark.usefixtures("reset_mt_config")
def test_main_init_parser_no_args(capsys):
@@ -408,8 +408,8 @@ def test_main_nodes(capsys):
iface = MagicMock(autospec=SerialInterface)
def mock_showNodes():
print("inside mocked showNodes")
def mock_showNodes(includeSelf, showFields):
print(f"inside mocked showNodes: {includeSelf} {showFields}")
iface.showNodes.side_effect = mock_showNodes
with patch("meshtastic.serial_interface.SerialInterface", return_value=iface) as mo:
@@ -454,6 +454,37 @@ def test_main_set_owner_short_to_bob(capsys):
assert err == ""
mo.assert_called()
@pytest.mark.unit
@pytest.mark.usefixtures("reset_mt_config")
def test_main_set_is_unmessageable_to_true(capsys):
"""Test --set-is-unmessageable true"""
sys.argv = ["", "--set-is-unmessageable", "true"]
mt_config.args = sys.argv
iface = MagicMock(autospec=SerialInterface)
with patch("meshtastic.serial_interface.SerialInterface", return_value=iface) as mo:
main()
out, err = capsys.readouterr()
assert re.search(r"Connected to radio", out, re.MULTILINE)
assert re.search(r"Setting device owner is_unmessageable to True", out, re.MULTILINE)
assert err == ""
mo.assert_called()
@pytest.mark.unit
@pytest.mark.usefixtures("reset_mt_config")
def test_main_set_is_unmessagable_to_true(capsys):
"""Test --set-is-unmessagable true"""
sys.argv = ["", "--set-is-unmessagable", "true"]
mt_config.args = sys.argv
iface = MagicMock(autospec=SerialInterface)
with patch("meshtastic.serial_interface.SerialInterface", return_value=iface) as mo:
main()
out, err = capsys.readouterr()
assert re.search(r"Connected to radio", out, re.MULTILINE)
assert re.search(r"Setting device owner is_unmessageable to True", out, re.MULTILINE)
assert err == ""
mo.assert_called()
@pytest.mark.unit
@pytest.mark.usefixtures("reset_mt_config")
@@ -494,6 +525,44 @@ def test_main_get_canned_messages(capsys, caplog, iface_with_nodes):
assert err == ""
mo.assert_called()
@pytest.mark.unit
@pytest.mark.usefixtures("reset_mt_config")
def test_main_set_ringtone(capsys):
"""Test --set-ringtone"""
sys.argv = ["", "--set-ringtone", "foo,bar"]
mt_config.args = sys.argv
iface = MagicMock(autospec=SerialInterface)
with patch("meshtastic.serial_interface.SerialInterface", return_value=iface) as mo:
main()
out, err = capsys.readouterr()
assert re.search(r"Connected to radio", out, re.MULTILINE)
assert re.search(r"Setting ringtone to foo,bar", out, re.MULTILINE)
assert err == ""
mo.assert_called()
@pytest.mark.unit
@pytest.mark.usefixtures("reset_mt_config")
def test_main_get_ringtone(capsys, caplog, iface_with_nodes):
"""Test --get-ringtone"""
sys.argv = ["", "--get-ringtone"]
mt_config.args = sys.argv
iface = iface_with_nodes
iface.devPath = "bar"
mocked_node = MagicMock(autospec=Node)
mocked_node.get_ringtone.return_value = "foo,bar"
iface.localNode = mocked_node
with caplog.at_level(logging.DEBUG):
with patch("meshtastic.serial_interface.SerialInterface", return_value=iface) as mo:
main()
out, err = capsys.readouterr()
assert re.search(r"Connected to radio", out, re.MULTILINE)
assert re.search(r"ringtone:foo,bar", out, re.MULTILINE)
assert err == ""
mo.assert_called()
@pytest.mark.unit
@pytest.mark.usefixtures("reset_mt_config")
@@ -593,10 +662,10 @@ def test_main_sendtext(capsys):
iface = MagicMock(autospec=SerialInterface)
def mock_sendText(
text, dest, wantAck=False, wantResponse=False, onResponse=None, channelIndex=0
text, dest, wantAck=False, wantResponse=False, onResponse=None, channelIndex=0, portNum=0
):
print("inside mocked sendText")
print(f"{text} {dest} {wantAck} {wantResponse} {channelIndex}")
print(f"{text} {dest} {wantAck} {wantResponse} {channelIndex} {portNum}")
iface.sendText.side_effect = mock_sendText
@@ -620,10 +689,10 @@ def test_main_sendtext_with_channel(capsys):
iface = MagicMock(autospec=SerialInterface)
def mock_sendText(
text, dest, wantAck=False, wantResponse=False, onResponse=None, channelIndex=0
text, dest, wantAck=False, wantResponse=False, onResponse=None, channelIndex=0, portNum=0
):
print("inside mocked sendText")
print(f"{text} {dest} {wantAck} {wantResponse} {channelIndex}")
print(f"{text} {dest} {wantAck} {wantResponse} {channelIndex} {portNum}")
iface.sendText.side_effect = mock_sendText
@@ -688,12 +757,11 @@ def test_main_sendtext_with_invalid_channel_nine(caplog, capsys):
@pytest.mark.unit
@pytest.mark.usefixtures("reset_mt_config")
@patch("termios.tcsetattr")
@patch("termios.tcgetattr")
@patch("meshtastic.serial_interface.SerialInterface._set_hupcl_with_termios")
@patch("builtins.open", new_callable=mock_open, read_data="data")
@patch("serial.Serial")
@patch("meshtastic.util.findPorts", return_value=["/dev/ttyUSBfake"])
def test_main_sendtext_with_dest(mock_findPorts, mock_serial, mocked_open, mock_get, mock_set, capsys, caplog, iface_with_nodes):
def test_main_sendtext_with_dest(mock_findPorts, mock_serial, mocked_open, mock_hupcl, capsys, caplog, iface_with_nodes):
"""Test --sendtext with --dest"""
sys.argv = ["", "--sendtext", "hello", "--dest", "foo"]
mt_config.args = sys.argv
@@ -726,8 +794,8 @@ def test_main_sendtext_with_dest(mock_findPorts, mock_serial, mocked_open, mock_
@pytest.mark.unit
@pytest.mark.usefixtures("reset_mt_config")
def test_main_removeposition_invalid(capsys):
"""Test --remove-position with an invalid dest"""
def test_main_removeposition_remote(capsys):
"""Test --remove-position with a remote dest"""
sys.argv = ["", "--remove-position", "--dest", "!12345678"]
mt_config.args = sys.argv
iface = MagicMock(autospec=SerialInterface)
@@ -735,14 +803,15 @@ def test_main_removeposition_invalid(capsys):
main()
out, err = capsys.readouterr()
assert re.search(r"Connected to radio", out, re.MULTILINE)
assert re.search(r"remote nodes is not supported", out, re.MULTILINE)
assert re.search(r"Removing fixed position and disabling fixed position setting", out, re.MULTILINE)
assert re.search(r"Waiting for an acknowledgment from remote node", out, re.MULTILINE)
assert err == ""
mo.assert_called()
@pytest.mark.unit
@pytest.mark.usefixtures("reset_mt_config")
def test_main_setlat_invalid(capsys):
"""Test --setlat with an invalid dest"""
def test_main_setlat_remote(capsys):
"""Test --setlat with a remote dest"""
sys.argv = ["", "--setlat", "37.5", "--dest", "!12345678"]
mt_config.args = sys.argv
iface = MagicMock(autospec=SerialInterface)
@@ -750,7 +819,8 @@ def test_main_setlat_invalid(capsys):
main()
out, err = capsys.readouterr()
assert re.search(r"Connected to radio", out, re.MULTILINE)
assert re.search(r"remote nodes is not supported", out, re.MULTILINE)
assert re.search(r"Setting device position and enabling fixed position setting", out, re.MULTILINE)
assert re.search(r"Waiting for an acknowledgment from remote node", out, re.MULTILINE)
assert err == ""
mo.assert_called()
@@ -769,7 +839,7 @@ def test_main_removeposition(capsys):
mocked_node.removeFixedPosition.side_effect = mock_removeFixedPosition
iface = MagicMock(autospec=SerialInterface)
iface.localNode = mocked_node
iface.getNode.return_value = mocked_node
with patch("meshtastic.serial_interface.SerialInterface", return_value=iface) as mo:
main()
@@ -796,7 +866,7 @@ def test_main_setlat(capsys):
mocked_node.setFixedPosition.side_effect = mock_setFixedPosition
iface = MagicMock(autospec=SerialInterface)
iface.localNode = mocked_node
iface.getNode.return_value = mocked_node
with patch("meshtastic.serial_interface.SerialInterface", return_value=iface) as mo:
main()
@@ -825,7 +895,7 @@ def test_main_setlon(capsys):
mocked_node.setFixedPosition.side_effect = mock_setFixedPosition
iface = MagicMock(autospec=SerialInterface)
iface.localNode = mocked_node
iface.getNode.return_value = mocked_node
with patch("meshtastic.serial_interface.SerialInterface", return_value=iface) as mo:
main()
@@ -854,7 +924,7 @@ def test_main_setalt(capsys):
mocked_node.setFixedPosition.side_effect = mock_setFixedPosition
iface = MagicMock(autospec=SerialInterface)
iface.localNode = mocked_node
iface.getNode.return_value = mocked_node
with patch("meshtastic.serial_interface.SerialInterface", return_value=iface) as mo:
main()
@@ -885,12 +955,11 @@ def test_main_seturl(capsys):
@pytest.mark.unit
@pytest.mark.usefixtures("reset_mt_config")
@patch("termios.tcsetattr")
@patch("termios.tcgetattr")
@patch("meshtastic.serial_interface.SerialInterface._set_hupcl_with_termios")
@patch("builtins.open", new_callable=mock_open, read_data="data")
@patch("serial.Serial")
@patch("meshtastic.util.findPorts", return_value=["/dev/ttyUSBfake"])
def test_main_set_valid(mocked_findports, mocked_serial, mocked_open, mocked_get, mocked_set, capsys):
def test_main_set_valid(mocked_findports, mocked_serial, mocked_open, mocked_hupcl, capsys):
"""Test --set with valid field"""
sys.argv = ["", "--set", "network.wifi_ssid", "foo"]
mt_config.args = sys.argv
@@ -910,12 +979,11 @@ def test_main_set_valid(mocked_findports, mocked_serial, mocked_open, mocked_get
@pytest.mark.unit
@pytest.mark.usefixtures("reset_mt_config")
@patch("termios.tcsetattr")
@patch("termios.tcgetattr")
@patch("meshtastic.serial_interface.SerialInterface._set_hupcl_with_termios")
@patch("builtins.open", new_callable=mock_open, read_data="data")
@patch("serial.Serial")
@patch("meshtastic.util.findPorts", return_value=["/dev/ttyUSBfake"])
def test_main_set_valid_wifi_psk(mocked_findports, mocked_serial, mocked_open, mocked_get, mocked_set, capsys):
def test_main_set_valid_wifi_psk(mocked_findports, mocked_serial, mocked_open, mocked_hupcl, capsys):
"""Test --set with valid field"""
sys.argv = ["", "--set", "network.wifi_psk", "123456789"]
mt_config.args = sys.argv
@@ -935,12 +1003,11 @@ def test_main_set_valid_wifi_psk(mocked_findports, mocked_serial, mocked_open, m
@pytest.mark.unit
@pytest.mark.usefixtures("reset_mt_config")
@patch("termios.tcsetattr")
@patch("termios.tcgetattr")
@patch("meshtastic.serial_interface.SerialInterface._set_hupcl_with_termios")
@patch("builtins.open", new_callable=mock_open, read_data="data")
@patch("serial.Serial")
@patch("meshtastic.util.findPorts", return_value=["/dev/ttyUSBfake"])
def test_main_set_invalid_wifi_psk(mocked_findports, mocked_serial, mocked_open, mocked_get, mocked_set, capsys):
def test_main_set_invalid_wifi_psk(mocked_findports, mocked_serial, mocked_open, mocked_hupcl, capsys):
"""Test --set with an invalid value (psk must be 8 or more characters)"""
sys.argv = ["", "--set", "network.wifi_psk", "1234567"]
mt_config.args = sys.argv
@@ -963,12 +1030,11 @@ def test_main_set_invalid_wifi_psk(mocked_findports, mocked_serial, mocked_open,
@pytest.mark.unit
@pytest.mark.usefixtures("reset_mt_config")
@patch("termios.tcsetattr")
@patch("termios.tcgetattr")
@patch("meshtastic.serial_interface.SerialInterface._set_hupcl_with_termios")
@patch("builtins.open", new_callable=mock_open, read_data="data")
@patch("serial.Serial")
@patch("meshtastic.util.findPorts", return_value=["/dev/ttyUSBfake"])
def test_main_set_valid_camel_case(mocked_findports, mocked_serial, mocked_open, mocked_get, mocked_set, capsys):
def test_main_set_valid_camel_case(mocked_findports, mocked_serial, mocked_open, mocked_hupcl, capsys):
"""Test --set with valid field"""
sys.argv = ["", "--set", "network.wifi_ssid", "foo"]
mt_config.args = sys.argv
@@ -989,12 +1055,11 @@ def test_main_set_valid_camel_case(mocked_findports, mocked_serial, mocked_open,
@pytest.mark.unit
@pytest.mark.usefixtures("reset_mt_config")
@patch("termios.tcsetattr")
@patch("termios.tcgetattr")
@patch("meshtastic.serial_interface.SerialInterface._set_hupcl_with_termios")
@patch("builtins.open", new_callable=mock_open, read_data="data")
@patch("serial.Serial")
@patch("meshtastic.util.findPorts", return_value=["/dev/ttyUSBfake"])
def test_main_set_with_invalid(mocked_findports, mocked_serial, mocked_open, mocked_get, mocked_set, capsys):
def test_main_set_with_invalid(mocked_findports, mocked_serial, mocked_open, mocked_hupcl, capsys):
"""Test --set with invalid field"""
sys.argv = ["", "--set", "foo", "foo"]
mt_config.args = sys.argv
@@ -1015,12 +1080,11 @@ def test_main_set_with_invalid(mocked_findports, mocked_serial, mocked_open, moc
# TODO: write some negative --configure tests
@pytest.mark.unit
@pytest.mark.usefixtures("reset_mt_config")
@patch("termios.tcsetattr")
@patch("termios.tcgetattr")
@patch("meshtastic.serial_interface.SerialInterface._set_hupcl_with_termios")
@patch("builtins.open", new_callable=mock_open, read_data="data")
@patch("serial.Serial")
@patch("meshtastic.util.findPorts", return_value=["/dev/ttyUSBfake"])
def test_main_configure_with_snake_case(mocked_findports, mocked_serial, mocked_open, mocked_get, mocked_set, capsys):
def test_main_configure_with_snake_case(mocked_findports, mocked_serial, mocked_open, mocked_hupcl, capsys):
"""Test --configure with valid file"""
sys.argv = ["", "--configure", "example_config.yaml"]
mt_config.args = sys.argv
@@ -1048,12 +1112,11 @@ def test_main_configure_with_snake_case(mocked_findports, mocked_serial, mocked_
@pytest.mark.unit
@pytest.mark.usefixtures("reset_mt_config")
@patch("termios.tcsetattr")
@patch("termios.tcgetattr")
@patch("meshtastic.serial_interface.SerialInterface._set_hupcl_with_termios")
@patch("builtins.open", new_callable=mock_open, read_data="data")
@patch("serial.Serial")
@patch("meshtastic.util.findPorts", return_value=["/dev/ttyUSBfake"])
def test_main_configure_with_camel_case_keys(mocked_findports, mocked_serial, mocked_open, mocked_get, mocked_set, capsys):
def test_main_configure_with_camel_case_keys(mocked_findports, mocked_serial, mocked_open, mocked_hupcl, capsys):
"""Test --configure with valid file"""
sys.argv = ["", "--configure", "exampleConfig.yaml"]
mt_config.args = sys.argv
@@ -1606,7 +1669,7 @@ def test_main_onReceive_empty(caplog, capsys):
assert re.search(r"in onReceive", caplog.text, re.MULTILINE)
out, err = capsys.readouterr()
assert re.search(
r"Warning: There is no field 'to' in the packet.", out, re.MULTILINE
r"Warning: Error processing received packet: 'to'.", out, re.MULTILINE
)
assert err == ""
@@ -1722,6 +1785,8 @@ def test_main_export_config(capsys):
mo.getLongName.return_value = "foo"
mo.getShortName.return_value = "oof"
mo.localNode.getURL.return_value = "bar"
mo.getCannedMessage.return_value = "foo|bar"
mo.getRingtone.return_value = "24:d=32,o=5"
mo.getMyNodeInfo().get.return_value = {
"latitudeI": 1100000000,
"longitudeI": 1200000000,
@@ -1736,7 +1801,8 @@ position_broadcast_smart: true
fixed_position: true
position_flags: 35"""
export_config(mo)
out, err = capsys.readouterr()
out = export_config(mo)
err = ""
# ensure we do not output this line
assert not re.search(r"Connected to radio", out, re.MULTILINE)
@@ -1823,6 +1889,41 @@ position_flags: 35"""
# mo.assert_called()
@pytest.mark.unit
def test_set_missing_flags_false():
"""Test set_missing_flags_false() function"""
config = {
"bluetooth": {
"enabled": True
},
"lora": {
"txEnabled": True
}
}
false_defaults = {
("bluetooth", "enabled"),
("lora", "sx126xRxBoostedGain"),
("lora", "txEnabled"),
("lora", "usePreset"),
("position", "positionBroadcastSmartEnabled"),
("security", "serialEnabled"),
("mqtt", "encryptionEnabled"),
}
set_missing_flags_false(config, false_defaults)
# Preserved
assert config["bluetooth"]["enabled"] is True
assert config["lora"]["txEnabled"] is True
# Added
assert config["lora"]["usePreset"] is False
assert config["lora"]["sx126xRxBoostedGain"] is False
assert config["position"]["positionBroadcastSmartEnabled"] is False
assert config["security"]["serialEnabled"] is False
assert config["mqtt"]["encryptionEnabled"] is False
@pytest.mark.unit
@pytest.mark.usefixtures("reset_mt_config")
def test_main_gpio_rd_no_gpio_channel(capsys):
@@ -2612,16 +2713,16 @@ def test_tunnel_subnet_arg_with_no_devices(mock_platform_system, caplog, capsys)
assert err == ""
@pytest.mark.skipif(sys.platform == "win32", reason="on windows is no fcntl module")
@pytest.mark.unit
@pytest.mark.usefixtures("reset_mt_config")
@patch("platform.system")
@patch("termios.tcsetattr")
@patch("termios.tcgetattr")
@patch("meshtastic.serial_interface.SerialInterface._set_hupcl_with_termios")
@patch("builtins.open", new_callable=mock_open, read_data="data")
@patch("serial.Serial")
@patch("meshtastic.util.findPorts", return_value=["/dev/ttyUSBfake"])
def test_tunnel_tunnel_arg(
mocked_findPorts, mocked_serial, mocked_open, mock_get, mock_set, mock_platform_system, caplog, iface_with_nodes, capsys
mocked_findPorts, mocked_serial, mocked_open, mock_hupcl, mock_platform_system, caplog, iface_with_nodes, capsys
):
"""Test tunnel with tunnel arg (act like we are on a linux system)"""
@@ -2650,3 +2751,152 @@ def test_tunnel_tunnel_arg(
out, err = capsys.readouterr()
assert re.search(r"Connected to radio", out, re.MULTILINE)
assert err == ""
@pytest.mark.unit
@pytest.mark.usefixtures("reset_mt_config")
def test_set_favorite_node():
"""Test --set-favorite-node node"""
sys.argv = ["", "--set-favorite-node", "!12345678"]
mt_config.args = sys.argv
mocked_node = MagicMock(autospec=Node)
iface = MagicMock(autospec=SerialInterface)
iface.getNode.return_value = mocked_node
with patch("meshtastic.serial_interface.SerialInterface", return_value=iface):
main()
mocked_node.setFavorite.assert_called_once_with("!12345678")
@pytest.mark.unit
@pytest.mark.usefixtures("reset_mt_config")
def test_remove_favorite_node():
"""Test --remove-favorite-node node"""
sys.argv = ["", "--remove-favorite-node", "!12345678"]
mt_config.args = sys.argv
mocked_node = MagicMock(autospec=Node)
iface = MagicMock(autospec=SerialInterface)
iface.getNode.return_value = mocked_node
mocked_node.iface = iface
with patch("meshtastic.serial_interface.SerialInterface", return_value=iface):
main()
mocked_node.removeFavorite.assert_called_once_with("!12345678")
@pytest.mark.unit
@pytest.mark.usefixtures("reset_mt_config")
def test_set_ignored_node():
"""Test --set-ignored-node node"""
sys.argv = ["", "--set-ignored-node", "!12345678"]
mt_config.args = sys.argv
mocked_node = MagicMock(autospec=Node)
iface = MagicMock(autospec=SerialInterface)
iface.getNode.return_value = mocked_node
with patch("meshtastic.serial_interface.SerialInterface", return_value=iface):
main()
mocked_node.setIgnored.assert_called_once_with("!12345678")
@pytest.mark.unit
@pytest.mark.usefixtures("reset_mt_config")
def test_remove_ignored_node():
"""Test --remove-ignored-node node"""
sys.argv = ["", "--remove-ignored-node", "!12345678"]
mt_config.args = sys.argv
mocked_node = MagicMock(autospec=Node)
iface = MagicMock(autospec=SerialInterface)
iface.getNode.return_value = mocked_node
mocked_node.iface = iface
with patch("meshtastic.serial_interface.SerialInterface", return_value=iface):
main()
mocked_node.removeIgnored.assert_called_once_with("!12345678")
@pytest.mark.unit
@pytest.mark.usefixtures("reset_mt_config")
def test_main_set_owner_whitespace_only(capsys):
"""Test --set-owner with whitespace-only name"""
sys.argv = ["", "--set-owner", " "]
mt_config.args = sys.argv
with pytest.raises(SystemExit) as excinfo:
main()
out, _ = capsys.readouterr()
assert "ERROR: Long Name cannot be empty or contain only whitespace characters" in out
assert excinfo.value.code == 1
@pytest.mark.unit
@pytest.mark.usefixtures("reset_mt_config")
def test_main_set_owner_empty_string(capsys):
"""Test --set-owner with empty string"""
sys.argv = ["", "--set-owner", ""]
mt_config.args = sys.argv
with pytest.raises(SystemExit) as excinfo:
main()
out, _ = capsys.readouterr()
assert "ERROR: Long Name cannot be empty or contain only whitespace characters" in out
assert excinfo.value.code == 1
@pytest.mark.unit
@pytest.mark.usefixtures("reset_mt_config")
def test_main_set_owner_short_whitespace_only(capsys):
"""Test --set-owner-short with whitespace-only name"""
sys.argv = ["", "--set-owner-short", " "]
mt_config.args = sys.argv
with pytest.raises(SystemExit) as excinfo:
main()
out, _ = capsys.readouterr()
assert "ERROR: Short Name cannot be empty or contain only whitespace characters" in out
assert excinfo.value.code == 1
@pytest.mark.unit
@pytest.mark.usefixtures("reset_mt_config")
def test_main_set_owner_short_empty_string(capsys):
"""Test --set-owner-short with empty string"""
sys.argv = ["", "--set-owner-short", ""]
mt_config.args = sys.argv
with pytest.raises(SystemExit) as excinfo:
main()
out, _ = capsys.readouterr()
assert "ERROR: Short Name cannot be empty or contain only whitespace characters" in out
assert excinfo.value.code == 1
@pytest.mark.unit
@pytest.mark.usefixtures("reset_mt_config")
def test_main_set_ham_whitespace_only(capsys):
"""Test --set-ham with whitespace-only name"""
sys.argv = ["", "--set-ham", " "]
mt_config.args = sys.argv
with pytest.raises(SystemExit) as excinfo:
main()
out, _ = capsys.readouterr()
assert "ERROR: Ham radio callsign cannot be empty or contain only whitespace characters" in out
assert excinfo.value.code == 1
@pytest.mark.unit
@pytest.mark.usefixtures("reset_mt_config")
def test_main_set_ham_empty_string(capsys):
"""Test --set-ham with empty string"""
sys.argv = ["", "--set-ham", ""]
mt_config.args = sys.argv
with pytest.raises(SystemExit) as excinfo:
main()
out, _ = capsys.readouterr()
assert "ERROR: Ham radio callsign cannot be empty or contain only whitespace characters" in out
assert excinfo.value.code == 1

View File

@@ -11,8 +11,12 @@ from ..protobuf import mesh_pb2, config_pb2
from .. import BROADCAST_ADDR, LOCAL_ADDR
from ..mesh_interface import MeshInterface, _timeago
from ..node import Node
from ..slog import LogSet
from ..powermon import SimPowerSupply
try:
# Depends upon the powermon group, not installed by default
from ..slog import LogSet
from ..powermon import SimPowerSupply
except ImportError:
pytest.skip("Can't import LogSet or SimPowerSupply", allow_module_level=True)
# TODO
# from ..config import Config
@@ -173,7 +177,23 @@ def test_getNode_not_local_timeout(capsys):
assert pytest_wrapped_e.type == SystemExit
assert pytest_wrapped_e.value.code == 1
out, err = capsys.readouterr()
assert re.match(r"Error: Timed out waiting for channels", out)
assert re.match(r"Timed out trying to retrieve channel info, retrying", out)
assert err == ""
@pytest.mark.unit
@pytest.mark.usefixtures("reset_mt_config")
def test_getNode_not_local_timeout_attempts(capsys):
"""Test getNode not local, simulate timeout"""
iface = MeshInterface(noProto=True)
anode = MagicMock(autospec=Node)
anode.waitForConfig.return_value = False
with patch("meshtastic.node.Node", return_value=anode):
with pytest.raises(SystemExit) as pytest_wrapped_e:
iface.getNode("bar2", requestChannelAttempts=2)
assert pytest_wrapped_e.type == SystemExit
assert pytest_wrapped_e.value.code == 1
out, err = capsys.readouterr()
assert out == 'Timed out trying to retrieve channel info, retrying\nError: Timed out waiting for channels, giving up\n'
assert err == ""
@@ -505,6 +525,28 @@ def test_getMyNodeInfo():
myinfo = iface.getMyNodeInfo()
assert myinfo == anode
@pytest.mark.unit
@pytest.mark.usefixtures("reset_mt_config")
def test_getCannedMessage():
"""Test MeshInterface.getCannedMessage()"""
iface = MeshInterface(noProto=True)
node = MagicMock()
node.get_canned_message.return_value = "Hi|Bye|Yes"
iface.localNode = node
result = iface.getCannedMessage()
assert result == "Hi|Bye|Yes"
@pytest.mark.unit
@pytest.mark.usefixtures("reset_mt_config")
def test_getRingtone():
"""Test MeshInterface.getRingtone()"""
iface = MeshInterface(noProto=True)
node = MagicMock()
node.get_ringtone.return_value = "foo,bar"
iface.localNode = node
result = iface.getRingtone()
assert result == "foo,bar"
@pytest.mark.unit
@pytest.mark.usefixtures("reset_mt_config")
@@ -523,7 +565,6 @@ def test_generatePacketId(capsys):
assert err == ""
assert pytest_wrapped_e.type == MeshInterface.MeshInterfaceError
@pytest.mark.unit
@pytest.mark.usefixtures("reset_mt_config")
def test_fixupPosition_empty_pos():
@@ -631,15 +672,21 @@ def test_getOrCreateByNum(iface_with_nodes):
@pytest.mark.unit
def test_exit_with_exception(caplog):
"""Test __exit__()"""
iface = MeshInterface(noProto=True)
with caplog.at_level(logging.ERROR):
iface.__exit__("foo", "bar", "baz")
assert re.search(
r"An exception of type foo with value bar has occurred",
caplog.text,
re.MULTILINE,
)
assert re.search(r"Traceback: baz", caplog.text, re.MULTILINE)
try:
with MeshInterface(noProto=True):
raise ValueError("Something went wrong")
except:
assert re.search(
r"An exception of type <class \'ValueError\'> with value Something went wrong has occurred",
caplog.text,
re.MULTILINE,
)
assert re.search(
r"Traceback:\n.*in test_exit_with_exception\n {4}raise ValueError\(\"Something went wrong\"\)",
caplog.text,
re.MULTILINE
)
@pytest.mark.unit

View File

@@ -6,7 +6,7 @@ from unittest.mock import MagicMock, patch
import pytest
from ..protobuf import localonly_pb2, config_pb2
from ..protobuf import admin_pb2, localonly_pb2, config_pb2
from ..protobuf.channel_pb2 import Channel # pylint: disable=E0611
from ..node import Node
from ..serial_interface import SerialInterface
@@ -231,7 +231,9 @@ def test_node(capsys):
@pytest.mark.unit
def test_exitSimulator(caplog):
"""Test exitSimulator"""
anode = Node("foo", "bar", noProto=True)
interface = MeshInterface()
interface.nodesByNum = {}
anode = Node(interface, "!ba400000", noProto=True)
with caplog.at_level(logging.DEBUG):
anode.exitSimulator()
assert re.search(r"in exitSimulator", caplog.text, re.MULTILINE)
@@ -240,7 +242,9 @@ def test_exitSimulator(caplog):
@pytest.mark.unit
def test_reboot(caplog):
"""Test reboot"""
anode = Node(MeshInterface(), 1234567890, noProto=True)
interface = MeshInterface()
interface.nodesByNum = {}
anode = Node(interface, 1234567890, noProto=True)
with caplog.at_level(logging.DEBUG):
anode.reboot()
assert re.search(r"Telling node to reboot", caplog.text, re.MULTILINE)
@@ -249,7 +253,9 @@ def test_reboot(caplog):
@pytest.mark.unit
def test_shutdown(caplog):
"""Test shutdown"""
anode = Node(MeshInterface(), 1234567890, noProto=True)
interface = MeshInterface()
interface.nodesByNum = {}
anode = Node(interface, 1234567890, noProto=True)
with caplog.at_level(logging.DEBUG):
anode.shutdown()
assert re.search(r"Telling node to shutdown", caplog.text, re.MULTILINE)
@@ -264,7 +270,7 @@ def test_setURL_empty_url(capsys):
assert pytest_wrapped_e.type == SystemExit
assert pytest_wrapped_e.value.code == 1
out, err = capsys.readouterr()
assert re.search(r"Warning: There were no settings.", out, re.MULTILINE)
assert re.search(r"Warning: config or channels not loaded", out, re.MULTILINE)
assert err == ""
@@ -298,7 +304,7 @@ def test_setURL_valid_URL_but_no_settings(capsys):
assert pytest_wrapped_e.type == SystemExit
assert pytest_wrapped_e.value.code == 1
out, err = capsys.readouterr()
assert re.search(r"Warning: There were no settings", out, re.MULTILINE)
assert re.search(r"Warning: config or channels not loaded", out, re.MULTILINE)
assert err == ""
@@ -836,6 +842,34 @@ def test_requestChannel_localNode(caplog):
assert re.search(r"Requesting channel 0", caplog.text, re.MULTILINE)
assert not re.search(r"from remote node", caplog.text, re.MULTILINE)
@pytest.mark.unit
def test_requestChannels_non_localNode(caplog):
"""Test requestChannels() with a starting index of 0"""
iface = MagicMock(autospec=SerialInterface)
with patch("meshtastic.serial_interface.SerialInterface", return_value=iface) as mo:
mo.localNode.getChannelByName.return_value = None
mo.myInfo.max_channels = 8
anode = Node(mo, "bar", noProto=True)
anode.partialChannels = ['0']
with caplog.at_level(logging.DEBUG):
anode.requestChannels(0)
assert re.search(f"Requesting channel 0 info from remote node", caplog.text, re.MULTILINE)
assert anode.partialChannels == []
@pytest.mark.unit
def test_requestChannels_non_localNode_starting_index(caplog):
"""Test requestChannels() with a starting index of non-0"""
iface = MagicMock(autospec=SerialInterface)
with patch("meshtastic.serial_interface.SerialInterface", return_value=iface) as mo:
mo.localNode.getChannelByName.return_value = None
mo.myInfo.max_channels = 8
anode = Node(mo, "bar", noProto=True)
anode.partialChannels = ['1']
with caplog.at_level(logging.DEBUG):
anode.requestChannels(3)
assert re.search(f"Requesting channel 3 info from remote node", caplog.text, re.MULTILINE)
# make sure it hasn't been initialized
assert anode.partialChannels == ['1']
# @pytest.mark.unit
# def test_onResponseRequestCannedMessagePluginMesagePart1(caplog):
@@ -1220,8 +1254,7 @@ def test_requestChannel_localNode(caplog):
# },
# 'id': 1692918436,
# 'hopLimit': 3,
# 'priority':
# 'RELIABLE',
# 'priority': 'RELIABLE',
# 'raw': 'fake',
# 'fromId': '!9388f81c',
# 'toId': '!9388f81c'
@@ -1392,6 +1425,131 @@ def test_requestChannel_localNode(caplog):
# assert err == ''
@pytest.mark.unit
@pytest.mark.parametrize("favorite", ["!1dec0ded", 502009325])
def test_set_favorite(favorite):
"""Test setFavorite"""
iface = MagicMock(autospec=SerialInterface)
node = Node(iface, 12345678)
amesg = admin_pb2.AdminMessage()
with patch("meshtastic.admin_pb2.AdminMessage", return_value=amesg):
node.setFavorite(favorite)
assert amesg.set_favorite_node == 502009325
iface.sendData.assert_called_once()
@pytest.mark.unit
@pytest.mark.parametrize("favorite", ["!1dec0ded", 502009325])
def test_remove_favorite(favorite):
"""Test setFavorite"""
iface = MagicMock(autospec=SerialInterface)
node = Node(iface, 12345678)
amesg = admin_pb2.AdminMessage()
with patch("meshtastic.admin_pb2.AdminMessage", return_value=amesg):
node.removeFavorite(favorite)
assert amesg.remove_favorite_node == 502009325
iface.sendData.assert_called_once()
@pytest.mark.unit
@pytest.mark.parametrize("ignored", ["!1dec0ded", 502009325])
def test_set_ignored(ignored):
"""Test setFavorite"""
iface = MagicMock(autospec=SerialInterface)
node = Node(iface, 12345678)
amesg = admin_pb2.AdminMessage()
with patch("meshtastic.admin_pb2.AdminMessage", return_value=amesg):
node.setIgnored(ignored)
assert amesg.set_ignored_node == 502009325
iface.sendData.assert_called_once()
@pytest.mark.unit
@pytest.mark.parametrize("ignored", ["!1dec0ded", 502009325])
def test_remove_ignored(ignored):
"""Test setFavorite"""
iface = MagicMock(autospec=SerialInterface)
node = Node(iface, 12345678)
amesg = admin_pb2.AdminMessage()
with patch("meshtastic.admin_pb2.AdminMessage", return_value=amesg):
node.removeIgnored(ignored)
assert amesg.remove_ignored_node == 502009325
iface.sendData.assert_called_once()
@pytest.mark.unit
def test_setOwner_whitespace_only_long_name(capsys):
"""Test setOwner with whitespace-only long name"""
iface = MagicMock(autospec=MeshInterface)
anode = Node(iface, 123, noProto=True)
with pytest.raises(SystemExit) as excinfo:
anode.setOwner(long_name=" ")
out, _ = capsys.readouterr()
assert "ERROR: Long Name cannot be empty or contain only whitespace characters" in out
assert excinfo.value.code == 1
@pytest.mark.unit
def test_setOwner_empty_long_name(capsys):
"""Test setOwner with empty long name"""
iface = MagicMock(autospec=MeshInterface)
anode = Node(iface, 123, noProto=True)
with pytest.raises(SystemExit) as excinfo:
anode.setOwner(long_name="")
out, _ = capsys.readouterr()
assert "ERROR: Long Name cannot be empty or contain only whitespace characters" in out
assert excinfo.value.code == 1
@pytest.mark.unit
def test_setOwner_whitespace_only_short_name(capsys):
"""Test setOwner with whitespace-only short name"""
iface = MagicMock(autospec=MeshInterface)
anode = Node(iface, 123, noProto=True)
with pytest.raises(SystemExit) as excinfo:
anode.setOwner(short_name=" ")
out, _ = capsys.readouterr()
assert "ERROR: Short Name cannot be empty or contain only whitespace characters" in out
assert excinfo.value.code == 1
@pytest.mark.unit
def test_setOwner_empty_short_name(capsys):
"""Test setOwner with empty short name"""
iface = MagicMock(autospec=MeshInterface)
anode = Node(iface, 123, noProto=True)
with pytest.raises(SystemExit) as excinfo:
anode.setOwner(short_name="")
out, _ = capsys.readouterr()
assert "ERROR: Short Name cannot be empty or contain only whitespace characters" in out
assert excinfo.value.code == 1
@pytest.mark.unit
def test_setOwner_valid_names(caplog):
"""Test setOwner with valid names"""
iface = MagicMock(autospec=MeshInterface)
anode = Node(iface, 123, noProto=True)
with caplog.at_level(logging.DEBUG):
anode.setOwner(long_name="ValidName", short_name="VN")
# Should not raise any exceptions
# Note: When noProto=True, _sendAdmin is not called as the method returns early
assert re.search(r'p.set_owner.long_name:ValidName:', caplog.text, re.MULTILINE)
assert re.search(r'p.set_owner.short_name:VN:', caplog.text, re.MULTILINE)
# TODO
# @pytest.mark.unitslow
# def test_waitForConfig():

View File

@@ -1,6 +1,8 @@
"""Meshtastic unit tests for serial_interface.py"""
# pylint: disable=R0917
import re
import sys
from unittest.mock import mock_open, patch
import pytest
@@ -8,16 +10,14 @@ import pytest
from ..serial_interface import SerialInterface
from ..protobuf import config_pb2
@pytest.mark.unit
@patch("time.sleep")
@patch("termios.tcsetattr")
@patch("termios.tcgetattr")
@patch("meshtastic.serial_interface.SerialInterface._set_hupcl_with_termios")
@patch("builtins.open", new_callable=mock_open, read_data="data")
@patch("serial.Serial")
@patch("meshtastic.util.findPorts", return_value=["/dev/ttyUSBfake"])
def test_SerialInterface_single_port(
mocked_findPorts, mocked_serial, mocked_open, mock_get, mock_set, mock_sleep, capsys
mocked_findPorts, mocked_serial, mocked_open, mock_hupcl, mock_sleep, capsys
):
"""Test that we can instantiate a SerialInterface with a single port"""
iface = SerialInterface(noProto=True)
@@ -27,9 +27,12 @@ def test_SerialInterface_single_port(
iface.close()
mocked_findPorts.assert_called()
mocked_serial.assert_called()
mocked_open.assert_called()
mock_get.assert_called()
mock_set.assert_called()
# doesn't get called in SerialInterface on windows
if sys.platform != "win32":
mocked_open.assert_called()
mock_hupcl.assert_called()
mock_sleep.assert_called()
out, err = capsys.readouterr()
assert re.search(r"Nodes in mesh", out, re.MULTILINE)

View File

@@ -9,7 +9,11 @@ import pytest
from meshtastic import mt_config
from ..tcp_interface import TCPInterface
from ..tunnel import Tunnel, onTunnelReceive
try:
# Depends upon pytap2, not installed by default
from ..tunnel import Tunnel, onTunnelReceive
except ImportError:
pytest.skip("Can't import Tunnel or onTunnelReceive", allow_module_level=True)
@pytest.mark.unit

View File

@@ -11,16 +11,19 @@ from hypothesis import given, strategies as st
from meshtastic.supported_device import SupportedDevice
from meshtastic.protobuf import mesh_pb2
from meshtastic.util import (
DEFAULT_KEY,
Timeout,
active_ports_on_supported_devices,
camel_to_snake,
catchAndIgnore,
channel_hash,
convert_mac_addr,
eliminate_duplicate_port,
findPorts,
fixme,
fromPSK,
fromStr,
generate_channel_hash,
genPSK256,
hexstr,
ipstr,
@@ -442,6 +445,13 @@ def test_is_windows11_false_win8_1(patched_platform, patched_release):
patched_platform.assert_called()
patched_release.assert_called()
@patch("platform.release", return_value="2022Server")
@patch("platform.system", return_value="Windows")
def test_is_windows11_false_winserver(patched_platform, patched_release):
"""Test is_windows11()"""
assert is_windows11() is False
patched_platform.assert_called()
patched_release.assert_called()
@pytest.mark.unit
@patch("platform.system", return_value="Linux")
@@ -556,7 +566,7 @@ def test_active_ports_on_supported_devices_mac_duplicates_check(mock_platform, m
def test_message_to_json_shows_all():
"""Test that message_to_json prints fields that aren't included in data passed in"""
actual = json.loads(message_to_json(mesh_pb2.MyNodeInfo()))
expected = { "myNodeNum": 0, "rebootCount": 0, "minAppVersion": 0 }
expected = { "myNodeNum": 0, "rebootCount": 0, "minAppVersion": 0, "deviceId": "", "pioEnv": "", 'firmwareEdition': 'VANILLA', 'nodedbCount': 0 }
assert actual == expected
@pytest.mark.unit
@@ -594,3 +604,114 @@ def test_roundtrip_snake_to_camel_camel_to_snake(a_string):
value0 = snake_to_camel(a_string=a_string)
value1 = camel_to_snake(a_string=value0)
assert a_string == value1, (a_string, value1)
@given(st.text())
def test_fuzz_camel_to_snake(a_string):
"""Test that camel_to_snake produces outputs with underscores for multi-word camelcase"""
result = camel_to_snake(a_string)
assert "_" in result or result == a_string.lower().replace("_", "")
@given(st.text())
def test_fuzz_snake_to_camel(a_string):
"""Test that snake_to_camel removes underscores"""
result = snake_to_camel(a_string)
assert "_" not in result or result == a_string.split("_")[0] + "".join(ele.title() for ele in a_string.split("_")[1:])
@given(st.text())
def test_fuzz_stripnl(s):
"""Test that stripnl always takes away newlines"""
result = stripnl(s)
assert "\n" not in result
@given(st.binary())
def test_fuzz_pskToString(psk):
"""Test that pskToString produces sane output for any bytes"""
result = pskToString(psk)
if len(psk) == 0:
assert result == "unencrypted"
elif len(psk) == 1:
b = psk[0]
if b == 0:
assert result == "unencrypted"
elif b == 1:
assert result == "default"
else:
assert result == f"simple{b - 1}"
else:
assert result == "secret"
@given(st.text())
def test_fuzz_fromStr(valstr):
"""Test that fromStr produces mostly-useful output given any string"""
result = fromStr(valstr)
if valstr.startswith("0x"):
assert isinstance(result, bytes)
elif valstr.startswith("base64:"):
assert isinstance(result, bytes)
elif len(valstr) == 0:
assert result == b''
elif valstr.lower() in {"t", "true", "yes"}:
assert result is True
elif valstr.lower() in {"f", "false", "no"}:
assert result is False
else:
try:
int(valstr)
assert isinstance(result, int)
except ValueError:
try:
float(valstr)
assert isinstance(result, float)
except ValueError:
assert isinstance(result, str)
def test_shorthex():
"""Test the shortest hex string representations"""
result = fromStr('0x0')
assert result == b'\x00'
result = fromStr('0x5')
assert result == b'\x05'
result = fromStr('0xffff')
assert result == b'\xff\xff'
def test_channel_hash_basics():
"Test the default key and LongFast with channel_hash"
assert channel_hash(DEFAULT_KEY) == 2
assert channel_hash("LongFast".encode("utf-8")) == 10
@given(st.text(min_size=1, max_size=12))
def test_channel_hash_fuzz(channel_name):
"Test channel_hash with fuzzed channel names, ensuring it produces single-byte values"
hashed = channel_hash(channel_name.encode("utf-8"))
assert 0 <= hashed <= 0xFF
def test_generate_channel_hash_basics():
"Test the default key and LongFast/MediumFast with generate_channel_hash"
assert generate_channel_hash("LongFast", "AQ==") == 8
assert generate_channel_hash("LongFast", bytes([1])) == 8
assert generate_channel_hash("LongFast", DEFAULT_KEY) == 8
assert generate_channel_hash("MediumFast", DEFAULT_KEY) == 31
@given(st.text(min_size=1, max_size=12))
def test_generate_channel_hash_fuzz_default_key(channel_name):
"Test generate_channel_hash with fuzzed channel names and the default key, ensuring it produces single-byte values"
hashed = generate_channel_hash(channel_name, DEFAULT_KEY)
assert 0 <= hashed <= 0xFF
@given(st.text(min_size=1, max_size=12), st.binary(min_size=1, max_size=1))
def test_generate_channel_hash_fuzz_simple(channel_name, key_bytes):
"Test generate_channel_hash with fuzzed channel names and one-byte keys, ensuring it produces single-byte values"
hashed = generate_channel_hash(channel_name, key_bytes)
assert 0 <= hashed <= 0xFF
@given(st.text(min_size=1, max_size=12), st.binary(min_size=16, max_size=16))
def test_generate_channel_hash_fuzz_aes128(channel_name, key_bytes):
"Test generate_channel_hash with fuzzed channel names and 128-bit keys, ensuring it produces single-byte values"
hashed = generate_channel_hash(channel_name, key_bytes)
assert 0 <= hashed <= 0xFF
@given(st.text(min_size=1, max_size=12), st.binary(min_size=32, max_size=32))
def test_generate_channel_hash_fuzz_aes256(channel_name, key_bytes):
"Test generate_channel_hash with fuzzed channel names and 256-bit keys, ensuring it produces single-byte values"
hashed = generate_channel_hash(channel_name, key_bytes)
assert 0 <= hashed <= 0xFF

View File

@@ -26,10 +26,11 @@ from meshtastic.protobuf import portnums_pb2
from meshtastic import mt_config
from meshtastic.util import ipstr, readnet_u16
logger = logging.getLogger(__name__)
def onTunnelReceive(packet, interface): # pylint: disable=W0613
"""Callback for received tunneled messages from mesh."""
logging.debug(f"in onTunnelReceive()")
logger.debug(f"in onTunnelReceive()")
tunnelInstance = mt_config.tunnelInstance
tunnelInstance.onReceive(packet)
@@ -43,7 +44,7 @@ class Tunnel:
self.message = message
super().__init__(self.message)
def __init__(self, iface, subnet="10.115", netmask="255.255.0.0"):
def __init__(self, iface, subnet: str="10.115", netmask: str="255.255.0.0") -> None:
"""
Constructor
@@ -92,7 +93,7 @@ class Tunnel:
self.LOG_TRACE = 5
# TODO: check if root?
logging.info(
logger.info(
"Starting IP to mesh tunnel (you must be root for this *pre-alpha* "
"feature to work). Mesh members:"
)
@@ -104,13 +105,13 @@ class Tunnel:
for node in self.iface.nodes.values():
nodeId = node["user"]["id"]
ip = self._nodeNumToIp(node["num"])
logging.info(f"Node { nodeId } has IP address { ip }")
logger.info(f"Node { nodeId } has IP address { ip }")
logging.debug("creating TUN device with MTU=200")
logger.debug("creating TUN device with MTU=200")
# FIXME - figure out real max MTU, it should be 240 - the overhead bytes for SubPacket and Data
self.tun = None
if self.iface.noProto:
logging.warning(
logger.warning(
f"Not creating a TapDevice() because it is disabled by noProto"
)
else:
@@ -120,11 +121,11 @@ class Tunnel:
self._rxThread = None
if self.iface.noProto:
logging.warning(
logger.warning(
f"Not starting TUN reader because it is disabled by noProto"
)
else:
logging.debug(f"starting TUN reader, our IP address is {myAddr}")
logger.debug(f"starting TUN reader, our IP address is {myAddr}")
self._rxThread = threading.Thread(
target=self.__tunReader, args=(), daemon=True
)
@@ -134,9 +135,9 @@ class Tunnel:
"""onReceive"""
p = packet["decoded"]["payload"]
if packet["from"] == self.iface.myInfo.my_node_num:
logging.debug("Ignoring message we sent")
logger.debug("Ignoring message we sent")
else:
logging.debug(f"Received mesh tunnel message type={type(p)} len={len(p)}")
logger.debug(f"Received mesh tunnel message type={type(p)} len={len(p)}")
# we don't really need to check for filtering here (sender should have checked),
# but this provides useful debug printing on types of packets received
if not self.iface.noProto:
@@ -152,7 +153,7 @@ class Tunnel:
ignore = False # Assume we will be forwarding the packet
if protocol in self.protocolBlacklist:
ignore = True
logging.log(
logger.log(
self.LOG_TRACE, f"Ignoring blacklisted protocol 0x{protocol:02x}"
)
elif protocol == 0x01: # ICMP
@@ -160,7 +161,7 @@ class Tunnel:
icmpCode = p[21]
checksum = p[22:24]
# pylint: disable=line-too-long
logging.debug(
logger.debug(
f"forwarding ICMP message src={ipstr(srcaddr)}, dest={ipstr(destAddr)}, type={icmpType}, code={icmpCode}, checksum={checksum}"
)
# reply to pings (swap src and dest but keep rest of packet unchanged)
@@ -171,19 +172,19 @@ class Tunnel:
destport = readnet_u16(p, subheader + 2)
if destport in self.udpBlacklist:
ignore = True
logging.log(self.LOG_TRACE, f"ignoring blacklisted UDP port {destport}")
logger.log(self.LOG_TRACE, f"ignoring blacklisted UDP port {destport}")
else:
logging.debug(f"forwarding udp srcport={srcport}, destport={destport}")
logger.debug(f"forwarding udp srcport={srcport}, destport={destport}")
elif protocol == 0x06: # TCP
srcport = readnet_u16(p, subheader)
destport = readnet_u16(p, subheader + 2)
if destport in self.tcpBlacklist:
ignore = True
logging.log(self.LOG_TRACE, f"ignoring blacklisted TCP port {destport}")
logger.log(self.LOG_TRACE, f"ignoring blacklisted TCP port {destport}")
else:
logging.debug(f"forwarding tcp srcport={srcport}, destport={destport}")
logger.debug(f"forwarding tcp srcport={srcport}, destport={destport}")
else:
logging.warning(
logger.warning(
f"forwarding unexpected protocol 0x{protocol:02x}, "
"src={ipstr(srcaddr)}, dest={ipstr(destAddr)}"
)
@@ -192,10 +193,10 @@ class Tunnel:
def __tunReader(self):
tap = self.tun
logging.debug("TUN reader running")
logger.debug("TUN reader running")
while True:
p = tap.read()
# logging.debug(f"IP packet received on TUN interface, type={type(p)}")
# logger.debug(f"IP packet received on TUN interface, type={type(p)}")
destAddr = p[16:20]
if not self._shouldFilterPacket(p):
@@ -210,7 +211,7 @@ class Tunnel:
for node in self.iface.nodes.values():
nodeNum = node["num"] & 0xFFFF
# logging.debug(f"Considering nodenum 0x{nodeNum:x} for ipBits 0x{ipBits:x}")
# logger.debug(f"Considering nodenum 0x{nodeNum:x} for ipBits 0x{ipBits:x}")
if (nodeNum) == ipBits:
return node["user"]["id"]
return None
@@ -222,12 +223,12 @@ class Tunnel:
"""Forward the provided IP packet into the mesh"""
nodeId = self._ipToNodeId(destAddr)
if nodeId is not None:
logging.debug(
logger.debug(
f"Forwarding packet bytelen={len(p)} dest={ipstr(destAddr)}, destNode={nodeId}"
)
self.iface.sendData(p, nodeId, portnums_pb2.IP_TUNNEL_APP, wantAck=False)
else:
logging.warning(
logger.warning(
f"Dropping packet because no node found for destIP={ipstr(destAddr)}"
)

View File

@@ -11,7 +11,7 @@ import threading
import time
import traceback
from queue import Queue
from typing import List, NoReturn, Union
from typing import Any, Dict, List, NoReturn, Optional, Set, Tuple, Union
from google.protobuf.json_format import MessageToJson
from google.protobuf.message import Message
@@ -31,29 +31,32 @@ from meshtastic.version import get_active_version
0925 Lakeview Research Saleae Logic (logic analyzer)
04b4:602a Cypress Semiconductor Corp. Hantek DSO-6022BL (oscilloscope)
"""
blacklistVids = dict.fromkeys([0x1366, 0x0483, 0x1915, 0x0925, 0x04b4])
blacklistVids: Dict = dict.fromkeys([0x1366, 0x0483, 0x1915, 0x0925, 0x04b4])
"""Some devices are highly likely to be meshtastic.
0x239a RAK4631
0x303a Heltec tracker"""
whitelistVids = dict.fromkeys([0x239a, 0x303a])
logger = logging.getLogger(__name__)
def quoteBooleans(a_string):
DEFAULT_KEY = base64.b64decode("1PG7OiApB1nwvP+rz05pAQ==".encode("utf-8"))
def quoteBooleans(a_string: str) -> str:
"""Quote booleans
given a string that contains ": true", replace with ": 'true'" (or false)
"""
tmp = a_string.replace(": true", ": 'true'")
tmp: str = a_string.replace(": true", ": 'true'")
tmp = tmp.replace(": false", ": 'false'")
return tmp
def genPSK256():
def genPSK256() -> bytes:
"""Generate a random preshared key"""
return os.urandom(32)
def fromPSK(valstr):
def fromPSK(valstr: str) -> Any:
"""A special version of fromStr that assumes the user is trying to set a PSK.
In that case we also allow "none", "default" or "random" (to have python generate one), or simpleN
"""
@@ -70,7 +73,7 @@ def fromPSK(valstr):
return fromStr(valstr)
def fromStr(valstr):
def fromStr(valstr: str) -> Any:
"""Try to parse as int, float or bool (and fallback to a string as last resort)
Returns: an int, bool, float, str or byte array (for strings of hex digits)
@@ -78,11 +81,12 @@ def fromStr(valstr):
Args:
valstr (string): A user provided string
"""
val: Any
if len(valstr) == 0: # Treat an emptystring as an empty bytes
val = bytes()
elif valstr.startswith("0x"):
# if needed convert to string with asBytes.decode('utf-8')
val = bytes.fromhex(valstr[2:])
val = bytes.fromhex(valstr[2:].zfill(2))
elif valstr.startswith("base64:"):
val = base64.b64decode(valstr[7:])
elif valstr.lower() in {"t", "true", "yes"}:
@@ -100,7 +104,15 @@ def fromStr(valstr):
return val
def pskToString(psk: bytes):
def toStr(raw_value):
"""Convert a value to a string that can be used in a config file"""
if isinstance(raw_value, bytes):
return "base64:" + base64.b64encode(raw_value).decode("utf-8")
return str(raw_value)
def pskToString(psk: bytes) -> str:
"""Given an array of PSK bytes, decode them into a human readable (but privacy protecting) string"""
if len(psk) == 0:
return "unencrypted"
@@ -122,17 +134,17 @@ def stripnl(s) -> str:
return " ".join(s.split())
def fixme(message):
def fixme(message: str) -> None:
"""Raise an exception for things that needs to be fixed"""
raise Exception(f"FIXME: {message}") # pylint: disable=W0719
def catchAndIgnore(reason, closure):
def catchAndIgnore(reason: str, closure) -> None:
"""Call a closure but if it throws an exception print it and continue"""
try:
closure()
except BaseException as ex:
logging.error(f"Exception thrown in {reason}: {ex}")
logger.error(f"Exception thrown in {reason}: {ex}")
def findPorts(eliminate_duplicates: bool=False) -> List[str]:
@@ -145,7 +157,7 @@ def findPorts(eliminate_duplicates: bool=False) -> List[str]:
all_ports = serial.tools.list_ports.comports()
# look for 'likely' meshtastic devices
ports = list(
ports: List = list(
map(
lambda port: port.device,
filter(
@@ -184,14 +196,14 @@ class dotdict(dict):
class Timeout:
"""Timeout class"""
def __init__(self, maxSecs: int=20):
def __init__(self, maxSecs: int=20) -> None:
self.expireTime: Union[int, float] = 0
self.sleepInterval: float = 0.1
self.expireTimeout: int = maxSecs
def reset(self):
def reset(self, expireTimeout=None):
"""Restart the waitForSet timer"""
self.expireTime = time.time() + self.expireTimeout
self.expireTime = time.time() + (self.expireTimeout if expireTimeout is None else expireTimeout)
def waitForSet(self, target, attrs=()) -> bool:
"""Block until the specified attributes are set. Returns True if config has been received."""
@@ -216,8 +228,7 @@ class Timeout:
def waitForTraceRoute(self, waitFactor, acknowledgment, attr="receivedTraceRoute") -> bool:
"""Block until traceroute response is received. Returns True if traceroute response has been received."""
self.expireTimeout *= waitFactor
self.reset()
self.reset(self.expireTimeout * waitFactor)
while time.time() < self.expireTime:
if getattr(acknowledgment, attr, None):
acknowledgment.reset()
@@ -245,10 +256,20 @@ class Timeout:
time.sleep(self.sleepInterval)
return False
def waitForWaypoint(self, acknowledgment) -> bool:
"""Block until waypoint response is received. Returns True if waypoint response has been received."""
self.reset()
while time.time() < self.expireTime:
if getattr(acknowledgment, "receivedWaypoint", None):
acknowledgment.reset()
return True
time.sleep(self.sleepInterval)
return False
class Acknowledgment:
"A class that records which type of acknowledgment was just received, if any."
def __init__(self):
def __init__(self) -> None:
"""initialize"""
self.receivedAck = False
self.receivedNak = False
@@ -256,8 +277,9 @@ class Acknowledgment:
self.receivedTraceRoute = False
self.receivedTelemetry = False
self.receivedPosition = False
self.receivedWaypoint = False
def reset(self):
def reset(self) -> None:
"""reset"""
self.receivedAck = False
self.receivedNak = False
@@ -265,29 +287,30 @@ class Acknowledgment:
self.receivedTraceRoute = False
self.receivedTelemetry = False
self.receivedPosition = False
self.receivedWaypoint = False
class DeferredExecution:
"""A thread that accepts closures to run, and runs them as they are received"""
def __init__(self, name):
self.queue = Queue()
def __init__(self, name) -> None:
self.queue: Queue = Queue()
# this thread must be marked as daemon, otherwise it will prevent clients from exiting
self.thread = threading.Thread(target=self._run, args=(), name=name, daemon=True)
self.thread.daemon = True
self.thread.start()
def queueWork(self, runnable):
def queueWork(self, runnable) -> None:
"""Queue up the work"""
self.queue.put(runnable)
def _run(self):
def _run(self) -> None:
while True:
try:
o = self.queue.get()
o()
except:
logging.error(
logger.error(
f"Unexpected error in deferred execution {sys.exc_info()[0]}"
)
print(traceback.format_exc())
@@ -301,7 +324,7 @@ def our_exit(message, return_value=1) -> NoReturn:
sys.exit(return_value)
def support_info():
def support_info() -> None:
"""Print out info that helps troubleshooting of the cli."""
print("")
print("If having issues with meshtastic cli or python library")
@@ -330,7 +353,7 @@ def support_info():
print("Please add the output from the command: meshtastic --info")
def remove_keys_from_dict(keys, adict):
def remove_keys_from_dict(keys: Union[Tuple, List, Set], adict: Dict) -> Dict:
"""Return a dictionary without some keys in it.
Will removed nested keys.
"""
@@ -344,34 +367,58 @@ def remove_keys_from_dict(keys, adict):
remove_keys_from_dict(keys, val)
return adict
def channel_hash(data: bytes) -> int:
"""Compute an XOR hash from bytes for channel evaluation."""
result = 0
for char in data:
result ^= char
return result
def hexstr(barray):
def generate_channel_hash(name: Union[str, bytes], key: Union[str, bytes]) -> int:
"""generate the channel number by hashing the channel name and psk (accepts str or bytes for both)"""
# Handle key as str or bytes
if isinstance(key, str):
key = base64.b64decode(key.replace("-", "+").replace("_", "/").encode("utf-8"))
if len(key) == 1:
key = DEFAULT_KEY[:-1] + key
# Handle name as str or bytes
if isinstance(name, str):
name = name.encode("utf-8")
h_name = channel_hash(name)
h_key = channel_hash(key)
result: int = h_name ^ h_key
return result
def hexstr(barray: bytes) -> str:
"""Print a string of hex digits"""
return ":".join(f"{x:02x}" for x in barray)
def ipstr(barray):
def ipstr(barray: bytes) -> str:
"""Print a string of ip digits"""
return ".".join(f"{x}" for x in barray)
def readnet_u16(p, offset):
def readnet_u16(p, offset: int) -> int:
"""Read big endian u16 (network byte order)"""
return p[offset] * 256 + p[offset + 1]
def convert_mac_addr(val):
def convert_mac_addr(val: str) -> Union[str, bytes]:
"""Convert the base 64 encoded value to a mac address
val - base64 encoded value (ex: '/c0gFyhb'))
returns: a string formatted like a mac address (ex: 'fd:cd:20:17:28:5b')
"""
if not re.match("[0-9a-f]{2}([-:]?)[0-9a-f]{2}(\\1[0-9a-f]{2}){4}$", val):
val_as_bytes = base64.b64decode(val)
val_as_bytes: bytes = base64.b64decode(val)
return hexstr(val_as_bytes)
return val
def snake_to_camel(a_string):
def snake_to_camel(a_string: str) -> str:
"""convert snake_case to camelCase"""
# split underscore using split
temp = a_string.split("_")
@@ -380,16 +427,16 @@ def snake_to_camel(a_string):
return result
def camel_to_snake(a_string):
def camel_to_snake(a_string: str) -> str:
"""convert camelCase to snake_case"""
return "".join(["_" + i.lower() if i.isupper() else i for i in a_string]).lstrip(
"_"
)
def detect_supported_devices():
def detect_supported_devices() -> Set:
"""detect supported devices based on vendor id"""
system = platform.system()
system: str = platform.system()
# print(f'system:{system}')
possible_devices = set()
@@ -447,9 +494,9 @@ def detect_supported_devices():
return possible_devices
def detect_windows_needs_driver(sd, print_reason=False):
def detect_windows_needs_driver(sd, print_reason=False) -> bool:
"""detect if Windows user needs to install driver for a supported device"""
need_to_install_driver = False
need_to_install_driver: bool = False
if sd:
system = platform.system()
@@ -475,7 +522,7 @@ def detect_windows_needs_driver(sd, print_reason=False):
return need_to_install_driver
def eliminate_duplicate_port(ports):
def eliminate_duplicate_port(ports: List) -> List:
"""Sometimes we detect 2 serial ports, but we really only need to use one of the ports.
ports is a list of ports
@@ -508,23 +555,23 @@ def eliminate_duplicate_port(ports):
return new_ports
def is_windows11():
def is_windows11() -> bool:
"""Detect if Windows 11"""
is_win11 = False
is_win11: bool = False
if platform.system() == "Windows":
if float(platform.release()) >= 10.0:
patch = platform.version().split(".")[2]
# in case they add some number suffix later, just get first 5 chars of patch
patch = patch[:5]
try:
try:
if float(platform.release()) >= 10.0:
patch = platform.version().split(".")[2]
# in case they add some number suffix later, just get first 5 chars of patch
patch = patch[:5]
if int(patch) >= 22000:
is_win11 = True
except Exception as e:
print(f"problem detecting win11 e:{e}")
except Exception as e:
print(f"problem detecting win11 e:{e}")
return is_win11
def get_unique_vendor_ids():
def get_unique_vendor_ids() -> Set[str]:
"""Return a set of unique vendor ids"""
vids = set()
for d in supported_devices:
@@ -533,7 +580,7 @@ def get_unique_vendor_ids():
return vids
def get_devices_with_vendor_id(vid):
def get_devices_with_vendor_id(vid: str) -> Set: #Set[SupportedDevice]
"""Return a set of unique devices with the vendor id"""
sd = set()
for d in supported_devices:
@@ -542,11 +589,11 @@ def get_devices_with_vendor_id(vid):
return sd
def active_ports_on_supported_devices(sds, eliminate_duplicates=False):
def active_ports_on_supported_devices(sds, eliminate_duplicates=False) -> Set[str]:
"""Return a set of active ports based on the supplied supported devices"""
ports = set()
baseports = set()
system = platform.system()
ports: Set = set()
baseports: Set = set()
system: str = platform.system()
# figure out what possible base ports there are
for d in sds:
@@ -604,13 +651,13 @@ def active_ports_on_supported_devices(sds, eliminate_duplicates=False):
for com_port in com_ports:
ports.add(com_port)
if eliminate_duplicates:
ports = eliminate_duplicate_port(list(ports))
ports.sort()
ports = set(ports)
portlist: List = eliminate_duplicate_port(list(ports))
portlist.sort()
ports = set(portlist)
return ports
def detect_windows_port(sd):
def detect_windows_port(sd) -> Set[str]: #"sd" is a SupportedDevice from meshtastic.supported_device
"""detect if Windows port"""
ports = set()
@@ -635,20 +682,26 @@ def detect_windows_port(sd):
return ports
def check_if_newer_version():
def check_if_newer_version() -> Optional[str]:
"""Check pip to see if we are running the latest version."""
pypi_version = None
pypi_version: Optional[str] = None
try:
url = "https://pypi.org/pypi/meshtastic/json"
url: str = "https://pypi.org/pypi/meshtastic/json"
data = requests.get(url, timeout=5).json()
pypi_version = data["info"]["version"]
except Exception:
pass
act_version = get_active_version()
if pypi_version is None:
return None
try:
parsed_act_version = pkg_version.parse(act_version)
parsed_pypi_version = pkg_version.parse(pypi_version)
#Note: if handed "None" when we can't download the pypi_version,
#this gets a TypeError:
#"TypeError: expected string or bytes-like object, got 'NoneType'"
#Handle that below?
except pkg_version.InvalidVersion:
return pypi_version
@@ -660,5 +713,38 @@ def check_if_newer_version():
def message_to_json(message: Message, multiline: bool=False) -> str:
"""Return protobuf message as JSON. Always print all fields, even when not present in data."""
json = MessageToJson(message, always_print_fields_with_no_presence=True)
try:
json = MessageToJson(message, always_print_fields_with_no_presence=True)
except TypeError:
json = MessageToJson(message, including_default_value_fields=True) # type: ignore[call-arg] # pylint: disable=E1123
return stripnl(json) if not multiline else json
def to_node_num(node_id: Union[int, str]) -> int:
"""
Normalize a node id from int | '!hex' | '0xhex' | 'decimal' to int.
"""
if isinstance(node_id, int):
return node_id
s = str(node_id).strip()
if s.startswith("!"):
s = s[1:]
if s.lower().startswith("0x"):
return int(s, 16)
try:
return int(s, 10)
except ValueError:
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

5492
poetry.lock generated
View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,31 +1,31 @@
[tool.poetry]
name = "meshtastic"
version = "2.4.1"
version = "2.7.5"
description = "Python API & client shell for talking to Meshtastic devices"
authors = ["Meshtastic Developers <contact@meshtastic.org>"]
license = "GPL-3.0-only"
readme = "README.md"
[tool.poetry.dependencies]
python = "^3.9,<3.13" # 3.9 is needed for pandas, bleak requires a max of 3.13 for some reason
python = "^3.9,<3.15" # 3.9 is needed for pandas
pyserial = "^3.5"
protobuf = ">=5.26.0"
dotmap = "^1.3.30"
pexpect = "^4.9.0"
pyqrcode = "^1.2.1"
protobuf = ">=4.21.12"
tabulate = "^0.9.0"
webencodings = "^0.5.1"
requests = "^2.31.0"
pyparsing = "^3.1.2"
pyyaml = "^6.0.1"
pypubsub = "^4.0.3"
bleak = "^0.21.1"
bleak = ">=0.22.3"
packaging = "^24.0"
print-color = "^0.4.6"
argcomplete = { version = "^3.5.2", optional = true }
pyqrcode = { version = "^1.2.1", optional = true }
dotmap = { version = "^1.3.30", optional = true }
print-color = { version = "^0.4.6", optional = true }
dash = { version = "^2.17.1", optional = true }
pytap2 = { version = "^2.3.0", optional = true }
dash-bootstrap-components = { version = "^1.6.0", optional = true }
pandas = { version = "^2.2.2", optional = true }
pandas-stubs = { version = "^2.2.2.240603", optional = true }
wcwidth = {version = "^0.2.13", optional = true}
[tool.poetry.group.dev.dependencies]
hypothesis = "^6.103.2"
@@ -34,16 +34,15 @@ pytest-cov = "^5.0.0"
pdoc3 = "^0.10.0"
autopep8 = "^2.1.0"
pylint = "^3.2.3"
pyinstaller = "^6.8.0"
pyinstaller = "^6.10.0"
mypy = "^1.10.0"
mypy-protobuf = "^3.6.0"
mypy-protobuf = "^3.3.0"
types-protobuf = "^5.26.0.20240422"
types-tabulate = "^0.9.0.20240106"
types-requests = "^2.31.0.20240406"
types-setuptools = "^69.5.0.20240423"
types-pyyaml = "^6.0.12.20240311"
pyarrow-stubs = "^10.0.1.7"
pandas-stubs = "^2.2.2.240603"
[tool.poetry.group.powermon]
optional = true
@@ -67,6 +66,7 @@ ipywidgets = "^8.1.3"
jupyterlab-widgets = "^3.0.11"
[tool.poetry.extras]
cli = ["pyqrcode", "print-color", "dotmap", "argcomplete", "wcwidth"]
tunnel = ["pytap2"]
analysis = ["dash", "dash-bootstrap-components", "pandas", "pandas-stubs"]