Compare commits

..

308 Commits

Author SHA1 Message Date
mkinney
a76ad6c686 Merge pull request #313 from mkinney/master
regen code from latest protobufs
2022-03-30 11:36:33 -07:00
Mike Kinney
3d9a55add3 regen code from updated protobufs 2022-03-30 11:32:47 -07:00
Mike Kinney
4ceac5e847 Merge remote-tracking branch 'upstream/master' 2022-03-30 11:25:45 -07:00
Jm Casler
0939022cb4 updating proto submodule to latest 2022-03-29 21:44:51 -07:00
Jm Casler
f7afb9ff15 updating proto submodule to latest 2022-03-29 20:04:43 -07:00
Jm Casler
13fd4ba614 updating proto submodule to latest 2022-03-26 09:32:15 -07:00
Jm Casler
2f80c9866a updating proto submodule to latest 2022-03-25 22:30:43 -07:00
Jm Casler
e2bca647ae updating proto submodule to latest 2022-03-25 22:23:58 -07:00
mkinney
ec2467486c Merge pull request #302 from raldi/master
Added examples/tcp_gps_example.py
2022-03-22 15:10:25 -07:00
Mike Schiraldi
3332271a97 lint 2022-03-22 12:41:50 -07:00
github-actions
7fdfd782d8 bump version 2022-03-21 17:50:05 +00:00
Mike Kinney
af7bf7ff7f add wifi min length check 2022-03-21 10:48:27 -07:00
github-actions
b6dc4d0bd2 bump version 2022-03-16 03:26:41 +00:00
mkinney
8ec5dbbf38 Merge pull request #307 from mkinney/master
there is no tlora_v2.1
2022-03-15 20:13:51 -07:00
Mike Kinney
0c760f3721 there is no tlora_v2.1 2022-03-15 20:11:14 -07:00
mkinney
c45568731f Merge pull request #306 from mkinney/master
regen protobuf code; tlora v1 vendor/product id fix
2022-03-15 19:57:45 -07:00
Mike Kinney
ef4c9dc338 fix the vendor and product id for tlora v1 2022-03-15 19:54:18 -07:00
Mike Kinney
89de553aba regen python based on updated protobufs 2022-03-15 19:30:54 -07:00
mkinney
247e7b2605 Merge pull request #303 from mkinney/supported_devices_fixes
fix some tlora entries
2022-03-15 19:27:42 -07:00
Mike Kinney
f81ba64b91 fix some tlora entries 2022-03-15 19:25:04 -07:00
Jm Casler
d6fbca1bf1 updating proto submodule to latest 2022-03-15 15:46:02 -07:00
Mike Schiraldi
ef9441e7d2 Added examples/tcp_gps_example.py 2022-03-14 23:32:42 -07:00
Jm Casler
6fbf78fa19 updating proto submodule to latest 2022-03-14 18:05:28 -07:00
github-actions
e38a614c37 bump version 2022-03-11 23:08:22 +00:00
mkinney
d8b9665946 Merge pull request #299 from mkinney/master
update to latest protobufs
2022-03-11 15:07:34 -08:00
Mike Kinney
e655beb01c update to latest protobufs 2022-03-11 15:04:09 -08:00
mkinney
aa1dcb7f99 Merge pull request #298 from mkinney/master
refactor code to util; add duplicate check
2022-03-08 10:53:33 -08:00
Mike Kinney
1a2519d647 refactor code to util 2022-03-08 10:45:11 -08:00
Mike Kinney
a3572efaa6 add duplicate check 2022-03-08 10:44:14 -08:00
github-actions
e28f0d5509 bump version 2022-03-08 06:16:57 +00:00
mkinney
789a14054f Update release.yml 2022-03-07 22:16:03 -08:00
github-actions
4655b5c732 bump version 2022-03-08 06:04:02 +00:00
mkinney
81ebd8c8e7 Merge pull request #296 from mkinney/fix_release_version
keep sha ref and use it for subsequent checkouts
2022-03-07 22:02:28 -08:00
Mike Kinney
7998520e4a keep sha ref and use it for subsequent checkouts 2022-03-07 21:59:46 -08:00
mkinney
460196a62b Merge pull request #295 from mkinney/bug_when_ports_not_sorted
do not checkout the code again
2022-03-07 21:48:28 -08:00
Mike Kinney
22f43851bd do not checkout the code again 2022-03-07 21:44:11 -08:00
mkinney
91bb63eb97 Merge pull request #294 from mkinney/bug_when_ports_not_sorted
fix when ports are not sorted
2022-03-07 21:25:42 -08:00
Mike Kinney
ede1b5f08b fix when ports are not sorted 2022-03-07 21:21:41 -08:00
mkinney
a26c157c65 Merge pull request #292 from mkinney/handle_ignore_incoming
handle ignore_incoming repeated field
2022-03-03 17:14:09 -08:00
Mike Kinney
bcf00a1f8d handle ignore_incoming repeated field 2022-03-03 17:11:22 -08:00
Jm Casler
749a94e6e4 updating proto submodule to latest 2022-03-02 18:49:49 -08:00
github-actions
fc9d1e077c bump version 2022-03-02 21:56:05 +00:00
mkinney
a40f0b4038 Merge pull request #291 from mkinney/1_3_python
min req of python is 3.7 now; bump nanopb version
2022-03-02 13:55:09 -08:00
Mike Kinney
dbf54396f3 min req of python is 3.7 now; bump nanopb version 2022-03-02 13:29:19 -08:00
github-actions
3fe881e45a bump version 2022-03-02 19:43:05 +00:00
mkinney
af03c5163c Merge pull request #290 from mkinney/master
change version per pypi reqs
2022-03-02 11:42:09 -08:00
Mike Kinney
58de84945f change version per pypi reqs 2022-03-02 11:39:07 -08:00
github-actions
a2d4252002 bump version 2022-03-02 19:32:52 +00:00
mkinney
0689fc19a9 Merge pull request #289 from mkinney/1_3_initial
got lint and pytest working
2022-03-02 11:31:49 -08:00
Mike Kinney
68530f6e11 change medium to mid 2022-03-02 11:29:38 -08:00
Mike Kinney
fd752bedc5 change version format 2022-03-02 11:24:13 -08:00
Mike Kinney
cac880eb1a change to alpha 2022-03-02 11:15:47 -08:00
Mike Kinney
2c66cd4a95 change the name not the package 2022-03-02 10:50:08 -08:00
Mike Kinney
64e428f182 change package name and version 2022-03-02 10:44:35 -08:00
Mike Kinney
6f2efdcefe got lint and pytest working 2022-03-02 10:28:07 -08:00
github-actions
9214b2ffcc bump version 2022-03-02 00:20:31 +00:00
mkinney
cfb14d4b77 Merge pull request #287 from mkinney/changes_to_smokevirt
increase timeout; ensure secondary channel is deleted before trying t…
2022-03-01 16:19:14 -08:00
Mike Kinney
5965615e17 increase timeout; ensure secondary channel is deleted before trying to add it 2022-03-02 00:04:21 +00:00
Jm Casler
40eb7d8515 updating proto submodule to latest 2022-02-28 22:03:42 -08:00
Jm Casler
e2f36a9bea updating proto submodule to latest 2022-02-28 19:36:02 -08:00
Jm Casler
a0ba644488 updating proto submodule to latest 2022-02-27 10:01:44 -08:00
Jm Casler
2f67f344b7 updating proto submodule to latest 2022-02-27 09:57:16 -08:00
Jm Casler
8bb208aed1 updating proto submodule to latest 2022-02-27 09:52:34 -08:00
Jm Casler
48b145a592 updating proto submodule to latest 2022-02-27 01:00:38 -08:00
Jm Casler
65de9200fb updating proto submodule to latest 2022-02-27 00:48:57 -08:00
Jm Casler
e51d7a7a18 updating proto submodule to latest 2022-02-27 00:38:09 -08:00
Jm Casler
c4af50d63a updating proto submodule to latest 2022-02-26 21:22:33 -08:00
Jm Casler
933fe8953a updating proto submodule to latest 2022-02-26 21:09:20 -08:00
mkinney
66aa492d50 Merge pull request #283 from mkinney/add_cm
add cm
2022-02-25 23:44:13 -08:00
Mike Kinney
730934f520 add cm 2022-02-25 23:36:05 -08:00
mkinney
d2ec09eaf8 Merge pull request #281 from prampec/extend_canned_message_length
Canned message - Extend messages length
2022-02-25 22:58:57 -08:00
mkinney
7fd3b313b2 Update release.yml 2022-02-25 22:15:36 -08:00
github-actions
89f1549741 bump version 2022-02-26 06:11:26 +00:00
github-actions
e91015f5c8 bump version 2022-02-26 06:07:50 +00:00
mkinney
d9c3edfb12 Update release.yml 2022-02-25 22:06:27 -08:00
mkinney
cbf9696f47 Merge pull request #282 from mkinney/master
improve the release process
2022-02-25 22:04:48 -08:00
Mike Kinney
24e556b9a7 improve the release process 2022-02-25 22:01:21 -08:00
mkinney
eaf29512b6 Update standalone_readme.txt 2022-02-25 21:38:23 -08:00
Jm Casler
0c1e0ec375 updating proto submodule to latest 2022-02-22 17:10:04 -08:00
Balazs Kelemen
b56a054f50 Canned message - Extend messages length 2022-02-21 22:06:03 +01:00
Jm Casler
33ff4e36de updating proto submodule to latest 2022-02-20 01:32:53 -08:00
Jm Casler
bf879934e6 updating proto submodule to latest 2022-02-19 23:48:24 -08:00
Jm Casler
b878fa3a80 updating proto submodule to latest 2022-02-19 23:17:50 -08:00
Jm Casler
901849f176 updating proto submodule to latest 2022-02-19 23:10:53 -08:00
Jm Casler
371c0d22c2 updating proto submodule to latest 2022-02-19 22:55:38 -08:00
mkinney
4f7f38e0a7 Update setup.py 2022-02-18 11:16:49 -08:00
mkinney
85dca2e14e Merge pull request #278 from mkinney/fix_smoke1
add detection of duplicate ports to findPorts; fix smoke1 test
2022-02-18 11:16:25 -08:00
Mike Kinney
e53a5023f1 add detection of duplicate ports to findPorts; fix smoke1 test 2022-02-18 11:13:48 -08:00
mkinney
ce8b75d96d Merge pull request #277 from mkinney/win11_delay_only
only delay on win11
2022-02-18 10:12:16 -08:00
Mike Kinney
26f65c4fee only delay on win11 2022-02-18 10:08:53 -08:00
mkinney
1ba1e51ca4 Merge pull request #276 from mkinney/keep_devpath_on_serial
keep the devPath used on serial connections
2022-02-18 10:03:43 -08:00
Mike Kinney
f674afc412 keep the devPath used on serial connections 2022-02-18 09:59:09 -08:00
mkinney
db90b898e1 Update setup.py 2022-02-16 10:16:10 -08:00
mkinney
71621c2225 Merge pull request #269 from mkinney/two_serials
if we have a duplicate serial port use the appropriate one
2022-02-16 10:12:57 -08:00
Mike Kinney
ed36fca4a2 remove testing prints 2022-02-16 10:10:32 -08:00
Mike Kinney
fdd3699ba5 if we have a duplicate serial port use the appropriate one 2022-02-16 10:08:57 -08:00
Jm Casler
7d4b39643b updating proto submodule to latest 2022-02-14 20:35:46 -08:00
Jm Casler
7698cd2c7d updating proto submodule to latest 2022-02-14 20:08:31 -08:00
Jm Casler
03c744df54 updating proto submodule to latest 2022-02-14 19:55:21 -08:00
Jm Casler
90978d1f35 updating proto submodule to latest 2022-02-14 19:42:06 -08:00
Jm Casler
68a2bf271a updating proto submodule to latest 2022-02-14 17:59:45 -08:00
Jm Casler
8301384c53 updating proto submodule to latest 2022-02-14 16:59:26 -08:00
Jm Casler
045592212a updating proto submodule to latest 2022-02-14 16:56:59 -08:00
mkinney
45d879e607 Update setup.py 2022-02-12 21:19:20 -08:00
mkinney
3e44ee1eba Merge pull request #266 from mkinney/increase_sleep_for_win11
increase sleep for win11
2022-02-12 21:18:53 -08:00
Mike Kinney
36c5fc8d3c increase sleep for win11 2022-02-12 21:16:11 -08:00
mkinney
38ceb85ad9 Update setup.py 2022-02-10 15:15:46 -08:00
mkinney
6d18d9226d Merge pull request #263 from mkinney/add_rak11200
Add rak11200
2022-02-10 15:15:15 -08:00
Mike Kinney
6d5ed2129a add rak11200 2022-02-10 15:12:28 -08:00
Mike Kinney
5cfb6ffa11 add examples to lint and fix warnings 2022-02-10 14:51:22 -08:00
mkinney
363b268ccf Update setup.py 2022-02-03 10:03:31 -08:00
mkinney
e6cddb0084 Merge pull request #260 from mkinney/update_protobufs
update protobufs to latest
2022-02-03 10:01:31 -08:00
Mike Kinney
5e77bf62b9 update protobufs to latest 2022-02-03 09:59:03 -08:00
github-actions
4905c0b179 Update protobuf submodule 2022-02-03 15:00:36 +00:00
Ben Meadors
43ab3be804 Update update_protobufs.yml 2022-02-03 08:59:32 -06:00
github-actions
14a6e581bc Update protobuf submodule 2022-02-03 14:38:02 +00:00
mkinney
cadad32bdb Update setup.py 2022-02-01 17:44:37 -08:00
mkinney
71c6411d1a Merge pull request #257 from mkinney/add_techo_info
add T-Echo info
2022-02-01 17:41:19 -08:00
Mike Kinney
85e8fad16f add T-Echo info 2022-02-01 17:18:05 -08:00
mkinney
67398222d4 Update setup.py 2022-02-01 13:56:03 -08:00
mkinney
1a82bdee75 Merge pull request #256 from mkinney/update_proto2
fix regen-protos script
2022-02-01 13:55:42 -08:00
Mike Kinney
9fff26c3db fix regen-protos script 2022-02-01 13:53:14 -08:00
mkinney
172f6c577d Merge pull request #254 from mkinney/force_win_encoding
specify a code page on windows
2022-02-01 12:03:08 -08:00
Mike Kinney
ff5652058d fix lint complaining about long lines 2022-02-01 12:00:58 -08:00
Mike Kinney
5241fabb33 change encoding to utf8 for windows 2022-02-01 11:56:07 -08:00
Mike Kinney
6c9aa5794f specify a code page on windows 2022-02-01 11:44:02 -08:00
mkinney
7c8cb375a3 Merge pull request #252 from mkinney/update_sd_info
add info about 19003
2022-01-30 23:11:22 -08:00
Mike Kinney
a6e770f548 add info about 19003 2022-01-30 22:38:35 -08:00
mkinney
eb7683450f Merge pull request #251 from mkinney/update_sd
add more info about some devices; fix tlora info
2022-01-30 21:15:40 -08:00
Mike Kinney
ee739d3414 add more info about some devices; fix tlora info 2022-01-30 17:36:15 -08:00
mkinney
b864bbdd5f Update setup.py 2022-01-29 21:48:57 -08:00
mkinney
5a740eddc3 Merge pull request #250 from mkinney/bugfix_for_owner_short
fix bug
2022-01-29 21:48:17 -08:00
Mike Kinney
fdced6f225 fix bug 2022-01-29 21:37:34 -08:00
mkinney
3d772845f9 Merge pull request #249 from mkinney/device_detection
Device detection
2022-01-29 21:11:40 -08:00
Mike Kinney
72326d467e only show active windows ports 2022-01-29 20:53:09 -08:00
Mike Kinney
a0944961b5 fix lint warnings 2022-01-29 20:47:58 -08:00
Mike Kinney
1fa61ece93 add windows device and port detection 2022-01-29 20:45:29 -08:00
Mike Kinney
288d0bb884 add detection on mac 2022-01-29 17:12:36 -08:00
Mike Kinney
28a2aa47e8 add device and port detection on linux 2022-01-30 00:57:56 +00:00
mkinney
069056edad Update setup.py 2022-01-27 17:12:50 -08:00
github-actions
19bd510975 Update protobuf submodule 2022-01-28 01:12:41 +00:00
mkinney
7979efc0a1 Merge pull request #247 from mkinney/remote_hardware
remote hardware tests
2022-01-27 17:11:37 -08:00
Mike Kinney
66866a4c65 fix logic 2022-01-27 17:09:13 -08:00
Mike Kinney
e6fb066fe5 get last couple of lines covered in remote_hardware 2022-01-27 17:05:16 -08:00
Mike Kinney
5841979566 remove line 2022-01-27 17:00:53 -08:00
Mike Kinney
529f50edc6 remote hardware tests 2022-01-27 16:54:47 -08:00
github-actions
5895e8fb4d Update protobuf submodule 2022-01-27 14:21:10 +00:00
mkinney
49dcf71116 Update setup.py 2022-01-26 20:32:45 -08:00
Jm Casler
5778552380 updating proto submodule to latest 2022-01-26 13:35:48 -08:00
Jm Casler
592ecc9997 updating proto submodule to latest 2022-01-26 12:27:52 -08:00
mkinney
1aaa205cc9 Merge pull request #246 from mkinney/update_help
add more info on --ch-set help
2022-01-26 11:22:05 -08:00
Mike Kinney
ff5a0927fa add more info on --ch-set help 2022-01-26 11:16:01 -08:00
mkinney
607127d46e Update regen-protos.sh 2022-01-23 15:15:14 -08:00
mkinney
cf61a5d39d Update update_protobufs.yml 2022-01-23 15:09:44 -08:00
mkinney
69b2599abb Update setup.py 2022-01-23 14:51:38 -08:00
mkinney
533e50de9d Update release.yml 2022-01-23 14:51:21 -08:00
mkinney
12b99f80dc Update release.yml 2022-01-23 14:48:59 -08:00
mkinney
0193ca052f Update setup.py 2022-01-23 14:47:26 -08:00
mkinney
e0af620cbe Merge pull request #245 from mkinney/standalone_readme
add readme.txt to release for standalone zip files
2022-01-23 14:47:06 -08:00
Mike Kinney
80367232ae add readme.txt to release for standalone zip files 2022-01-23 14:44:59 -08:00
mkinney
1c480bd74f Update setup.py 2022-01-23 14:25:01 -08:00
mkinney
ba05de263c Update release.yml 2022-01-23 14:24:48 -08:00
mkinney
f045c3bc2b Update setup.py 2022-01-23 14:17:05 -08:00
mkinney
9ec5f33a85 Update release.yml 2022-01-23 14:16:50 -08:00
mkinney
e1cc657059 Update setup.py 2022-01-23 14:10:58 -08:00
mkinney
9c3dce4bbb Update release.yml 2022-01-23 14:10:43 -08:00
mkinney
5418539275 Update setup.py 2022-01-23 14:04:51 -08:00
mkinney
3103640688 Update release.yml 2022-01-23 14:04:36 -08:00
mkinney
86beb2f212 Update setup.py 2022-01-23 14:03:05 -08:00
mkinney
dd1c96fcba Update release.yml 2022-01-23 14:02:48 -08:00
mkinney
b282baadb7 Update setup.py 2022-01-23 13:55:08 -08:00
mkinney
fccb38fced Update release.yml 2022-01-23 13:54:53 -08:00
mkinney
700a7e27d3 Update setup.py 2022-01-23 13:51:06 -08:00
mkinney
bb1c40c7f8 Update release.yml 2022-01-23 13:50:49 -08:00
mkinney
407490c654 Update setup.py 2022-01-23 13:38:54 -08:00
mkinney
dfd61af346 Update release.yml 2022-01-23 13:38:36 -08:00
mkinney
a85b4ad985 Update setup.py 2022-01-23 13:35:02 -08:00
mkinney
eacffa79fa Update release.yml 2022-01-23 13:34:47 -08:00
mkinney
ca5449f1b8 Update release.yml 2022-01-23 13:21:50 -08:00
mkinney
6751f5bd53 Update release.yml 2022-01-23 13:12:17 -08:00
mkinney
c5c70fe83e Update setup.py 2022-01-23 13:10:06 -08:00
mkinney
0c0cd81253 Update release.yml 2022-01-23 13:09:40 -08:00
mkinney
553a03e40f Update setup.py 2022-01-23 12:53:51 -08:00
mkinney
b50190aeb0 Update release.yml 2022-01-23 12:53:32 -08:00
mkinney
bc231a8a9d Update setup.py 2022-01-23 12:34:36 -08:00
mkinney
c1c9cb8139 Update release.yml 2022-01-23 12:34:12 -08:00
mkinney
0a915e305d Update setup.py 2022-01-23 12:09:55 -08:00
mkinney
308eb1f321 Update release.yml 2022-01-23 12:09:36 -08:00
mkinney
d576c0e30b Update setup.py 2022-01-23 12:01:41 -08:00
mkinney
e63d4c3a30 Update release.yml 2022-01-23 12:01:17 -08:00
mkinney
2041e81101 Update setup.py 2022-01-23 11:49:04 -08:00
mkinney
f396374fa8 Update release.yml 2022-01-23 11:48:13 -08:00
mkinney
1a0db63f32 Merge pull request #244 from mkinney/release_improvements
improve the release process
2022-01-23 11:44:33 -08:00
Mike Kinney
0d32a83e66 improve the release process 2022-01-23 11:41:49 -08:00
mkinney
7ac3328db8 Merge pull request #243 from mkinney/short_name
Short name
2022-01-23 11:25:06 -08:00
Mike Kinney
4f72987a29 add some config tests 2022-01-23 11:22:05 -08:00
Mike Kinney
aa5af53348 add option to configure just the owner short name 2022-01-23 11:17:41 -08:00
Jm Casler
030ee8554c updating proto submodule to latest 2022-01-23 09:10:37 -08:00
mkinney
80d447d64b Update setup.py 2022-01-21 17:15:26 -08:00
mkinney
bfac5a418c Merge pull request #239 from meshtastic/update-protos-for-shutdown
Shutdown command (should mirror reboot)
2022-01-21 17:05:26 -08:00
mkinney
a3b09f028a Merge pull request #242 from mkinney/help_update_protos_for_shutdown
ignore new pb file
2022-01-21 17:00:19 -08:00
Mike Kinney
930292aaad ignore new pb file 2022-01-21 16:59:17 -08:00
mkinney
1d3b59a7e0 Merge pull request #240 from mkinney/help_update_protos_for_shutdown
update protos
2022-01-21 16:56:59 -08:00
Mike Kinney
d54b518bfd update protos 2022-01-21 16:55:09 -08:00
Ben Meadors
ccb8bb83a6 Consistent spacing 2022-01-21 18:33:09 -06:00
Ben Meadors
076e865f77 Node level test 2022-01-21 18:20:50 -06:00
Ben Meadors
8ceb9faf2d Shutdown command 2022-01-21 18:11:26 -06:00
github-actions
b15e5f516a Update protobuf submodule 2022-01-21 23:58:36 +00:00
github-actions
e161d75986 Update protobuf submodule 2022-01-20 22:18:17 +00:00
mkinney
084473a340 Update README.md
add pypi downloads
2022-01-20 10:10:42 -08:00
Jm Casler
74d6d4d634 updating proto submodule to latest 2022-01-19 18:18:59 -08:00
github-actions
38ac4298a0 Update protobuf submodule 2022-01-19 23:56:04 +00:00
mkinney
9b2c0e7954 Merge pull request #237 from mkinney/master
regen proto from updated protobufs
2022-01-16 17:06:35 -08:00
Mike Kinney
62472034f1 regen proto from updated protobufs 2022-01-17 00:26:31 +00:00
github-actions
13cfc19841 Update protobuf submodule 2022-01-17 00:21:35 +00:00
mkinney
4955ab8df2 Update setup.py 2022-01-16 12:48:35 -08:00
mkinney
de39c98e50 Merge pull request #236 from mkinney/fix_enum_listing
fix enum listing; add tests for pref values
2022-01-16 12:47:49 -08:00
Mike Kinney
51378bb0eb fix enum listing; add tests for pref values 2022-01-16 12:45:28 -08:00
mkinney
aff3bdd78e Update setup.py 2022-01-15 14:19:36 -08:00
mkinney
e9a8e26e76 Merge pull request #235 from mkinney/fix_for_setPref
looks like we needed some of that dead code after all
2022-01-15 14:19:07 -08:00
Mike Kinney
83439679c1 looks like we needed some of that dead code after all 2022-01-15 14:15:55 -08:00
mkinney
968027a439 Update setup.py 2022-01-15 10:38:40 -08:00
mkinney
cc24b6ebc5 Merge pull request #231 from mkinney/camelCase
handle snake_case or camelCase
2022-01-15 10:32:08 -08:00
Mike Kinney
db09b4718d add two more lines to code coverage 2022-01-15 10:21:24 -08:00
Jm Casler
e0edbc6288 updating proto submodule to latest 2022-01-15 09:34:14 -08:00
Mike Kinney
ae9ae91af5 remove dead code 2022-01-15 00:24:41 -08:00
Mike Kinney
7921db007b add some coverage to getPref() and setPref() 2022-01-15 00:01:44 -08:00
Mike Kinney
e85af2f9e9 Merge branch 'camelCase' of github.com:mkinney/Meshtastic-python into camelCase 2022-01-14 20:16:26 -08:00
Mike Kinney
8ccc64f92e Merge remote-tracking branch 'upstream/master' into camelCase 2022-01-14 20:16:10 -08:00
Mike Kinney
afb21c6dc3 remove code not needed 2022-01-14 20:15:25 -08:00
mkinney
3291bc7097 Merge branch 'meshtastic:master' into camelCase 2022-01-14 20:06:40 -08:00
Mike Kinney
a7d56504be handle snake_case or camelCase 2022-01-14 16:36:53 -08:00
Jm Casler
90e5b473d9 updating proto submodule to latest 2022-01-13 16:55:22 -08:00
mkinney
52834e9966 Update setup.py 2022-01-12 17:25:00 -08:00
mkinney
63c60d4cea Merge pull request #229 from mkinney/check_for_multiple_mac_conversions
suggested fix from MitchConner912 for not converting mac address more…
2022-01-12 17:13:14 -08:00
Mike Kinney
6a2a9d2093 suggested fix from MitchConner912 for not converting mac address more than once 2022-01-12 17:10:51 -08:00
mkinney
1410448808 Merge pull request #228 from mkinney/more_unit_tests
add more unit tests
2022-01-12 17:06:24 -08:00
Mike Kinney
ad8f2222db cover a few more lines 2022-01-12 16:50:29 -08:00
Mike Kinney
48265e73b1 no need to import pygatt here 2022-01-12 15:54:07 -08:00
Mike Kinney
f3139a8aa0 add more unit tests 2022-01-12 15:50:16 -08:00
mkinney
8d68e36703 Merge pull request #227 from mkinney/pylint_fixes
fix the consider-using-f-string warnings
2022-01-12 14:57:50 -08:00
Mike Kinney
d2d93fbe80 no longer need to disable this pylint warning 2022-01-12 14:50:10 -08:00
Mike Kinney
ed8510468d no need to disable these pylint warnings anymore 2022-01-12 14:47:49 -08:00
Mike Kinney
e7680e07c2 fix pylint global-statement warnings 2022-01-12 14:46:21 -08:00
Mike Kinney
0f89baa36e fix the pylint unused-argument warnings 2022-01-12 14:41:49 -08:00
Mike Kinney
48ed7690af fix the consider-using-f-string warnings 2022-01-12 13:46:01 -08:00
mkinney
59b94ea650 Merge pull request #226 from mkinney/add_get_hw_example
add example how you can get the hwModel using api
2022-01-12 10:59:43 -08:00
Mike Kinney
5b992734fb add example how you can get the hwModel using api 2022-01-12 10:57:03 -08:00
mkinney
3b74b911f8 Merge pull request #224 from mkinney/add_set_owner_example
simple example showing how you can set the long and short name
2022-01-12 10:26:22 -08:00
Mike Kinney
b6570e3c27 simple example showing how you can set the long and short name 2022-01-12 10:23:51 -08:00
mkinney
e1e1664b96 Merge pull request #223 from mkinney/make_pygatt_linux_only
make pygatt linux only in requirements.txt
2022-01-12 10:23:06 -08:00
Mike Kinney
cb1913dfc3 make pygatt linux only in requirements.txt 2022-01-12 10:15:06 -08:00
Sacha Weatherstone
b813a6f8c5 Update requirements.txt 2022-01-12 14:39:20 +11:00
mkinney
0b662318e1 Merge pull request #222 from mkinney/testing_on_mac_air
fixes for working on mac air
2022-01-11 16:48:01 -08:00
Mike Kinney
a6ccc1a246 add conditional lib if linux 2022-01-11 16:42:25 -08:00
Mike Kinney
bc17e9b389 fixes for working on mac air 2022-01-11 16:36:39 -08:00
mkinney
b08e96b66a Update setup.py
bump version for release
2022-01-11 11:02:26 -08:00
mkinney
9e74ead54e Merge pull request #219 from mkinney/smoke_virt
add smoke test for virtual device
2022-01-11 10:56:33 -08:00
Mike Kinney
2cf52f3df6 add a convenience target in Makefile for running the smokevirt test 2022-01-11 18:50:18 +00:00
Mike Kinney
50e9a0a9f7 exclude smokevirt from ci 2022-01-11 18:44:39 +00:00
Mike Kinney
841c44e05c add smoke test for virtual device 2022-01-11 18:39:49 +00:00
mkinney
465bafeb30 Merge pull request #218 from mkinney/fix_remote_admin_message
refactor code to only call local node when necessary; fix tests
2022-01-10 16:51:04 -08:00
Mike Kinney
cb8dafbd31 add more coverage 2022-01-10 16:40:47 -08:00
Mike Kinney
52db617b06 refactor code to only call local node when necessary; fix tests 2022-01-10 16:20:11 -08:00
Jm Casler
ec622590da updating proto submodule to latest 2022-01-09 22:23:07 -08:00
mkinney
345cb1bdc4 Merge pull request #217 from mkinney/fix_remote_admin_message
Fix remote admin message
2022-01-06 12:36:03 -08:00
Mike Kinney
8ca692a26e cannot call os.getlogin() on github instances 2022-01-06 12:33:30 -08:00
Mike Kinney
d1ea68d7dc add code coverage to recently added code 2022-01-06 12:17:42 -08:00
Mike Kinney
b56440a4e8 should not need to talk with remote node if just doing sendtext 2022-01-06 11:55:36 -08:00
github-actions
1401b949a3 Update protobuf submodule 2022-01-06 17:09:45 +00:00
mkinney
97f3ce6198 Merge pull request #215 from mkinney/combine_build_single_executables
combine the building of single executables into one action
2022-01-06 00:25:46 -08:00
Mike Kinney
8019391914 combine the building of single executables into one action 2022-01-06 00:24:59 -08:00
mkinney
8d10010ab1 Merge pull request #214 from mkinney/more_testing_stuff
move slower unit tests to unitslow
2022-01-06 00:00:42 -08:00
mkinney
a2b4d2a96a Update build_mac.yml 2022-01-05 23:47:02 -08:00
mkinney
4de558bdf6 Update build_mac.yml 2022-01-05 23:40:49 -08:00
mkinney
8ff06a0de1 Update build_mac.yml 2022-01-05 23:37:35 -08:00
mkinney
f8ad6061c1 Update build_mac.yml
revert
2022-01-05 23:06:49 -08:00
mkinney
aa4cdb6aea Update build_mac.yml 2022-01-05 23:01:13 -08:00
Mike Kinney
cba424fde0 move slower unit tests to unitslow 2022-01-05 21:12:50 -08:00
mkinney
6c7a870645 Merge pull request #213 from mkinney/add_python_310
check python v3.10 as well
2022-01-05 18:20:55 -08:00
Mike Kinney
f42b1ad4e0 check python v3.10 as well 2022-01-05 18:18:31 -08:00
mkinney
30a51952e0 Merge pull request #212 from mkinney/serial_perms
improve the permission error on linux
2022-01-05 15:08:24 -08:00
Mike Kinney
5de754c5ab improve the permission error on linux 2022-01-05 23:03:06 +00:00
mkinney
bda446c7b5 Update build_mac.yml 2022-01-05 14:13:42 -08:00
mkinney
f79540f197 Update build_mac.yml 2022-01-05 14:09:17 -08:00
mkinney
d507697e56 Merge pull request #210 from mkinney/work_on_single_executable
install meshtastic before building single executable
2022-01-05 13:58:47 -08:00
Mike Kinney
acbc1f2e30 install meshtastic before building single executable 2022-01-05 13:56:07 -08:00
mkinney
7b3c68119c Merge pull request #209 from mkinney/add_modules_for_building_single_executables
collect the meshtastic libs
2022-01-05 13:36:25 -08:00
Mike Kinney
39f97166b0 collect the meshtastic libs 2022-01-05 13:34:28 -08:00
mkinney
541d19cafb Merge pull request #208 from mkinney/create_actions_for_building_single_executables
Create actions for building single executables
2022-01-05 13:26:47 -08:00
Mike Kinney
969a81b779 Merge remote-tracking branch 'upstream/master' into create_actions_for_building_single_executables 2022-01-05 13:24:33 -08:00
Mike Kinney
3f76c1efb0 refactor version info so pyinstaller will work; add build mac and ubuntu standalone executables 2022-01-05 13:22:37 -08:00
mkinney
774849189f Merge pull request #207 from mkinney/create_build_windows_action
add a build windows action
2022-01-05 12:46:10 -08:00
Mike Kinney
53d626aa72 add a build windows action 2022-01-05 12:44:05 -08:00
mkinney
1a3a840269 Merge pull request #206 from mkinney/fully_qualify_imports
need to fully qualify imports so projects consuming the library will …
2022-01-05 11:24:13 -08:00
Mike Kinney
fe69f05e75 add python 3.6, 3.7, 3.8, and 3.9 for ci and validation 2022-01-05 11:20:04 -08:00
Mike Kinney
5c662822b9 need to fully qualify imports so projects consuming the library will work 2022-01-05 11:16:08 -08:00
mkinney
c049d3424a Merge pull request #205 from mkinney/remove_nested_keys
remove nested keys from nodes so we do not display garbage
2022-01-02 11:28:07 -08:00
Mike Kinney
471535853b bump version 2022-01-02 11:20:15 -08:00
Mike Kinney
676148cc14 meant to use decoded not decode 2022-01-02 11:19:17 -08:00
Mike Kinney
a915b05240 remove nested keys from nodes so we do not display garbage 2022-01-02 11:15:19 -08:00
Jm Casler
a1668e8c66 updating proto submodule to latest 2022-01-01 23:25:22 -08:00
mkinney
e7664cb40b Merge pull request #204 from mkinney/add_more_unit_tests
get last two lines covered in node
2022-01-01 15:51:21 -08:00
Mike Kinney
83c18f4008 working on more unit tests 2022-01-01 15:48:33 -08:00
Mike Kinney
8b6321ce7f add a few more tests 2022-01-01 15:21:53 -08:00
Mike Kinney
9fac981ba6 test heartbeatTimer 2022-01-01 14:53:57 -08:00
Mike Kinney
ccc71930f7 get last two lines covered in node 2022-01-01 13:25:36 -08:00
mkinney
9380f048fa Update setup.py
bump version
2022-01-01 11:11:54 -08:00
85 changed files with 7198 additions and 1031 deletions

View File

@@ -10,12 +10,18 @@ on:
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
python-version:
- "3.6"
- "3.7"
- "3.8"
- "3.9"
- "3.10"
steps:
- uses: actions/checkout@v2
- name: Install Python 3
uses: actions/setup-python@v1
with:
python-version: 3.9
- name: Uninstall meshtastic
run: |
pip3 uninstall meshtastic
@@ -29,7 +35,7 @@ jobs:
which meshtastic
meshtastic --version
- name: Run pylint
run: pylint meshtastic
run: pylint meshtastic examples/
- name: Run tests with pytest
run: pytest --cov=meshtastic
- name: Generate coverage report
@@ -46,12 +52,18 @@ jobs:
fail_ci_if_error: true
validate:
runs-on: ubuntu-latest
strategy:
matrix:
python-version:
- "3.6"
- "3.7"
- "3.8"
- "3.9"
- "3.10"
steps:
- uses: actions/checkout@v2
- name: Install Python 3
uses: actions/setup-python@v1
with:
python-version: 3.9
- name: Install meshtastic from local
run: |
pip3 install .

View File

@@ -1,35 +0,0 @@
name: Publish PyPI
on: workflow_dispatch
jobs:
build-and-publish:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up Python 3.9
uses: actions/setup-python@v2
with:
python-version: 3.9
- name: Install pypa/build
run: >-
python -m
pip install
build
--user
- name: Build a binary wheel and a source tarball
run: >-
python -m
build
--sdist
--wheel
--outdir dist/
.
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@master
with:
username: __token__
password: ${{ secrets.PYPI_API_TOKEN }}

198
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,198 @@
name: Make Release
on: workflow_dispatch
jobs:
release_create:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.get_version.outputs.version }}
upload_url: ${{ steps.create_release.outputs.upload_url }}
new_sha: ${{ steps.commit_updated.outputs.sha }}
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Bump version
run: >-
bin/bump_version.py
- name: Commit updated version.py
id: commit_updated
run: |
git config --global user.name 'github-actions'
git config --global user.email 'bot@noreply.github.com'
git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}
git add setup.py
git commit -m "bump version" && git push || echo "No changes to commit"
git log -n 1 --pretty=format:"%H" | tail -n 1 | awk '{print "::set-output name=sha::"$0}'
- name: Get version
id: get_version
run: >-
bin/show_version.py
- name: Create GitHub release
uses: actions/create-release@v1
id: create_release
with:
draft: true
prerelease: true
release_name: Meshtastic Python ${{ steps.get_version.outputs.version }}
tag_name: ${{ steps.get_version.outputs.version }}
body: |
Autogenerated by github action, developer should edit as required before publishing...
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Python 3.9
uses: actions/setup-python@v2
with:
python-version: 3.9
- name: Install pypa/build
run: >-
python -m
pip install
build
--user
- name: Build a binary wheel and a source tarball
run: >-
python -m
build
--sdist
--wheel
--outdir dist/
.
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@master
with:
user: __token__
password: ${{ secrets.PYPI_API_TOKEN }}
build-and-publish-mac:
runs-on: macos-latest
needs: release_create
steps:
- name: Checkout
uses: actions/checkout@v3
with:
ref: ${{ needs.release_create.outputs.new_sha }}
- name: Set up Python 3.9
uses: actions/setup-python@v2
with:
python-version: 3.9
- name: Setup code signing
env:
MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }}
MACOS_CERTIFICATE_PWD: ${{ secrets.MACOS_CERTIFICATE_PWD }}
MACOS_KEYCHAIN_PASSWORD: ${{ secrets.MACOS_KEYCHAIN_PASSWORD }}
run: |
echo $MACOS_CERTIFICATE | base64 --decode > certificate.p12
security create-keychain -p "$MACOS_KEYCHAIN_PASSWORD" meshtastic.keychain
security default-keychain -s meshtastic.keychain
security unlock-keychain -p "$MACOS_KEYCHAIN_PASSWORD" meshtastic.keychain
security import certificate.p12 -k meshtastic.keychain -P "$MACOS_CERTIFICATE_PWD" -T /usr/bin/codesign
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$MACOS_KEYCHAIN_PASSWORD" meshtastic.keychain
- name: Build
env:
MACOS_SIGNING_IDENTITY: ${{ secrets.MACOS_SIGNING_IDENTITY }}
run: |
pip install pyinstaller
pip install -r requirements.txt
pip install .
pyinstaller -F -n meshtastic --collect-all meshtastic --codesign-identity "$MACOS_SIGNING_IDENTITY" meshtastic/__main__.py
- name: Add mac to release
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.release_create.outputs.upload_url }}
asset_path: dist/meshtastic
asset_name: meshtastic_mac
asset_content_type: application/zip
build-and-publish-ubuntu:
runs-on: ubuntu-latest
needs: release_create
steps:
- name: Checkout
uses: actions/checkout@v3
with:
ref: ${{ needs.release_create.outputs.new_sha }}
- name: Set up Python 3.9
uses: actions/setup-python@v2
with:
python-version: 3.9
- name: Build
run: |
pip install pyinstaller
pip install -r requirements.txt
pip install .
pyinstaller -F -n meshtastic --collect-all meshtastic meshtastic/__main__.py
- name: Add ubuntu to release
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.release_create.outputs.upload_url }}
asset_path: dist/meshtastic
asset_name: meshtastic_ubuntu
asset_content_type: application/zip
- name: Add readme.txt to release
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.release_create.outputs.upload_url }}
asset_path: standalone_readme.txt
asset_name: readme.txt
asset_content_type: text/plain
build-and-publish-windows:
runs-on: windows-latest
needs: release_create
steps:
- name: Checkout
uses: actions/checkout@v3
with:
ref: ${{ needs.release_create.outputs.new_sha }}
- name: Set up Python 3.9
uses: actions/setup-python@v2
with:
python-version: 3.9
- name: Build
run: |
pip install pyinstaller
pip install -r requirements.txt
pip install .
pyinstaller -F -n meshtastic --collect-all meshtastic meshtastic/__main__.py
- name: Add windows to release
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.release_create.outputs.upload_url }}
asset_path: dist/meshtastic.exe
asset_name: meshtastic_windows
asset_content_type: application/zip

View File

@@ -15,10 +15,22 @@ jobs:
run: |
git pull --recurse-submodules
git submodule update --remote --recursive
- name: Download nanopb
run: |
wget https://jpa.kapsi.fi/nanopb/download/nanopb-0.4.4-linux-x86.tar.gz
tar xvzf nanopb-0.4.4-linux-x86.tar.gz
mv nanopb-0.4.4-linux-x86 nanopb-0.4.4
- name: Re-generate protocol buffers
run: |
./bin/regen-protos.sh
- name: Commit update
run: |
git config --global user.name 'github-actions'
git config --global user.email 'bot@noreply.github.com'
git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}
git add proto
git add meshtastic
git commit -m "Update protobuf submodule" && git push || echo "No changes to commit"

2
.gitignore vendored
View File

@@ -6,6 +6,7 @@ dist
log_*
.eggs
nanopb-0.4.4
nanopb-0.4.5
.*swp
.coverage
*.py-E
@@ -14,3 +15,4 @@ venv/
.DS_Store
__pycache__
examples/__pycache__
meshtastic.spec

View File

@@ -7,7 +7,7 @@
# Add files or directories matching the regex patterns to the blacklist. The
# regex matches against base names, not paths.
ignore-patterns=mqtt_pb2.py,channel_pb2.py,environmental_measurement_pb2.py,admin_pb2.py,radioconfig_pb2.py,deviceonly_pb2.py,apponly_pb2.py,remote_hardware_pb2.py,portnums_pb2.py,mesh_pb2.py,storeforward_pb2.py
ignore-patterns=mqtt_pb2.py,channel_pb2.py,telemetry_pb2.py,admin_pb2.py,radioconfig_pb2.py,deviceonly_pb2.py,apponly_pb2.py,remote_hardware_pb2.py,portnums_pb2.py,mesh_pb2.py,storeforward_pb2.py,cannedmessages_pb2.py
@@ -23,7 +23,7 @@ ignore-patterns=mqtt_pb2.py,channel_pb2.py,environmental_measurement_pb2.py,admi
# no Warning level messages displayed, use"--disable=all --enable=classes
# --disable=W"
#
disable=invalid-name,fixme,logging-fstring-interpolation,too-many-statements,too-many-branches,too-many-locals,no-member,f-string-without-interpolation,protected-access,no-self-use,pointless-string-statement,too-few-public-methods,consider-using-f-string,broad-except,no-else-return,unused-argument,global-statement,global-variable-not-assigned,too-many-boolean-expressions,no-else-raise,bare-except,c-extension-no-member
disable=invalid-name,fixme,logging-fstring-interpolation,too-many-statements,too-many-branches,too-many-locals,no-member,f-string-without-interpolation,protected-access,no-self-use,pointless-string-statement,too-few-public-methods,broad-except,no-else-return,no-else-raise,bare-except,too-many-public-methods
[BASIC]
@@ -41,7 +41,7 @@ bad-names=foo,bar,baz,toto,tutu,tata
max-line-length=150
# Maximum number of lines in a module
max-module-lines=1200
max-module-lines=1400

View File

@@ -2,6 +2,10 @@
test:
pytest -m unit
# only run the smoke tests against the virtual device
virt:
pytest -m smokevirt
# local install
install:
pip install .
@@ -12,11 +16,17 @@ docs:
# lint the codebase
lint:
pylint meshtastic
pylint meshtastic examples
# show the slowest unit tests
slow:
pytest --durations=5
pytest -m unit --durations=5
proto: FORCE
git submodule update --init --recursive
git pull --rebase
git submodule update --remote --merge
./bin/regen-protos.sh
# run the coverage report and open results in a browser
cov:

View File

@@ -3,6 +3,7 @@
[![Open in Visual Studio Code](https://open.vscode.dev/badges/open-in-vscode.svg)](https://open.vscode.dev/meshtastic/Meshtastic-python)
![Unit Tests](https://github.com/meshtastic/Meshtastic-python/actions/workflows/ci.yml/badge.svg)
[![codecov](https://codecov.io/gh/meshtastic/Meshtastic-python/branch/master/graph/badge.svg?token=TIWPJL73KV)](https://codecov.io/gh/meshtastic/Meshtastic-python)
![PyPI - Downloads](https://img.shields.io/pypi/dm/meshtastic)
A python client for using [Meshtastic](https://www.meshtastic.org) 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.

25
bin/bump_version.py Executable file
View File

@@ -0,0 +1,25 @@
#!/usr/bin/env python
"""Bump the version number"""
version_filename = "setup.py"
lines = None
with open(version_filename, 'r', encoding='utf-8') as f:
lines = f.readlines()
with open(version_filename, 'w', encoding='utf-8') as f:
for line in lines:
if line.lstrip().startswith("version="):
# get rid of quotes around the version
line = line.replace('"', '')
# get rid of trailing comma
line = line.replace(",", "")
# split on '='
words = line.split("=")
# split the version into parts (by period)
v = words[1].split(".")
ver = f'{v[0]}.{v[1]}.{int(v[2]) + 1}'
f.write(f' version="{ver}",\n')
else:
f.write(line)

View File

@@ -1,15 +1,15 @@
#!/bin/bash
./nanopb-0.4.4/generator-bin/protoc -I=proto --python_out meshtastic `ls proto/*.proto`
./nanopb-0.4.5/generator-bin/protoc -I=proto --python_out meshtastic `ls proto/*.proto`
# workaround for import bug in protoc https://github.com/protocolbuffers/protobuf/issues/1491#issuecomment-690618628
if [[ $OSTYPE == 'darwin'* ]]; then
sed -i -E 's/^\(import.*_pb2\)/from . \1/' meshtastic/*.py
sed -i '' -E 's/^(import.*_pb2)/from . \1/' meshtastic/*.py
# automate the current workaround (may be related to Meshtastic-protobufs issue #27 https://github.com/meshtastic/Meshtastic-protobufs/issues/27)
sed -i '' -E "s/^None = 0/globals()['None'] = 0/" meshtastic/mesh_pb2.py
else
sed -i -E 's/^import.*_pb2/from . \0/' meshtastic/*.py
sed -i -e 's/^import.*_pb2/from . \0/' meshtastic/*.py
# automate the current workaround (may be related to Meshtastic-protobufs issue #27 https://github.com/meshtastic/Meshtastic-protobufs/issues/27)
sed -i -e "s/^None = 0/globals()['None'] = 0/" meshtastic/mesh_pb2.py
fi
# automate the current workaround (may be related to Meshtastic-protobufs issue #27 https://github.com/meshtastic/Meshtastic-protobufs/issues/27)
sed -i '' -e "s/^None = 0/globals()['None'] = 0/" meshtastic/mesh_pb2.py

20
bin/show_version.py Executable file
View File

@@ -0,0 +1,20 @@
#!/usr/bin/env python
"""Show the version number"""
version_filename = "setup.py"
lines = None
with open(version_filename, 'r', encoding='utf-8') as f:
lines = f.readlines()
for line in lines:
if line.lstrip().startswith("version="):
# get rid of quotes around the version
line2 = line.replace('"', '')
# get rid of the trailing comma
line2 = line2.replace(',', '')
# split on =
words = line2.split("=")
# Note: This format is for github actions
print(f'::set-output name=version::{words[1].strip()}')

17
exampleConfig.yaml Normal file
View File

@@ -0,0 +1,17 @@
# example config using camelCase keys
owner: Bob TBeam
ownerShort: BOB
channelUrl: https://www.meshtastic.org/d/#CgUYAyIBAQ
location:
lat: 35.88888
lon: -93.88888
alt: 304
userPrefs:
region: 1
isAlwaysPowered: 'true'
sendOwnerInterval: 2
screenOnSecs: 31536000
waitBluetoothSecs: 31536000

View File

@@ -1,4 +1,6 @@
# example configuration file with snake_case keys
owner: Bob TBeam
owner_short: BOB
channel_url: https://www.meshtastic.org/d/#CgUYAyIBAQ
@@ -13,3 +15,4 @@ user_prefs:
send_owner_interval: 2
screen_on_secs: 31536000
wait_bluetooth_secs: 31536000
location_share: 'LocEnabled'

20
examples/get_hw.py Normal file
View File

@@ -0,0 +1,20 @@
"""Simple program to demo how to use meshtastic library.
To run: python examples/get_hw.py
"""
import sys
import meshtastic
import meshtastic.serial_interface
# simple arg check
if len(sys.argv) != 1:
print(f"usage: {sys.argv[0]}")
print("Print the hardware model for the local node.")
sys.exit(3)
iface = meshtastic.serial_interface.SerialInterface()
if iface.nodes:
for n in iface.nodes.values():
if n['num'] == iface.myInfo.my_node_num:
print(n['user']['hwModel'])
iface.close()

23
examples/info_example.py Normal file
View File

@@ -0,0 +1,23 @@
"""Simple program to demo how to use meshtastic library.
To run: python examples/info.py
"""
import meshtastic
import meshtastic.serial_interface
iface = meshtastic.serial_interface.SerialInterface()
# call showInfo() just to ensure values are populated
#info = iface.showInfo()
if iface.myInfo:
#print(f'myInfo:{iface.myInfo}')
print(f'firmware_version:{iface.myInfo.firmware_version}')
if iface.nodes:
for n in iface.nodes.values():
if n['num'] == iface.myInfo.my_node_num:
print(n['user']['hwModel'])
break
iface.close()

View File

@@ -13,7 +13,7 @@ if len(sys.argv) < 2:
print(f"usage: {sys.argv[0]} host")
sys.exit(1)
def onConnection(interface, topic=pub.AUTO_TOPIC):
def onConnection(interface, topic=pub.AUTO_TOPIC): # pylint: disable=unused-argument
"""This is called when we (re)connect to the radio."""
print(interface.myInfo)
interface.close()

View File

@@ -14,11 +14,11 @@ if len(sys.argv) < 2:
print(f"usage: {sys.argv[0]} host")
sys.exit(1)
def onReceive(packet, interface):
def onReceive(packet, interface): # pylint: disable=unused-argument
"""called when a packet arrives"""
print(f"Received: {packet}")
def onConnection(interface, topic=pub.AUTO_TOPIC):
def onConnection(interface, topic=pub.AUTO_TOPIC): # pylint: disable=unused-argument
"""called when we (re)connect to the radio"""
# defaults to broadcast, specify a destination ID if you wish
interface.sendText("hello mesh")

View File

@@ -0,0 +1,24 @@
"""Program to scan for hardware
To run: python examples/scan_for_devices.py
"""
import sys
from meshtastic.util import detect_supported_devices, get_unique_vendor_ids, active_ports_on_supported_devices
# simple arg check
if len(sys.argv) != 1:
print(f"usage: {sys.argv[0]}")
print("Detect which device we might have.")
sys.exit(3)
vids = get_unique_vendor_ids()
print(f'Searching for all devices with these vendor ids {vids}')
sds = detect_supported_devices()
if len(sds) > 0:
print('Detected possible devices:')
for d in sds:
print(f' name:{d.name}{d.version} firmware:{d.for_firmware}')
ports = active_ports_on_supported_devices(sds)
print(f'ports:{ports}')

20
examples/set_owner.py Normal file
View File

@@ -0,0 +1,20 @@
"""Simple program to demo how to use meshtastic library.
To run: python examples/set_owner.py Bobby 333
"""
import sys
import meshtastic
import meshtastic.serial_interface
# simple arg check
if len(sys.argv) < 2:
print(f"usage: {sys.argv[0]} long_name [short_name]")
sys.exit(3)
iface = meshtastic.serial_interface.SerialInterface()
long_name = sys.argv[1]
short_name = None
if len(sys.argv) > 2:
short_name = sys.argv[2]
iface.localNode.setOwner(long_name, short_name)
iface.close()

6
examples/show_ports.py Normal file
View File

@@ -0,0 +1,6 @@
"""Simple program to show serial ports.
"""
from meshtastic.util import findPorts
print(findPorts())

View File

@@ -0,0 +1,14 @@
"""Demonstration of how to look up a radio's location via its LAN connection.
Before running, connect your machine to the same WiFi network as the radio.
"""
import meshtastic
import meshtastic.tcp_interface
radio_hostname = "meshtastic.local" # Can also be an IP
iface = meshtastic.tcp_interface.TCPInterface(radio_hostname)
my_node_num = iface.myInfo.my_node_num
pos = iface.nodesByNum[my_node_num]["position"]
print (pos)
iface.close()

20
info/mac/heltec.txt Normal file
View File

@@ -0,0 +1,20 @@
ioreg -p IOUSB > /tmp/d
> | +-o CP2102 USB to UART Bridge Controller@14400000 <class AppleUSBDevice, id 0x10005c048, registered, matched, active, busy 0 (9 ms), retain 12>
system_profiler SPUSBDataType > /tmp/b
37a38,50
> CP2102 USB to UART Bridge Controller:
>
> Product ID: 0xea60
> Vendor ID: 0x10c4 (Silicon Laboratories, Inc.)
> Version: 1.00
> Serial Number: 0001
> Speed: Up to 12 Mb/s
> Manufacturer: Silicon Labs
> Location ID: 0x14400000 / 53
> Current Available (mA): 500
> Current Required (mA): 100
> Extra Operating Current (mA): 0

52
info/mac/rak11200.txt Normal file
View File

@@ -0,0 +1,52 @@
% ioreg -p IOUSB > /tmp/a
# only a solid red light (pins GRND and BOOT0 jumpered)
% ioreg -p IOUSB > /tmp/b
# solid red light and solid green light
% ioreg -p IOUSB > /tmp/c
# nothing plugged in
% diff /tmp/a /tmp/c
13c13
< +-o AppleUSBXHCI Root Hub Simulation@14000000 <class AppleUSBRootHubDevice, id 0x100000589, registered, matched, active, busy 0 (15 ms), retain 11>
---
> +-o AppleUSBXHCI Root Hub Simulation@14000000 <class AppleUSBRootHubDevice, id 0x100000589, registered, matched, active, busy 0 (15 ms), retain 10>
18d17
< +-o USB Serial@14300000 <class AppleUSBDevice, id 0x100004ca4, registered, matched, active, busy 0 (10 ms), retain 12>
% diff /tmp/b /tmp/c
13c13
< +-o AppleUSBXHCI Root Hub Simulation@14000000 <class AppleUSBRootHubDevice, id 0x100000589, registered, matched, active, busy 0 (15 ms), retain 11>
---
> +-o AppleUSBXHCI Root Hub Simulation@14000000 <class AppleUSBRootHubDevice, id 0x100000589, registered, matched, active, busy 0 (15 ms), retain 10>
18d17
< +-o USB Serial@14300000 <class AppleUSBDevice, id 0x100004ce5, registered, matched, active, busy 0 (11 ms), retain 12>
system_profiler SPUSBDataType > /tmp/d
# red solid
system_profiler SPUSBDataType > /tmp/e
# nothing
% diff /tmp/d /tmp/e
38,48d37
< USB Serial:
<
< Product ID: 0x7523
< Vendor ID: 0x1a86
< Version: 2.64
< Speed: Up to 12 Mb/s
< Location ID: 0x14300000 / 33
< Current Available (mA): 500
< Current Required (mA): 98
< Extra Operating Current (mA): 0

View File

@@ -0,0 +1,68 @@
> | +-o WisCore RAK4631 Board@14400000 <class AppleUSBDevice, id 0x10005c158, registered, matched, active, busy 0 (18 ms), retain 14>
/dev/cu.usbmodem14401
% ls -al /dev/*modem*
crw-rw-rw- 1 root wheel 0x9000005 Jan 29 15:32 /dev/cu.usbmodem14401
crw-rw-rw- 1 root wheel 0x9000004 Jan 29 15:31 /dev/tty.usbmodem14401
Note: On a Mac Air, output is:
% system_profiler SPUSBDataType
USB:
USB 3.1 Bus:
Host Controller Driver: AppleT8103USBXHCI
USB 3.1 Bus:
Host Controller Driver: AppleT8103USBXHCI
WisCore RAK4631 Board:
Product ID: 0x8029
Vendor ID: 0x239a
Version: 1.00
Serial Number: E6CF9502B1D410D8
Speed: Up to 12 Mb/s
Manufacturer: RAKwireless
Location ID: 0x01100000 / 2
Current Available (mA): 500
Current Required (mA): 100
Extra Operating Current (mA): 0
However, in FTHR840BOOT mode, it shows this:
% system_profiler SPUSBDataType
USB:
USB 3.1 Bus:
Host Controller Driver: AppleT8103USBXHCI
USB 3.1 Bus:
Host Controller Driver: AppleT8103USBXHCI
Feather nRF52840 Express:
Product ID: 0x0029
Vendor ID: 0x239a
Version: 1.00
Serial Number: E6CF9502B1D410D8
Speed: Up to 12 Mb/s
Manufacturer: Adafruit Industries
Location ID: 0x01100000 / 1
Current Available (mA): 500
Current Required (mA): 100
Extra Operating Current (mA): 0
Media:
nRF UF2:
Capacity: 33.7 MB (33,690,112 bytes)
Removable Media: Yes
BSD Name: disk4
Logical Unit: 0
Partition Map Type: Unknown
S.M.A.R.T. status: Verified
USB Interface: 2
$ cat /Volumes/FTHR840BOOT/INFO_UF2.TXT
UF2 Bootloader 0.3.2-109-gd6b28e6-dirty lib/nrfx (v2.0.0) lib/tinyusb (0.6.0-272-g4e6aa0d8) lib/uf2 (heads/master)
Model: Adafruit Feather nRF52840 Express
Board-ID: nRF52840-Feather-revD
SoftDevice: S140 version 6.1.1
Date: Jun 16 2020

18
info/mac/rak4631_5005.txt Normal file
View File

@@ -0,0 +1,18 @@
no device plugged in
% ioreg -p IOUSB > /tmp/a
device plugged in, in "boot" mode
% ioreg -p IOUSB > /tmp/b
device plugged in, botted to Meshtastic firmware
% ioreg -p IOUSB > /tmp/c
(venv) sweet Meshtastic-python % diff /tmp/a /tmp/b (with most info removed)
> | +-o Feather nRF52840 Express@14400000 <class AppleUSBDevice, id 0x10005c0ff, registered, matched, active, busy 0 (22 ms), retain 16>
diff /tmp/a /tmp/c (with most info removed)
> | +-o WisCore RAK4631 Board@14400000 <class AppleUSBDevice, id 0x10005c134, registered, matched, active, busy 0 (17 ms), retain 14>
Meshtastic detected port on /dev/cu.usbmodem14401

21
info/mac/tbeam.txt Normal file
View File

@@ -0,0 +1,21 @@
meshtastic detected port: /dev/cu.usbmodem53230050571
ioreg -p IOUSB > /tmp/c
> | +-o USB Single Serial@14400000 <class AppleUSBDevice, id 0x10005bff7, registered, matched, active, busy 0 (15 ms), retain 14>
system_profiler SPUSBDataType > /tmp/a
> USB Single Serial:
>
> Product ID: 0x55d4
> Vendor ID: 0x1a86
> Version: 4.43
> Serial Number: 5323005057
> Speed: Up to 12 Mb/s
> Location ID: 0x14400000 / 50
> Current Available (mA): 500
> Current Required (mA): 134
> Extra Operating Current (mA): 0

40
info/mac/techo.txt Normal file
View File

@@ -0,0 +1,40 @@
in boot mode:
% mount
<snip>
/dev/disk122 on /Volumes/TECHOBOOT (msdos, local, nodev, nosuid, noowners)
% ls -al /Volumes/TECHOBOOT
total 3735
drwxrwxrwx@ 1 bob staff 2048 Feb 1 16:47 .
drwxr-xr-x 5 root wheel 160 Feb 1 16:47 ..
drwxrwxrwx 1 bob staff 512 Feb 1 16:47 .fseventsd
-rwxrwxrwx 1 bob staff 1908736 Oct 13 08:37 CURRENT.UF2
-rwxrwxrwx 1 bob staff 129 Oct 13 08:37 INDEX.HTM
-rwxrwxrwx 1 bob staff 237 Oct 13 08:37 INFO_UF2.TXT
# nothing plugged in
% ioreg -p IOUSB > /tmp/a
# not boot mode
% ioreg -p IOUSB > /tmp/b
# bootmode
% ioreg -p IOUSB > /tmp/c
% diff /tmp/a /tmp/b
<snip>
> | +-o TTGO_eink@14300000 <class AppleUSBDevice, id 0x100060fe4, registered, matched, active, busy 0 (18 ms), retain 14>
% diff /tmp/a /tmp/c
<snip>
> | +-o T-Echo v1@14300000 <class AppleUSBDevice, id 0x100061000, registered, matched, active, busy 0 (25 ms), retain 16>
contents of: INFO_UF2.TXT
UF2 Bootloader 0.6.1-2-g1224915 lib/nrfx (v2.0.0) lib/tinyusb (0.10.1-293-gaf8e5a90) lib/uf2 (remotes/origin/configupdate-9-gadbb8c7)
Model: LilyGo T-Echo
Board-ID: nRF52840-TEcho-v1
SoftDevice: S140 version 6.1.1
Date: Oct 13 2021

16
info/mac/tlora.txt Normal file
View File

@@ -0,0 +1,16 @@
diff of ioreg -p IOUSB
> | +-o USB Single Serial@14300000 <class AppleUSBDevice, id 0x10005bf0f, registered, matched, active, busy 0 (18 ms), retain 14>
Diff of system_profiler SPUSBDataType
< USB Single Serial:
<
< Product ID: 0x55d4
< Vendor ID: 0x1a86
< Version: 4.43
< Speed: Up to 12 Mb/s
< Location ID: 0x14300000 / 46
< Current Available (mA): 500
< Current Required (mA): 134
< Extra Operating Current (mA): 0

85
info/mac/tlora_2.1.6.txt Normal file
View File

@@ -0,0 +1,85 @@
lsusb
Bus 001 Device 001: ID 0bda:2172 Realtek Semiconductor Corp. BillBoard Device Serial: 00000000000000000
Bus 000 Device 002: ID 2109:0817 VIA Labs, Inc. USB3.0 Hub Serial: 000000000
Bus 000 Device 003: ID 2109:0715 VIA Labs, Inc. VLI Product String Serial: 000000075003
Bus 000 Device 004: ID 0bda:0306 Realtek Semiconductor Corp. USB3.0-CRW Serial: 60000719201300000
Bus 000 Device 005: ID 2109:0817 VIA Labs, Inc. USB3.0 Hub Serial: 000000000
Bus 000 Device 001: ID 2109:2817 VIA Labs, Inc. USB2.0 Hub Serial: 000000000
Bus 000 Device 009: ID 1a86:55d4 1a86 USB Single Serial Serial: 533C005215
Bus 000 Device 006: ID 2109:2817 VIA Labs, Inc. USB2.0 Hub Serial: 000000000
Bus 000 Device 007: ID 2109:8817 VIA Labs, Inc. USB Billboard Device Serial: 0000000000000001
Bus 000 Device 008: ID 2109:8817 VIA Labs, Inc. USB Billboard Device Serial: 0000000000000001
Bus 002 Device 001: ID 1a40:0101 TERMINUS TECHNOLOGY INC. USB 2.0 Hub
Bus 002 Device 003: ID 0922:001f Dymo Corporation DYMO LabelWriter 4XL Serial: 17032316350940
Bus 002 Device 002: ID 046d:082d Logitech Inc. HD Pro Webcam C920 Serial: A21C905F
Bus 000 Device 000: ID 0bda:2172 Realtek Semiconductor Corp. USB 3.1 Bus
Bus 000 Device 000: ID 2109:0817 VIA Labs, Inc. USB 3.1 Bus
Bus 000 Device 001: ID 1d6b:1100 Linux Foundation USB 3.0 Bus
% lsusb -v (with parts snipped)
USB2.0 Hub :
Product ID: 0x2817
Vendor ID: 0x2109 (VIA Labs, Inc.)
Version: 6.03
Serial Number: 000000000
Speed: Up to 480 Mb/s
Manufacturer: VIA Labs, Inc.
Location ID: 0x00100000 / 1
Current Available (mA): 500
Current Required (mA): 0
Extra Operating Current (mA): 0
USB Single Serial:
Product ID: 0x55d4
Vendor ID: 0x1a86
Version: 4.43
Serial Number: 533C005215
Speed: Up to 12 Mb/s
Location ID: 0x00140000 / 9
Current Available (mA): 500
Current Required (mA): 134
Extra Operating Current (mA): 0
USB2.0 Hub :
Product ID: 0x2817
Vendor ID: 0x2109 (VIA Labs, Inc.)
Version: 6.03
Serial Number: 000000000
Speed: Up to 480 Mb/s
Manufacturer: VIA Labs, Inc.
Location ID: 0x00110000 / 6
Current Available (mA): 500
Current Required (mA): 0
Extra Operating Current (mA): 0
USB Billboard Device :
Product ID: 0x8817
Vendor ID: 0x2109 (VIA Labs, Inc.)
Version: 0.01
Serial Number: 0000000000000001
Speed: Up to 480 Mb/s
Manufacturer: VIA Labs, Inc.
Location ID: 0x00115000 / 7
Current Available (mA): 500
Current Required (mA): 100
Extra Operating Current (mA): 0
USB Billboard Device :
Product ID: 0x8817
Vendor ID: 0x2109 (VIA Labs, Inc.)
Version: 0.01
Serial Number: 0000000000000001
Speed: Up to 480 Mb/s
Manufacturer: VIA Labs, Inc.
Location ID: 0x00150000 / 8
Current Available (mA): 500
Current Required (mA): 100
Extra Operating Current (mA): 0

3
info/readme.txt Normal file
View File

@@ -0,0 +1,3 @@
Gathering OS-level info for devices.
This info might be helpful for developers detecting info about devices.

67
info/ubuntu/diy.txt Normal file
View File

@@ -0,0 +1,67 @@
lsusb
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 004: ID 10c4:ea60 Silicon Labs CP210x UART Bridge
Bus 001 Device 003: ID 0bda:2838 Realtek Semiconductor Corp. RTL2838 DVB-T
Bus 001 Device 002: ID 2109:3431 VIA Labs, Inc. Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
lsusb -d 10c4: -v
Bus 001 Device 004: ID 10c4:ea60 Silicon Labs CP210x UART Bridge
Couldn't open device, some information will be missing
Device Descriptor:
bLength 18
bDescriptorType 1
bcdUSB 2.00
bDeviceClass 0
bDeviceSubClass 0
bDeviceProtocol 0
bMaxPacketSize0 64
idVendor 0x10c4 Silicon Labs
idProduct 0xea60 CP210x UART Bridge
bcdDevice 1.00
iManufacturer 1
iProduct 2
iSerial 3
bNumConfigurations 1
Configuration Descriptor:
bLength 9
bDescriptorType 2
wTotalLength 0x0020
bNumInterfaces 1
bConfigurationValue 1
iConfiguration 0
bmAttributes 0x80
(Bus Powered)
MaxPower 100mA
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 0
bAlternateSetting 0
bNumEndpoints 2
bInterfaceClass 255 Vendor Specific Class
bInterfaceSubClass 0
bInterfaceProtocol 0
iInterface 2
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x01 EP 1 OUT
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0040 1x 64 bytes
bInterval 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x82 EP 2 IN
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0040 1x 64 bytes
bInterval 0

85
info/ubuntu/heltec_v2.txt Normal file
View File

@@ -0,0 +1,85 @@
Run on Ubuntu 20
Command:
lsusb -d 10c4: -v
Output:
Bus 001 Device 091: ID 10c4:ea60 Silicon Labs CP210x UART Bridge
Device Descriptor:
bLength 18
bDescriptorType 1
bcdUSB 1.10
bDeviceClass 0
bDeviceSubClass 0
bDeviceProtocol 0
bMaxPacketSize0 64
idVendor 0x10c4 Silicon Labs
idProduct 0xea60 CP210x UART Bridge
bcdDevice 1.00
iManufacturer 1
iProduct 2
iSerial 3
bNumConfigurations 1
Configuration Descriptor:
bLength 9
bDescriptorType 2
wTotalLength 0x0020
bNumInterfaces 1
bConfigurationValue 1
iConfiguration 0
bmAttributes 0x80
(Bus Powered)
MaxPower 100mA
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 0
bAlternateSetting 0
bNumEndpoints 2
bInterfaceClass 255 Vendor Specific Class
bInterfaceSubClass 0
bInterfaceProtocol 0
iInterface 2
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x81 EP 1 IN
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0040 1x 64 bytes
bInterval 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x01 EP 1 OUT
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0040 1x 64 bytes
bInterval 0
Command:
esptool.py chip_id
Output:
esptool.py v3.2
Found 3 serial ports
Serial port /dev/ttyUSB0
Connecting....
Detecting chip type... Unsupported detection protocol, switching and trying again...
Connecting.....
Detecting chip type... ESP32
Chip is ESP32-D0WDQ6 (revision 1)
Features: WiFi, BT, Dual Core, 240MHz, VRef calibration in efuse, Coding Scheme None
Crystal is 40MHz
MAC: 24:0a:c4:fc:be:f0
Uploading stub...
Running stub...
Stub running...
Warning: ESP32 has no Chip ID. Reading MAC instead.
MAC: 24:0a:c4:fc:be:f0
Hard resetting via RTS pin...

View File

@@ -0,0 +1,118 @@
Note: Meshtastic firmware was installed when running these commands
$ ls -al /dev/ttyACM*
crw-rw---- 1 root dialout 166, 0 Jan 29 21:50 /dev/ttyACM0
lsusb -d 239a: -v
Bus 001 Device 097: ID 239a:8029
Couldn't open device, some information will be missing
Device Descriptor:
bLength 18
bDescriptorType 1
bcdUSB 2.00
bDeviceClass 239 Miscellaneous Device
bDeviceSubClass 2
bDeviceProtocol 1 Interface Association
bMaxPacketSize0 64
idVendor 0x239a
idProduct 0x8029
bcdDevice 1.00
iManufacturer 1
iProduct 2
iSerial 3
bNumConfigurations 1
Configuration Descriptor:
bLength 9
bDescriptorType 2
wTotalLength 0x004b
bNumInterfaces 2
bConfigurationValue 1
iConfiguration 0
bmAttributes 0xa0
(Bus Powered)
Remote Wakeup
MaxPower 100mA
Interface Association:
bLength 8
bDescriptorType 11
bFirstInterface 0
bInterfaceCount 2
bFunctionClass 2 Communications
bFunctionSubClass 2 Abstract (modem)
bFunctionProtocol 0
iFunction 0
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 0
bAlternateSetting 0
bNumEndpoints 1
bInterfaceClass 2 Communications
bInterfaceSubClass 2 Abstract (modem)
bInterfaceProtocol 0
iInterface 4
CDC Header:
bcdCDC 1.20
CDC Call Management:
bmCapabilities 0x00
bDataInterface 1
CDC ACM:
bmCapabilities 0x02
line coding and serial state
CDC Union:
bMasterInterface 0
bSlaveInterface 1
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x81 EP 1 IN
bmAttributes 3
Transfer Type Interrupt
Synch Type None
Usage Type Data
wMaxPacketSize 0x0008 1x 8 bytes
bInterval 16
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 1
bAlternateSetting 0
bNumEndpoints 2
bInterfaceClass 10 CDC Data
bInterfaceSubClass 0
bInterfaceProtocol 0
iInterface 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x01 EP 1 OUT
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0040 1x 64 bytes
bInterval 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x82 EP 2 IN
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0040 1x 64 bytes
bInterval 0
$ lsusb
Bus 002 Device 005: ID 046d:c31c Logitech, Inc. Keyboard K120
Bus 002 Device 002: ID 8087:8000 Intel Corp.
Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 097: ID 239a:8029
Bus 001 Device 002: ID 8087:8008 Intel Corp.
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Note: esptool.py chip_id does not detect device

View File

@@ -0,0 +1,148 @@
Note: Device has Meshtastic firmware installed.
$ ls -al /dev/ttyACM*
crw-rw---- 1 root dialout 166, 0 Jan 29 21:44 /dev/ttyACM0
$ lsusb -d 239a: -v
Bus 001 Device 098: ID 239a:0029
Couldn't open device, some information will be missing
Device Descriptor:
bLength 18
bDescriptorType 1
bcdUSB 2.00
bDeviceClass 239 Miscellaneous Device
bDeviceSubClass 2
bDeviceProtocol 1 Interface Association
bMaxPacketSize0 64
idVendor 0x239a
idProduct 0x0029
bcdDevice 1.00
iManufacturer 1
iProduct 2
iSerial 3
bNumConfigurations 1
Configuration Descriptor:
bLength 9
bDescriptorType 2
wTotalLength 0x0062
bNumInterfaces 3
bConfigurationValue 1
iConfiguration 0
bmAttributes 0xa0
(Bus Powered)
Remote Wakeup
MaxPower 100mA
Interface Association:
bLength 8
bDescriptorType 11
bFirstInterface 0
bInterfaceCount 2
bFunctionClass 2 Communications
bFunctionSubClass 2 Abstract (modem)
bFunctionProtocol 0
iFunction 0
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 0
bAlternateSetting 0
bNumEndpoints 1
bInterfaceClass 2 Communications
bInterfaceSubClass 2 Abstract (modem)
bInterfaceProtocol 0
iInterface 4
CDC Header:
bcdCDC 1.20
CDC Call Management:
bmCapabilities 0x00
bDataInterface 1
CDC ACM:
bmCapabilities 0x02
line coding and serial state
CDC Union:
bMasterInterface 0
bSlaveInterface 1
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x81 EP 1 IN
bmAttributes 3
Transfer Type Interrupt
Synch Type None
Usage Type Data
wMaxPacketSize 0x0008 1x 8 bytes
bInterval 16
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 1
bAlternateSetting 0
bNumEndpoints 2
bInterfaceClass 10 CDC Data
bInterfaceSubClass 0
bInterfaceProtocol 0
iInterface 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x02 EP 2 OUT
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0040 1x 64 bytes
bInterval 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x82 EP 2 IN
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0040 1x 64 bytes
bInterval 0
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 2
bAlternateSetting 0
bNumEndpoints 2
bInterfaceClass 8 Mass Storage
bInterfaceSubClass 6 SCSI
bInterfaceProtocol 80 Bulk-Only
iInterface 5
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x03 EP 3 OUT
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0040 1x 64 bytes
bInterval 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x83 EP 3 IN
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0040 1x 64 bytes
bInterval 0
$ lsusb
Bus 002 Device 005: ID 046d:c31c Logitech, Inc. Keyboard K120
Bus 002 Device 002: ID 8087:8000 Intel Corp.
Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 098: ID 239a:0029
Bus 001 Device 002: ID 8087:8008 Intel Corp.
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Note: esptool.py chip_id does not detect device

1
info/ubuntu/readme.txt Normal file
View File

@@ -0,0 +1 @@
info run on ubuntu

94
info/ubuntu/tbeam.txt Normal file
View File

@@ -0,0 +1,94 @@
Run on Ubuntu 20
Command:
lsusb -d 1a86: -v
Output:
Bus 001 Device 096: ID 1a86:55d4 QinHeng Electronics
Device Descriptor:
bLength 18
bDescriptorType 1
bcdUSB 1.10
bDeviceClass 2 Communications
bDeviceSubClass 0
bDeviceProtocol 0
bMaxPacketSize0 8
idVendor 0x1a86 QinHeng Electronics
idProduct 0x55d4
bcdDevice 4.43
iManufacturer 0
iProduct 2
iSerial 3
bNumConfigurations 1
Configuration Descriptor:
bLength 9
bDescriptorType 2
wTotalLength 0x0043
bNumInterfaces 2
bConfigurationValue 1
iConfiguration 0
bmAttributes 0xa0
(Bus Powered)
Remote Wakeup
MaxPower 134mA
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 0
bAlternateSetting 0
bNumEndpoints 1
bInterfaceClass 2 Communications
bInterfaceSubClass 2 Abstract (modem)
bInterfaceProtocol 1 AT-commands (v.25ter)
iInterface 0
CDC Header:
bcdCDC 1.10
CDC Call Management:
bmCapabilities 0x00
bDataInterface 1
CDC ACM:
bmCapabilities 0x02
line coding and serial state
CDC Union:
bMasterInterface 0
bSlaveInterface 1
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x83 EP 3 IN
bmAttributes 3
Transfer Type Interrupt
Synch Type None
Usage Type Data
wMaxPacketSize 0x0010 1x 16 bytes
bInterval 1
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 1
bAlternateSetting 0
bNumEndpoints 2
bInterfaceClass 10 CDC Data
bInterfaceSubClass 0
bInterfaceProtocol 0
iInterface 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x02 EP 2 OUT
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0020 1x 32 bytes
bInterval 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x82 EP 2 IN
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0040 1x 64 bytes
bInterval 0

141
info/ubuntu/techo.txt Normal file
View File

@@ -0,0 +1,141 @@
$ lsusb
Bus 002 Device 005: ID 046d:c31c Logitech, Inc. Keyboard K120
Bus 002 Device 002: ID 8087:8000 Intel Corp.
Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 107: ID 239a:0029
Bus 001 Device 002: ID 8087:8008 Intel Corp.
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
$ lsusb -d 239a: -v
Bus 001 Device 107: ID 239a:0029
Couldn't open device, some information will be missing
Device Descriptor:
bLength 18
bDescriptorType 1
bcdUSB 2.00
bDeviceClass 239 Miscellaneous Device
bDeviceSubClass 2
bDeviceProtocol 1 Interface Association
bMaxPacketSize0 64
idVendor 0x239a
idProduct 0x0029
bcdDevice 1.00
iManufacturer 1
iProduct 2
iSerial 3
bNumConfigurations 1
Configuration Descriptor:
bLength 9
bDescriptorType 2
wTotalLength 0x0062
bNumInterfaces 3
bConfigurationValue 1
iConfiguration 0
bmAttributes 0xa0
(Bus Powered)
Remote Wakeup
MaxPower 100mA
Interface Association:
bLength 8
bDescriptorType 11
bFirstInterface 0
bInterfaceCount 2
bFunctionClass 2 Communications
bFunctionSubClass 2 Abstract (modem)
bFunctionProtocol 0
iFunction 0
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 0
bAlternateSetting 0
bNumEndpoints 1
bInterfaceClass 2 Communications
bInterfaceSubClass 2 Abstract (modem)
bInterfaceProtocol 0
iInterface 4
CDC Header:
bcdCDC 1.20
CDC Call Management:
bmCapabilities 0x00
bDataInterface 1
CDC ACM:
bmCapabilities 0x02
line coding and serial state
CDC Union:
bMasterInterface 0
bSlaveInterface 1
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x81 EP 1 IN
bmAttributes 3
Transfer Type Interrupt
Synch Type None
Usage Type Data
wMaxPacketSize 0x0008 1x 8 bytes
bInterval 16
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 1
bAlternateSetting 0
bNumEndpoints 2
bInterfaceClass 10 CDC Data
bInterfaceSubClass 0
bInterfaceProtocol 0
iInterface 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x02 EP 2 OUT
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0040 1x 64 bytes
bInterval 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x82 EP 2 IN
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0040 1x 64 bytes
bInterval 0
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 2
bAlternateSetting 0
bNumEndpoints 2
bInterfaceClass 8 Mass Storage
bInterfaceSubClass 6 SCSI
bInterfaceProtocol 80 Bulk-Only
iInterface 5
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x03 EP 3 OUT
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0040 1x 64 bytes
bInterval 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x83 EP 3 IN
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0040 1x 64 bytes
bInterval 0

107
info/ubuntu/tlora.txt Normal file
View File

@@ -0,0 +1,107 @@
Run on Ubuntu 20
Note: Device has Meshtastic firmware installed
$ lsusb
Bus 002 Device 005: ID 046d:c31c Logitech, Inc. Keyboard K120
Bus 002 Device 002: ID 8087:8000 Intel Corp.
Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 099: ID 1a86:55d4 QinHeng Electronics
Bus 001 Device 002: ID 8087:8008 Intel Corp.
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
$ lsusb -d 1a86: -v
Bus 001 Device 099: ID 1a86:55d4 QinHeng Electronics
Couldn't open device, some information will be missing
Device Descriptor:
bLength 18
bDescriptorType 1
bcdUSB 1.10
bDeviceClass 2 Communications
bDeviceSubClass 0
bDeviceProtocol 0
bMaxPacketSize0 8
idVendor 0x1a86 QinHeng Electronics
idProduct 0x55d4
bcdDevice 4.43
iManufacturer 0
iProduct 2
iSerial 0
bNumConfigurations 1
Configuration Descriptor:
bLength 9
bDescriptorType 2
wTotalLength 0x0043
bNumInterfaces 2
bConfigurationValue 1
iConfiguration 0
bmAttributes 0xa0
(Bus Powered)
Remote Wakeup
MaxPower 134mA
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 0
bAlternateSetting 0
bNumEndpoints 1
bInterfaceClass 2 Communications
bInterfaceSubClass 2 Abstract (modem)
bInterfaceProtocol 1 AT-commands (v.25ter)
iInterface 0
CDC Header:
bcdCDC 1.10
CDC Call Management:
bmCapabilities 0x00
bDataInterface 1
CDC ACM:
bmCapabilities 0x02
line coding and serial state
CDC Union:
bMasterInterface 0
bSlaveInterface 1
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x83 EP 3 IN
bmAttributes 3
Transfer Type Interrupt
Synch Type None
Usage Type Data
wMaxPacketSize 0x0010 1x 16 bytes
bInterval 1
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 1
bAlternateSetting 0
bNumEndpoints 2
bInterfaceClass 10 CDC Data
bInterfaceSubClass 0
bInterfaceProtocol 0
iInterface 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x02 EP 2 OUT
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0020 1x 32 bytes
bInterval 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x82 EP 2 IN
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0040 1x 64 bytes
bInterval 0

106
info/windows/heltec.txt Normal file
View File

@@ -0,0 +1,106 @@
Run from Windows 10
Might work... (nope)
Get-PnpDevice -Class 'USB' -PresentOnly | Format-List
> Get-PnpDevice -PresentOnly | Format-List > b
> Compare-Object (get-content a) (Get-Content b)
InputObject SideIndicator
----------- -------------
Caption : CP2102 USB to UART Bridge Controller <=
Description : CP2102 USB to UART Bridge Controller <=
Name : CP2102 USB to UART Bridge Controller <=
Status : Error <=
ConfigManagerErrorCode : CM_PROB_FAILED_INSTALL <=
DeviceID : USB\VID_10C4&PID_EA60\0001 <=
PNPDeviceID : USB\VID_10C4&PID_EA60\0001 <=
CompatibleID : {USB\Class_FF&SubClass_00&Prot_00, USB\Class_FF&SubClass_00, USB\Class_FF} <=
HardwareID : {USB\VID_10C4&PID_EA60&REV_0100, USB\VID_10C4&PID_EA60} <=
FriendlyName : CP2102 USB to UART Bridge Controller <=
InstanceId : USB\VID_10C4&PID_EA60\0001 <=
Problem : CM_PROB_FAILED_INSTALL <=
ClassGuid : <=
Manufacturer : <=
PNPClass : <=
Class : <=
Service : <=
InstallDate : <=
Availability : <=
ConfigManagerUserConfig : False <=
CreationClassName : Win32_PnPEntity <=
ErrorCleared : <=
ErrorDescription : <=
LastErrorCode : <=
PowerManagementCapabilities : <=
PowerManagementSupported : <=
StatusInfo : <=
SystemCreationClassName : Win32_ComputerSystem <=
SystemName : DESKTOP-FRFQN8H <=
Present : True <=
PSComputerName : <=
ProblemDescription : <=
<=
> Get-PnpDevice -DeviceID 'USB\VID_10C4&PID_EA60\0001'
Status Class FriendlyName InstanceId
------ ----- ------------ ----------
Error CP2102 USB to UART Bridge Controller USB\VID_...
> Get-PnpDevice -PresentOnly -DeviceID 'USB\VID_10C4&PID_EA60\0001'
Get-PnpDevice : No matching Win32_PnPEntity objects found by CIM query for instances of the ROOT\cimv2\Win32_PnPEntity
class on the CIM server: SELECT * FROM Win32_PnPEntity WHERE ((DeviceId LIKE 'USB\\VID[_]10C4&PID[_]EA60\\0001'))
AND ((Present = TRUE)). Verify query parameters and retry.
At line:1 char:1
+ Get-PnpDevice -PresentOnly -DeviceID 'USB\VID_10C4&PID_EA60\0001'
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (Win32_PnPEntity:String) [Get-PnpDevice], CimJobException
+ FullyQualifiedErrorId : CmdletizationQuery_NotFound,Get-PnpDevice
> Get-PnpDevice -PresentOnly -DeviceID 'USB\VID_10C4&PID_EA60\0001'
Status Class FriendlyName InstanceId
------ ----- ------------ ----------
Error CP2102 USB to UART Bridge Controller USB\VID_...
If need to install driver
Get-PnpDevice -DeviceID 'USB\VID_10C4&PID_EA60\0001' | Format-List
Caption : CP2102 USB to UART Bridge Controller
Description : CP2102 USB to UART Bridge Controller
InstallDate :
Name : CP2102 USB to UART Bridge Controller
Status : Error
Availability :
ConfigManagerErrorCode : CM_PROB_FAILED_INSTALL
ConfigManagerUserConfig : False
CreationClassName : Win32_PnPEntity
DeviceID : USB\VID_10C4&PID_EA60\0001
ErrorCleared :
ErrorDescription :
LastErrorCode :
PNPDeviceID : USB\VID_10C4&PID_EA60\0001
PowerManagementCapabilities :
PowerManagementSupported :
StatusInfo :
SystemCreationClassName : Win32_ComputerSystem
SystemName : DESKTOP-FRFQN8H
ClassGuid :
CompatibleID : {USB\Class_FF&SubClass_00&Prot_00, USB\Class_FF&SubClass_00, USB\Class_FF}
HardwareID : {USB\VID_10C4&PID_EA60&REV_0100, USB\VID_10C4&PID_EA60}
Manufacturer :
PNPClass :
Present : True
Service :
PSComputerName :
Class :
FriendlyName : CP2102 USB to UART Bridge Controller
InstanceId : USB\VID_10C4&PID_EA60\0001
Problem : CM_PROB_FAILED_INSTALL
ProblemDescription :

View File

@@ -0,0 +1,78 @@
Run from Windows 10
> Get-PnpDevice -PresentOnly | Format-List >a
> Get-PnpDevice -PresentOnly | Format-List >b
> Compare-Object (get-content a) (Get-Content b)
InputObject Side
Indi
cato
r
----------- ----
Caption : USB Serial Device (COM4) =>
Description : USB Serial Device =>
Name : USB Serial Device (COM4) =>
DeviceID : USB\VID_239A&PID_8029&MI_00\6&E8876D1&0&0000 =>
PNPDeviceID : USB\VID_239A&PID_8029&MI_00\6&E8876D1&0&0000 =>
ClassGuid : {4d36e978-e325-11ce-bfc1-08002be10318} =>
CompatibleID : {USB\Class_02&SubClass_02&Prot_00, USB\Class_02&SubClass_02, USB\Class_02} =>
HardwareID : {USB\VID_239A&PID_8029&REV_0100&MI_00, USB\VID_239A&PID_8029&MI_00} =>
PNPClass : Ports =>
Service : usbser =>
Class : Ports =>
FriendlyName : USB Serial Device (COM4) =>
InstanceId : USB\VID_239A&PID_8029&MI_00\6&E8876D1&0&0000 =>
Caption : USB Composite Device =>
Description : USB Composite Device =>
Name : USB Composite Device =>
DeviceID : USB\VID_239A&PID_8029\E6CF9502B1D410D8 =>
PNPDeviceID : USB\VID_239A&PID_8029\E6CF9502B1D410D8 =>
ClassGuid : {36fc9e60-c465-11cf-8056-444553540000} =>
CompatibleID : {USB\DevClass_00&SubClass_00&Prot_00, USB\DevClass_00&SubClass_00, USB\DevClass_00, =>
USB\COMPOSITE} =>
HardwareID : {USB\VID_239A&PID_8029&REV_0100, USB\VID_239A&PID_8029} =>
Manufacturer : (Standard USB Host Controller) =>
PNPClass : USB =>
Service : usbccgp =>
Class : USB =>
FriendlyName : USB Composite Device =>
InstanceId : USB\VID_239A&PID_8029\E6CF9502B1D410D8 =>
InstallDate : =>
Status : OK =>
Availability : =>
ConfigManagerErrorCode : CM_PROB_NONE =>
ConfigManagerUserConfig : False =>
CreationClassName : Win32_PnPEntity =>
ErrorCleared : =>
ErrorDescription : =>
LastErrorCode : =>
PowerManagementCapabilities : =>
PowerManagementSupported : =>
StatusInfo : =>
SystemCreationClassName : Win32_ComputerSystem =>
SystemName : DESKTOP-FRFQN8H =>
Present : True =>
PSComputerName : =>
Problem : CM_PROB_NONE =>
ProblemDescription : =>
InstallDate : =>
Status : OK =>
Availability : =>
ConfigManagerErrorCode : CM_PROB_NONE =>
ConfigManagerUserConfig : False =>
CreationClassName : Win32_PnPEntity =>
ErrorCleared : =>
ErrorDescription : =>
LastErrorCode : =>
PowerManagementCapabilities : =>
PowerManagementSupported : =>
StatusInfo : =>
SystemCreationClassName : Win32_ComputerSystem =>
SystemName : DESKTOP-FRFQN8H =>
Manufacturer : Microsoft =>
Present : True =>
PSComputerName : =>
Problem : CM_PROB_NONE =>
ProblemDescription : =>
=>
=>

View File

@@ -0,0 +1,254 @@
Run from Windows 10
> Get-PnpDevice -PresentOnly | Format-List >a
> Get-PnpDevice -PresentOnly | Format-List >b
> Compare-Object (get-content a) (Get-Content b)
In "boot" mode:
InputObject
-----------
Caption : FTHR840BOOT
Description : nRF UF2
Name : FTHR840BOOT
DeviceID : SWD\WPDBUSENUM\_??_USBSTOR#DISK&VEN_ADAFRUIT&PROD_NRF_UF2&REV_1.0#7&27E1626&0&D121BD1C90B
93EA2&0#{53F56307-B6BF-11D0-94F2-00A0C91EFB8B}
PNPDeviceID : SWD\WPDBUSENUM\_??_USBSTOR#DISK&VEN_ADAFRUIT&PROD_NRF_UF2&REV_1.0#7&27E1626&0&D121BD1C90B
93EA2&0#{53F56307-B6BF-11D0-94F2-00A0C91EFB8B}
ClassGuid : {eec5ad98-8080-425f-922a-dabf3de3f69a}
CompatibleID : {wpdbusenum\fs, SWD\Generic}
Manufacturer : Adafruit
PNPClass : WPD
Service : WUDFWpdFs
Class : WPD
FriendlyName : FTHR840BOOT
InstanceId : SWD\WPDBUSENUM\_??_USBSTOR#DISK&VEN_ADAFRUIT&PROD_NRF_UF2&REV_1.0#7&27E1626&0&D121BD1C90B
93EA2&0#{53F56307-B6BF-11D0-94F2-00A0C91EFB8B}
DeviceID : STORAGE\VOLUME\_??_USBSTOR#DISK&VEN_ADAFRUIT&PROD_NRF_UF2&REV_1.0#7&27E1626&0&D121BD1C90B
93EA2&0#{53F56307-B6BF-11D0-94F2-00A0C91EFB8B}
PNPDeviceID : STORAGE\VOLUME\_??_USBSTOR#DISK&VEN_ADAFRUIT&PROD_NRF_UF2&REV_1.0#7&27E1626&0&D121BD1C90B
93EA2&0#{53F56307-B6BF-11D0-94F2-00A0C91EFB8B}
InstanceId : STORAGE\VOLUME\_??_USBSTOR#DISK&VEN_ADAFRUIT&PROD_NRF_UF2&REV_1.0#7&27E1626&0&D121BD1C90B
93EA2&0#{53F56307-B6BF-11D0-94F2-00A0C91EFB8B}
Caption : USB Mass Storage Device
Description : USB Mass Storage Device
Name : USB Mass Storage Device
DeviceID : USB\VID_239A&PID_0029&MI_02\6&175793A&0&0002
PNPDeviceID : USB\VID_239A&PID_0029&MI_02\6&175793A&0&0002
CompatibleID : {USB\Class_08&SubClass_06&Prot_50, USB\Class_08&SubClass_06, USB\Class_08}
HardwareID : {USB\VID_239A&PID_0029&REV_0100&MI_02, USB\VID_239A&PID_0029&MI_02}
Manufacturer : Compatible USB storage device
Service : USBSTOR
FriendlyName : USB Mass Storage Device
InstanceId : USB\VID_239A&PID_0029&MI_02\6&175793A&0&0002
Caption : USB Serial Device (COM5)
Description : USB Serial Device
Name : USB Serial Device (COM5)
DeviceID : USB\VID_239A&PID_0029&MI_00\6&175793A&0&0000
PNPDeviceID : USB\VID_239A&PID_0029&MI_00\6&175793A&0&0000
ClassGuid : {4d36e978-e325-11ce-bfc1-08002be10318}
CompatibleID : {USB\Class_02&SubClass_02&Prot_00, USB\Class_02&SubClass_02, USB\Class_02}
HardwareID : {USB\VID_239A&PID_0029&REV_0100&MI_00, USB\VID_239A&PID_0029&MI_00}
PNPClass : Ports
Service : usbser
Class : Ports
FriendlyName : USB Serial Device (COM5)
InstanceId : USB\VID_239A&PID_0029&MI_00\6&175793A&0&0000
DeviceID : USB\VID_239A&PID_0029\D121BD1C90B93EA2
PNPDeviceID : USB\VID_239A&PID_0029\D121BD1C90B93EA2
ClassGuid : {36fc9e60-c465-11cf-8056-444553540000}
HardwareID : {USB\VID_239A&PID_0029&REV_0100, USB\VID_239A&PID_0029}
PNPClass : USB
Class : USB
InstanceId : USB\VID_239A&PID_0029\D121BD1C90B93EA2
Caption : USB Composite Device
Description : USB Composite Device
Name : USB Composite Device
ClassGuid : {36fc9e60-c465-11cf-8056-444553540000}
CompatibleID : {USB\DevClass_00&SubClass_00&Prot_00, USB\DevClass_00&SubClass_00, USB\DevClass_00,
USB\COMPOSITE}
Manufacturer : (Standard USB Host Controller)
PNPClass : USB
Service : usbccgp
Class : USB
FriendlyName : USB Composite Device
Caption : Volume
Description : Volume
Name : Volume
ClassGuid : {71a27cdd-812a-11d0-bec7-08002be2092f}
HardwareID : {STORAGE\Volume}
PNPClass : Volume
Service : volume
Class : Volume
FriendlyName : Volume
HardwareID :
Caption : Adafruit nRF UF2 USB Device
Name : Adafruit nRF UF2 USB Device
DeviceID : USBSTOR\DISK&VEN_ADAFRUIT&PROD_NRF_UF2&REV_1.0\7&27E1626&0&D121BD1C90B93EA2&0
PNPDeviceID : USBSTOR\DISK&VEN_ADAFRUIT&PROD_NRF_UF2&REV_1.0\7&27E1626&0&D121BD1C90B93EA2&0
CompatibleID : {USBSTOR\Disk, USBSTOR\RAW, GenDisk}
HardwareID : {USBSTOR\DiskAdafruitnRF_UF2_________1.0_, USBSTOR\DiskAdafruitnRF_UF2_________,
USBSTOR\DiskAdafruit, USBSTOR\AdafruitnRF_UF2_________1...}
FriendlyName : Adafruit nRF UF2 USB Device
InstanceId : USBSTOR\DISK&VEN_ADAFRUIT&PROD_NRF_UF2&REV_1.0\7&27E1626&0&D121BD1C90B93EA2&0
Description : Disk drive
ClassGuid : {4d36e967-e325-11ce-bfc1-08002be10318}
Manufacturer : (Standard disk drives)
PNPClass : DiskDrive
Service : disk
Class : DiskDrive
CompatibleID :
InstallDate :
Status : OK
Availability :
ConfigManagerErrorCode : CM_PROB_NONE
ConfigManagerUserConfig : False
CreationClassName : Win32_PnPEntity
ErrorCleared :
ErrorDescription :
LastErrorCode :
PowerManagementCapabilities :
PowerManagementSupported :
StatusInfo :
SystemCreationClassName : Win32_ComputerSystem
SystemName : DESKTOP-FRFQN8H
Present : True
PSComputerName :
Problem : CM_PROB_NONE
ProblemDescription :
InstallDate :
Status : OK
Availability :
ConfigManagerErrorCode : CM_PROB_NONE
ConfigManagerUserConfig : False
CreationClassName : Win32_PnPEntity
ErrorCleared :
ErrorDescription :
LastErrorCode :
PowerManagementCapabilities :
PowerManagementSupported :
StatusInfo :
SystemCreationClassName : Win32_ComputerSystem
SystemName : DESKTOP-FRFQN8H
Present : True
PSComputerName :
Problem : CM_PROB_NONE
ProblemDescription :
InstallDate :
Status : OK
Availability :
ConfigManagerErrorCode : CM_PROB_NONE
ConfigManagerUserConfig : False
CreationClassName : Win32_PnPEntity
ErrorCleared :
ErrorDescription :
LastErrorCode :
PowerManagementCapabilities :
PowerManagementSupported :
StatusInfo :
SystemCreationClassName : Win32_ComputerSystem
SystemName : DESKTOP-FRFQN8H
Present : True
PSComputerName :
Problem : CM_PROB_NONE
ProblemDescription :
InstallDate :
Status : OK
Availability :
ConfigManagerErrorCode : CM_PROB_NONE
ConfigManagerUserConfig : False
CreationClassName : Win32_PnPEntity
ErrorCleared :
ErrorDescription :
LastErrorCode :
PowerManagementCapabilities :
PowerManagementSupported :
StatusInfo :
SystemCreationClassName : Win32_ComputerSystem
SystemName : DESKTOP-FRFQN8H
Manufacturer : Microsoft
Present : True
PSComputerName :
Problem : CM_PROB_NONE
ProblemDescription :
When you press the RST to load Meshtastic:
> Get-PnpDevice -PresentOnly | Format-List >b
> Compare-Object (get-content a) (Get-Content b)
InputObject Side
Indi
cato
r
----------- ----
DeviceID : USB\VID_239A&PID_8029\D121BD1C90B93EA2 =>
PNPDeviceID : USB\VID_239A&PID_8029\D121BD1C90B93EA2 =>
HardwareID : {USB\VID_239A&PID_8029&REV_0100, USB\VID_239A&PID_8029} =>
InstanceId : USB\VID_239A&PID_8029\D121BD1C90B93EA2 =>
Caption : USB Composite Device =>
Description : USB Composite Device =>
Name : USB Composite Device =>
ClassGuid : {36fc9e60-c465-11cf-8056-444553540000} =>
CompatibleID : {USB\DevClass_00&SubClass_00&Prot_00, USB\DevClass_00&SubClass_00, USB\DevClass_00, =>
USB\COMPOSITE} =>
Manufacturer : (Standard USB Host Controller) =>
PNPClass : USB =>
Service : usbccgp =>
Class : USB =>
FriendlyName : USB Composite Device =>
Caption : USB Serial Device (COM6) =>
Description : USB Serial Device =>
Name : USB Serial Device (COM6) =>
DeviceID : USB\VID_239A&PID_8029&MI_00\6&39B279E2&0&0000 =>
PNPDeviceID : USB\VID_239A&PID_8029&MI_00\6&39B279E2&0&0000 =>
ClassGuid : {4d36e978-e325-11ce-bfc1-08002be10318} =>
CompatibleID : {USB\Class_02&SubClass_02&Prot_00, USB\Class_02&SubClass_02, USB\Class_02} =>
HardwareID : {USB\VID_239A&PID_8029&REV_0100&MI_00, USB\VID_239A&PID_8029&MI_00} =>
PNPClass : Ports =>
Service : usbser =>
Class : Ports =>
FriendlyName : USB Serial Device (COM6) =>
InstanceId : USB\VID_239A&PID_8029&MI_00\6&39B279E2&0&0000 =>
InstallDate : =>
Status : OK =>
Availability : =>
ConfigManagerErrorCode : CM_PROB_NONE =>
ConfigManagerUserConfig : False =>
CreationClassName : Win32_PnPEntity =>
ErrorCleared : =>
ErrorDescription : =>
LastErrorCode : =>
PowerManagementCapabilities : =>
PowerManagementSupported : =>
StatusInfo : =>
SystemCreationClassName : Win32_ComputerSystem =>
SystemName : DESKTOP-FRFQN8H =>
Present : True =>
PSComputerName : =>
Problem : CM_PROB_NONE =>
ProblemDescription : =>
InstallDate : =>
Status : OK =>
Availability : =>
ConfigManagerErrorCode : CM_PROB_NONE =>
ConfigManagerUserConfig : False =>
CreationClassName : Win32_PnPEntity =>
ErrorCleared : =>
ErrorDescription : =>
LastErrorCode : =>
PowerManagementCapabilities : =>
PowerManagementSupported : =>
StatusInfo : =>
SystemCreationClassName : Win32_ComputerSystem =>
SystemName : DESKTOP-FRFQN8H =>
Manufacturer : Microsoft =>
Present : True =>
PSComputerName : =>
Problem : CM_PROB_NONE =>
ProblemDescription : =>
=>

42
info/windows/tbeam.txt Normal file
View File

@@ -0,0 +1,42 @@
Run from Windows 10
> Get-PnpDevice -PresentOnly | Format-List >a
> Get-PnpDevice -PresentOnly | Format-List >b
> Compare-Object (get-content a) (Get-Content b)
InputObject SideIndicator
----------- -------------
Caption : USB-Enhanced-SERIAL CH9102 (COM7) =>
Description : USB-Enhanced-SERIAL CH9102 =>
Name : USB-Enhanced-SERIAL CH9102 (COM7) =>
DeviceID : USB\VID_1A86&PID_55D4\5323005057 =>
PNPDeviceID : USB\VID_1A86&PID_55D4\5323005057 =>
ClassGuid : {4d36e978-e325-11ce-bfc1-08002be10318} =>
CompatibleID : {USB\Class_02&SubClass_02&Prot_01, USB\Class_02&SubClass_02, USB\Class_02} =>
HardwareID : {USB\VID_1A86&PID_55D4&REV_0443, USB\VID_1A86&PID_55D4} =>
Manufacturer : wch.cn =>
PNPClass : Ports =>
Service : CH343SER_A64 =>
Class : Ports =>
FriendlyName : USB-Enhanced-SERIAL CH9102 (COM7) =>
InstanceId : USB\VID_1A86&PID_55D4\5323005057 =>
InstallDate : =>
Status : OK =>
Availability : =>
ConfigManagerErrorCode : CM_PROB_NONE =>
ConfigManagerUserConfig : False =>
CreationClassName : Win32_PnPEntity =>
ErrorCleared : =>
ErrorDescription : =>
LastErrorCode : =>
PowerManagementCapabilities : =>
PowerManagementSupported : =>
StatusInfo : =>
SystemCreationClassName : Win32_ComputerSystem =>
SystemName : DESKTOP-FRFQN8H =>
Present : True =>
PSComputerName : =>
Problem : CM_PROB_NONE =>
ProblemDescription : =>
=>

302
info/windows/techo.txt Normal file
View File

@@ -0,0 +1,302 @@
PS > Get-PnpDevice -PresentOnly | Format-List >a
PS > Get-PnpDevice -PresentOnly | Format-List >b
PS > Compare-Object (get-content a) (get-content b)
Note: Not in boot mode
InputObject Side
Indi
cato
r
----------- ----
DeviceID : USB\VID_239A&PID_4405\D02012062C578951 =>
PNPDeviceID : USB\VID_239A&PID_4405\D02012062C578951 =>
HardwareID : {USB\VID_239A&PID_4405&REV_0100, USB\VID_239A&PID_4405} =>
InstanceId : USB\VID_239A&PID_4405\D02012062C578951 =>
Caption : USB Composite Device =>
Description : USB Composite Device =>
Name : USB Composite Device =>
ClassGuid : {36fc9e60-c465-11cf-8056-444553540000} =>
CompatibleID : {USB\DevClass_00&SubClass_00&Prot_00, USB\DevClass_00&SubClass_00, USB\DevClass_00, =>
USB\COMPOSITE} =>
Manufacturer : (Standard USB Host Controller) =>
PNPClass : USB =>
Service : usbccgp =>
Class : USB =>
FriendlyName : USB Composite Device =>
Caption : USB Serial Device (COM10) =>
Description : USB Serial Device =>
Name : USB Serial Device (COM10) =>
DeviceID : USB\VID_239A&PID_4405&MI_00\6&1B68A3E6&0&0000 =>
PNPDeviceID : USB\VID_239A&PID_4405&MI_00\6&1B68A3E6&0&0000 =>
ClassGuid : {4d36e978-e325-11ce-bfc1-08002be10318} =>
CompatibleID : {USB\Class_02&SubClass_02&Prot_00, USB\Class_02&SubClass_02, USB\Class_02} =>
HardwareID : {USB\VID_239A&PID_4405&REV_0100&MI_00, USB\VID_239A&PID_4405&MI_00} =>
PNPClass : Ports =>
Service : usbser =>
Class : Ports =>
FriendlyName : USB Serial Device (COM10) =>
InstanceId : USB\VID_239A&PID_4405&MI_00\6&1B68A3E6&0&0000 =>
InstallDate : =>
Status : OK =>
Availability : =>
ConfigManagerErrorCode : CM_PROB_NONE =>
ConfigManagerUserConfig : False =>
CreationClassName : Win32_PnPEntity =>
ErrorCleared : =>
ErrorDescription : =>
LastErrorCode : =>
PowerManagementCapabilities : =>
PowerManagementSupported : =>
StatusInfo : =>
SystemCreationClassName : Win32_ComputerSystem =>
SystemName : DESKTOP-FRFQN8H =>
Present : True =>
PSComputerName : =>
Problem : CM_PROB_NONE =>
ProblemDescription : =>
InstallDate : =>
Status : OK =>
Availability : =>
ConfigManagerErrorCode : CM_PROB_NONE =>
ConfigManagerUserConfig : False =>
CreationClassName : Win32_PnPEntity =>
ErrorCleared : =>
ErrorDescription : =>
LastErrorCode : =>
PowerManagementCapabilities : =>
PowerManagementSupported : =>
StatusInfo : =>
SystemCreationClassName : Win32_ComputerSystem =>
SystemName : DESKTOP-FRFQN8H =>
Manufacturer : Microsoft =>
Present : True =>
PSComputerName : =>
Problem : CM_PROB_NONE =>
ProblemDescription : =>
=>
in boot mode
PS > Get-PnpDevice -PresentOnly | Format-List >c
PS > Compare-Object (get-content a) (get-content c)
InputObject
-----------
Caption : Adafruit nRF UF2 USB Device
Name : Adafruit nRF UF2 USB Device
DeviceID : USBSTOR\DISK&VEN_ADAFRUIT&PROD_NRF_UF2&REV_1.0\7&10304CB2&0&D02012062C578951&0
PNPDeviceID : USBSTOR\DISK&VEN_ADAFRUIT&PROD_NRF_UF2&REV_1.0\7&10304CB2&0&D02012062C578951&0
CompatibleID : {USBSTOR\Disk, USBSTOR\RAW, GenDisk}
HardwareID : {USBSTOR\DiskAdafruitnRF_UF2_________1.0_, USBSTOR\DiskAdafruitnRF_UF2_________,
USBSTOR\DiskAdafruit, USBSTOR\AdafruitnRF_UF2_________1...}
FriendlyName : Adafruit nRF UF2 USB Device
InstanceId : USBSTOR\DISK&VEN_ADAFRUIT&PROD_NRF_UF2&REV_1.0\7&10304CB2&0&D02012062C578951&0
Caption : USB Mass Storage Device
Description : USB Mass Storage Device
Name : USB Mass Storage Device
DeviceID : USB\VID_239A&PID_0029&MI_02\6&2AE8D65&0&0002
PNPDeviceID : USB\VID_239A&PID_0029&MI_02\6&2AE8D65&0&0002
CompatibleID : {USB\Class_08&SubClass_06&Prot_50, USB\Class_08&SubClass_06, USB\Class_08}
HardwareID : {USB\VID_239A&PID_0029&REV_0100&MI_02, USB\VID_239A&PID_0029&MI_02}
Manufacturer : Compatible USB storage device
Service : USBSTOR
FriendlyName : USB Mass Storage Device
InstanceId : USB\VID_239A&PID_0029&MI_02\6&2AE8D65&0&0002
Caption : USB Serial Device (COM11)
Description : USB Serial Device
Name : USB Serial Device (COM11)
DeviceID : USB\VID_239A&PID_0029&MI_00\6&2AE8D65&0&0000
PNPDeviceID : USB\VID_239A&PID_0029&MI_00\6&2AE8D65&0&0000
ClassGuid : {4d36e978-e325-11ce-bfc1-08002be10318}
CompatibleID : {USB\Class_02&SubClass_02&Prot_00, USB\Class_02&SubClass_02, USB\Class_02}
HardwareID : {USB\VID_239A&PID_0029&REV_0100&MI_00, USB\VID_239A&PID_0029&MI_00}
PNPClass : Ports
Service : usbser
Class : Ports
FriendlyName : USB Serial Device (COM11)
InstanceId : USB\VID_239A&PID_0029&MI_00\6&2AE8D65&0&0000
ClassGuid : {36fc9e60-c465-11cf-8056-444553540000}
PNPClass : USB
Class : USB
Caption : USB Composite Device
Description : USB Composite Device
Name : USB Composite Device
DeviceID : USB\VID_239A&PID_0029\D02012062C578951
PNPDeviceID : USB\VID_239A&PID_0029\D02012062C578951
ClassGuid : {36fc9e60-c465-11cf-8056-444553540000}
CompatibleID : {USB\DevClass_00&SubClass_00&Prot_00, USB\DevClass_00&SubClass_00, USB\DevClass_00,
USB\COMPOSITE}
HardwareID : {USB\VID_239A&PID_0029&REV_0100, USB\VID_239A&PID_0029}
Manufacturer : (Standard USB Host Controller)
PNPClass : USB
Service : usbccgp
Class : USB
FriendlyName : USB Composite Device
InstanceId : USB\VID_239A&PID_0029\D02012062C578951
DeviceID : STORAGE\VOLUME\_??_USBSTOR#DISK&VEN_ADAFRUIT&PROD_NRF_UF2&REV_1.0#7&10304CB2&0&D02012062C
578951&0#{53F56307-B6BF-11D0-94F2-00A0C91EFB8B}
PNPDeviceID : STORAGE\VOLUME\_??_USBSTOR#DISK&VEN_ADAFRUIT&PROD_NRF_UF2&REV_1.0#7&10304CB2&0&D02012062C
578951&0#{53F56307-B6BF-11D0-94F2-00A0C91EFB8B}
InstanceId : STORAGE\VOLUME\_??_USBSTOR#DISK&VEN_ADAFRUIT&PROD_NRF_UF2&REV_1.0#7&10304CB2&0&D02012062C
578951&0#{53F56307-B6BF-11D0-94F2-00A0C91EFB8B}
Caption : Volume
Description : Volume
Name : Volume
ClassGuid : {71a27cdd-812a-11d0-bec7-08002be2092f}
HardwareID : {STORAGE\Volume}
PNPClass : Volume
Service : volume
Class : Volume
FriendlyName : Volume
Description : Disk drive
ClassGuid : {4d36e967-e325-11ce-bfc1-08002be10318}
Manufacturer : (Standard disk drives)
PNPClass : DiskDrive
Service : disk
Class : DiskDrive
Caption : TECHOBOOT
Description : nRF UF2
Name : TECHOBOOT
DeviceID : SWD\WPDBUSENUM\_??_USBSTOR#DISK&VEN_ADAFRUIT&PROD_NRF_UF2&REV_1.0#7&10304CB2&0&D02012062C
578951&0#{53F56307-B6BF-11D0-94F2-00A0C91EFB8B}
PNPDeviceID : SWD\WPDBUSENUM\_??_USBSTOR#DISK&VEN_ADAFRUIT&PROD_NRF_UF2&REV_1.0#7&10304CB2&0&D02012062C
578951&0#{53F56307-B6BF-11D0-94F2-00A0C91EFB8B}
ClassGuid : {eec5ad98-8080-425f-922a-dabf3de3f69a}
CompatibleID : {wpdbusenum\fs, SWD\Generic}
HardwareID :
Manufacturer : Adafruit
PNPClass : WPD
Service : WUDFWpdFs
Class : WPD
FriendlyName : TECHOBOOT
InstanceId : SWD\WPDBUSENUM\_??_USBSTOR#DISK&VEN_ADAFRUIT&PROD_NRF_UF2&REV_1.0#7&10304CB2&0&D02012062C
578951&0#{53F56307-B6BF-11D0-94F2-00A0C91EFB8B}
CompatibleID :
InstallDate :
Status : OK
Availability :
ConfigManagerErrorCode : CM_PROB_NONE
ConfigManagerUserConfig : False
CreationClassName : Win32_PnPEntity
ErrorCleared :
ErrorDescription :
LastErrorCode :
PowerManagementCapabilities :
PowerManagementSupported :
StatusInfo :
SystemCreationClassName : Win32_ComputerSystem
SystemName : DESKTOP-FRFQN8H
Present : True
PSComputerName :
Problem : CM_PROB_NONE
ProblemDescription :
InstallDate :
Status : OK
Availability :
ConfigManagerErrorCode : CM_PROB_NONE
ConfigManagerUserConfig : False
CreationClassName : Win32_PnPEntity
ErrorCleared :
ErrorDescription :
LastErrorCode :
PowerManagementCapabilities :
PowerManagementSupported :
StatusInfo :
SystemCreationClassName : Win32_ComputerSystem
SystemName : DESKTOP-FRFQN8H
Present : True
PSComputerName :
Problem : CM_PROB_NONE
ProblemDescription :
InstallDate :
Status : OK
Availability :
ConfigManagerErrorCode : CM_PROB_NONE
ConfigManagerUserConfig : False
CreationClassName : Win32_PnPEntity
ErrorCleared :
ErrorDescription :
LastErrorCode :
PowerManagementCapabilities :
PowerManagementSupported :
StatusInfo :
SystemCreationClassName : Win32_ComputerSystem
SystemName : DESKTOP-FRFQN8H
Present : True
PSComputerName :
Problem : CM_PROB_NONE
ProblemDescription :
InstallDate :
Status : OK
Availability :
ConfigManagerErrorCode : CM_PROB_NONE
ConfigManagerUserConfig : False
CreationClassName : Win32_PnPEntity
ErrorCleared :
ErrorDescription :
LastErrorCode :
PowerManagementCapabilities :
PowerManagementSupported :
StatusInfo :
SystemCreationClassName : Win32_ComputerSystem
SystemName : DESKTOP-FRFQN8H
Manufacturer : Microsoft
Present : True
PSComputerName :
Problem : CM_PROB_NONE
ProblemDescription :
InstallDate :
Status : OK
Availability :
ConfigManagerErrorCode : CM_PROB_NONE
ConfigManagerUserConfig : False
CreationClassName : Win32_PnPEntity
ErrorCleared :
ErrorDescription :
LastErrorCode :
PowerManagementCapabilities :
PowerManagementSupported :
StatusInfo :
SystemCreationClassName : Win32_ComputerSystem
SystemName : DESKTOP-FRFQN8H
Present : True
PSComputerName :
Problem : CM_PROB_NONE
ProblemDescription :
InstallDate :
Status : OK
Availability :
ConfigManagerErrorCode : CM_PROB_NONE
ConfigManagerUserConfig : False
CreationClassName : Win32_PnPEntity
ErrorCleared :
ErrorDescription :
LastErrorCode :
PowerManagementCapabilities :
PowerManagementSupported :
StatusInfo :
SystemCreationClassName : Win32_ComputerSystem
SystemName : DESKTOP-FRFQN8H
Manufacturer : Microsoft
Present : True
PSComputerName :
Problem : CM_PROB_NONE
ProblemDescription :
PS > Get-Volume
DriveLetter FriendlyName FileSystemType DriveType HealthStatus OperationalStatus SizeRemaining Size
----------- ------------ -------------- --------- ------------ ----------------- ------------- ----
Recovery NTFS Fixed Healthy OK 301.99 MB 854 MB
C NTFS Fixed Healthy OK 22.3 GB 56.67 GB
D TECHOBOOT FAT Removable Healthy OK 30.05 MB 31.88 MB

41
info/windows/tlora.txt Normal file
View File

@@ -0,0 +1,41 @@
Run from Windows 10
PS > Get-PnpDevice -PresentOnly | Format-List > a
PS > Get-PnpDevice -PresentOnly | Format-List > b
PS > Compare-Object (get-content a) (Get-Content b)
InputObject SideIndicator
----------- -------------
Caption : USB-Enhanced-SERIAL CH9102 (COM3) <=
Description : USB-Enhanced-SERIAL CH9102 <=
Name : USB-Enhanced-SERIAL CH9102 (COM3) <=
DeviceID : USB\VID_1A86&PID_55D4\5&27435A1F&0&1 <=
PNPDeviceID : USB\VID_1A86&PID_55D4\5&27435A1F&0&1 <=
ClassGuid : {4d36e978-e325-11ce-bfc1-08002be10318} <=
CompatibleID : {USB\Class_02&SubClass_02&Prot_01, USB\Class_02&SubClass_02, USB\Class_02} <=
HardwareID : {USB\VID_1A86&PID_55D4&REV_0443, USB\VID_1A86&PID_55D4} <=
Manufacturer : wch.cn <=
PNPClass : Ports <=
Service : CH343SER_A64 <=
Class : Ports <=
FriendlyName : USB-Enhanced-SERIAL CH9102 (COM3) <=
InstanceId : USB\VID_1A86&PID_55D4\5&27435A1F&0&1 <=
InstallDate : <=
Status : OK <=
Availability : <=
ConfigManagerErrorCode : CM_PROB_NONE <=
ConfigManagerUserConfig : False <=
CreationClassName : Win32_PnPEntity <=
ErrorCleared : <=
ErrorDescription : <=
LastErrorCode : <=
PowerManagementCapabilities : <=
PowerManagementSupported : <=
StatusInfo : <=
SystemCreationClassName : Win32_ComputerSystem <=
SystemName : DESKTOP-FRFQN8H <=
Present : True <=
PSComputerName : <=
Problem : CM_PROB_NONE <=
ProblemDescription : <=
<=

71
info/windows/tlora_v1.txt Normal file
View File

@@ -0,0 +1,71 @@
InputObject SideIn
dicato
r
----------- ------
Caption : Silicon Labs CP210x USB to UART Bridge (COM5) =>
Description : Silicon Labs CP210x USB to UART Bridge =>
Name : Silicon Labs CP210x USB to UART Bridge (COM5) =>
DeviceID : USB\VID_10C4&PID_EA60\0001 =>
PNPDeviceID : USB\VID_10C4&PID_EA60\0001 =>
HardwareID : {USB\VID_10C4&PID_EA60&REV_0100, USB\VID_10C4&PID_EA60} =>
Manufacturer : Silicon Laboratories =>
Service : silabser =>
FriendlyName : Silicon Labs CP210x USB to UART Bridge (COM5) =>
InstanceId : USB\VID_10C4&PID_EA60\0001 =>
CompatibleID : {USB\Class_ff&SubClass_00&Prot_00, USB\Class_ff&SubClass_00, USB\Class_ff} =>
ClassGuid : {4d36e978-e325-11ce-bfc1-08002be10318} =>
PNPClass : Ports =>
Class : Ports =>
InstallDate : =>
Status : OK =>
Availability : =>
ConfigManagerErrorCode : CM_PROB_NONE =>
ConfigManagerUserConfig : False =>
CreationClassName : Win32_PnPEntity =>
ErrorCleared : =>
ErrorDescription : =>
LastErrorCode : =>
PowerManagementCapabilities : =>
PowerManagementSupported : =>
StatusInfo : =>
SystemCreationClassName : Win32_ComputerSystem =>
SystemName : MTI-ATPLT1 =>
Present : True =>
PSComputerName : =>
Problem : CM_PROB_NONE =>
ProblemDescription : =>
Caption : Microsoft Serial Mouse =>
Description : Microsoft Serial Mouse =>
InstallDate : =>
Name : Microsoft Serial Mouse =>
Status : Error =>
Availability : =>
ConfigManagerErrorCode : CM_PROB_FAILED_START =>
ConfigManagerUserConfig : False =>
CreationClassName : Win32_PnPEntity =>
DeviceID : SILABENM\MOUSE\C&1EBF522&0&0000 =>
ErrorCleared : =>
ErrorDescription : =>
LastErrorCode : =>
PNPDeviceID : SILABENM\MOUSE\C&1EBF522&0&0000 =>
PowerManagementCapabilities : =>
PowerManagementSupported : =>
StatusInfo : =>
SystemCreationClassName : Win32_ComputerSystem =>
SystemName : MTI-ATPLT1 =>
ClassGuid : {4d36e96f-e325-11ce-bfc1-08002be10318} =>
CompatibleID : {SERIAL_MOUSE} =>
HardwareID : {*PNP0F01} =>
Manufacturer : Microsoft =>
PNPClass : Mouse =>
Present : True =>
Service : sermouse =>
PSComputerName : =>
Class : Mouse =>
FriendlyName : Microsoft Serial Mouse =>
InstanceId : SILABENM\MOUSE\C&1EBF522&0&0000 =>
Problem : CM_PROB_FAILED_START =>
ProblemDescription : =>
=>
=>

View File

@@ -72,14 +72,16 @@ from typing import *
import serial
import timeago
import google.protobuf.json_format
import pygatt
from pubsub import pub
from dotmap import DotMap
from tabulate import tabulate
from google.protobuf.json_format import MessageToJson
from .util import fixme, catchAndIgnore, stripnl, DeferredExecution, Timeout
from .node import Node
from . import mesh_pb2, portnums_pb2, apponly_pb2, admin_pb2, environmental_measurement_pb2, remote_hardware_pb2, channel_pb2, radioconfig_pb2, util
from meshtastic.util import fixme, catchAndIgnore, stripnl, DeferredExecution, Timeout
from meshtastic.node import Node
from meshtastic import (mesh_pb2, portnums_pb2, apponly_pb2, admin_pb2,
telemetry_pb2, remote_hardware_pb2,
channel_pb2, radioconfig_pb2, util)
# Note: To follow PEP224, comments should be after the module variable.
@@ -127,6 +129,7 @@ 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}')
try:
asBytes = asDict["decoded"]["payload"]
asDict["decoded"]["text"] = asBytes.decode("utf-8")
@@ -137,22 +140,30 @@ def _onTextReceive(iface, asDict):
def _onPositionReceive(iface, asDict):
"""Special auto parsing for received messages"""
p = asDict["decoded"]["position"]
iface._fixupPosition(p)
# update node DB as needed
iface._getOrCreateByNum(asDict["from"])["position"] = p
logging.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}')
p = iface._fixupPosition(p)
logging.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"""
p = asDict["decoded"]["user"]
# decode user protobufs and update nodedb, provide decoded version as "position" in the published msg
# update node DB as needed
n = iface._getOrCreateByNum(asDict["from"])
n["user"] = p
# We now have a node ID, make sure it is uptodate in that table
iface.nodes[p["id"]] = n
_receiveInfoUpdate(iface, asDict)
logging.debug(f'in _onNodeInfoReceive() asDict:{asDict}')
if 'decoded' in asDict:
if 'user' in asDict['decoded'] and 'from' in asDict:
p = asDict["decoded"]["user"]
# decode user protobufs and update nodedb, provide decoded version as "position" in the published msg
# update node DB as needed
n = iface._getOrCreateByNum(asDict["from"])
n["user"] = p
# We now have a node ID, make sure it is uptodate in that table
iface.nodes[p["id"]] = n
_receiveInfoUpdate(iface, asDict)
def _receiveInfoUpdate(iface, asDict):
@@ -170,7 +181,6 @@ 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.ROUTING_APP: KnownProtocol("routing", mesh_pb2.Routing),
portnums_pb2.PortNum.ENVIRONMENTAL_MEASUREMENT_APP: KnownProtocol("environmental", environmental_measurement_pb2.EnvironmentalMeasurement),
portnums_pb2.PortNum.REMOTE_HARDWARE_APP: KnownProtocol(
"remotehw", remote_hardware_pb2.HardwareMessage)
portnums_pb2.PortNum.TELEMETRY_APP: KnownProtocol("telemetry", telemetry_pb2.Telemetry),
portnums_pb2.PortNum.REMOTE_HARDWARE_APP: KnownProtocol("remotehw", remote_hardware_pb2.HardwareMessage)
}

View File

@@ -5,6 +5,7 @@
import argparse
import platform
import logging
import os
import sys
import time
import yaml
@@ -13,11 +14,11 @@ import pyqrcode
import pkg_resources
import meshtastic.util
import meshtastic.test
from . import remote_hardware
from .ble_interface import BLEInterface
from . import portnums_pb2, channel_pb2, radioconfig_pb2
from .globals import Globals
from meshtastic import remote_hardware
from meshtastic.ble_interface import BLEInterface
from meshtastic import portnums_pb2, channel_pb2, radioconfig_pb2
from meshtastic.globals import Globals
from meshtastic.__init__ import BROADCAST_ADDR
def onReceive(packet, interface):
"""Callback invoked when a packet arrives"""
@@ -38,7 +39,7 @@ def onReceive(packet, interface):
rxSnr = packet['rxSnr']
hopLimit = packet['hopLimit']
print(f"message: {msg}")
reply = "got msg \'{}\' with rxSnr: {} and hopLimit: {}".format(msg, rxSnr, hopLimit)
reply = f"got msg \'{msg}\' with rxSnr: {rxSnr} and hopLimit: {hopLimit}"
print("Sending reply: ", reply)
interface.sendText(reply)
@@ -46,7 +47,7 @@ def onReceive(packet, interface):
print(f'Warning: There is no field {ex} in the packet.')
def onConnection(interface, topic=pub.AUTO_TOPIC):
def onConnection(interface, topic=pub.AUTO_TOPIC): # pylint: disable=W0613
"""Callback invoked when we connect/disconnect from a radio"""
print(f"Connection changed: {topic.getName()}")
@@ -54,48 +55,73 @@ def onConnection(interface, topic=pub.AUTO_TOPIC):
def getPref(attributes, name):
"""Get a channel or preferences value"""
camel_name = meshtastic.util.snake_to_camel(name)
# Note: protobufs has the keys in snake_case, so snake internally
snake_name = meshtastic.util.camel_to_snake(name)
logging.debug(f'snake_name:{snake_name} camel_name:{camel_name}')
logging.debug(f'use camel:{Globals.getInstance().get_camel_case()}')
objDesc = attributes.DESCRIPTOR
field = objDesc.fields_by_name.get(name)
field = objDesc.fields_by_name.get(snake_name)
if not field:
print(f"{attributes.__class__.__name__} does not have an attribute called {name}, so you can not get it.")
if Globals.getInstance().get_camel_case():
print(f"{attributes.__class__.__name__} does not have an attribute called {camel_name}, so you can not get it.")
else:
print(f"{attributes.__class__.__name__} does not have an attribute called {snake_name}, so you can not get it.")
print(f"Choices in sorted order are:")
names = []
for f in objDesc.fields:
names.append(f'{f.name}')
tmp_name = f'{f.name}'
if Globals.getInstance().get_camel_case():
tmp_name = meshtastic.util.snake_to_camel(tmp_name)
names.append(tmp_name)
for temp_name in sorted(names):
print(f" {temp_name}")
return
# okay - try to read the value
try:
try:
val = getattr(attributes, name)
except TypeError:
# The getter didn't like our arg type guess try again as a string
val = getattr(attributes, name)
# read the value
val = getattr(attributes, snake_name)
# succeeded!
print(f"{name}: {str(val)}")
except Exception as ex:
print(f"Can't get {name} due to {ex}")
if Globals.getInstance().get_camel_case():
print(f"{camel_name}: {str(val)}")
logging.debug(f"{camel_name}: {str(val)}")
else:
print(f"{snake_name}: {str(val)}")
logging.debug(f"{snake_name}: {str(val)}")
def setPref(attributes, name, valStr):
"""Set a channel or preferences value"""
snake_name = meshtastic.util.camel_to_snake(name)
camel_name = meshtastic.util.snake_to_camel(name)
logging.debug(f'snake_name:{snake_name}')
logging.debug(f'camel_name:{camel_name}')
objDesc = attributes.DESCRIPTOR
field = objDesc.fields_by_name.get(name)
field = objDesc.fields_by_name.get(snake_name)
if not field:
print(f"{attributes.__class__.__name__} does not have an attribute called {name}, so you can not set it.")
if Globals.getInstance().get_camel_case():
print(f"{attributes.__class__.__name__} does not have an attribute called {camel_name}, so you can not set it.")
else:
print(f"{attributes.__class__.__name__} does not have an attribute called {snake_name}, so you can not set it.")
print(f"Choices in sorted order are:")
names = []
for f in objDesc.fields:
names.append(f'{f.name}')
tmp_name = f'{f.name}'
if Globals.getInstance().get_camel_case():
tmp_name = meshtastic.util.snake_to_camel(tmp_name)
names.append(tmp_name)
for temp_name in sorted(names):
print(f" {temp_name}")
return
val = meshtastic.util.fromStr(valStr)
logging.debug(f'valStr:{valStr} val:{val}')
if snake_name == 'wifi_password' and len(valStr) < 8:
print(f"Warning: wifi_password must be 8 or more characters.")
return
enumType = field.enum_type
# pylint: disable=C0123
@@ -105,27 +131,39 @@ def setPref(attributes, name, valStr):
if e:
val = e.number
else:
print(f"{name} does not have an enum called {val}, so you can not set it.")
if Globals.getInstance().get_camel_case():
print(f"{camel_name} does not have an enum called {val}, so you can not set it.")
else:
print(f"{snake_name} does not have an enum called {val}, so you can not set it.")
print(f"Choices in sorted order are:")
names = []
for f in enumType.values:
# Note: We must use the value of the enum (regardless if camel or snake case)
names.append(f'{f.name}')
for temp_name in sorted(names):
print(f" {temp_name}")
return
# okay - try to read the value
try:
# note: 'ignore_incoming' is a repeating field
if snake_name != 'ignore_incoming':
try:
setattr(attributes, name, val)
setattr(attributes, snake_name, val)
except TypeError:
# The setter didn't like our arg type guess try again as a string
setattr(attributes, name, valStr)
setattr(attributes, snake_name, valStr)
else:
if val == 0:
# clear values
print("Clearing ignore_incoming list")
del attributes.ignore_incoming[:]
else:
print(f"Adding '{val}' to the ignore_incoming list")
attributes.ignore_incoming.extend([val])
# succeeded!
print(f"Set {name} to {valStr}")
except Exception as ex:
print(f"Can't set {name} due to {ex}")
if Globals.getInstance().get_camel_case():
print(f"Set {camel_name} to {valStr}")
else:
print(f"Set {snake_name} to {valStr}")
def onConnected(interface):
@@ -139,14 +177,6 @@ def onConnected(interface):
if not args.export_config:
print("Connected to radio")
def getNode():
"""This operation could be expensive, so we try to cache the results"""
targetNode = our_globals.get_target_node()
if not targetNode:
targetNode = interface.getNode(args.destOrLocal)
our_globals.set_target_node(targetNode)
return targetNode
if args.setlat or args.setlon or args.setalt:
closeNow = True
@@ -178,12 +208,23 @@ def onConnected(interface):
if args.set_owner:
closeNow = True
print(f"Setting device owner to {args.set_owner}")
getNode().setOwner(args.set_owner)
interface.getNode(args.dest).setOwner(args.set_owner)
if args.set_owner_short:
closeNow = True
print(f"Setting device owner short to {args.set_owner_short}")
interface.getNode(args.dest).setOwner(long_name=None, short_name=args.set_owner_short)
# TODO: add to export-config and configure
if args.set_canned_message:
closeNow = True
print(f"Setting canned plugin message to {args.set_canned_message}")
interface.getNode(args.dest).set_canned_message(args.set_canned_message)
if args.pos_fields:
# If --pos-fields invoked with args, set position fields
closeNow = True
prefs = getNode().radioConfig.preferences
prefs = interface.getNode(args.dest).radioConfig.preferences
allFields = 0
try:
@@ -198,14 +239,14 @@ def onConnected(interface):
else:
print(f"Setting position fields to {allFields}")
setPref(prefs, 'position_flags', ('%d' % allFields))
setPref(prefs, 'position_flags', f'{allFields:d}')
print("Writing modified preferences to device")
getNode().writeConfig()
interface.getNode(args.dest).writeConfig()
elif args.pos_fields is not None:
# If --pos-fields invoked without args, read and display current value
closeNow = True
prefs = getNode().radioConfig.preferences
prefs = interface.getNode(args.dest).radioConfig.preferences
fieldNames = []
for bit in radioconfig_pb2.PositionFlags.values():
@@ -224,81 +265,88 @@ def onConnected(interface):
print(sorted(meshtastic.mesh_pb2.Team.keys()))
else:
print(f"Setting team to {meshtastic.mesh_pb2.Team.Name(v_team)}")
getNode().setOwner(team=v_team)
interface.getNode(args.dest).setOwner(team=v_team)
if args.set_ham:
closeNow = True
print(f"Setting Ham ID to {args.set_ham} and turning off encryption")
getNode().setOwner(args.set_ham, is_licensed=True)
interface.getNode(args.dest).setOwner(args.set_ham, is_licensed=True)
# Must turn off encryption on primary channel
getNode().turnOffEncryptionOnPrimaryChannel()
interface.getNode(args.dest).turnOffEncryptionOnPrimaryChannel()
if args.reboot:
closeNow = True
getNode().reboot()
interface.getNode(args.dest).reboot()
if args.shutdown:
closeNow = True
interface.getNode(args.dest).shutdown()
if args.sendtext:
closeNow = True
channelIndex = 0
if args.ch_index is not None:
channelIndex = int(args.ch_index)
ch = getNode().getChannelByChannelIndex(channelIndex)
ch = interface.localNode.getChannelByChannelIndex(channelIndex)
logging.debug(f'ch:{ch}')
if ch and ch.role != channel_pb2.Channel.Role.DISABLED:
print(f"Sending text message {args.sendtext} to {args.destOrAll} on channelIndex:{channelIndex}")
interface.sendText(args.sendtext, args.destOrAll, wantAck=True, channelIndex=channelIndex)
print(f"Sending text message {args.sendtext} to {args.dest} on channelIndex:{channelIndex}")
interface.sendText(args.sendtext, args.dest, wantAck=True, channelIndex=channelIndex)
else:
meshtastic.util.our_exit(f"Warning: {channelIndex} is not a valid channel. Channel must not be DISABLED.")
if args.sendping:
payload = str.encode("test string")
print(f"Sending ping message to {args.destOrAll}")
interface.sendData(payload, args.destOrAll, portNum=portnums_pb2.PortNum.REPLY_APP,
print(f"Sending ping message to {args.dest}")
interface.sendData(payload, args.dest, portNum=portnums_pb2.PortNum.REPLY_APP,
wantAck=True, wantResponse=True)
if args.gpio_wrb or args.gpio_rd or args.gpio_watch:
rhc = remote_hardware.RemoteHardwareClient(interface)
if args.dest == BROADCAST_ADDR:
meshtastic.util.our_exit("Warning: Must use a destination node ID.")
else:
rhc = remote_hardware.RemoteHardwareClient(interface)
if args.gpio_wrb:
bitmask = 0
bitval = 0
for wrpair in (args.gpio_wrb or []):
bitmask |= 1 << int(wrpair[0])
bitval |= int(wrpair[1]) << int(wrpair[0])
print(f"Writing GPIO mask 0x{bitmask:x} with value 0x{bitval:x} to {args.dest}")
rhc.writeGPIOs(args.dest, bitmask, bitval)
closeNow = True
if args.gpio_wrb:
bitmask = 0
bitval = 0
for wrpair in (args.gpio_wrb or []):
bitmask |= 1 << int(wrpair[0])
bitval |= int(wrpair[1]) << int(wrpair[0])
print(f"Writing GPIO mask 0x{bitmask:x} with value 0x{bitval:x} to {args.dest}")
rhc.writeGPIOs(args.dest, bitmask, bitval)
closeNow = True
if args.gpio_rd:
bitmask = int(args.gpio_rd, 16)
print(f"Reading GPIO mask 0x{bitmask:x} from {args.dest}")
interface.mask = bitmask
rhc.readGPIOs(args.dest, bitmask, None)
if not interface.noProto:
if args.gpio_rd:
bitmask = int(args.gpio_rd, 16)
print(f"Reading GPIO mask 0x{bitmask:x} from {args.dest}")
interface.mask = bitmask
rhc.readGPIOs(args.dest, bitmask, None)
# wait up to X seconds for a response
for _ in range(10):
time.sleep(1)
if interface.gotResponse:
break
logging.debug(f'end of gpio_rd')
logging.debug(f'end of gpio_rd')
if args.gpio_watch:
bitmask = int(args.gpio_watch, 16)
print(f"Watching GPIO mask 0x{bitmask:x} from {args.dest}. Press ctrl-c to exit")
while True:
rhc.watchGPIOs(args.dest, bitmask)
time.sleep(1)
if args.gpio_watch:
bitmask = int(args.gpio_watch, 16)
print(f"Watching GPIO mask 0x{bitmask:x} from {args.dest}. Press ctrl-c to exit")
while True:
rhc.watchGPIOs(args.dest, bitmask)
time.sleep(1)
# handle settings
if args.set:
closeNow = True
prefs = getNode().radioConfig.preferences
prefs = interface.getNode(args.dest).radioConfig.preferences
# Handle the int/float/bool arguments
for pref in args.set:
setPref(prefs, pref[0], pref[1])
print("Writing modified preferences to device")
getNode().writeConfig()
interface.getNode(args.dest).writeConfig()
if args.configure:
with open(args.configure[0], encoding='utf8') as file:
@@ -307,11 +355,23 @@ def onConnected(interface):
if 'owner' in configuration:
print(f"Setting device owner to {configuration['owner']}")
getNode().setOwner(configuration['owner'])
interface.getNode(args.dest).setOwner(configuration['owner'])
if 'owner_short' in configuration:
print(f"Setting device owner short to {configuration['owner_short']}")
interface.getNode(args.dest).setOwner(long_name=None, short_name=configuration['owner_short'])
if 'ownerShort' in configuration:
print(f"Setting device owner short to {configuration['ownerShort']}")
interface.getNode(args.dest).setOwner(long_name=None, short_name=configuration['ownerShort'])
if 'channel_url' in configuration:
print("Setting channel url to", configuration['channel_url'])
getNode().setURL(configuration['channel_url'])
interface.getNode(args.dest).setURL(configuration['channel_url'])
if 'channelUrl' in configuration:
print("Setting channel url to", configuration['channelUrl'])
interface.getNode(args.dest).setURL(configuration['channelUrl'])
if 'location' in configuration:
alt = 0
@@ -336,11 +396,18 @@ def onConnected(interface):
interface.localNode.writeConfig()
if 'user_prefs' in configuration:
prefs = getNode().radioConfig.preferences
prefs = interface.getNode(args.dest).radioConfig.preferences
for pref in configuration['user_prefs']:
setPref(prefs, pref, str(configuration['user_prefs'][pref]))
print("Writing modified preferences to device")
getNode().writeConfig()
interface.getNode(args.dest).writeConfig()
if 'userPrefs' in configuration:
prefs = interface.getNode(args.dest).radioConfig.preferences
for pref in configuration['userPrefs']:
setPref(prefs, pref, str(configuration['userPrefs'][pref]))
print("Writing modified preferences to device")
interface.getNode(args.dest).writeConfig()
if args.export_config:
# export the configuration (the opposite of '--configure')
@@ -349,7 +416,7 @@ def onConnected(interface):
if args.seturl:
closeNow = True
getNode().setURL(args.seturl)
interface.getNode(args.dest).setURL(args.seturl)
# handle changing channels
@@ -357,7 +424,7 @@ def onConnected(interface):
closeNow = True
if len(args.ch_add) > 10:
meshtastic.util.our_exit("Warning: Channel name must be shorter. Channel not added.")
n = getNode()
n = interface.getNode(args.dest)
ch = n.getChannelByName(args.ch_add)
if ch:
meshtastic.util.our_exit(f"Warning: This node already has a '{args.ch_add}' channel. No changes were made.")
@@ -385,10 +452,10 @@ def onConnected(interface):
meshtastic.util.our_exit("Warning: Cannot delete primary channel.", 1)
else:
print(f"Deleting channel {channelIndex}")
ch = getNode().deleteChannel(channelIndex)
ch = interface.getNode(args.dest).deleteChannel(channelIndex)
ch_changes = [args.ch_longslow, args.ch_longfast,
args.ch_mediumslow, args.ch_mediumfast,
ch_changes = [args.ch_vlongslow, args.ch_longslow, args.ch_longfast,
args.ch_midslow, args.ch_midfast,
args.ch_shortslow, args.ch_shortfast]
any_primary_channel_changes = any(x for x in ch_changes)
if args.ch_set or any_primary_channel_changes or args.ch_enable or args.ch_disable:
@@ -401,7 +468,7 @@ def onConnected(interface):
channelIndex = 0
else:
meshtastic.util.our_exit("Warning: Need to specify '--ch-index'.", 1)
ch = getNode().channels[channelIndex]
ch = interface.getNode(args.dest).channels[channelIndex]
if any_primary_channel_changes or args.ch_enable or args.ch_disable:
@@ -429,23 +496,26 @@ def onConnected(interface):
ch.settings.CopyFrom(chs)
# handle the simple channel set commands
if args.ch_vlongslow:
setSimpleChannel(channel_pb2.ChannelSettings.ModemConfig.VLongSlow)
if args.ch_longslow:
setSimpleChannel(channel_pb2.ChannelSettings.ModemConfig.Bw125Cr48Sf4096)
setSimpleChannel(channel_pb2.ChannelSettings.ModemConfig.LongSlow)
if args.ch_longfast:
setSimpleChannel(channel_pb2.ChannelSettings.ModemConfig.Bw31_25Cr48Sf512)
setSimpleChannel(channel_pb2.ChannelSettings.ModemConfig.LongFast)
if args.ch_mediumslow:
setSimpleChannel(channel_pb2.ChannelSettings.ModemConfig.Bw250Cr46Sf2048)
if args.ch_midslow:
setSimpleChannel(channel_pb2.ChannelSettings.ModemConfig.MidSlow)
if args.ch_mediumfast:
setSimpleChannel(channel_pb2.ChannelSettings.ModemConfig.Bw250Cr47Sf1024)
if args.ch_midfast:
setSimpleChannel(channel_pb2.ChannelSettings.ModemConfig.MidFast)
if args.ch_shortslow:
setSimpleChannel(channel_pb2.ChannelSettings.ModemConfig.Bw125Cr45Sf128)
setSimpleChannel(channel_pb2.ChannelSettings.ModemConfig.ShortSlow)
if args.ch_shortfast:
setSimpleChannel(channel_pb2.ChannelSettings.ModemConfig.Bw500Cr45Sf128)
setSimpleChannel(channel_pb2.ChannelSettings.ModemConfig.ShortFast)
# Handle the channel settings
for pref in (args.ch_set or []):
@@ -462,21 +532,27 @@ def onConnected(interface):
ch.role = channel_pb2.Channel.Role.DISABLED
print(f"Writing modified channels to device")
getNode().writeChannel(channelIndex)
interface.getNode(args.dest).writeChannel(channelIndex)
if args.get_canned_message:
closeNow = True
print("")
interface.getNode(args.dest).get_canned_message()
if args.info:
print("")
if not args.dest: # If we aren't trying to talk to our local node, don't show it
# If we aren't trying to talk to our local node, don't show it
if args.dest == BROADCAST_ADDR:
interface.showInfo()
print("")
getNode().showInfo()
interface.getNode(args.dest).showInfo()
closeNow = True # FIXME, for now we leave the link up while talking to remote nodes
print("")
if args.get:
closeNow = True
prefs = getNode().radioConfig.preferences
prefs = interface.getNode(args.dest).radioConfig.preferences
# Handle the int/float/bool arguments
for pref in args.get:
@@ -534,6 +610,7 @@ def subscribe():
def export_config(interface):
"""used in--export-config"""
owner = interface.getLongName()
owner_short = interface.getShortName()
channel_url = interface.localNode.getURL()
myinfo = interface.getMyNodeInfo()
pos = myinfo.get('position')
@@ -548,8 +625,13 @@ def export_config(interface):
config = "# start of Meshtastic configure yaml\n"
if owner:
config += f"owner: {owner}\n\n"
if owner_short:
config += f"owner_short: {owner_short}\n\n"
if channel_url:
config += f"channel_url: {channel_url}\n\n"
if Globals.getInstance().get_camel_case():
config += f"channelUrl: {channel_url}\n\n"
else:
config += f"channel_url: {channel_url}\n\n"
if lat or lon or alt:
config += "location:\n"
if lat:
@@ -562,9 +644,16 @@ def export_config(interface):
preferences = f'{interface.localNode.radioConfig.preferences}'
prefs = preferences.splitlines()
if prefs:
config += "user_prefs:\n"
if Globals.getInstance().get_camel_case():
config += "userPrefs:\n"
else:
config += "user_prefs:\n"
for pref in prefs:
config += f" {meshtastic.util.quoteBooleans(pref)}\n"
if Globals.getInstance().get_camel_case():
# Note: This may not work if the value has '_'
config += f" {meshtastic.util.snake_to_camel(meshtastic.util.quoteBooleans(pref))}\n"
else:
config += f" {meshtastic.util.quoteBooleans(pref)}\n"
print(config)
return config
@@ -590,13 +679,8 @@ def common():
channelIndex = int(args.ch_index)
our_globals.set_channel_index(channelIndex)
# Some commands require dest to be set, so we now use destOrAll/destOrLocal for more lenient commands
if not args.dest:
args.destOrAll = "^all"
args.destOrLocal = "^local"
else:
args.destOrAll = args.dest
args.destOrLocal = args.dest # FIXME, temp hack for debugging remove
args.dest = BROADCAST_ADDR
if not args.seriallog:
if args.noproto:
@@ -635,12 +719,19 @@ def common():
elif args.host:
client = meshtastic.tcp_interface.TCPInterface(args.host, debugOut=logfile, noProto=args.noproto)
else:
client = meshtastic.serial_interface.SerialInterface(args.port, debugOut=logfile, noProto=args.noproto)
try:
client = meshtastic.serial_interface.SerialInterface(args.port, debugOut=logfile, noProto=args.noproto)
except PermissionError as ex:
username = os.getlogin()
message = "Permission Error:\n"
message += " Need to add yourself to the 'dialout' group by running:\n"
message += f" sudo usermod -a -G dialout {username}\n"
message += " After running that command, log out and re-login for it to take effect.\n"
message += f"Error was:{ex}"
meshtastic.util.our_exit(message)
# We assume client is fully connected now
onConnected(client)
#if logfile:
#logfile.close()
have_tunnel = platform.system() == 'Linux'
if args.noproto or args.reply or (have_tunnel and args.tunnel): # loop until someone presses ctrlc
@@ -684,6 +775,9 @@ def initParser():
parser.add_argument("--info", help="Read and display the radio config information",
action="store_true")
parser.add_argument("--get-canned-message", help="Show the canned message plugin message",
action="store_true")
parser.add_argument("--nodes", help="Print Node List in a pretty formatted table",
action="store_true")
@@ -691,10 +785,12 @@ def initParser():
action="store_true")
parser.add_argument(
"--get", help="Get a preferences field. Use an invalid field such as '0' to get a list of all fields.", nargs=1, action='append')
"--get", help=("Get a preferences field. Use an invalid field such as '0' to get a list of all fields."
" Can use either snake_case or camelCase format. (ex: 'ls_secs' or 'lsSecs')"),
nargs=1, action='append')
parser.add_argument(
"--set", help="Set a preferences field", nargs=2, action='append')
"--set", help="Set a preferences field. Can use either snake_case or camelCase format. (ex: 'ls_secs' or 'lsSecs')", nargs=2, action='append')
parser.add_argument(
"--seturl", help="Set a channel URL", action="store")
@@ -716,7 +812,15 @@ def initParser():
"--ch-disable", help="Disable the specified channel", action="store_true", dest="ch_disable", default=False)
parser.add_argument(
"--ch-set", help="Set a channel parameter", nargs=2, action='append')
"--ch-set", help=("Set a channel parameter. To see channel settings available:'--ch-set all all --ch-index 0'. "
"Can set the 'psk' using this command. To disable encryption on primary channel:'--ch-set psk none --ch-index 0'. "
"To set encryption with a new random key on second channel:'--ch-set psk random --ch-index 1'. "
"To set encryption back to the default:'--ch-set default --ch-index 0'. To set encryption with your "
"own key: '--ch-set psk 0x1a1a1a1a2b2b2b2b1a1a1a1a2b2b2b2b1a1a1a1a2b2b2b2b1a1a1a1a2b2b2b2b --ch-index 0'."),
nargs=2, action='append')
parser.add_argument(
"--ch-vlongslow", help="Change to the very long-range and slow channel", action='store_true')
parser.add_argument(
"--ch-longslow", help="Change to the long-range and slow channel", action='store_true')
@@ -725,10 +829,10 @@ def initParser():
"--ch-longfast", help="Change to the long-range and fast channel", action='store_true')
parser.add_argument(
"--ch-mediumslow", help="Change to the medium-range and slow channel", action='store_true')
"--ch-midslow", help="Change to the mid-range and slow channel", action='store_true')
parser.add_argument(
"--ch-mediumfast", help="Change to the medium-range and fast channel", action='store_true')
"--ch-midfast", help="Change to the mid-range and fast channel", action='store_true')
parser.add_argument(
"--ch-shortslow", help="Change to the short-range and slow channel", action='store_true')
@@ -736,10 +840,15 @@ def initParser():
parser.add_argument(
"--ch-shortfast", help="Change to the short-range and fast channel", action='store_true')
parser.add_argument(
"--set-owner", help="Set device owner name", action="store")
parser.add_argument(
"--set-canned-message", help="Set the canned messages plugin message (up to 1000 characters).", action="store")
parser.add_argument(
"--set-owner-short", help="Set device owner short name", action="store")
parser.add_argument(
"--set-team", help="Set team affiliation (an invalid team will list valid values)", action="store")
@@ -758,6 +867,9 @@ def initParser():
parser.add_argument(
"--reboot", help="Tell the destination node to reboot", action="store_true")
parser.add_argument(
"--shutdown", help="Tell the destination node to shutdown", action="store_true")
parser.add_argument(
"--reply", help="Reply to received messages",
action="store_true")
@@ -818,8 +930,8 @@ def initParser():
parser.set_defaults(deprecated=None)
parser.add_argument('--version', action='version',
version=f"{pkg_resources.require('meshtastic')[0].version}")
the_version = pkg_resources.get_distribution("meshtastic").version
parser.add_argument('--version', action='version', version=f"{the_version}")
parser.add_argument(
"--support", action='store_true', help="Show support info (useful when troubleshooting an issue)")

View File

@@ -12,8 +12,8 @@ _sym_db = _symbol_database.Default()
from . import channel_pb2 as channel__pb2
from . import radioconfig_pb2 as radioconfig__pb2
from . import mesh_pb2 as mesh__pb2
from . import radioconfig_pb2 as radioconfig__pb2
DESCRIPTOR = _descriptor.FileDescriptor(
@@ -21,9 +21,9 @@ DESCRIPTOR = _descriptor.FileDescriptor(
package='',
syntax='proto3',
serialized_options=b'\n\023com.geeksville.meshB\013AdminProtosH\003Z!github.com/meshtastic/gomeshproto',
serialized_pb=b'\n\x0b\x61\x64min.proto\x1a\rchannel.proto\x1a\x11radioconfig.proto\x1a\nmesh.proto\"\xbd\x03\n\x0c\x41\x64minMessage\x12!\n\tset_radio\x18\x01 \x01(\x0b\x32\x0c.RadioConfigH\x00\x12\x1a\n\tset_owner\x18\x02 \x01(\x0b\x32\x05.UserH\x00\x12\x1f\n\x0bset_channel\x18\x03 \x01(\x0b\x32\x08.ChannelH\x00\x12\x1b\n\x11get_radio_request\x18\x04 \x01(\x08H\x00\x12*\n\x12get_radio_response\x18\x05 \x01(\x0b\x32\x0c.RadioConfigH\x00\x12\x1d\n\x13get_channel_request\x18\x06 \x01(\rH\x00\x12(\n\x14get_channel_response\x18\x07 \x01(\x0b\x32\x08.ChannelH\x00\x12\x1b\n\x11get_owner_request\x18\x08 \x01(\x08H\x00\x12#\n\x12get_owner_response\x18\t \x01(\x0b\x32\x05.UserH\x00\x12\x1d\n\x13\x63onfirm_set_channel\x18 \x01(\x08H\x00\x12\x1b\n\x11\x63onfirm_set_radio\x18! \x01(\x08H\x00\x12\x18\n\x0e\x65xit_simulator\x18\" \x01(\x08H\x00\x12\x18\n\x0ereboot_seconds\x18# \x01(\x05H\x00\x42\t\n\x07variantBG\n\x13\x63om.geeksville.meshB\x0b\x41\x64minProtosH\x03Z!github.com/meshtastic/gomeshprotob\x06proto3'
serialized_pb=b'\n\x0b\x61\x64min.proto\x1a\rchannel.proto\x1a\nmesh.proto\x1a\x11radioconfig.proto\"\xa1\x08\n\x0c\x41\x64minMessage\x12!\n\tset_radio\x18\x01 \x01(\x0b\x32\x0c.RadioConfigH\x00\x12\x1a\n\tset_owner\x18\x02 \x01(\x0b\x32\x05.UserH\x00\x12\x1f\n\x0bset_channel\x18\x03 \x01(\x0b\x32\x08.ChannelH\x00\x12\x1b\n\x11get_radio_request\x18\x04 \x01(\x08H\x00\x12*\n\x12get_radio_response\x18\x05 \x01(\x0b\x32\x0c.RadioConfigH\x00\x12\x1d\n\x13get_channel_request\x18\x06 \x01(\rH\x00\x12(\n\x14get_channel_response\x18\x07 \x01(\x0b\x32\x08.ChannelH\x00\x12\x1b\n\x11get_owner_request\x18\x08 \x01(\x08H\x00\x12#\n\x12get_owner_response\x18\t \x01(\x0b\x32\x05.UserH\x00\x12\x1d\n\x13\x63onfirm_set_channel\x18 \x01(\x08H\x00\x12\x1b\n\x11\x63onfirm_set_radio\x18! \x01(\x08H\x00\x12\x18\n\x0e\x65xit_simulator\x18\" \x01(\x08H\x00\x12\x18\n\x0ereboot_seconds\x18# \x01(\x05H\x00\x12\x31\n\'get_canned_message_module_part1_request\x18$ \x01(\x08H\x00\x12\x32\n(get_canned_message_module_part1_response\x18% \x01(\tH\x00\x12\x31\n\'get_canned_message_module_part2_request\x18& \x01(\x08H\x00\x12\x32\n(get_canned_message_module_part2_response\x18\' \x01(\tH\x00\x12\x31\n\'get_canned_message_module_part3_request\x18( \x01(\x08H\x00\x12\x32\n(get_canned_message_module_part3_response\x18) \x01(\tH\x00\x12\x31\n\'get_canned_message_module_part4_request\x18* \x01(\x08H\x00\x12\x32\n(get_canned_message_module_part4_response\x18+ \x01(\tH\x00\x12)\n\x1fset_canned_message_module_part1\x18, \x01(\tH\x00\x12)\n\x1fset_canned_message_module_part2\x18- \x01(\tH\x00\x12)\n\x1fset_canned_message_module_part3\x18. \x01(\tH\x00\x12)\n\x1fset_canned_message_module_part4\x18/ \x01(\tH\x00\x12\x1a\n\x10shutdown_seconds\x18\x33 \x01(\x05H\x00\x42\t\n\x07variantBG\n\x13\x63om.geeksville.meshB\x0b\x41\x64minProtosH\x03Z!github.com/meshtastic/gomeshprotob\x06proto3'
,
dependencies=[channel__pb2.DESCRIPTOR,radioconfig__pb2.DESCRIPTOR,mesh__pb2.DESCRIPTOR,])
dependencies=[channel__pb2.DESCRIPTOR,mesh__pb2.DESCRIPTOR,radioconfig__pb2.DESCRIPTOR,])
@@ -126,6 +126,97 @@ _ADMINMESSAGE = _descriptor.Descriptor(
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='get_canned_message_module_part1_request', full_name='AdminMessage.get_canned_message_module_part1_request', index=13,
number=36, type=8, cpp_type=7, label=1,
has_default_value=False, default_value=False,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='get_canned_message_module_part1_response', full_name='AdminMessage.get_canned_message_module_part1_response', index=14,
number=37, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=b"".decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='get_canned_message_module_part2_request', full_name='AdminMessage.get_canned_message_module_part2_request', index=15,
number=38, type=8, cpp_type=7, label=1,
has_default_value=False, default_value=False,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='get_canned_message_module_part2_response', full_name='AdminMessage.get_canned_message_module_part2_response', index=16,
number=39, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=b"".decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='get_canned_message_module_part3_request', full_name='AdminMessage.get_canned_message_module_part3_request', index=17,
number=40, type=8, cpp_type=7, label=1,
has_default_value=False, default_value=False,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='get_canned_message_module_part3_response', full_name='AdminMessage.get_canned_message_module_part3_response', index=18,
number=41, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=b"".decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='get_canned_message_module_part4_request', full_name='AdminMessage.get_canned_message_module_part4_request', index=19,
number=42, type=8, cpp_type=7, label=1,
has_default_value=False, default_value=False,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='get_canned_message_module_part4_response', full_name='AdminMessage.get_canned_message_module_part4_response', index=20,
number=43, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=b"".decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='set_canned_message_module_part1', full_name='AdminMessage.set_canned_message_module_part1', index=21,
number=44, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=b"".decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='set_canned_message_module_part2', full_name='AdminMessage.set_canned_message_module_part2', index=22,
number=45, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=b"".decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='set_canned_message_module_part3', full_name='AdminMessage.set_canned_message_module_part3', index=23,
number=46, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=b"".decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='set_canned_message_module_part4', full_name='AdminMessage.set_canned_message_module_part4', index=24,
number=47, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=b"".decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='shutdown_seconds', full_name='AdminMessage.shutdown_seconds', index=25,
number=51, type=5, cpp_type=1, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
],
extensions=[
],
@@ -142,7 +233,7 @@ _ADMINMESSAGE = _descriptor.Descriptor(
index=0, containing_type=None, fields=[]),
],
serialized_start=62,
serialized_end=507,
serialized_end=1119,
)
_ADMINMESSAGE.fields_by_name['set_radio'].message_type = radioconfig__pb2._RADIOCONFIG
@@ -190,6 +281,45 @@ _ADMINMESSAGE.fields_by_name['exit_simulator'].containing_oneof = _ADMINMESSAGE.
_ADMINMESSAGE.oneofs_by_name['variant'].fields.append(
_ADMINMESSAGE.fields_by_name['reboot_seconds'])
_ADMINMESSAGE.fields_by_name['reboot_seconds'].containing_oneof = _ADMINMESSAGE.oneofs_by_name['variant']
_ADMINMESSAGE.oneofs_by_name['variant'].fields.append(
_ADMINMESSAGE.fields_by_name['get_canned_message_module_part1_request'])
_ADMINMESSAGE.fields_by_name['get_canned_message_module_part1_request'].containing_oneof = _ADMINMESSAGE.oneofs_by_name['variant']
_ADMINMESSAGE.oneofs_by_name['variant'].fields.append(
_ADMINMESSAGE.fields_by_name['get_canned_message_module_part1_response'])
_ADMINMESSAGE.fields_by_name['get_canned_message_module_part1_response'].containing_oneof = _ADMINMESSAGE.oneofs_by_name['variant']
_ADMINMESSAGE.oneofs_by_name['variant'].fields.append(
_ADMINMESSAGE.fields_by_name['get_canned_message_module_part2_request'])
_ADMINMESSAGE.fields_by_name['get_canned_message_module_part2_request'].containing_oneof = _ADMINMESSAGE.oneofs_by_name['variant']
_ADMINMESSAGE.oneofs_by_name['variant'].fields.append(
_ADMINMESSAGE.fields_by_name['get_canned_message_module_part2_response'])
_ADMINMESSAGE.fields_by_name['get_canned_message_module_part2_response'].containing_oneof = _ADMINMESSAGE.oneofs_by_name['variant']
_ADMINMESSAGE.oneofs_by_name['variant'].fields.append(
_ADMINMESSAGE.fields_by_name['get_canned_message_module_part3_request'])
_ADMINMESSAGE.fields_by_name['get_canned_message_module_part3_request'].containing_oneof = _ADMINMESSAGE.oneofs_by_name['variant']
_ADMINMESSAGE.oneofs_by_name['variant'].fields.append(
_ADMINMESSAGE.fields_by_name['get_canned_message_module_part3_response'])
_ADMINMESSAGE.fields_by_name['get_canned_message_module_part3_response'].containing_oneof = _ADMINMESSAGE.oneofs_by_name['variant']
_ADMINMESSAGE.oneofs_by_name['variant'].fields.append(
_ADMINMESSAGE.fields_by_name['get_canned_message_module_part4_request'])
_ADMINMESSAGE.fields_by_name['get_canned_message_module_part4_request'].containing_oneof = _ADMINMESSAGE.oneofs_by_name['variant']
_ADMINMESSAGE.oneofs_by_name['variant'].fields.append(
_ADMINMESSAGE.fields_by_name['get_canned_message_module_part4_response'])
_ADMINMESSAGE.fields_by_name['get_canned_message_module_part4_response'].containing_oneof = _ADMINMESSAGE.oneofs_by_name['variant']
_ADMINMESSAGE.oneofs_by_name['variant'].fields.append(
_ADMINMESSAGE.fields_by_name['set_canned_message_module_part1'])
_ADMINMESSAGE.fields_by_name['set_canned_message_module_part1'].containing_oneof = _ADMINMESSAGE.oneofs_by_name['variant']
_ADMINMESSAGE.oneofs_by_name['variant'].fields.append(
_ADMINMESSAGE.fields_by_name['set_canned_message_module_part2'])
_ADMINMESSAGE.fields_by_name['set_canned_message_module_part2'].containing_oneof = _ADMINMESSAGE.oneofs_by_name['variant']
_ADMINMESSAGE.oneofs_by_name['variant'].fields.append(
_ADMINMESSAGE.fields_by_name['set_canned_message_module_part3'])
_ADMINMESSAGE.fields_by_name['set_canned_message_module_part3'].containing_oneof = _ADMINMESSAGE.oneofs_by_name['variant']
_ADMINMESSAGE.oneofs_by_name['variant'].fields.append(
_ADMINMESSAGE.fields_by_name['set_canned_message_module_part4'])
_ADMINMESSAGE.fields_by_name['set_canned_message_module_part4'].containing_oneof = _ADMINMESSAGE.oneofs_by_name['variant']
_ADMINMESSAGE.oneofs_by_name['variant'].fields.append(
_ADMINMESSAGE.fields_by_name['shutdown_seconds'])
_ADMINMESSAGE.fields_by_name['shutdown_seconds'].containing_oneof = _ADMINMESSAGE.oneofs_by_name['variant']
DESCRIPTOR.message_types_by_name['AdminMessage'] = _ADMINMESSAGE
_sym_db.RegisterFileDescriptor(DESCRIPTOR)

View File

@@ -1,10 +1,16 @@
"""Bluetooth interface
"""
import logging
import pygatt
import platform
from meshtastic.mesh_interface import MeshInterface
from meshtastic.util import our_exit
if platform.system() == 'Linux':
# pylint: disable=E0401
import pygatt
from .mesh_interface import MeshInterface
# Our standard BLE characteristics
TORADIO_UUID = "f75c76d2-129e-4dad-a1dd-7866124401e7"
@@ -16,6 +22,8 @@ class BLEInterface(MeshInterface):
"""A not quite ready - FIXME - BLE interface to devices"""
def __init__(self, address, noProto=False, debugOut=None):
if platform.system() != 'Linux':
our_exit("Linux is the only platform with experimental BLE support.", 1)
self.address = address
if not noProto:
self.adapter = pygatt.GATTToolBackend() # BGAPIBackend()
@@ -31,7 +39,7 @@ class BLEInterface(MeshInterface):
self._readFromRadio() # read the initial responses
def handle_data(handle, data):
def handle_data(handle, data): # pylint: disable=W0613
self._handleFromRadio(data)
if self.device:

View File

@@ -0,0 +1,90 @@
# -*- coding: utf-8 -*-
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: cannedmessages.proto
from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
from google.protobuf import reflection as _reflection
from google.protobuf import symbol_database as _symbol_database
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor.FileDescriptor(
name='cannedmessages.proto',
package='',
syntax='proto3',
serialized_options=b'\n\023com.geeksville.meshB\031CannedMessageConfigProtosH\003Z!github.com/meshtastic/gomeshproto',
serialized_pb=b'\n\x14\x63\x61nnedmessages.proto\"w\n\x19\x43\x61nnedMessageModuleConfig\x12\x15\n\rmessagesPart1\x18\x0b \x01(\t\x12\x15\n\rmessagesPart2\x18\x0c \x01(\t\x12\x15\n\rmessagesPart3\x18\r \x01(\t\x12\x15\n\rmessagesPart4\x18\x0e \x01(\tBU\n\x13\x63om.geeksville.meshB\x19\x43\x61nnedMessageConfigProtosH\x03Z!github.com/meshtastic/gomeshprotob\x06proto3'
)
_CANNEDMESSAGEMODULECONFIG = _descriptor.Descriptor(
name='CannedMessageModuleConfig',
full_name='CannedMessageModuleConfig',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='messagesPart1', full_name='CannedMessageModuleConfig.messagesPart1', index=0,
number=11, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=b"".decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='messagesPart2', full_name='CannedMessageModuleConfig.messagesPart2', index=1,
number=12, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=b"".decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='messagesPart3', full_name='CannedMessageModuleConfig.messagesPart3', index=2,
number=13, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=b"".decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='messagesPart4', full_name='CannedMessageModuleConfig.messagesPart4', index=3,
number=14, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=b"".decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
],
extensions=[
],
nested_types=[],
enum_types=[
],
serialized_options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
serialized_start=24,
serialized_end=143,
)
DESCRIPTOR.message_types_by_name['CannedMessageModuleConfig'] = _CANNEDMESSAGEMODULECONFIG
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
CannedMessageModuleConfig = _reflection.GeneratedProtocolMessageType('CannedMessageModuleConfig', (_message.Message,), {
'DESCRIPTOR' : _CANNEDMESSAGEMODULECONFIG,
'__module__' : 'cannedmessages_pb2'
# @@protoc_insertion_point(class_scope:CannedMessageModuleConfig)
})
_sym_db.RegisterMessage(CannedMessageModuleConfig)
DESCRIPTOR._options = None
# @@protoc_insertion_point(module_scope)

View File

@@ -18,7 +18,7 @@ DESCRIPTOR = _descriptor.FileDescriptor(
package='',
syntax='proto3',
serialized_options=b'\n\023com.geeksville.meshB\rChannelProtosH\003Z!github.com/meshtastic/gomeshproto',
serialized_pb=b'\n\rchannel.proto\"\x91\x03\n\x0f\x43hannelSettings\x12\x10\n\x08tx_power\x18\x01 \x01(\x05\x12\x32\n\x0cmodem_config\x18\x03 \x01(\x0e\x32\x1c.ChannelSettings.ModemConfig\x12\x11\n\tbandwidth\x18\x06 \x01(\r\x12\x15\n\rspread_factor\x18\x07 \x01(\r\x12\x13\n\x0b\x63oding_rate\x18\x08 \x01(\r\x12\x13\n\x0b\x63hannel_num\x18\t \x01(\r\x12\x0b\n\x03psk\x18\x04 \x01(\x0c\x12\x0c\n\x04name\x18\x05 \x01(\t\x12\n\n\x02id\x18\n \x01(\x07\x12\x16\n\x0euplink_enabled\x18\x10 \x01(\x08\x12\x18\n\x10\x64ownlink_enabled\x18\x11 \x01(\x08\"\x8a\x01\n\x0bModemConfig\x12\x12\n\x0e\x42w125Cr45Sf128\x10\x00\x12\x12\n\x0e\x42w500Cr45Sf128\x10\x01\x12\x14\n\x10\x42w31_25Cr48Sf512\x10\x02\x12\x13\n\x0f\x42w125Cr48Sf4096\x10\x03\x12\x13\n\x0f\x42w250Cr46Sf2048\x10\x04\x12\x13\n\x0f\x42w250Cr47Sf1024\x10\x05\"\x8b\x01\n\x07\x43hannel\x12\r\n\x05index\x18\x01 \x01(\x05\x12\"\n\x08settings\x18\x02 \x01(\x0b\x32\x10.ChannelSettings\x12\x1b\n\x04role\x18\x03 \x01(\x0e\x32\r.Channel.Role\"0\n\x04Role\x12\x0c\n\x08\x44ISABLED\x10\x00\x12\x0b\n\x07PRIMARY\x10\x01\x12\r\n\tSECONDARY\x10\x02\x42I\n\x13\x63om.geeksville.meshB\rChannelProtosH\x03Z!github.com/meshtastic/gomeshprotob\x06proto3'
serialized_pb=b'\n\rchannel.proto\"\xf6\x02\n\x0f\x43hannelSettings\x12\x10\n\x08tx_power\x18\x01 \x01(\x05\x12\x32\n\x0cmodem_config\x18\x03 \x01(\x0e\x32\x1c.ChannelSettings.ModemConfig\x12\x11\n\tbandwidth\x18\x06 \x01(\r\x12\x15\n\rspread_factor\x18\x07 \x01(\r\x12\x13\n\x0b\x63oding_rate\x18\x08 \x01(\r\x12\x13\n\x0b\x63hannel_num\x18\t \x01(\r\x12\x0b\n\x03psk\x18\x04 \x01(\x0c\x12\x0c\n\x04name\x18\x05 \x01(\t\x12\n\n\x02id\x18\n \x01(\x07\x12\x16\n\x0euplink_enabled\x18\x10 \x01(\x08\x12\x18\n\x10\x64ownlink_enabled\x18\x11 \x01(\x08\"p\n\x0bModemConfig\x12\r\n\tVLongSlow\x10\x00\x12\x0c\n\x08LongSlow\x10\x01\x12\x0c\n\x08LongFast\x10\x02\x12\x0b\n\x07MidSlow\x10\x03\x12\x0b\n\x07MidFast\x10\x04\x12\r\n\tShortSlow\x10\x05\x12\r\n\tShortFast\x10\x06\"\x8b\x01\n\x07\x43hannel\x12\r\n\x05index\x18\x01 \x01(\x05\x12\"\n\x08settings\x18\x02 \x01(\x0b\x32\x10.ChannelSettings\x12\x1b\n\x04role\x18\x03 \x01(\x0e\x32\r.Channel.Role\"0\n\x04Role\x12\x0c\n\x08\x44ISABLED\x10\x00\x12\x0b\n\x07PRIMARY\x10\x01\x12\r\n\tSECONDARY\x10\x02\x42I\n\x13\x63om.geeksville.meshB\rChannelProtosH\x03Z!github.com/meshtastic/gomeshprotob\x06proto3'
)
@@ -30,34 +30,38 @@ _CHANNELSETTINGS_MODEMCONFIG = _descriptor.EnumDescriptor(
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='Bw125Cr45Sf128', index=0, number=0,
name='VLongSlow', index=0, number=0,
serialized_options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='Bw500Cr45Sf128', index=1, number=1,
name='LongSlow', index=1, number=1,
serialized_options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='Bw31_25Cr48Sf512', index=2, number=2,
name='LongFast', index=2, number=2,
serialized_options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='Bw125Cr48Sf4096', index=3, number=3,
name='MidSlow', index=3, number=3,
serialized_options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='Bw250Cr46Sf2048', index=4, number=4,
name='MidFast', index=4, number=4,
serialized_options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='Bw250Cr47Sf1024', index=5, number=5,
name='ShortSlow', index=5, number=5,
serialized_options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='ShortFast', index=6, number=6,
serialized_options=None,
type=None),
],
containing_type=None,
serialized_options=None,
serialized_start=281,
serialized_end=419,
serialized_start=280,
serialized_end=392,
)
_sym_db.RegisterEnumDescriptor(_CHANNELSETTINGS_MODEMCONFIG)
@@ -82,8 +86,8 @@ _CHANNEL_ROLE = _descriptor.EnumDescriptor(
],
containing_type=None,
serialized_options=None,
serialized_start=513,
serialized_end=561,
serialized_start=486,
serialized_end=534,
)
_sym_db.RegisterEnumDescriptor(_CHANNEL_ROLE)
@@ -186,7 +190,7 @@ _CHANNELSETTINGS = _descriptor.Descriptor(
oneofs=[
],
serialized_start=18,
serialized_end=419,
serialized_end=392,
)
@@ -231,8 +235,8 @@ _CHANNEL = _descriptor.Descriptor(
extension_ranges=[],
oneofs=[
],
serialized_start=422,
serialized_end=561,
serialized_start=395,
serialized_end=534,
)
_CHANNELSETTINGS.fields_by_name['modem_config'].enum_type = _CHANNELSETTINGS_MODEMCONFIG

View File

@@ -13,7 +13,6 @@ _sym_db = _symbol_database.Default()
from . import channel_pb2 as channel__pb2
from . import mesh_pb2 as mesh__pb2
from . import radioconfig_pb2 as radioconfig__pb2
DESCRIPTOR = _descriptor.FileDescriptor(
@@ -21,74 +20,13 @@ DESCRIPTOR = _descriptor.FileDescriptor(
package='',
syntax='proto3',
serialized_options=b'\n\023com.geeksville.meshB\nDeviceOnlyH\003Z!github.com/meshtastic/gomeshproto',
serialized_pb=b'\n\x10\x64\x65viceonly.proto\x1a\rchannel.proto\x1a\nmesh.proto\x1a\x11radioconfig.proto\"\x80\x01\n\x11LegacyRadioConfig\x12\x39\n\x0bpreferences\x18\x01 \x01(\x0b\x32$.LegacyRadioConfig.LegacyPreferences\x1a\x30\n\x11LegacyPreferences\x12\x1b\n\x06region\x18\x0f \x01(\x0e\x32\x0b.RegionCode\"\x8f\x02\n\x0b\x44\x65viceState\x12\'\n\x0blegacyRadio\x18\x01 \x01(\x0b\x32\x12.LegacyRadioConfig\x12\x1c\n\x07my_node\x18\x02 \x01(\x0b\x32\x0b.MyNodeInfo\x12\x14\n\x05owner\x18\x03 \x01(\x0b\x32\x05.User\x12\x1a\n\x07node_db\x18\x04 \x03(\x0b\x32\t.NodeInfo\x12\"\n\rreceive_queue\x18\x05 \x03(\x0b\x32\x0b.MeshPacket\x12\x0f\n\x07version\x18\x08 \x01(\r\x12$\n\x0frx_text_message\x18\x07 \x01(\x0b\x32\x0b.MeshPacket\x12\x0f\n\x07no_save\x18\t \x01(\x08\x12\x15\n\rdid_gps_reset\x18\x0b \x01(\x08J\x04\x08\x0c\x10\r\")\n\x0b\x43hannelFile\x12\x1a\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x08.ChannelBF\n\x13\x63om.geeksville.meshB\nDeviceOnlyH\x03Z!github.com/meshtastic/gomeshprotob\x06proto3'
serialized_pb=b'\n\x10\x64\x65viceonly.proto\x1a\rchannel.proto\x1a\nmesh.proto\"\xe6\x01\n\x0b\x44\x65viceState\x12\x1c\n\x07my_node\x18\x02 \x01(\x0b\x32\x0b.MyNodeInfo\x12\x14\n\x05owner\x18\x03 \x01(\x0b\x32\x05.User\x12\x1a\n\x07node_db\x18\x04 \x03(\x0b\x32\t.NodeInfo\x12\"\n\rreceive_queue\x18\x05 \x03(\x0b\x32\x0b.MeshPacket\x12\x0f\n\x07version\x18\x08 \x01(\r\x12$\n\x0frx_text_message\x18\x07 \x01(\x0b\x32\x0b.MeshPacket\x12\x0f\n\x07no_save\x18\t \x01(\x08\x12\x15\n\rdid_gps_reset\x18\x0b \x01(\x08J\x04\x08\x0c\x10\r\")\n\x0b\x43hannelFile\x12\x1a\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x08.ChannelBF\n\x13\x63om.geeksville.meshB\nDeviceOnlyH\x03Z!github.com/meshtastic/gomeshprotob\x06proto3'
,
dependencies=[channel__pb2.DESCRIPTOR,mesh__pb2.DESCRIPTOR,radioconfig__pb2.DESCRIPTOR,])
dependencies=[channel__pb2.DESCRIPTOR,mesh__pb2.DESCRIPTOR,])
_LEGACYRADIOCONFIG_LEGACYPREFERENCES = _descriptor.Descriptor(
name='LegacyPreferences',
full_name='LegacyRadioConfig.LegacyPreferences',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='region', full_name='LegacyRadioConfig.LegacyPreferences.region', index=0,
number=15, type=14, cpp_type=8, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
],
extensions=[
],
nested_types=[],
enum_types=[
],
serialized_options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
serialized_start=147,
serialized_end=195,
)
_LEGACYRADIOCONFIG = _descriptor.Descriptor(
name='LegacyRadioConfig',
full_name='LegacyRadioConfig',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='preferences', full_name='LegacyRadioConfig.preferences', index=0,
number=1, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
],
extensions=[
],
nested_types=[_LEGACYRADIOCONFIG_LEGACYPREFERENCES, ],
enum_types=[
],
serialized_options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
serialized_start=67,
serialized_end=195,
)
_DEVICESTATE = _descriptor.Descriptor(
name='DeviceState',
full_name='DeviceState',
@@ -97,63 +35,56 @@ _DEVICESTATE = _descriptor.Descriptor(
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='legacyRadio', full_name='DeviceState.legacyRadio', index=0,
number=1, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='my_node', full_name='DeviceState.my_node', index=1,
name='my_node', full_name='DeviceState.my_node', index=0,
number=2, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='owner', full_name='DeviceState.owner', index=2,
name='owner', full_name='DeviceState.owner', index=1,
number=3, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='node_db', full_name='DeviceState.node_db', index=3,
name='node_db', full_name='DeviceState.node_db', index=2,
number=4, type=11, cpp_type=10, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='receive_queue', full_name='DeviceState.receive_queue', index=4,
name='receive_queue', full_name='DeviceState.receive_queue', index=3,
number=5, type=11, cpp_type=10, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='version', full_name='DeviceState.version', index=5,
name='version', full_name='DeviceState.version', index=4,
number=8, type=13, cpp_type=3, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='rx_text_message', full_name='DeviceState.rx_text_message', index=6,
name='rx_text_message', full_name='DeviceState.rx_text_message', index=5,
number=7, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='no_save', full_name='DeviceState.no_save', index=7,
name='no_save', full_name='DeviceState.no_save', index=6,
number=9, type=8, cpp_type=7, label=1,
has_default_value=False, default_value=False,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='did_gps_reset', full_name='DeviceState.did_gps_reset', index=8,
name='did_gps_reset', full_name='DeviceState.did_gps_reset', index=7,
number=11, type=8, cpp_type=7, label=1,
has_default_value=False, default_value=False,
message_type=None, enum_type=None, containing_type=None,
@@ -171,8 +102,8 @@ _DEVICESTATE = _descriptor.Descriptor(
extension_ranges=[],
oneofs=[
],
serialized_start=198,
serialized_end=469,
serialized_start=48,
serialized_end=278,
)
@@ -202,40 +133,20 @@ _CHANNELFILE = _descriptor.Descriptor(
extension_ranges=[],
oneofs=[
],
serialized_start=471,
serialized_end=512,
serialized_start=280,
serialized_end=321,
)
_LEGACYRADIOCONFIG_LEGACYPREFERENCES.fields_by_name['region'].enum_type = radioconfig__pb2._REGIONCODE
_LEGACYRADIOCONFIG_LEGACYPREFERENCES.containing_type = _LEGACYRADIOCONFIG
_LEGACYRADIOCONFIG.fields_by_name['preferences'].message_type = _LEGACYRADIOCONFIG_LEGACYPREFERENCES
_DEVICESTATE.fields_by_name['legacyRadio'].message_type = _LEGACYRADIOCONFIG
_DEVICESTATE.fields_by_name['my_node'].message_type = mesh__pb2._MYNODEINFO
_DEVICESTATE.fields_by_name['owner'].message_type = mesh__pb2._USER
_DEVICESTATE.fields_by_name['node_db'].message_type = mesh__pb2._NODEINFO
_DEVICESTATE.fields_by_name['receive_queue'].message_type = mesh__pb2._MESHPACKET
_DEVICESTATE.fields_by_name['rx_text_message'].message_type = mesh__pb2._MESHPACKET
_CHANNELFILE.fields_by_name['channels'].message_type = channel__pb2._CHANNEL
DESCRIPTOR.message_types_by_name['LegacyRadioConfig'] = _LEGACYRADIOCONFIG
DESCRIPTOR.message_types_by_name['DeviceState'] = _DEVICESTATE
DESCRIPTOR.message_types_by_name['ChannelFile'] = _CHANNELFILE
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
LegacyRadioConfig = _reflection.GeneratedProtocolMessageType('LegacyRadioConfig', (_message.Message,), {
'LegacyPreferences' : _reflection.GeneratedProtocolMessageType('LegacyPreferences', (_message.Message,), {
'DESCRIPTOR' : _LEGACYRADIOCONFIG_LEGACYPREFERENCES,
'__module__' : 'deviceonly_pb2'
# @@protoc_insertion_point(class_scope:LegacyRadioConfig.LegacyPreferences)
})
,
'DESCRIPTOR' : _LEGACYRADIOCONFIG,
'__module__' : 'deviceonly_pb2'
# @@protoc_insertion_point(class_scope:LegacyRadioConfig)
})
_sym_db.RegisterMessage(LegacyRadioConfig)
_sym_db.RegisterMessage(LegacyRadioConfig.LegacyPreferences)
DeviceState = _reflection.GeneratedProtocolMessageType('DeviceState', (_message.Message,), {
'DESCRIPTOR' : _DEVICESTATE,
'__module__' : 'deviceonly_pb2'

View File

@@ -1,83 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: environmental_measurement.proto
from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
from google.protobuf import reflection as _reflection
from google.protobuf import symbol_database as _symbol_database
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor.FileDescriptor(
name='environmental_measurement.proto',
package='',
syntax='proto3',
serialized_options=b'Z!github.com/meshtastic/gomeshproto',
serialized_pb=b'\n\x1f\x65nvironmental_measurement.proto\"g\n\x18\x45nvironmentalMeasurement\x12\x13\n\x0btemperature\x18\x01 \x01(\x02\x12\x19\n\x11relative_humidity\x18\x02 \x01(\x02\x12\x1b\n\x13\x62\x61rometric_pressure\x18\x03 \x01(\x02\x42#Z!github.com/meshtastic/gomeshprotob\x06proto3'
)
_ENVIRONMENTALMEASUREMENT = _descriptor.Descriptor(
name='EnvironmentalMeasurement',
full_name='EnvironmentalMeasurement',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='temperature', full_name='EnvironmentalMeasurement.temperature', index=0,
number=1, type=2, cpp_type=6, label=1,
has_default_value=False, default_value=float(0),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='relative_humidity', full_name='EnvironmentalMeasurement.relative_humidity', index=1,
number=2, type=2, cpp_type=6, label=1,
has_default_value=False, default_value=float(0),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='barometric_pressure', full_name='EnvironmentalMeasurement.barometric_pressure', index=2,
number=3, type=2, cpp_type=6, label=1,
has_default_value=False, default_value=float(0),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
],
extensions=[
],
nested_types=[],
enum_types=[
],
serialized_options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
serialized_start=35,
serialized_end=138,
)
DESCRIPTOR.message_types_by_name['EnvironmentalMeasurement'] = _ENVIRONMENTALMEASUREMENT
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
EnvironmentalMeasurement = _reflection.GeneratedProtocolMessageType('EnvironmentalMeasurement', (_message.Message,), {
'DESCRIPTOR' : _ENVIRONMENTALMEASUREMENT,
'__module__' : 'environmental_measurement_pb2'
# @@protoc_insertion_point(class_scope:EnvironmentalMeasurement)
})
_sym_db.RegisterMessage(EnvironmentalMeasurement)
DESCRIPTOR._options = None
# @@protoc_insertion_point(module_scope)

View File

@@ -27,19 +27,21 @@ class Globals:
Globals.__instance = self
self.args = None
self.parser = None
self.target_node = None
self.channel_index = None
self.logfile = None
self.tunnelInstance = None
# TODO: to migrate to camel_case for v1.3 change this value to True
self.camel_case = False
def reset(self):
"""Reset all of our globals. If you add a member, add it to this method, too."""
self.args = None
self.parser = None
self.target_node = None
self.channel_index = None
self.logfile = None
self.tunnelInstance = None
# TODO: to migrate to camel_case for v1.3 change this value to True
self.camel_case = False
# setters
def set_args(self, args):
@@ -50,10 +52,6 @@ class Globals:
"""Set the parser"""
self.parser = parser
def set_target_node(self, target_node):
"""Set the target_node"""
self.target_node = target_node
def set_channel_index(self, channel_index):
"""Set the channel_index"""
self.channel_index = channel_index
@@ -66,6 +64,10 @@ class Globals:
"""Set the tunnelInstance"""
self.tunnelInstance = tunnelInstance
def set_camel_case(self):
"""Force using camelCase for things like prefs/set/set"""
self.camel_case = True
# getters
def get_args(self):
"""Get args"""
@@ -75,10 +77,6 @@ class Globals:
"""Get parser"""
return self.parser
def get_target_node(self):
"""Get target_node"""
return self.target_node
def get_channel_index(self):
"""Get channel_index"""
return self.channel_index
@@ -90,3 +88,7 @@ class Globals:
def get_tunnelInstance(self):
"""Get tunnelInstance"""
return self.tunnelInstance
def get_camel_case(self):
"""Get whether or not to use camelCase"""
return self.camel_case

View File

@@ -17,9 +17,9 @@ from google.protobuf.json_format import MessageToJson
import meshtastic.node
from . import portnums_pb2, mesh_pb2
from .util import stripnl, Timeout, our_exit, remove_keys_from_dict, convert_mac_addr
from .__init__ import LOCAL_ADDR, BROADCAST_NUM, BROADCAST_ADDR, ResponseHandler, publishingThread, OUR_APP_VERSION, protocols
from meshtastic import portnums_pb2, mesh_pb2
from meshtastic.util import stripnl, Timeout, our_exit, remove_keys_from_dict, convert_mac_addr
from meshtastic.__init__ import LOCAL_ADDR, BROADCAST_NUM, BROADCAST_ADDR, ResponseHandler, publishingThread, OUR_APP_VERSION, protocols
class MeshInterface:
"""Interface class for meshtastic devices
@@ -68,13 +68,12 @@ class MeshInterface:
def __exit__(self, exc_type, exc_value, traceback):
if exc_type is not None and exc_value is not None:
logging.error(
f'An exception of type {exc_type} with value {exc_value} has occurred')
logging.error(f'An exception of type {exc_type} with value {exc_value} has occurred')
if traceback is not None:
logging.error(f'Traceback: {traceback}')
self.close()
def showInfo(self, file=sys.stdout):
def showInfo(self, file=sys.stdout): # pylint: disable=W0613
"""Show human readable summary about this object"""
owner = f"Owner: {self.getLongName()} ({self.getShortName()})"
myinfo = ''
@@ -84,10 +83,10 @@ class MeshInterface:
nodes = ""
if self.nodes:
for n in self.nodes.values():
# when the TBeam is first booted, it sometimes shows the 'raw' data
# when the TBeam is first booted, it sometimes shows the raw data
# so, we will just remove any raw keys
n2 = remove_keys_from_dict('raw', n)
n2 = remove_keys_from_dict('decode', n2)
keys_to_remove = ('raw', 'decoded', 'payload')
n2 = remove_keys_from_dict(keys_to_remove, n)
# if we have 'macaddr', re-format it
if 'macaddr' in n2['user']:
@@ -101,7 +100,7 @@ class MeshInterface:
print(infos)
return infos
def showNodes(self, includeSelf=True, file=sys.stdout):
def showNodes(self, includeSelf=True, file=sys.stdout): # pylint: disable=W0613
"""Show table summary of nodes in mesh"""
def formatFloat(value, precision=2, unit=''):
"""Format a float value with precsion."""
@@ -160,7 +159,7 @@ class MeshInterface:
def getNode(self, nodeId):
"""Return a node object which contains device settings and channel info"""
if nodeId == LOCAL_ADDR:
if nodeId in (LOCAL_ADDR, BROADCAST_ADDR):
return self.localNode
else:
n = meshtastic.node.Node(self, nodeId)
@@ -255,11 +254,12 @@ class MeshInterface:
meshPacket.decoded.payload = data
meshPacket.decoded.portnum = portNum
meshPacket.decoded.want_response = wantResponse
meshPacket.id = self._generatePacketId()
if onResponse is not None:
self._addResponseHandler(meshPacket.id, onResponse)
p = self._sendPacket(meshPacket, destinationId,
wantAck=wantAck, hopLimit=hopLimit)
if onResponse is not None:
self._addResponseHandler(p.id, onResponse)
return p
def sendPosition(self, latitude=0.0, longitude=0.0, altitude=0, timeSec=0,
@@ -394,11 +394,11 @@ class MeshInterface:
return user.get('shortName', None)
return None
def _waitConnected(self):
def _waitConnected(self, timeout=15.0):
"""Block until the initial node db download is complete, or timeout
and raise an exception"""
if not self.noProto:
if not self.isConnected.wait(15.0): # timeout after x seconds
if not self.isConnected.wait(timeout): # timeout after x seconds
raise Exception("Timed out waiting for connection completion")
# If we failed while connecting, raise the connection to the client
@@ -416,8 +416,7 @@ class MeshInterface:
def _disconnected(self):
"""Called by subclasses to tell clients this interface has disconnected"""
self.isConnected.clear()
publishingThread.queueWork(lambda: pub.sendMessage(
"meshtastic.connection.lost", interface=self))
publishingThread.queueWork(lambda: pub.sendMessage("meshtastic.connection.lost", interface=self))
def _startHeartbeat(self):
"""We need to send a heartbeat message to the device every X seconds"""
@@ -443,8 +442,7 @@ class MeshInterface:
if not self.isConnected.is_set():
self.isConnected.set()
self._startHeartbeat()
publishingThread.queueWork(lambda: pub.sendMessage(
"meshtastic.connection.established", interface=self))
publishingThread.queueWork(lambda: pub.sendMessage("meshtastic.connection.established", interface=self))
def _startConfig(self):
"""Start device packets flowing"""
@@ -634,11 +632,13 @@ class MeshInterface:
# asObj = DotMap(asDict)
topic = "meshtastic.receive" # Generic unknown packet type
decoded = asDict["decoded"]
# The default MessageToDict converts byte arrays into base64 strings.
# We don't want that - it messes up data payload. So slam in the correct
# byte array.
decoded["payload"] = meshPacket.decoded.payload
decoded = None
if 'decoded' in asDict:
decoded = asDict["decoded"]
# The default MessageToDict converts byte arrays into base64 strings.
# We don't want that - it messes up data payload. So slam in the correct
# byte array.
decoded["payload"] = meshPacket.decoded.payload
# UNKNOWN_APP is the default protobuf portnum value, and therefore if not
# set it will not be populated at all to make API usage easier, set

View File

File diff suppressed because one or more lines are too long

View File

@@ -3,11 +3,10 @@
import logging
import base64
import time
from google.protobuf.json_format import MessageToJson
from . import portnums_pb2, apponly_pb2, admin_pb2, channel_pb2
from .util import pskToString, stripnl, Timeout, our_exit, fromPSK
from meshtastic import portnums_pb2, apponly_pb2, admin_pb2, channel_pb2
from meshtastic.util import pskToString, stripnl, Timeout, our_exit, fromPSK
class Node:
@@ -26,6 +25,15 @@ class Node:
self.partialChannels = None
self.noProto = noProto
self.cannedPluginMessage = None
self.cannedPluginMessagePart1 = None
self.cannedPluginMessagePart2 = None
self.cannedPluginMessagePart3 = None
self.cannedPluginMessagePart4 = None
self.gotResponse = None
def showChannels(self):
"""Show human readable description of our channels."""
print("Channels:")
@@ -58,6 +66,14 @@ class Node:
self.channels = None
self.partialChannels = [] # We keep our channels in a temp array until finished
# Note: We do not get the canned plugin message, unless get_canned_message() is called
self.cannedPluginMessage = None
self.cannedPluginMessagePart1 = None
self.cannedPluginMessagePart2 = None
self.cannedPluginMessagePart3 = None
self.cannedPluginMessagePart4 = None
self._requestSettings()
def turnOffEncryptionOnPrimaryChannel(self):
@@ -66,9 +82,9 @@ class Node:
print("Writing modified channels to device")
self.writeChannel(0)
def waitForConfig(self):
def waitForConfig(self, attribute='channels'):
"""Block until radio config is received. Returns True if config has been received."""
return self._timeout.waitForSet(self, attrs=('radioConfig', 'channels'))
return self._timeout.waitForSet(self, attrs=('radioConfig', attribute))
def writeConfig(self):
"""Write the current (edited) radioConfig to the device"""
@@ -239,7 +255,7 @@ class Node:
"""Handle the response packet for requesting settings _requestSettings()"""
logging.debug(f'onResponseRequestSetting() p:{p}')
errorFound = False
if 'routing' in p["decoded"]:
if "routing" in p["decoded"]:
if p["decoded"]["routing"]["errorReason"] != "NONE":
errorFound = True
print(f'Error on response: {p["decoded"]["routing"]["errorReason"]}')
@@ -257,6 +273,7 @@ class Node:
p = admin_pb2.AdminMessage()
p.get_radio_request = True
# TODO: should we check that localNode has an 'admin' channel?
# Show progress message for super slow operations
if self != self.iface.localNode:
print("Requesting preferences from remote node.")
@@ -269,6 +286,156 @@ class Node:
return self._sendAdmin(p, wantResponse=True, onResponse=self.onResponseRequestSettings)
def onResponseRequestCannedMessagePluginMessagePart1(self, p):
"""Handle the response packet for requesting canned message plugin message part 1"""
logging.debug(f'onResponseRequestCannedMessagePluginMessagePart1() p:{p}')
errorFound = False
if "routing" in p["decoded"]:
if p["decoded"]["routing"]["errorReason"] != "NONE":
errorFound = True
print(f'Error on response: {p["decoded"]["routing"]["errorReason"]}')
if errorFound is False:
if "decoded" in p:
if "admin" in p["decoded"]:
if "raw" in p["decoded"]["admin"]:
self.cannedPluginMessagePart1 = p["decoded"]["admin"]["raw"].get_canned_message_plugin_part1_response
logging.debug(f'self.cannedPluginMessagePart1:{self.cannedPluginMessagePart1}')
self.gotResponse = True
def onResponseRequestCannedMessagePluginMessagePart2(self, p):
"""Handle the response packet for requesting canned message plugin message part 2"""
logging.debug(f'onResponseRequestCannedMessagePluginMessagePart2() p:{p}')
errorFound = False
if "routing" in p["decoded"]:
if p["decoded"]["routing"]["errorReason"] != "NONE":
errorFound = True
print(f'Error on response: {p["decoded"]["routing"]["errorReason"]}')
if errorFound is False:
if "decoded" in p:
if "admin" in p["decoded"]:
if "raw" in p["decoded"]["admin"]:
self.cannedPluginMessagePart2 = p["decoded"]["admin"]["raw"].get_canned_message_plugin_part2_response
logging.debug(f'self.cannedPluginMessagePart2:{self.cannedPluginMessagePart2}')
self.gotResponse = True
def onResponseRequestCannedMessagePluginMessagePart3(self, p):
"""Handle the response packet for requesting canned message plugin message part 3"""
logging.debug(f'onResponseRequestCannedMessagePluginMessagePart3() p:{p}')
errorFound = False
if "routing" in p["decoded"]:
if p["decoded"]["routing"]["errorReason"] != "NONE":
errorFound = True
print(f'Error on response: {p["decoded"]["routing"]["errorReason"]}')
if errorFound is False:
if "decoded" in p:
if "admin" in p["decoded"]:
if "raw" in p["decoded"]["admin"]:
self.cannedPluginMessagePart3 = p["decoded"]["admin"]["raw"].get_canned_message_plugin_part3_response
logging.debug(f'self.cannedPluginMessagePart3:{self.cannedPluginMessagePart3}')
self.gotResponse = True
def onResponseRequestCannedMessagePluginMessagePart4(self, p):
"""Handle the response packet for requesting canned message plugin message part 4"""
logging.debug(f'onResponseRequestCannedMessagePluginMessagePart4() p:{p}')
errorFound = False
if "routing" in p["decoded"]:
if p["decoded"]["routing"]["errorReason"] != "NONE":
errorFound = True
print(f'Error on response: {p["decoded"]["routing"]["errorReason"]}')
if errorFound is False:
if "decoded" in p:
if "admin" in p["decoded"]:
if "raw" in p["decoded"]["admin"]:
self.cannedPluginMessagePart4 = p["decoded"]["admin"]["raw"].get_canned_message_plugin_part4_response
logging.debug(f'self.cannedPluginMessagePart4:{self.cannedPluginMessagePart4}')
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()')
if not self.cannedPluginMessage:
p1 = admin_pb2.AdminMessage()
p1.get_canned_message_plugin_part1_request = True
self.gotResponse = False
self._sendAdmin(p1, wantResponse=True, onResponse=self.onResponseRequestCannedMessagePluginMessagePart1)
while self.gotResponse is False:
time.sleep(0.1)
p2 = admin_pb2.AdminMessage()
p2.get_canned_message_plugin_part2_request = True
self.gotResponse = False
self._sendAdmin(p2, wantResponse=True, onResponse=self.onResponseRequestCannedMessagePluginMessagePart2)
while self.gotResponse is False:
time.sleep(0.1)
p3 = admin_pb2.AdminMessage()
p3.get_canned_message_plugin_part3_request = True
self.gotResponse = False
self._sendAdmin(p3, wantResponse=True, onResponse=self.onResponseRequestCannedMessagePluginMessagePart3)
while self.gotResponse is False:
time.sleep(0.1)
p4 = admin_pb2.AdminMessage()
p4.get_canned_message_plugin_part4_request = True
self.gotResponse = False
self._sendAdmin(p4, wantResponse=True, onResponse=self.onResponseRequestCannedMessagePluginMessagePart4)
while self.gotResponse is False:
time.sleep(0.1)
# TODO: This feels wrong to have a sleep here. Is there a way to ensure that
# all requests are complete? Perhaps change to a while loop any parts are None... maybe?
time.sleep(3)
logging.debug(f'self.cannedPluginMessagePart1:{self.cannedPluginMessagePart1}')
logging.debug(f'self.cannedPluginMessagePart2:{self.cannedPluginMessagePart2}')
logging.debug(f'self.cannedPluginMessagePart3:{self.cannedPluginMessagePart3}')
logging.debug(f'self.cannedPluginMessagePart4:{self.cannedPluginMessagePart4}')
self.cannedPluginMessage = ""
if self.cannedPluginMessagePart1:
self.cannedPluginMessage += self.cannedPluginMessagePart1
if self.cannedPluginMessagePart2:
self.cannedPluginMessage += self.cannedPluginMessagePart2
if self.cannedPluginMessagePart3:
self.cannedPluginMessage += self.cannedPluginMessagePart3
if self.cannedPluginMessagePart4:
self.cannedPluginMessage += self.cannedPluginMessagePart4
print(f'canned_plugin_message:{self.cannedPluginMessage}')
logging.debug(f'canned_plugin_message:{self.cannedPluginMessage}')
return self.cannedPluginMessage
def set_canned_message(self, message):
"""Set the canned message. Split into parts of 200 chars each."""
if len(message) > 800:
our_exit("Warning: The canned message must be less than 800 characters.")
# split into chunks
chunks = []
chunks_size = 200
for i in range(0, len(message), chunks_size):
chunks.append(message[i: i + chunks_size])
# for each chunk, send a message to set the values
#for i in range(0, len(chunks)):
for i, chunk in enumerate(chunks):
p = admin_pb2.AdminMessage()
# TODO: should be a way to improve this
if i == 0:
p.set_canned_message_plugin_part1 = chunk
elif i == 1:
p.set_canned_message_plugin_part2 = chunk
elif i == 2:
p.set_canned_message_plugin_part3 = chunk
elif i == 3:
p.set_canned_message_plugin_part4 = chunk
logging.debug(f"Setting canned message '{chunk}' part {i+1}")
self._sendAdmin(p)
def exitSimulator(self):
"""Tell a simulator node to exit (this message
is ignored for other nodes)"""
@@ -286,6 +453,14 @@ class Node:
return self._sendAdmin(p)
def shutdown(self, secs: int = 10):
"""Tell the node to shutdown."""
p = admin_pb2.AdminMessage()
p.shutdown_seconds = secs
logging.info(f"Telling node to shutdown in {secs} seconds")
return self._sendAdmin(p)
def _fixupChannels(self):
"""Fixup indexes and add disabled channels as needed"""

View File

@@ -19,7 +19,7 @@ DESCRIPTOR = _descriptor.FileDescriptor(
package='',
syntax='proto3',
serialized_options=b'\n\023com.geeksville.meshB\010PortnumsH\003Z!github.com/meshtastic/gomeshproto',
serialized_pb=b'\n\x0eportnums.proto*\xcb\x02\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\r\n\tREPLY_APP\x10 \x12\x11\n\rIP_TUNNEL_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!\n\x1d\x45NVIRONMENTAL_MEASUREMENT_APP\x10\x43\x12\x0b\n\x07ZPS_APP\x10\x44\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\x44\n\x13\x63om.geeksville.meshB\x08PortnumsH\x03Z!github.com/meshtastic/gomeshprotob\x06proto3'
serialized_pb=b'\n\x0eportnums.proto*\xbb\x02\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\r\n\tREPLY_APP\x10 \x12\x11\n\rIP_TUNNEL_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\x10\n\x0bPRIVATE_APP\x10\x80\x02\x12\x13\n\x0e\x41TAK_FORWARDER\x10\x81\x02\x12\x08\n\x03MAX\x10\xff\x03\x42\x44\n\x13\x63om.geeksville.meshB\x08PortnumsH\x03Z!github.com/meshtastic/gomeshprotob\x06proto3'
)
_PORTNUM = _descriptor.EnumDescriptor(
@@ -77,7 +77,7 @@ _PORTNUM = _descriptor.EnumDescriptor(
serialized_options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='ENVIRONMENTAL_MEASUREMENT_APP', index=12, number=67,
name='TELEMETRY_APP', index=12, number=67,
serialized_options=None,
type=None),
_descriptor.EnumValueDescriptor(
@@ -100,7 +100,7 @@ _PORTNUM = _descriptor.EnumDescriptor(
containing_type=None,
serialized_options=None,
serialized_start=19,
serialized_end=350,
serialized_end=334,
)
_sym_db.RegisterEnumDescriptor(_PORTNUM)
@@ -117,7 +117,7 @@ IP_TUNNEL_APP = 33
SERIAL_APP = 64
STORE_FORWARD_APP = 65
RANGE_TEST_APP = 66
ENVIRONMENTAL_MEASUREMENT_APP = 67
TELEMETRY_APP = 67
ZPS_APP = 68
PRIVATE_APP = 256
ATAK_FORWARDER = 257

View File

File diff suppressed because one or more lines are too long

View File

@@ -2,16 +2,25 @@
"""
import logging
from pubsub import pub
from . import portnums_pb2, remote_hardware_pb2
from .util import our_exit
from meshtastic import portnums_pb2, remote_hardware_pb2
from meshtastic.util import our_exit
def onGPIOreceive(packet, interface):
"""Callback for received GPIO responses
"""
logging.debug(f"packet:{packet} interface:{interface}")
gpioValue = 0
hw = packet["decoded"]["remotehw"]
gpioValue = hw["gpioValue"]
if "gpioValue" in hw:
gpioValue = hw["gpioValue"]
else:
if not "gpioMask" in hw:
# we did get a reply, but due to protobufs, 0 for numeric value is not sent
# see https://developers.google.com/protocol-buffers/docs/proto3#default
# so, we set it here
gpioValue = 0
#print(f'mask:{interface.mask}')
value = int(gpioValue) & int(interface.mask)
print(f'Received RemoteHardware typ={hw["typ"]}, gpio_value={gpioValue} value={value}')

View File

@@ -6,7 +6,7 @@ import platform
import serial
import meshtastic.util
from .stream_interface import StreamInterface
from meshtastic.stream_interface import StreamInterface
if platform.system() != 'Windows':
import termios
@@ -24,8 +24,10 @@ class SerialInterface(StreamInterface):
"""
self.noProto = noProto
if devPath is None:
ports = meshtastic.util.findPorts()
self.devPath = devPath
if self.devPath is None:
ports = meshtastic.util.findPorts(True)
logging.debug(f"ports:{ports}")
if len(ports) == 0:
meshtastic.util.our_exit("Warning: No Meshtastic devices detected.")
@@ -34,34 +36,31 @@ class SerialInterface(StreamInterface):
message += f" Ports detected:{ports}"
meshtastic.util.our_exit(message)
else:
devPath = ports[0]
self.devPath = ports[0]
logging.debug(f"Connecting to {devPath}")
logging.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 not self.noProto:
if platform.system() != 'Windows':
with open(devPath, encoding='utf8') as f:
attrs = termios.tcgetattr(f)
attrs[2] = attrs[2] & ~termios.HUPCL
termios.tcsetattr(f, termios.TCSAFLUSH, attrs)
f.close()
time.sleep(0.1)
self.stream = serial.Serial(devPath, 921600, exclusive=True, timeout=0.5, write_timeout=0)
if not self.noProto:
self.stream.flush()
if platform.system() != 'Windows':
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()
time.sleep(0.1)
self.stream = serial.Serial(self.devPath, 921600, exclusive=True, timeout=0.5, write_timeout=0)
self.stream.flush()
time.sleep(0.1)
StreamInterface.__init__(self, debugOut=debugOut, noProto=noProto, connectNow=connectNow)
def close(self):
"""Close a connection to the device"""
if not self.noProto:
self.stream.flush()
time.sleep(0.1)
self.stream.flush()
time.sleep(0.1)
self.stream.flush()
time.sleep(0.1)
self.stream.flush()
time.sleep(0.1)
logging.debug("Closing Serial stream")
StreamInterface.close(self)

View File

@@ -7,8 +7,8 @@ import traceback
import serial
from .mesh_interface import MeshInterface
from .util import stripnl
from meshtastic.mesh_interface import MeshInterface
from meshtastic.util import stripnl, is_windows11
START1 = 0x94
@@ -38,6 +38,8 @@ class StreamInterface(MeshInterface):
self._rxBuf = bytes() # empty
self._wantExit = False
self.is_windows11 = is_windows11()
# FIXME, figure out why daemon=True causes reader thread to exit too early
self._rxThread = threading.Thread(target=self.__reader, args=(), daemon=True)
@@ -62,8 +64,7 @@ class StreamInterface(MeshInterface):
# because we want to ensure it is looking for START1)
p = bytearray([START2] * 32)
self._writeBytes(p)
if not self.noProto:
time.sleep(0.1) # wait 100ms to give device time to start running
time.sleep(0.1) # wait 100ms to give device time to start running
self._rxThread.start()
@@ -89,8 +90,11 @@ class StreamInterface(MeshInterface):
if self.stream: # ignore writes when stream is closed
self.stream.write(b)
self.stream.flush()
# we sleep here to give the TBeam a chance to work
if not self.noProto:
# win11 might need a bit more time, too
if self.is_windows11:
time.sleep(1.0)
else:
# we sleep here to give the TBeam a chance to work
time.sleep(0.1)
def _readBytes(self, length):

86
meshtastic/supported_device.py Executable file
View File

@@ -0,0 +1,86 @@
""" 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.
"""
# 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
class SupportedDevice():
"""Devices supported on Meshtastic"""
def __init__(self, name, version=None, for_firmware=None, device_class="esp32",
baseport_on_linux=None, baseport_on_mac=None, baseport_on_windows="COM",
usb_vendor_id_in_hex=None, usb_product_id_in_hex=None):
""" constructor """
self.name = name
self.version = version
self.for_firmware = for_firmware
self.device_class = device_class # could be "nrf52"
# when you run "lsusb -d xxxx:" in linux
self.usb_vendor_id_in_hex = usb_vendor_id_in_hex # store in lower case
self.usb_product_id_in_hex = usb_product_id_in_hex # store in lower case
self.baseport_on_linux = baseport_on_linux # ex: ttyUSB or ttyACM
self.baseport_on_mac = baseport_on_mac
self.baseport_on_windows = baseport_on_windows
# supported devices
tbeam_v0_7 = SupportedDevice(name="T-Beam", version="0.7", for_firmware="tbeam0.7",
baseport_on_linux="ttyACM", baseport_on_mac="cu.usbmodem",
usb_vendor_id_in_hex="1a86", usb_product_id_in_hex="55d4")
tbeam_v1_1 = SupportedDevice(name="T-Beam", version="1.1", for_firmware="tbeam",
baseport_on_linux="ttyACM", baseport_on_mac="cu.usbmodem",
usb_vendor_id_in_hex="1a86", usb_product_id_in_hex="55d4")
tbeam_M8N = SupportedDevice(name="T-Beam", version="M8N", for_firmware="tbeam",
baseport_on_linux="ttyACM", baseport_on_mac="cu.usbmodem",
usb_vendor_id_in_hex="1a86", usb_product_id_in_hex="55d4")
tbeam_M8N_SX1262 = SupportedDevice(name="T-Beam", version="M8N_SX1262", for_firmware="tbeam",
baseport_on_linux="ttyACM", baseport_on_mac="cu.usbmodem",
usb_vendor_id_in_hex="1a86", usb_product_id_in_hex="55d4")
tlora_v1 = SupportedDevice(name="T-Lora", version="1", for_firmware="tlora-v1",
baseport_on_linux="ttyUSB", baseport_on_mac="cu.usbserial",
usb_vendor_id_in_hex="1a86", usb_product_id_in_hex="55d4")
tlora_v1_3 = SupportedDevice(name="T-Lora", version="1.3", for_firmware="tlora-v1-3",
baseport_on_linux="ttyUSB", baseport_on_mac="cu.usbserial",
usb_vendor_id_in_hex="10c4", usb_product_id_in_hex="ea60")
tlora_v2 = SupportedDevice(name="T-Lora", version="2", for_firmware="tlora-v2",
baseport_on_linux="ttyACM", baseport_on_mac="cu.usbmodem",
usb_vendor_id_in_hex="1a86", usb_product_id_in_hex="55d4")
tlora_v2_1_1_6 = SupportedDevice(name="T-Lora", version="2.1-1.6", for_firmware="tlora-v2-1-1.6",
baseport_on_linux="ttyACM", baseport_on_mac="cu.usbmodem",
usb_vendor_id_in_hex="1a86", usb_product_id_in_hex="55d4")
heltec_v1 = SupportedDevice(name="Heltec", version="1", for_firmware="heltec-v1",
baseport_on_linux="ttyUSB", baseport_on_mac="cu.usbserial-",
usb_vendor_id_in_hex="10c4", usb_product_id_in_hex="ea60")
heltec_v2_0 = SupportedDevice(name="Heltec", version="2.0", for_firmware="heltec-v2.0",
baseport_on_linux="ttyUSB", baseport_on_mac="cu.usbserial-",
usb_vendor_id_in_hex="10c4", usb_product_id_in_hex="ea60")
heltec_v2_1 = SupportedDevice(name="Heltec", version="2.1", for_firmware="heltec-v2.1",
baseport_on_linux="ttyUSB", baseport_on_mac="cu.usbserial-",
usb_vendor_id_in_hex="10c4", usb_product_id_in_hex="ea60")
rak11200 = SupportedDevice(name="RAK 11200", version="", for_firmware="rak11200",
baseport_on_linux="ttyUSB", baseport_on_mac="cu.usbserial-",
usb_vendor_id_in_hex="1a86", usb_product_id_in_hex="7523")
meshtastic_diy_v1 = SupportedDevice(name="Meshtastic DIY", version="1", for_firmware="meshtastic-diy-v1",
baseport_on_linux="ttyUSB", baseport_on_mac="cu.usbserial-",
usb_vendor_id_in_hex="10c4", usb_product_id_in_hex="ea60")
# Note: The T-Echo reports product id in boot mode
techo_1 = SupportedDevice(name="T-Echo", version="1", for_firmware="t-echo-1", device_class="nrf52",
baseport_on_linux="ttyACM", baseport_on_mac="cu.usbmodem",
usb_vendor_id_in_hex="239a", usb_product_id_in_hex="0029")
rak4631_5005 = SupportedDevice(name="RAK 4631 5005", version="", for_firmware="rak4631_5005",
device_class="nrf52",
baseport_on_linux="ttyACM", baseport_on_mac="cu.usbmodem",
usb_vendor_id_in_hex="239a", usb_product_id_in_hex="0029")
# Note: The 19003 reports same product id as 5005 in boot mode
rak4631_19003 = SupportedDevice(name="RAK 4631 19003", version="", for_firmware="rak4631_19003",
device_class="nrf52",
baseport_on_linux="ttyACM", baseport_on_mac="cu.usbmodem",
usb_vendor_id_in_hex="239a", usb_product_id_in_hex="8029")
supported_devices = [tbeam_v0_7, tbeam_v1_1, tbeam_M8N, tbeam_M8N_SX1262,
tlora_v1, tlora_v1_3, tlora_v2, tlora_v2_1_1_6,
heltec_v1, heltec_v2_0, heltec_v2_1,
meshtastic_diy_v1, techo_1, rak4631_5005, rak4631_19003,
rak11200]

View File

@@ -4,7 +4,7 @@ import logging
import socket
from typing import AnyStr
from .stream_interface import StreamInterface
from meshtastic.stream_interface import StreamInterface
class TCPInterface(StreamInterface):
"""Interface class for meshtastic devices over a TCP link"""
@@ -33,6 +33,12 @@ class TCPInterface(StreamInterface):
StreamInterface.__init__(self, debugOut=debugOut, noProto=noProto,
connectNow=connectNow)
def _socket_shutdown(self):
"""Shutdown the socket.
Note: Broke out this line so the exception could be unit tested.
"""
self.socket.shutdown(socket.SHUT_RDWR)
def myConnect(self):
"""Connect to socket"""
server_address = (self.hostname, self.portNumber)
@@ -48,7 +54,7 @@ class TCPInterface(StreamInterface):
self._wantExit = True
if not self.socket is None:
try:
self.socket.shutdown(socket.SHUT_RDWR)
self._socket_shutdown()
except:
pass # Ignore errors in shutdown, because we might have a race with the server
self.socket.close()

228
meshtastic/telemetry_pb2.py Normal file
View File

@@ -0,0 +1,228 @@
# -*- coding: utf-8 -*-
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: telemetry.proto
from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
from google.protobuf import reflection as _reflection
from google.protobuf import symbol_database as _symbol_database
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor.FileDescriptor(
name='telemetry.proto',
package='',
syntax='proto3',
serialized_options=b'\n\023com.geeksville.meshB\017TelemetryProtosH\003Z!github.com/meshtastic/gomeshproto',
serialized_pb=b'\n\x0ftelemetry.proto\"i\n\rDeviceMetrics\x12\x15\n\rbattery_level\x18\x01 \x01(\r\x12\x0f\n\x07voltage\x18\x02 \x01(\x02\x12\x1b\n\x13\x63hannel_utilization\x18\x03 \x01(\x02\x12\x13\n\x0b\x61ir_util_tx\x18\x04 \x01(\x02\"\x9b\x01\n\x12\x45nvironmentMetrics\x12\x13\n\x0btemperature\x18\x01 \x01(\x02\x12\x19\n\x11relative_humidity\x18\x02 \x01(\x02\x12\x1b\n\x13\x62\x61rometric_pressure\x18\x03 \x01(\x02\x12\x16\n\x0egas_resistance\x18\x04 \x01(\x02\x12\x0f\n\x07voltage\x18\x05 \x01(\x02\x12\x0f\n\x07\x63urrent\x18\x06 \x01(\x02\"\x82\x01\n\tTelemetry\x12\x0c\n\x04time\x18\x01 \x01(\x07\x12(\n\x0e\x64\x65vice_metrics\x18\x02 \x01(\x0b\x32\x0e.DeviceMetricsH\x00\x12\x32\n\x13\x65nvironment_metrics\x18\x03 \x01(\x0b\x32\x13.EnvironmentMetricsH\x00\x42\t\n\x07variantBK\n\x13\x63om.geeksville.meshB\x0fTelemetryProtosH\x03Z!github.com/meshtastic/gomeshprotob\x06proto3'
)
_DEVICEMETRICS = _descriptor.Descriptor(
name='DeviceMetrics',
full_name='DeviceMetrics',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='battery_level', full_name='DeviceMetrics.battery_level', index=0,
number=1, type=13, cpp_type=3, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='voltage', full_name='DeviceMetrics.voltage', index=1,
number=2, type=2, cpp_type=6, label=1,
has_default_value=False, default_value=float(0),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='channel_utilization', full_name='DeviceMetrics.channel_utilization', index=2,
number=3, type=2, cpp_type=6, label=1,
has_default_value=False, default_value=float(0),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='air_util_tx', full_name='DeviceMetrics.air_util_tx', index=3,
number=4, type=2, cpp_type=6, label=1,
has_default_value=False, default_value=float(0),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
],
extensions=[
],
nested_types=[],
enum_types=[
],
serialized_options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
serialized_start=19,
serialized_end=124,
)
_ENVIRONMENTMETRICS = _descriptor.Descriptor(
name='EnvironmentMetrics',
full_name='EnvironmentMetrics',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='temperature', full_name='EnvironmentMetrics.temperature', index=0,
number=1, type=2, cpp_type=6, label=1,
has_default_value=False, default_value=float(0),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='relative_humidity', full_name='EnvironmentMetrics.relative_humidity', index=1,
number=2, type=2, cpp_type=6, label=1,
has_default_value=False, default_value=float(0),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='barometric_pressure', full_name='EnvironmentMetrics.barometric_pressure', index=2,
number=3, type=2, cpp_type=6, label=1,
has_default_value=False, default_value=float(0),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='gas_resistance', full_name='EnvironmentMetrics.gas_resistance', index=3,
number=4, type=2, cpp_type=6, label=1,
has_default_value=False, default_value=float(0),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='voltage', full_name='EnvironmentMetrics.voltage', index=4,
number=5, type=2, cpp_type=6, label=1,
has_default_value=False, default_value=float(0),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='current', full_name='EnvironmentMetrics.current', index=5,
number=6, type=2, cpp_type=6, label=1,
has_default_value=False, default_value=float(0),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
],
extensions=[
],
nested_types=[],
enum_types=[
],
serialized_options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
serialized_start=127,
serialized_end=282,
)
_TELEMETRY = _descriptor.Descriptor(
name='Telemetry',
full_name='Telemetry',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='time', full_name='Telemetry.time', index=0,
number=1, type=7, cpp_type=3, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='device_metrics', full_name='Telemetry.device_metrics', index=1,
number=2, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='environment_metrics', full_name='Telemetry.environment_metrics', index=2,
number=3, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR),
],
extensions=[
],
nested_types=[],
enum_types=[
],
serialized_options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
_descriptor.OneofDescriptor(
name='variant', full_name='Telemetry.variant',
index=0, containing_type=None, fields=[]),
],
serialized_start=285,
serialized_end=415,
)
_TELEMETRY.fields_by_name['device_metrics'].message_type = _DEVICEMETRICS
_TELEMETRY.fields_by_name['environment_metrics'].message_type = _ENVIRONMENTMETRICS
_TELEMETRY.oneofs_by_name['variant'].fields.append(
_TELEMETRY.fields_by_name['device_metrics'])
_TELEMETRY.fields_by_name['device_metrics'].containing_oneof = _TELEMETRY.oneofs_by_name['variant']
_TELEMETRY.oneofs_by_name['variant'].fields.append(
_TELEMETRY.fields_by_name['environment_metrics'])
_TELEMETRY.fields_by_name['environment_metrics'].containing_oneof = _TELEMETRY.oneofs_by_name['variant']
DESCRIPTOR.message_types_by_name['DeviceMetrics'] = _DEVICEMETRICS
DESCRIPTOR.message_types_by_name['EnvironmentMetrics'] = _ENVIRONMENTMETRICS
DESCRIPTOR.message_types_by_name['Telemetry'] = _TELEMETRY
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
DeviceMetrics = _reflection.GeneratedProtocolMessageType('DeviceMetrics', (_message.Message,), {
'DESCRIPTOR' : _DEVICEMETRICS,
'__module__' : 'telemetry_pb2'
# @@protoc_insertion_point(class_scope:DeviceMetrics)
})
_sym_db.RegisterMessage(DeviceMetrics)
EnvironmentMetrics = _reflection.GeneratedProtocolMessageType('EnvironmentMetrics', (_message.Message,), {
'DESCRIPTOR' : _ENVIRONMENTMETRICS,
'__module__' : 'telemetry_pb2'
# @@protoc_insertion_point(class_scope:EnvironmentMetrics)
})
_sym_db.RegisterMessage(EnvironmentMetrics)
Telemetry = _reflection.GeneratedProtocolMessageType('Telemetry', (_message.Message,), {
'DESCRIPTOR' : _TELEMETRY,
'__module__' : 'telemetry_pb2'
# @@protoc_insertion_point(class_scope:Telemetry)
})
_sym_db.RegisterMessage(Telemetry)
DESCRIPTOR._options = None
# @@protoc_insertion_point(module_scope)

View File

@@ -8,9 +8,9 @@ import traceback
from dotmap import DotMap
from pubsub import pub
import meshtastic.util
from .__init__ import BROADCAST_NUM
from .serial_interface import SerialInterface
from .tcp_interface import TCPInterface
from meshtastic.__init__ import BROADCAST_NUM
from meshtastic.serial_interface import SerialInterface
from meshtastic.tcp_interface import TCPInterface
"""The interfaces we are using for our tests"""
@@ -63,6 +63,7 @@ def testSend(fromInterface, toInterface, isBroadcast=False, asBinary=False, want
Returns:
boolean -- True for success
"""
# pylint: disable=W0603
global receivedPackets
receivedPackets = []
fromNode = fromInterface.myInfo.my_node_num
@@ -74,6 +75,7 @@ def testSend(fromInterface, toInterface, isBroadcast=False, asBinary=False, want
logging.debug(
f"Sending test wantAck={wantAck} packet from {fromNode} to {toNode}")
# pylint: disable=W0603
global sendingInterface
sendingInterface = fromInterface
if not asBinary:
@@ -94,6 +96,7 @@ def runTests(numTests=50, wantAck=False, maxFailures=0):
numFail = 0
numSuccess = 0
for _ in range(numTests):
# pylint: disable=W0603
global testNumber
testNumber = testNumber + 1
isBroadcast = True
@@ -146,12 +149,13 @@ def testAll(numTests=5):
This is called from the cli with the "--test" option.
"""
ports = meshtastic.util.findPorts()
ports = meshtastic.util.findPorts(True)
if len(ports) < 2:
meshtastic.util.our_exit("Warning: Must have at least two devices connected to USB.")
pub.subscribe(onConnection, "meshtastic.connection")
pub.subscribe(onReceive, "meshtastic.receive")
# pylint: disable=W0603
global interfaces
interfaces = list(map(lambda port: SerialInterface(
port, debugOut=openDebugLog(port), connectNow=True), ports))

View File

@@ -1,12 +1,15 @@
"""Meshtastic unit tests for ble_interface.py"""
from unittest.mock import patch
import pytest
from ..ble_interface import BLEInterface
@pytest.mark.unit
def test_BLEInterface():
@patch('platform.system', return_value='Linux')
def test_BLEInterface(mock_platform):
"""Test that we can instantiate a BLEInterface"""
iface = BLEInterface('foo', debugOut=True, noProto=True)
iface.close()
mock_platform.assert_called()

View File

@@ -0,0 +1,61 @@
"""Meshtastic unit tests for __init__.py"""
import re
import logging
from unittest.mock import MagicMock
import pytest
from meshtastic.__init__ import _onTextReceive, _onPositionReceive, _onNodeInfoReceive
from ..serial_interface import SerialInterface
from ..globals import Globals
@pytest.mark.unit
def test_init_onTextReceive_with_exception(caplog):
"""Test _onTextReceive"""
args = MagicMock()
Globals.getInstance().set_args(args)
iface = MagicMock(autospec=SerialInterface)
packet = {}
with caplog.at_level(logging.DEBUG):
_onTextReceive(iface, packet)
assert re.search(r'in _onTextReceive', caplog.text, re.MULTILINE)
assert re.search(r'Malformatted', caplog.text, re.MULTILINE)
@pytest.mark.unit
def test_init_onPositionReceive(caplog):
"""Test _onPositionReceive"""
args = MagicMock()
Globals.getInstance().set_args(args)
iface = MagicMock(autospec=SerialInterface)
packet = {
'from': 'foo',
'decoded': {
'position': {}
}
}
with caplog.at_level(logging.DEBUG):
_onPositionReceive(iface, packet)
assert re.search(r'in _onPositionReceive', caplog.text, re.MULTILINE)
@pytest.mark.unit
def test_init_onNodeInfoReceive(caplog, iface_with_nodes):
"""Test _onNodeInfoReceive"""
args = MagicMock()
Globals.getInstance().set_args(args)
iface = iface_with_nodes
iface.myInfo.my_node_num = 2475227164
packet = {
'from': 'foo',
'decoded': {
'user': {
'id': 'bar',
},
}
}
with caplog.at_level(logging.DEBUG):
_onNodeInfoReceive(iface, packet)
assert re.search(r'in _onNodeInfoReceive', caplog.text, re.MULTILINE)

View File

File diff suppressed because it is too large Load Diff

View File

@@ -10,10 +10,13 @@ from ..mesh_interface import MeshInterface
from ..node import Node
from .. import mesh_pb2
from ..__init__ import LOCAL_ADDR, BROADCAST_ADDR
from ..radioconfig_pb2 import RadioConfig
from ..util import Timeout
@pytest.mark.unit
def test_MeshInterface(capsys, reset_globals):
@pytest.mark.usefixtures("reset_globals")
def test_MeshInterface(capsys):
"""Test that we can instantiate a MeshInterface"""
iface = MeshInterface(noProto=True)
anode = Node('foo', 'bar')
@@ -54,7 +57,8 @@ def test_MeshInterface(capsys, reset_globals):
@pytest.mark.unit
def test_getMyUser(reset_globals, iface_with_nodes):
@pytest.mark.usefixtures("reset_globals")
def test_getMyUser(iface_with_nodes):
"""Test getMyUser()"""
iface = iface_with_nodes
iface.myInfo.my_node_num = 2475227164
@@ -64,7 +68,8 @@ def test_getMyUser(reset_globals, iface_with_nodes):
@pytest.mark.unit
def test_getLongName(reset_globals, iface_with_nodes):
@pytest.mark.usefixtures("reset_globals")
def test_getLongName(iface_with_nodes):
"""Test getLongName()"""
iface = iface_with_nodes
iface.myInfo.my_node_num = 2475227164
@@ -73,7 +78,8 @@ def test_getLongName(reset_globals, iface_with_nodes):
@pytest.mark.unit
def test_getShortName(reset_globals, iface_with_nodes):
@pytest.mark.usefixtures("reset_globals")
def test_getShortName(iface_with_nodes):
"""Test getShortName()."""
iface = iface_with_nodes
iface.myInfo.my_node_num = 2475227164
@@ -82,7 +88,8 @@ def test_getShortName(reset_globals, iface_with_nodes):
@pytest.mark.unit
def test_handlePacketFromRadio_no_from(capsys, reset_globals):
@pytest.mark.usefixtures("reset_globals")
def test_handlePacketFromRadio_no_from(capsys):
"""Test _handlePacketFromRadio with no 'from' in the mesh packet."""
iface = MeshInterface(noProto=True)
meshPacket = mesh_pb2.MeshPacket()
@@ -93,7 +100,8 @@ def test_handlePacketFromRadio_no_from(capsys, reset_globals):
@pytest.mark.unit
def test_handlePacketFromRadio_with_a_portnum(caplog, reset_globals):
@pytest.mark.usefixtures("reset_globals")
def test_handlePacketFromRadio_with_a_portnum(caplog):
"""Test _handlePacketFromRadio with a portnum
Since we have an attribute called 'from', we cannot simply 'set' it.
Had to implement a hack just to be able to test some code.
@@ -108,7 +116,8 @@ def test_handlePacketFromRadio_with_a_portnum(caplog, reset_globals):
@pytest.mark.unit
def test_handlePacketFromRadio_no_portnum(caplog, reset_globals):
@pytest.mark.usefixtures("reset_globals")
def test_handlePacketFromRadio_no_portnum(caplog):
"""Test _handlePacketFromRadio without a portnum"""
iface = MeshInterface(noProto=True)
meshPacket = mesh_pb2.MeshPacket()
@@ -119,7 +128,8 @@ def test_handlePacketFromRadio_no_portnum(caplog, reset_globals):
@pytest.mark.unit
def test_getNode_with_local(reset_globals):
@pytest.mark.usefixtures("reset_globals")
def test_getNode_with_local():
"""Test getNode"""
iface = MeshInterface(noProto=True)
anode = iface.getNode(LOCAL_ADDR)
@@ -127,7 +137,8 @@ def test_getNode_with_local(reset_globals):
@pytest.mark.unit
def test_getNode_not_local(reset_globals, caplog):
@pytest.mark.usefixtures("reset_globals")
def test_getNode_not_local(caplog):
"""Test getNode not local"""
iface = MeshInterface(noProto=True)
anode = MagicMock(autospec=Node)
@@ -139,7 +150,8 @@ def test_getNode_not_local(reset_globals, caplog):
@pytest.mark.unit
def test_getNode_not_local_timeout(reset_globals, capsys):
@pytest.mark.usefixtures("reset_globals")
def test_getNode_not_local_timeout(capsys):
"""Test getNode not local, simulate timeout"""
iface = MeshInterface(noProto=True)
anode = MagicMock(autospec=Node)
@@ -155,7 +167,8 @@ def test_getNode_not_local_timeout(reset_globals, capsys):
@pytest.mark.unit
def test_sendPosition(reset_globals, caplog):
@pytest.mark.usefixtures("reset_globals")
def test_sendPosition(caplog):
"""Test sendPosition"""
iface = MeshInterface(noProto=True)
with caplog.at_level(logging.DEBUG):
@@ -165,7 +178,25 @@ def test_sendPosition(reset_globals, caplog):
@pytest.mark.unit
def test_handleFromRadio_empty_payload(reset_globals, caplog):
@pytest.mark.usefixtures("reset_globals")
def test_close_with_heartbeatTimer(caplog):
"""Test close() with heartbeatTimer"""
iface = MeshInterface(noProto=True)
anode = Node('foo', 'bar')
radioConfig = RadioConfig()
radioConfig.preferences.phone_timeout_secs = 10
anode.radioConfig = radioConfig
iface.localNode = anode
assert iface.heartbeatTimer is None
with caplog.at_level(logging.DEBUG):
iface._startHeartbeat()
assert iface.heartbeatTimer is not None
iface.close()
@pytest.mark.unit
@pytest.mark.usefixtures("reset_globals")
def test_handleFromRadio_empty_payload(caplog):
"""Test _handleFromRadio"""
iface = MeshInterface(noProto=True)
with caplog.at_level(logging.DEBUG):
@@ -175,19 +206,20 @@ def test_handleFromRadio_empty_payload(reset_globals, caplog):
@pytest.mark.unit
def test_handleFromRadio_with_my_info(reset_globals, caplog):
@pytest.mark.usefixtures("reset_globals")
def test_handleFromRadio_with_my_info(caplog):
"""Test _handleFromRadio with my_info"""
# Note: I captured the '--debug --info' for the bytes below.
# It "translates" to this:
# my_info {
# my_node_num: 682584012
# num_bands: 13
# firmware_version: "1.2.49.5354c49"
# reboot_count: 13
# bitrate: 17.088470458984375
# message_timeout_msec: 300000
# min_app_version: 20200
# max_channels: 8
# has_wifi: true
# }
from_radio_bytes = b'\x1a,\x08\xcc\xcf\xbd\xc5\x02\x18\r2\x0e1.2.49.5354c49P\r]0\xb5\x88Ah\xe0\xa7\x12p\xe8\x9d\x01x\x08\x90\x01\x01'
iface = MeshInterface(noProto=True)
@@ -195,12 +227,12 @@ def test_handleFromRadio_with_my_info(reset_globals, caplog):
iface._handleFromRadio(from_radio_bytes)
iface.close()
assert re.search(r'Received myinfo', caplog.text, re.MULTILINE)
assert re.search(r'num_bands: 13', caplog.text, re.MULTILINE)
assert re.search(r'max_channels: 8', caplog.text, re.MULTILINE)
@pytest.mark.unit
def test_handleFromRadio_with_node_info(reset_globals, caplog, capsys):
@pytest.mark.usefixtures("reset_globals")
def test_handleFromRadio_with_node_info(caplog, capsys):
"""Test _handleFromRadio with node_info"""
# Note: I captured the '--debug --info' for the bytes below.
# It "translates" to this:
@@ -236,7 +268,8 @@ def test_handleFromRadio_with_node_info(reset_globals, caplog, capsys):
@pytest.mark.unit
def test_handleFromRadio_with_node_info_tbeam1(reset_globals, caplog, capsys):
@pytest.mark.usefixtures("reset_globals")
def test_handleFromRadio_with_node_info_tbeam1(caplog, capsys):
"""Test _handleFromRadio with node_info"""
# Note: Captured the '--debug --info' for the bytes below.
# pylint: disable=C0301
@@ -259,7 +292,8 @@ def test_handleFromRadio_with_node_info_tbeam1(reset_globals, caplog, capsys):
@pytest.mark.unit
def test_handleFromRadio_with_node_info_tbeam_with_bad_data(reset_globals, caplog, capsys):
@pytest.mark.usefixtures("reset_globals")
def test_handleFromRadio_with_node_info_tbeam_with_bad_data(caplog):
"""Test _handleFromRadio with node_info with some bad data (issue#172) - ensure we do not throw exception"""
# Note: Captured the '--debug --info' for the bytes below.
from_radio_bytes = b'"\x17\x08\xdc\x8a\x8a\xae\x02\x12\x08"\x06\x00\x00\x00\x00\x00\x00\x1a\x00=\x00\x00\xb8@'
@@ -270,7 +304,8 @@ def test_handleFromRadio_with_node_info_tbeam_with_bad_data(reset_globals, caplo
@pytest.mark.unit
def test_MeshInterface_sendToRadioImpl(caplog, reset_globals):
@pytest.mark.usefixtures("reset_globals")
def test_MeshInterface_sendToRadioImpl(caplog):
"""Test _sendToRadioImp()"""
iface = MeshInterface(noProto=True)
with caplog.at_level(logging.DEBUG):
@@ -280,7 +315,8 @@ def test_MeshInterface_sendToRadioImpl(caplog, reset_globals):
@pytest.mark.unit
def test_MeshInterface_sendToRadio_no_proto(caplog, reset_globals):
@pytest.mark.usefixtures("reset_globals")
def test_MeshInterface_sendToRadio_no_proto(caplog):
"""Test sendToRadio()"""
iface = MeshInterface()
with caplog.at_level(logging.DEBUG):
@@ -290,7 +326,8 @@ def test_MeshInterface_sendToRadio_no_proto(caplog, reset_globals):
@pytest.mark.unit
def test_sendData_too_long(caplog, reset_globals):
@pytest.mark.usefixtures("reset_globals")
def test_sendData_too_long(caplog):
"""Test when data payload is too big"""
iface = MeshInterface(noProto=True)
some_large_text = b'This is a long text that will be too long for send text.'
@@ -314,7 +351,8 @@ def test_sendData_too_long(caplog, reset_globals):
@pytest.mark.unit
def test_sendData_unknown_app(capsys, reset_globals):
@pytest.mark.usefixtures("reset_globals")
def test_sendData_unknown_app(capsys):
"""Test sendData when unknown app"""
iface = MeshInterface(noProto=True)
with pytest.raises(SystemExit) as pytest_wrapped_e:
@@ -327,7 +365,8 @@ def test_sendData_unknown_app(capsys, reset_globals):
@pytest.mark.unit
def test_sendPosition_with_a_position(caplog, reset_globals):
@pytest.mark.usefixtures("reset_globals")
def test_sendPosition_with_a_position(caplog):
"""Test sendPosition when lat/long/alt"""
iface = MeshInterface(noProto=True)
with caplog.at_level(logging.DEBUG):
@@ -338,7 +377,8 @@ def test_sendPosition_with_a_position(caplog, reset_globals):
@pytest.mark.unit
def test_sendPacket_with_no_destination(capsys, reset_globals):
@pytest.mark.usefixtures("reset_globals")
def test_sendPacket_with_no_destination(capsys):
"""Test _sendPacket()"""
iface = MeshInterface(noProto=True)
with pytest.raises(SystemExit) as pytest_wrapped_e:
@@ -351,7 +391,8 @@ def test_sendPacket_with_no_destination(capsys, reset_globals):
@pytest.mark.unit
def test_sendPacket_with_destination_as_int(caplog, reset_globals):
@pytest.mark.usefixtures("reset_globals")
def test_sendPacket_with_destination_as_int(caplog):
"""Test _sendPacket() with int as a destination"""
iface = MeshInterface(noProto=True)
with caplog.at_level(logging.DEBUG):
@@ -361,7 +402,8 @@ def test_sendPacket_with_destination_as_int(caplog, reset_globals):
@pytest.mark.unit
def test_sendPacket_with_destination_starting_with_a_bang(caplog, reset_globals):
@pytest.mark.usefixtures("reset_globals")
def test_sendPacket_with_destination_starting_with_a_bang(caplog):
"""Test _sendPacket() with int as a destination"""
iface = MeshInterface(noProto=True)
with caplog.at_level(logging.DEBUG):
@@ -371,7 +413,8 @@ def test_sendPacket_with_destination_starting_with_a_bang(caplog, reset_globals)
@pytest.mark.unit
def test_sendPacket_with_destination_as_BROADCAST_ADDR(caplog, reset_globals):
@pytest.mark.usefixtures("reset_globals")
def test_sendPacket_with_destination_as_BROADCAST_ADDR(caplog):
"""Test _sendPacket() with BROADCAST_ADDR as a destination"""
iface = MeshInterface(noProto=True)
with caplog.at_level(logging.DEBUG):
@@ -381,7 +424,8 @@ def test_sendPacket_with_destination_as_BROADCAST_ADDR(caplog, reset_globals):
@pytest.mark.unit
def test_sendPacket_with_destination_as_LOCAL_ADDR_no_myInfo(capsys, reset_globals):
@pytest.mark.usefixtures("reset_globals")
def test_sendPacket_with_destination_as_LOCAL_ADDR_no_myInfo(capsys):
"""Test _sendPacket() with LOCAL_ADDR as a destination with no myInfo"""
iface = MeshInterface(noProto=True)
with pytest.raises(SystemExit) as pytest_wrapped_e:
@@ -395,11 +439,13 @@ def test_sendPacket_with_destination_as_LOCAL_ADDR_no_myInfo(capsys, reset_globa
@pytest.mark.unit
def test_sendPacket_with_destination_as_LOCAL_ADDR_with_myInfo(caplog, reset_globals):
@pytest.mark.usefixtures("reset_globals")
def test_sendPacket_with_destination_as_LOCAL_ADDR_with_myInfo(caplog):
"""Test _sendPacket() with LOCAL_ADDR as a destination with myInfo"""
iface = MeshInterface(noProto=True)
myInfo = MagicMock()
iface.myInfo = myInfo
iface.myInfo.my_node_num = 1
with caplog.at_level(logging.DEBUG):
meshPacket = mesh_pb2.MeshPacket()
iface._sendPacket(meshPacket, destinationId=LOCAL_ADDR)
@@ -407,7 +453,8 @@ def test_sendPacket_with_destination_as_LOCAL_ADDR_with_myInfo(caplog, reset_glo
@pytest.mark.unit
def test_sendPacket_with_destination_is_blank_with_nodes(capsys, reset_globals, iface_with_nodes):
@pytest.mark.usefixtures("reset_globals")
def test_sendPacket_with_destination_is_blank_with_nodes(capsys, iface_with_nodes):
"""Test _sendPacket() with '' as a destination with myInfo"""
iface = iface_with_nodes
meshPacket = mesh_pb2.MeshPacket()
@@ -421,7 +468,8 @@ def test_sendPacket_with_destination_is_blank_with_nodes(capsys, reset_globals,
@pytest.mark.unit
def test_sendPacket_with_destination_is_blank_without_nodes(caplog, reset_globals, iface_with_nodes):
@pytest.mark.usefixtures("reset_globals")
def test_sendPacket_with_destination_is_blank_without_nodes(caplog, iface_with_nodes):
"""Test _sendPacket() with '' as a destination with myInfo"""
iface = iface_with_nodes
iface.nodes = None
@@ -432,7 +480,8 @@ def test_sendPacket_with_destination_is_blank_without_nodes(caplog, reset_global
@pytest.mark.unit
def test_getMyNodeInfo(reset_globals):
@pytest.mark.usefixtures("reset_globals")
def test_getMyNodeInfo():
"""Test getMyNodeInfo()"""
iface = MeshInterface(noProto=True)
anode = iface.getNode(LOCAL_ADDR)
@@ -446,7 +495,8 @@ def test_getMyNodeInfo(reset_globals):
@pytest.mark.unit
def test_generatePacketId(capsys, reset_globals):
@pytest.mark.usefixtures("reset_globals")
def test_generatePacketId(capsys):
"""Test _generatePacketId() when no currentPacketId (not connected)"""
iface = MeshInterface(noProto=True)
# not sure when this condition would ever happen... but we can simulate it
@@ -461,7 +511,8 @@ def test_generatePacketId(capsys, reset_globals):
@pytest.mark.unit
def test_fixupPosition_empty_pos(capsys, reset_globals):
@pytest.mark.usefixtures("reset_globals")
def test_fixupPosition_empty_pos():
"""Test _fixupPosition()"""
iface = MeshInterface(noProto=True)
pos = {}
@@ -470,7 +521,8 @@ def test_fixupPosition_empty_pos(capsys, reset_globals):
@pytest.mark.unit
def test_fixupPosition_no_changes_needed(capsys, reset_globals):
@pytest.mark.usefixtures("reset_globals")
def test_fixupPosition_no_changes_needed():
"""Test _fixupPosition()"""
iface = MeshInterface(noProto=True)
pos = {"latitude": 101, "longitude": 102}
@@ -479,7 +531,8 @@ def test_fixupPosition_no_changes_needed(capsys, reset_globals):
@pytest.mark.unit
def test_fixupPosition(capsys, reset_globals):
@pytest.mark.usefixtures("reset_globals")
def test_fixupPosition():
"""Test _fixupPosition()"""
iface = MeshInterface(noProto=True)
pos = {"latitudeI": 1010000000, "longitudeI": 1020000000}
@@ -491,7 +544,8 @@ def test_fixupPosition(capsys, reset_globals):
@pytest.mark.unit
def test_nodeNumToId(capsys, reset_globals, iface_with_nodes):
@pytest.mark.usefixtures("reset_globals")
def test_nodeNumToId(iface_with_nodes):
"""Test _nodeNumToId()"""
iface = iface_with_nodes
iface.myInfo.my_node_num = 2475227164
@@ -500,7 +554,8 @@ def test_nodeNumToId(capsys, reset_globals, iface_with_nodes):
@pytest.mark.unit
def test_nodeNumToId_not_found(capsys, reset_globals, iface_with_nodes):
@pytest.mark.usefixtures("reset_globals")
def test_nodeNumToId_not_found(iface_with_nodes):
"""Test _nodeNumToId()"""
iface = iface_with_nodes
iface.myInfo.my_node_num = 2475227164
@@ -509,7 +564,8 @@ def test_nodeNumToId_not_found(capsys, reset_globals, iface_with_nodes):
@pytest.mark.unit
def test_nodeNumToId_to_all(capsys, reset_globals, iface_with_nodes):
@pytest.mark.usefixtures("reset_globals")
def test_nodeNumToId_to_all(iface_with_nodes):
"""Test _nodeNumToId()"""
iface = iface_with_nodes
iface.myInfo.my_node_num = 2475227164
@@ -518,7 +574,8 @@ def test_nodeNumToId_to_all(capsys, reset_globals, iface_with_nodes):
@pytest.mark.unit
def test_getOrCreateByNum_minimal(capsys, reset_globals, iface_with_nodes):
@pytest.mark.usefixtures("reset_globals")
def test_getOrCreateByNum_minimal(iface_with_nodes):
"""Test _getOrCreateByNum()"""
iface = iface_with_nodes
iface.myInfo.my_node_num = 2475227164
@@ -527,7 +584,8 @@ def test_getOrCreateByNum_minimal(capsys, reset_globals, iface_with_nodes):
@pytest.mark.unit
def test_getOrCreateByNum_not_found(capsys, reset_globals, iface_with_nodes):
@pytest.mark.usefixtures("reset_globals")
def test_getOrCreateByNum_not_found(iface_with_nodes):
"""Test _getOrCreateByNum()"""
iface = iface_with_nodes
iface.myInfo.my_node_num = 2475227164
@@ -537,9 +595,77 @@ def test_getOrCreateByNum_not_found(capsys, reset_globals, iface_with_nodes):
@pytest.mark.unit
def test_getOrCreateByNum(capsys, reset_globals, iface_with_nodes):
@pytest.mark.usefixtures("reset_globals")
def test_getOrCreateByNum(iface_with_nodes):
"""Test _getOrCreateByNum()"""
iface = iface_with_nodes
iface.myInfo.my_node_num = 2475227164
tmp = iface._getOrCreateByNum(2475227164)
assert tmp['num'] == 2475227164
@pytest.mark.unit
def test_enter():
"""Test __enter__()"""
iface = MeshInterface(noProto=True)
assert iface == iface.__enter__()
@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)
@pytest.mark.unit
def test_showNodes_exclude_self(capsys, caplog, iface_with_nodes):
"""Test that we hit that continue statement"""
with caplog.at_level(logging.DEBUG):
iface = iface_with_nodes
iface.localNode.nodeNum = 2475227164
iface.showNodes()
iface.showNodes(includeSelf=False)
capsys.readouterr()
@pytest.mark.unitslow
def test_waitForConfig(capsys):
"""Test waitForConfig()"""
iface = MeshInterface(noProto=True)
# override how long to wait
iface._timeout = Timeout(0.01)
with pytest.raises(Exception) as pytest_wrapped_e:
iface.waitForConfig()
assert pytest_wrapped_e.type == Exception
out, err = capsys.readouterr()
assert re.search(r'Exception: Timed out waiting for interface config', err, re.MULTILINE)
assert out == ''
@pytest.mark.unit
def test_waitConnected_raises_an_exception(capsys):
"""Test waitConnected()"""
iface = MeshInterface(noProto=True)
with pytest.raises(Exception) as pytest_wrapped_e:
iface.failure = "warn about something"
iface._waitConnected(0.01)
assert pytest_wrapped_e.type == Exception
out, err = capsys.readouterr()
assert re.search(r'warn about something', err, re.MULTILINE)
assert out == ''
@pytest.mark.unit
def test_waitConnected_isConnected_timeout(capsys):
"""Test waitConnected()"""
with pytest.raises(Exception) as pytest_wrapped_e:
iface = MeshInterface()
iface._waitConnected(0.01)
assert pytest_wrapped_e.type == Exception
out, err = capsys.readouterr()
assert re.search(r'warn about something', err, re.MULTILINE)
assert out == ''

View File

@@ -11,6 +11,10 @@ from ..serial_interface import SerialInterface
from ..admin_pb2 import AdminMessage
from ..channel_pb2 import Channel
from ..radioconfig_pb2 import RadioConfig
#from ..cannedmessages_pb2 import (CannedMessagePluginMessagePart1, CannedMessagePluginMessagePart2,
# CannedMessagePluginMessagePart3, CannedMessagePluginMessagePart4,
# CannedMessagePluginMessagePart5)
from ..util import Timeout
@pytest.mark.unit
@@ -42,6 +46,122 @@ def test_node_requestConfig(capsys):
assert err == ''
#@pytest.mark.unit
#def test_node_get_canned_message_with_all_parts(capsys):
# """Test run get_canned_message()"""
# iface = MagicMock(autospec=SerialInterface)
# amesg = MagicMock(autospec=AdminMessage)
# with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
# with patch('meshtastic.admin_pb2.AdminMessage', return_value=amesg):
# # we have a sleep in this method, so override it so it goes fast
# with patch('time.sleep'):
# anode = Node(mo, 'bar')
# anode.cannedPluginMessagePart1 = 'a'
# anode.cannedPluginMessagePart2 = 'b'
# anode.cannedPluginMessagePart3 = 'c'
# anode.cannedPluginMessagePart4 = 'd'
# anode.cannedPluginMessagePart5 = 'e'
# anode.get_canned_message()
# out, err = capsys.readouterr()
# assert re.search(r'canned_plugin_message:abcde', out, re.MULTILINE)
# assert err == ''
#
#
#@pytest.mark.unit
#def test_node_get_canned_message_with_some_parts(capsys):
# """Test run get_canned_message()"""
# iface = MagicMock(autospec=SerialInterface)
# amesg = MagicMock(autospec=AdminMessage)
# with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
# with patch('meshtastic.admin_pb2.AdminMessage', return_value=amesg):
# # we have a sleep in this method, so override it so it goes fast
# with patch('time.sleep'):
# anode = Node(mo, 'bar')
# anode.cannedPluginMessagePart1 = 'a'
# anode.get_canned_message()
# out, err = capsys.readouterr()
# assert re.search(r'canned_plugin_message:a', out, re.MULTILINE)
# assert err == ''
#
#
#@pytest.mark.unit
#def test_node_set_canned_message_one_part(caplog):
# """Test run set_canned_message()"""
# iface = MagicMock(autospec=SerialInterface)
# amesg = MagicMock(autospec=AdminMessage)
# with caplog.at_level(logging.DEBUG):
# with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
# with patch('meshtastic.admin_pb2.AdminMessage', return_value=amesg):
# anode = Node(mo, 'bar')
# anode.set_canned_message('foo')
# assert re.search(r"Setting canned message 'foo' part 1", caplog.text, re.MULTILINE)
# assert not re.search(r"Setting canned message '' part 2", caplog.text, re.MULTILINE)
#
#
#@pytest.mark.unit
#def test_node_set_canned_message_200(caplog):
# """Test run set_canned_message() 200 characters long"""
# iface = MagicMock(autospec=SerialInterface)
# amesg = MagicMock(autospec=AdminMessage)
# with caplog.at_level(logging.DEBUG):
# with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
# with patch('meshtastic.admin_pb2.AdminMessage', return_value=amesg):
# anode = Node(mo, 'bar')
# message_200_chars_long = 'a' * 200
# anode.set_canned_message(message_200_chars_long)
# assert re.search(r" part 1", caplog.text, re.MULTILINE)
# assert not re.search(r"Setting canned message '' part 2", caplog.text, re.MULTILINE)
#
#
#@pytest.mark.unit
#def test_node_set_canned_message_201(caplog):
# """Test run set_canned_message() 201 characters long"""
# iface = MagicMock(autospec=SerialInterface)
# amesg = MagicMock(autospec=AdminMessage)
# with caplog.at_level(logging.DEBUG):
# with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
# with patch('meshtastic.admin_pb2.AdminMessage', return_value=amesg):
# anode = Node(mo, 'bar')
# message_201_chars_long = 'a' * 201
# anode.set_canned_message(message_201_chars_long)
# assert re.search(r" part 1", caplog.text, re.MULTILINE)
# assert re.search(r"Setting canned message 'a' part 2", caplog.text, re.MULTILINE)
#
#
#@pytest.mark.unit
#def test_node_set_canned_message_1000(caplog):
# """Test run set_canned_message() 1000 characters long"""
# iface = MagicMock(autospec=SerialInterface)
# amesg = MagicMock(autospec=AdminMessage)
# with caplog.at_level(logging.DEBUG):
# with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
# with patch('meshtastic.admin_pb2.AdminMessage', return_value=amesg):
# anode = Node(mo, 'bar')
# message_1000_chars_long = 'a' * 1000
# anode.set_canned_message(message_1000_chars_long)
# assert re.search(r" part 1", caplog.text, re.MULTILINE)
# assert re.search(r" part 2", caplog.text, re.MULTILINE)
# assert re.search(r" part 3", caplog.text, re.MULTILINE)
# assert re.search(r" part 4", caplog.text, re.MULTILINE)
# assert re.search(r" part 5", caplog.text, re.MULTILINE)
#
#
#@pytest.mark.unit
#def test_node_set_canned_message_1001(capsys):
# """Test run set_canned_message() 1001 characters long"""
# iface = MagicMock(autospec=SerialInterface)
# with pytest.raises(SystemExit) as pytest_wrapped_e:
# with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
# anode = Node(mo, 'bar')
# message_1001_chars_long = 'a' * 1001
# anode.set_canned_message(message_1001_chars_long)
# assert pytest_wrapped_e.type == SystemExit
# assert pytest_wrapped_e.value.code == 1
# out, err = capsys.readouterr()
# assert re.search(r'Warning: The canned message', out, re.MULTILINE)
# assert err == ''
@pytest.mark.unit
def test_setOwner_and_team(caplog):
"""Test setOwner"""
@@ -54,6 +174,15 @@ def test_setOwner_and_team(caplog):
assert re.search(r'p.set_owner.team:1', caplog.text, re.MULTILINE)
@pytest.mark.unit
def test_setOwnerShort(caplog):
"""Test setOwner"""
anode = Node('foo', 'bar', noProto=True)
with caplog.at_level(logging.DEBUG):
anode.setOwner(long_name=None, short_name='123')
assert re.search(r'p.set_owner.short_name:123:', caplog.text, re.MULTILINE)
@pytest.mark.unit
def test_setOwner_no_short_name(caplog):
"""Test setOwner"""
@@ -90,6 +219,16 @@ def test_setOwner_no_short_name_and_long_name_has_words(caplog):
assert re.search(r'p.set_owner.team:0', caplog.text, re.MULTILINE)
@pytest.mark.unit
def test_setOwner_long_name_no_short(caplog):
"""Test setOwner"""
anode = Node('foo', 'bar', noProto=True)
with caplog.at_level(logging.DEBUG):
anode.setOwner(long_name ='Aabo', is_licensed=True)
assert re.search(r'p.set_owner.long_name:Aabo:', caplog.text, re.MULTILINE)
assert re.search(r'p.set_owner.short_name:Aab:', caplog.text, re.MULTILINE)
@pytest.mark.unit
def test_exitSimulator(caplog):
"""Test exitSimulator"""
@@ -108,6 +247,15 @@ def test_reboot(caplog):
assert re.search(r'Telling node to reboot', caplog.text, re.MULTILINE)
@pytest.mark.unit
def test_shutdown(caplog):
"""Test shutdown"""
anode = Node('foo', 'bar', noProto=True)
with caplog.at_level(logging.DEBUG):
anode.shutdown()
assert re.search(r'Telling node to shutdown', caplog.text, re.MULTILINE)
@pytest.mark.unit
def test_setURL_empty_url(capsys):
"""Test reboot"""
@@ -133,13 +281,13 @@ def test_setURL_valid_URL(caplog):
anode.channels = channels
anode.setURL(url)
assert re.search(r'Channel i:0', caplog.text, re.MULTILINE)
assert re.search(r'modem_config: Bw125Cr48Sf4096', caplog.text, re.MULTILINE)
assert re.search(r'modem_config: MidSlow', caplog.text, re.MULTILINE)
assert re.search(r'psk: "\\001"', caplog.text, re.MULTILINE)
assert re.search(r'role: PRIMARY', caplog.text, re.MULTILINE)
@pytest.mark.unit
def test_setURL_valid_URL_but_no_settings(caplog, capsys):
def test_setURL_valid_URL_but_no_settings(capsys):
"""Test setURL"""
iface = MagicMock(autospec=SerialInterface)
url = "https://www.meshtastic.org/d/#"
@@ -187,7 +335,7 @@ def test_showChannels(capsys):
# primary channel
assert re.search(r'Primary channel URL', out, re.MULTILINE)
assert re.search(r'PRIMARY psk=default ', out, re.MULTILINE)
assert re.search(r'"modemConfig": "Bw125Cr48Sf4096"', out, re.MULTILINE)
assert re.search(r'"modemConfig": "MidSlow"', out, re.MULTILINE)
assert re.search(r'"psk": "AQ=="', out, re.MULTILINE)
# secondary channel
assert re.search(r'SECONDARY psk=secret ', out, re.MULTILINE)
@@ -419,7 +567,7 @@ def test_deleteChannel_secondary_with_admin_channel_before_testing():
@pytest.mark.unit
def test_getChannelByName(capsys):
def test_getChannelByName():
"""Get a channel by the name."""
anode = Node('foo', 'bar')
@@ -446,7 +594,7 @@ def test_getChannelByName(capsys):
@pytest.mark.unit
def test_getChannelByName_invalid_name(capsys):
def test_getChannelByName_invalid_name():
"""Get a channel by the name but one that is not present."""
anode = Node('foo', 'bar')
@@ -473,7 +621,7 @@ def test_getChannelByName_invalid_name(capsys):
@pytest.mark.unit
def test_getDisabledChannel(capsys):
def test_getDisabledChannel():
"""Get the first disabled channel."""
anode = Node('foo', 'bar')
@@ -503,7 +651,7 @@ def test_getDisabledChannel(capsys):
@pytest.mark.unit
def test_getDisabledChannel_where_all_channels_are_used(capsys):
def test_getDisabledChannel_where_all_channels_are_used():
"""Get the first disabled channel."""
anode = Node('foo', 'bar')
@@ -527,7 +675,7 @@ def test_getDisabledChannel_where_all_channels_are_used(capsys):
@pytest.mark.unit
def test_getAdminChannelIndex(capsys):
def test_getAdminChannelIndex():
"""Get the 'admin' channel index."""
anode = Node('foo', 'bar')
@@ -554,7 +702,7 @@ def test_getAdminChannelIndex(capsys):
@pytest.mark.unit
def test_getAdminChannelIndex_when_no_admin_named_channel(capsys):
def test_getAdminChannelIndex_when_no_admin_named_channel():
"""Get the 'admin' channel when there is not one."""
anode = Node('foo', 'bar')
@@ -665,6 +813,352 @@ def test_requestChannel_localNode(caplog):
assert not re.search(r'from remote node', caplog.text, re.MULTILINE)
#@pytest.mark.unit
#def test_onResponseRequestCannedMessagePluginMesagePart1(caplog):
# """Test onResponseRequestCannedMessagePluginMessagePart1()"""
#
# part1 = CannedMessagePluginMessagePart1()
# part1.text = 'foo1'
#
# msg1 = MagicMock(autospec=AdminMessage)
# msg1.get_canned_message_plugin_part1_response = part1
#
# packet = {
# 'from': 682968612,
# 'to': 682968612,
# 'decoded': {
# 'portnum': 'ADMIN_APP',
# 'payload': 'faked',
# 'requestId': 927039000,
# 'admin': {
# 'getCannedMessagePluginPart1Response': {'text': 'foo1'},
# 'raw': msg1
# }
# },
# 'id': 589440320,
# 'rxTime': 1642710843,
# 'hopLimit': 3,
# 'priority': 'RELIABLE',
# 'raw': 'faked',
# 'fromId': '!28b54624',
# 'toId': '!28b54624'
# }
#
# iface = MagicMock(autospec=SerialInterface)
# with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
# anode = Node(mo, 'bar', noProto=True)
# # Note: Have to do this next line because every call to MagicMock object/method returns a new magic mock
# mo.localNode = anode
#
# with caplog.at_level(logging.DEBUG):
# anode.onResponseRequestCannedMessagePluginMessagePart1(packet)
# assert re.search(r'onResponseRequestCannedMessagePluginMessagePart1', caplog.text, re.MULTILINE)
# assert anode.cannedPluginMessagePart1 == 'foo1'
#@pytest.mark.unit
#def test_onResponseRequestCannedMessagePluginMesagePart2(caplog):
# """Test onResponseRequestCannedMessagePluginMessagePart2()"""
#
# part2 = CannedMessagePluginMessagePart2()
# part2.text = 'foo2'
#
# msg2 = MagicMock(autospec=AdminMessage)
# msg2.get_canned_message_plugin_part2_response = part2
#
# packet = {
# 'from': 682968612,
# 'to': 682968612,
# 'decoded': {
# 'portnum': 'ADMIN_APP',
# 'payload': 'faked',
# 'requestId': 927039000,
# 'admin': {
# 'getCannedMessagePluginPart2Response': {'text': 'foo2'},
# 'raw': msg2
# }
# },
# 'id': 589440320,
# 'rxTime': 1642710843,
# 'hopLimit': 3,
# 'priority': 'RELIABLE',
# 'raw': 'faked',
# 'fromId': '!28b54624',
# 'toId': '!28b54624'
# }
#
# iface = MagicMock(autospec=SerialInterface)
# with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
# anode = Node(mo, 'bar', noProto=True)
# # Note: Have to do this next line because every call to MagicMock object/method returns a new magic mock
# mo.localNode = anode
#
# with caplog.at_level(logging.DEBUG):
# anode.onResponseRequestCannedMessagePluginMessagePart2(packet)
# assert re.search(r'onResponseRequestCannedMessagePluginMessagePart2', caplog.text, re.MULTILINE)
# assert anode.cannedPluginMessagePart2 == 'foo2'
#@pytest.mark.unit
#def test_onResponseRequestCannedMessagePluginMesagePart3(caplog):
# """Test onResponseRequestCannedMessagePluginMessagePart3()"""
#
# part3 = CannedMessagePluginMessagePart3()
# part3.text = 'foo3'
#
# msg3 = MagicMock(autospec=AdminMessage)
# msg3.get_canned_message_plugin_part3_response = part3
#
# packet = {
# 'from': 682968612,
# 'to': 682968612,
# 'decoded': {
# 'portnum': 'ADMIN_APP',
# 'payload': 'faked',
# 'requestId': 927039000,
# 'admin': {
# 'getCannedMessagePluginPart3Response': {'text': 'foo3'},
# 'raw': msg3
# }
# },
# 'id': 589440320,
# 'rxTime': 1642710843,
# 'hopLimit': 3,
# 'priority': 'RELIABLE',
# 'raw': 'faked',
# 'fromId': '!28b54624',
# 'toId': '!28b54624'
# }
#
# iface = MagicMock(autospec=SerialInterface)
# with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
# anode = Node(mo, 'bar', noProto=True)
# # Note: Have to do this next line because every call to MagicMock object/method returns a new magic mock
# mo.localNode = anode
#
# with caplog.at_level(logging.DEBUG):
# anode.onResponseRequestCannedMessagePluginMessagePart3(packet)
# assert re.search(r'onResponseRequestCannedMessagePluginMessagePart3', caplog.text, re.MULTILINE)
# assert anode.cannedPluginMessagePart3 == 'foo3'
#@pytest.mark.unit
#def test_onResponseRequestCannedMessagePluginMesagePart4(caplog):
# """Test onResponseRequestCannedMessagePluginMessagePart4()"""
#
# part4 = CannedMessagePluginMessagePart4()
# part4.text = 'foo4'
#
# msg4 = MagicMock(autospec=AdminMessage)
# msg4.get_canned_message_plugin_part4_response = part4
#
# packet = {
# 'from': 682968612,
# 'to': 682968612,
# 'decoded': {
# 'portnum': 'ADMIN_APP',
# 'payload': 'faked',
# 'requestId': 927039000,
# 'admin': {
# 'getCannedMessagePluginPart4Response': {'text': 'foo4'},
# 'raw': msg4
# }
# },
# 'id': 589440320,
# 'rxTime': 1642710843,
# 'hopLimit': 3,
# 'priority': 'RELIABLE',
# 'raw': 'faked',
# 'fromId': '!28b54624',
# 'toId': '!28b54624'
# }
#
# iface = MagicMock(autospec=SerialInterface)
# with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
# anode = Node(mo, 'bar', noProto=True)
# # Note: Have to do this next line because every call to MagicMock object/method returns a new magic mock
# mo.localNode = anode
#
# with caplog.at_level(logging.DEBUG):
# anode.onResponseRequestCannedMessagePluginMessagePart4(packet)
# assert re.search(r'onResponseRequestCannedMessagePluginMessagePart4', caplog.text, re.MULTILINE)
# assert anode.cannedPluginMessagePart4 == 'foo4'
#@pytest.mark.unit
#def test_onResponseRequestCannedMessagePluginMesagePart5(caplog):
# """Test onResponseRequestCannedMessagePluginMessagePart5()"""
#
# part5 = CannedMessagePluginMessagePart5()
# part5.text = 'foo5'
#
# msg5 = MagicMock(autospec=AdminMessage)
# msg5.get_canned_message_plugin_part5_response = part5
#
#
# packet = {
# 'from': 682968612,
# 'to': 682968612,
# 'decoded': {
# 'portnum': 'ADMIN_APP',
# 'payload': 'faked',
# 'requestId': 927039000,
# 'admin': {
# 'getCannedMessagePluginPart5Response': {'text': 'foo5'},
# 'raw': msg5
# }
# },
# 'id': 589440320,
# 'rxTime': 1642710843,
# 'hopLimit': 3,
# 'priority': 'RELIABLE',
# 'raw': 'faked',
# 'fromId': '!28b54624',
# 'toId': '!28b54624'
# }
#
# iface = MagicMock(autospec=SerialInterface)
# with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
# anode = Node(mo, 'bar', noProto=True)
# # Note: Have to do this next line because every call to MagicMock object/method returns a new magic mock
# mo.localNode = anode
#
# with caplog.at_level(logging.DEBUG):
# anode.onResponseRequestCannedMessagePluginMessagePart5(packet)
# assert re.search(r'onResponseRequestCannedMessagePluginMessagePart5', caplog.text, re.MULTILINE)
# assert anode.cannedPluginMessagePart5 == 'foo5'
#@pytest.mark.unit
#def test_onResponseRequestCannedMessagePluginMesagePart1_error(caplog, capsys):
# """Test onResponseRequestCannedMessagePluginMessagePart1() with error"""
#
# packet = {
# 'decoded': {
# 'routing': {
# 'errorReason': 'some made up error',
# },
# },
# }
#
# iface = MagicMock(autospec=SerialInterface)
# with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
# anode = Node(mo, 'bar', noProto=True)
# # Note: Have to do this next line because every call to MagicMock object/method returns a new magic mock
# mo.localNode = anode
#
# with caplog.at_level(logging.DEBUG):
# anode.onResponseRequestCannedMessagePluginMessagePart1(packet)
# assert re.search(r'onResponseRequestCannedMessagePluginMessagePart1', caplog.text, re.MULTILINE)
# out, err = capsys.readouterr()
# assert re.search(r'Error on response', out)
# assert err == ''
#@pytest.mark.unit
#def test_onResponseRequestCannedMessagePluginMesagePart2_error(caplog, capsys):
# """Test onResponseRequestCannedMessagePluginMessagePart2() with error"""
#
# packet = {
# 'decoded': {
# 'routing': {
# 'errorReason': 'some made up error',
# },
# },
# }
#
# iface = MagicMock(autospec=SerialInterface)
# with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
# anode = Node(mo, 'bar', noProto=True)
# # Note: Have to do this next line because every call to MagicMock object/method returns a new magic mock
# mo.localNode = anode
#
# with caplog.at_level(logging.DEBUG):
# anode.onResponseRequestCannedMessagePluginMessagePart2(packet)
# assert re.search(r'onResponseRequestCannedMessagePluginMessagePart2', caplog.text, re.MULTILINE)
# out, err = capsys.readouterr()
# assert re.search(r'Error on response', out)
# assert err == ''
#@pytest.mark.unit
#def test_onResponseRequestCannedMessagePluginMesagePart3_error(caplog, capsys):
# """Test onResponseRequestCannedMessagePluginMessagePart3() with error"""
#
# packet = {
# 'decoded': {
# 'routing': {
# 'errorReason': 'some made up error',
# },
# },
# }
#
# iface = MagicMock(autospec=SerialInterface)
# with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
# anode = Node(mo, 'bar', noProto=True)
# # Note: Have to do this next line because every call to MagicMock object/method returns a new magic mock
# mo.localNode = anode
#
# with caplog.at_level(logging.DEBUG):
# anode.onResponseRequestCannedMessagePluginMessagePart3(packet)
# assert re.search(r'onResponseRequestCannedMessagePluginMessagePart3', caplog.text, re.MULTILINE)
# out, err = capsys.readouterr()
# assert re.search(r'Error on response', out)
# assert err == ''
#
#
#@pytest.mark.unit
#def test_onResponseRequestCannedMessagePluginMesagePart4_error(caplog, capsys):
# """Test onResponseRequestCannedMessagePluginMessagePart4() with error"""
#
# packet = {
# 'decoded': {
# 'routing': {
# 'errorReason': 'some made up error',
# },
# },
# }
#
# iface = MagicMock(autospec=SerialInterface)
# with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
# anode = Node(mo, 'bar', noProto=True)
# # Note: Have to do this next line because every call to MagicMock object/method returns a new magic mock
# mo.localNode = anode
#
# with caplog.at_level(logging.DEBUG):
# anode.onResponseRequestCannedMessagePluginMessagePart4(packet)
# assert re.search(r'onResponseRequestCannedMessagePluginMessagePart4', caplog.text, re.MULTILINE)
# out, err = capsys.readouterr()
# assert re.search(r'Error on response', out)
# assert err == ''
#
#
#@pytest.mark.unit
#def test_onResponseRequestCannedMessagePluginMesagePart5_error(caplog, capsys):
# """Test onResponseRequestCannedMessagePluginMessagePart5() with error"""
#
# packet = {
# 'decoded': {
# 'routing': {
# 'errorReason': 'some made up error',
# },
# },
# }
#
# iface = MagicMock(autospec=SerialInterface)
# with patch('meshtastic.serial_interface.SerialInterface', return_value=iface) as mo:
# anode = Node(mo, 'bar', noProto=True)
# # Note: Have to do this next line because every call to MagicMock object/method returns a new magic mock
# mo.localNode = anode
#
# with caplog.at_level(logging.DEBUG):
# anode.onResponseRequestCannedMessagePluginMessagePart5(packet)
# assert re.search(r'onResponseRequestCannedMessagePluginMessagePart5', caplog.text, re.MULTILINE)
# out, err = capsys.readouterr()
# assert re.search(r'Error on response', out)
# assert err == ''
@pytest.mark.unit
def test_onResponseRequestChannel(caplog):
"""Test onResponseRequestChannel()"""
@@ -869,3 +1363,14 @@ def test_onResponseRequestSetting_with_error(capsys):
out, err = capsys.readouterr()
assert re.search(r'Error on response', out)
assert err == ''
@pytest.mark.unitslow
def test_waitForConfig():
"""Test waitForConfig()"""
anode = Node('foo', 'bar')
radioConfig = RadioConfig()
anode.radioConfig = radioConfig
anode._timeout = Timeout(0.01)
result = anode.waitForConfig()
assert not result

View File

@@ -3,15 +3,19 @@
import re
from unittest.mock import patch
from unittest.mock import patch, mock_open
import pytest
from ..serial_interface import SerialInterface
@pytest.mark.unit
@patch("time.sleep")
@patch("termios.tcsetattr")
@patch("termios.tcgetattr")
@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, capsys):
def test_SerialInterface_single_port(mocked_findPorts, mocked_serial, mocked_open, mock_get, mock_set, mock_sleep, capsys):
"""Test that we can instantiate a SerialInterface with a single port"""
iface = SerialInterface(noProto=True)
iface.showInfo()
@@ -19,6 +23,10 @@ def test_SerialInterface_single_port(mocked_findPorts, mocked_serial, capsys):
iface.close()
mocked_findPorts.assert_called()
mocked_serial.assert_called()
mocked_open.assert_called()
mock_get.assert_called()
mock_set.assert_called()
mock_sleep.assert_called()
out, err = capsys.readouterr()
assert re.search(r'Nodes in mesh', out, re.MULTILINE)
assert re.search(r'Preferences', out, re.MULTILINE)

View File

@@ -160,7 +160,7 @@ def test_smoke1_send_hello():
def test_smoke1_port():
"""Test --port"""
# first, get the ports
ports = findPorts()
ports = findPorts(True)
# hopefully there is just one
assert len(ports) == 1
port = ports[0]

View File

@@ -0,0 +1,721 @@
"""Meshtastic smoke tests with a single virtual device via localhost.
During the CI build of the Meshtastic-device, a build.zip file is created.
Inside that build.zip is a standalone executable meshtasticd_linux_amd64.
That linux executable will simulate a Meshtastic device listening on localhost.
This smoke test runs against that localhost.
"""
import re
import subprocess
import time
import platform
import os
# Do not like using hard coded sleeps, but it probably makes
# sense to pause for the radio at apprpriate times
import pytest
from ..util import findPorts
# seconds to pause after running a meshtastic command
PAUSE_AFTER_COMMAND = 0.1
PAUSE_AFTER_REBOOT = 0.2
#TODO: need to fix the virtual device to have a reboot. When you issue the command
# below, you get "FIXME implement reboot for this platform"
#@pytest.mark.smokevirt
#def test_smokevirt_reboot():
# """Test reboot"""
# return_value, _ = subprocess.getstatusoutput('meshtastic --host localhost --reboot')
# assert return_value == 0
# # pause for the radio to reset
# time.sleep(8)
@pytest.mark.smokevirt
def test_smokevirt_info():
"""Test --info"""
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --info')
assert re.match(r'Connected to radio', out)
assert re.search(r'^Owner', out, re.MULTILINE)
assert re.search(r'^My info', out, re.MULTILINE)
assert re.search(r'^Nodes in mesh', out, re.MULTILINE)
assert re.search(r'^Preferences', out, re.MULTILINE)
assert re.search(r'^Channels', out, re.MULTILINE)
assert re.search(r'^ PRIMARY', out, re.MULTILINE)
assert re.search(r'^Primary channel URL', out, re.MULTILINE)
assert return_value == 0
@pytest.mark.smokevirt
def test_smokevirt_sendping():
"""Test --sendping"""
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --sendping')
assert re.match(r'Connected to radio', out)
assert re.search(r'^Sending ping message', out, re.MULTILINE)
assert return_value == 0
@pytest.mark.smokevirt
def test_get_with_invalid_setting():
"""Test '--get a_bad_setting'."""
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --get a_bad_setting')
assert re.search(r'Choices in sorted order', out)
assert return_value == 0
@pytest.mark.smokevirt
def test_set_with_invalid_setting():
"""Test '--set a_bad_setting'."""
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --set a_bad_setting foo')
assert re.search(r'Choices in sorted order', out)
assert return_value == 0
@pytest.mark.smokevirt
def test_ch_set_with_invalid_settingpatch_find_ports():
"""Test '--ch-set with a_bad_setting'."""
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-set invalid_setting foo --ch-index 0')
assert re.search(r'Choices in sorted order', out)
assert return_value == 0
@pytest.mark.smokevirt
def test_smokevirt_pos_fields():
"""Test --pos-fields (with some values POS_ALTITUDE POS_ALT_MSL POS_BATTERY)"""
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --pos-fields POS_ALTITUDE POS_ALT_MSL POS_BATTERY')
assert re.match(r'Connected to radio', out)
assert re.search(r'^Setting position fields to 35', out, re.MULTILINE)
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_COMMAND)
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --pos-fields')
assert re.match(r'Connected to radio', out)
assert re.search(r'POS_ALTITUDE', out, re.MULTILINE)
assert re.search(r'POS_ALT_MSL', out, re.MULTILINE)
assert re.search(r'POS_BATTERY', out, re.MULTILINE)
assert return_value == 0
@pytest.mark.smokevirt
def test_smokevirt_test_with_arg_but_no_hardware():
"""Test --test
Note: Since only one device is connected, it will not do much.
"""
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --test')
assert re.search(r'^Warning: Must have at least two devices', out, re.MULTILINE)
assert return_value == 1
@pytest.mark.smokevirt
def test_smokevirt_debug():
"""Test --debug"""
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --info --debug')
assert re.search(r'^Owner', out, re.MULTILINE)
assert re.search(r'^DEBUG file', out, re.MULTILINE)
assert return_value == 0
@pytest.mark.smokevirt
def test_smokevirt_seriallog_to_file():
"""Test --seriallog to a file creates a file"""
filename = 'tmpoutput.txt'
if os.path.exists(f"{filename}"):
os.remove(f"{filename}")
return_value, _ = subprocess.getstatusoutput(f'meshtastic --host localhost --info --seriallog {filename}')
assert os.path.exists(f"{filename}")
assert return_value == 0
os.remove(f"{filename}")
@pytest.mark.smokevirt
def test_smokevirt_qr():
"""Test --qr"""
filename = 'tmpqr'
if os.path.exists(f"{filename}"):
os.remove(f"{filename}")
return_value, _ = subprocess.getstatusoutput(f'meshtastic --host localhost --qr > {filename}')
assert os.path.exists(f"{filename}")
# not really testing that a valid qr code is created, just that the file size
# is reasonably big enough for a qr code
assert os.stat(f"{filename}").st_size > 20000
assert return_value == 0
os.remove(f"{filename}")
@pytest.mark.smokevirt
def test_smokevirt_nodes():
"""Test --nodes"""
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --nodes')
assert re.match(r'Connected to radio', out)
if platform.system() != 'Windows':
assert re.search(r' User ', out, re.MULTILINE)
assert re.search(r' 1 ', out, re.MULTILINE)
assert return_value == 0
@pytest.mark.smokevirt
def test_smokevirt_send_hello():
"""Test --sendtext hello"""
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --sendtext hello')
assert re.match(r'Connected to radio', out)
assert re.search(r'^Sending text message hello to \^all', out, re.MULTILINE)
assert return_value == 0
@pytest.mark.smokevirt
def test_smokevirt_port():
"""Test --port"""
# first, get the ports
ports = findPorts()
# hopefully there is none
assert len(ports) == 0
@pytest.mark.smokevirt
def test_smokevirt_set_is_router_true():
"""Test --set is_router true"""
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --set is_router true')
assert re.match(r'Connected to radio', out)
assert re.search(r'^Set is_router to true', out, re.MULTILINE)
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_COMMAND)
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --get is_router')
assert re.search(r'^is_router: True', out, re.MULTILINE)
assert return_value == 0
@pytest.mark.smokevirt
def test_smokevirt_set_location_info():
"""Test --setlat, --setlon and --setalt """
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --setlat 32.7767 --setlon -96.7970 --setalt 1337')
assert re.match(r'Connected to radio', out)
assert re.search(r'^Fixing altitude', out, re.MULTILINE)
assert re.search(r'^Fixing latitude', out, re.MULTILINE)
assert re.search(r'^Fixing longitude', out, re.MULTILINE)
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_COMMAND)
return_value, out2 = subprocess.getstatusoutput('meshtastic --host localhost --info')
assert re.search(r'1337', out2, re.MULTILINE)
assert re.search(r'32.7767', out2, re.MULTILINE)
assert re.search(r'-96.797', out2, re.MULTILINE)
assert return_value == 0
@pytest.mark.smokevirt
def test_smokevirt_set_is_router_false():
"""Test --set is_router false"""
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --set is_router false')
assert re.match(r'Connected to radio', out)
assert re.search(r'^Set is_router to false', out, re.MULTILINE)
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_COMMAND)
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --get is_router')
assert re.search(r'^is_router: False', out, re.MULTILINE)
assert return_value == 0
@pytest.mark.smokevirt
def test_smokevirt_set_owner():
"""Test --set-owner name"""
# make sure the owner is not Joe
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --set-owner Bob')
assert re.match(r'Connected to radio', out)
assert re.search(r'^Setting device owner to Bob', out, re.MULTILINE)
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_COMMAND)
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --info')
assert not re.search(r'Owner: Joe', out, re.MULTILINE)
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_COMMAND)
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --set-owner Joe')
assert re.match(r'Connected to radio', out)
assert re.search(r'^Setting device owner to Joe', out, re.MULTILINE)
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_COMMAND)
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --info')
assert re.search(r'Owner: Joe', out, re.MULTILINE)
assert return_value == 0
@pytest.mark.smokevirt
def test_smokevirt_set_team():
"""Test --set-team """
# unset the team
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --set-team CLEAR')
assert re.match(r'Connected to radio', out)
assert re.search(r'^Setting team to CLEAR', out, re.MULTILINE)
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_REBOOT)
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --set-team CYAN')
assert re.search(r'Setting team to CYAN', out, re.MULTILINE)
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_REBOOT)
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --info')
assert re.search(r'CYAN', out, re.MULTILINE)
assert return_value == 0
@pytest.mark.smokevirt
def test_smokevirt_ch_values():
"""Test --ch-longslow, --ch-longfast, --ch-mediumslow, --ch-mediumsfast,
--ch-shortslow, and --ch-shortfast arguments
"""
exp = {
'--ch-longslow': 'Bw125Cr48Sf4096',
'--ch-longfast': 'Bw31_25Cr48Sf512',
'--ch-mediumslow': 'Bw250Cr46Sf2048',
'--ch-mediumfast': 'Bw250Cr47Sf1024',
'--ch-shortslow': '{ "psk',
'--ch-shortfast': 'Bw500Cr45Sf128'
}
for key, val in exp.items():
return_value, out = subprocess.getstatusoutput(f'meshtastic --host localhost {key}')
assert re.match(r'Connected to radio', out)
assert re.search(r'Writing modified channels to device', out, re.MULTILINE)
assert return_value == 0
# pause for the radio (might reboot)
time.sleep(PAUSE_AFTER_REBOOT)
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --info')
assert re.search(val, out, re.MULTILINE)
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_COMMAND)
@pytest.mark.smokevirt
def test_smokevirt_ch_set_name():
"""Test --ch-set name"""
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --info')
assert not re.search(r'MyChannel', out, re.MULTILINE)
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_COMMAND)
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-set name MyChannel')
assert re.match(r'Connected to radio', out)
assert re.search(r'Warning: Need to specify', out, re.MULTILINE)
assert return_value == 1
# pause for the radio
time.sleep(PAUSE_AFTER_COMMAND)
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-set name MyChannel --ch-index 0')
assert re.match(r'Connected to radio', out)
assert re.search(r'^Set name to MyChannel', out, re.MULTILINE)
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_COMMAND)
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --info')
assert re.search(r'MyChannel', out, re.MULTILINE)
assert return_value == 0
@pytest.mark.smokevirt
def test_smokevirt_ch_set_downlink_and_uplink():
"""Test -ch-set downlink_enabled X and --ch-set uplink_enabled X"""
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-set downlink_enabled false --ch-set uplink_enabled false')
assert re.match(r'Connected to radio', out)
assert re.search(r'Warning: Need to specify', out, re.MULTILINE)
assert return_value == 1
# pause for the radio
time.sleep(PAUSE_AFTER_COMMAND)
# pylint: disable=C0301
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-set downlink_enabled false --ch-set uplink_enabled false --ch-index 0')
assert re.match(r'Connected to radio', out)
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_COMMAND)
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --info')
assert not re.search(r'uplinkEnabled', out, re.MULTILINE)
assert not re.search(r'downlinkEnabled', out, re.MULTILINE)
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_COMMAND)
# pylint: disable=C0301
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-set downlink_enabled true --ch-set uplink_enabled true --ch-index 0')
assert re.match(r'Connected to radio', out)
assert re.search(r'^Set downlink_enabled to true', out, re.MULTILINE)
assert re.search(r'^Set uplink_enabled to true', out, re.MULTILINE)
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_COMMAND)
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --info')
assert re.search(r'uplinkEnabled', out, re.MULTILINE)
assert re.search(r'downlinkEnabled', out, re.MULTILINE)
assert return_value == 0
@pytest.mark.smokevirt
def test_smokevirt_ch_add_and_ch_del():
"""Test --ch-add"""
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-index 1 --ch-del')
assert re.search(r'Deleting channel 1', out, re.MULTILINE)
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_REBOOT)
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-add testing')
assert re.search(r'Writing modified channels to device', out, re.MULTILINE)
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_COMMAND)
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --info')
assert re.match(r'Connected to radio', out)
assert re.search(r'SECONDARY', out, re.MULTILINE)
assert re.search(r'testing', out, re.MULTILINE)
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_COMMAND)
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-index 1 --ch-del')
assert re.search(r'Deleting channel 1', out, re.MULTILINE)
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_REBOOT)
# make sure the secondary channel is not there
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --info')
assert re.match(r'Connected to radio', out)
assert not re.search(r'SECONDARY', out, re.MULTILINE)
assert not re.search(r'testing', out, re.MULTILINE)
assert return_value == 0
@pytest.mark.smokevirt
def test_smokevirt_ch_enable_and_disable():
"""Test --ch-enable and --ch-disable"""
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-index 1 --ch-del')
assert re.search(r'Deleting channel 1', out, re.MULTILINE)
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_REBOOT)
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-add testing')
assert re.search(r'Writing modified channels to device', out, re.MULTILINE)
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_COMMAND)
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --info')
assert re.match(r'Connected to radio', out)
assert re.search(r'SECONDARY', out, re.MULTILINE)
assert re.search(r'testing', out, re.MULTILINE)
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_COMMAND)
# ensure they need to specify a --ch-index
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-disable')
assert return_value == 1
# pause for the radio
time.sleep(PAUSE_AFTER_COMMAND)
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-disable --ch-index 1')
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_COMMAND)
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --info')
assert re.match(r'Connected to radio', out)
assert re.search(r'DISABLED', out, re.MULTILINE)
assert re.search(r'testing', out, re.MULTILINE)
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_COMMAND)
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-enable --ch-index 1')
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_COMMAND)
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --info')
assert re.match(r'Connected to radio', out)
assert re.search(r'SECONDARY', out, re.MULTILINE)
assert re.search(r'testing', out, re.MULTILINE)
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_COMMAND)
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-del --ch-index 1')
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_COMMAND)
@pytest.mark.smokevirt
def test_smokevirt_ch_del_a_disabled_non_primary_channel():
"""Test --ch-del will work on a disabled non-primary channel."""
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-index 1 --ch-del')
assert re.search(r'Deleting channel 1', out, re.MULTILINE)
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_REBOOT)
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-add testing')
assert re.search(r'Writing modified channels to device', out, re.MULTILINE)
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_COMMAND)
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --info')
assert re.match(r'Connected to radio', out)
assert re.search(r'SECONDARY', out, re.MULTILINE)
assert re.search(r'testing', out, re.MULTILINE)
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_COMMAND)
# ensure they need to specify a --ch-index
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-disable')
assert return_value == 1
# pause for the radio
time.sleep(PAUSE_AFTER_COMMAND)
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-del --ch-index 1')
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_COMMAND)
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --info')
assert re.match(r'Connected to radio', out)
assert not re.search(r'DISABLED', out, re.MULTILINE)
assert not re.search(r'SECONDARY', out, re.MULTILINE)
assert not re.search(r'testing', out, re.MULTILINE)
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_COMMAND)
@pytest.mark.smokevirt
def test_smokevirt_attempt_to_delete_primary_channel():
"""Test that we cannot delete the PRIMARY channel."""
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-del --ch-index 0')
assert re.search(r'Warning: Cannot delete primary channel', out, re.MULTILINE)
assert return_value == 1
# pause for the radio
time.sleep(PAUSE_AFTER_COMMAND)
@pytest.mark.smokevirt
def test_smokevirt_attempt_to_disable_primary_channel():
"""Test that we cannot disable the PRIMARY channel."""
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-disable --ch-index 0')
assert re.search(r'Warning: Cannot enable', out, re.MULTILINE)
assert return_value == 1
# pause for the radio
time.sleep(PAUSE_AFTER_COMMAND)
@pytest.mark.smokevirt
def test_smokevirt_attempt_to_enable_primary_channel():
"""Test that we cannot enable the PRIMARY channel."""
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-enable --ch-index 0')
assert re.search(r'Warning: Cannot enable', out, re.MULTILINE)
assert return_value == 1
# pause for the radio
time.sleep(PAUSE_AFTER_COMMAND)
@pytest.mark.smokevirt
def test_smokevirt_ensure_ch_del_second_of_three_channels():
"""Test that when we delete the 2nd of 3 channels, that it deletes the correct channel."""
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-add testing1')
assert re.match(r'Connected to radio', out)
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_COMMAND)
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --info')
assert re.match(r'Connected to radio', out)
assert re.search(r'SECONDARY', out, re.MULTILINE)
assert re.search(r'testing1', out, re.MULTILINE)
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_COMMAND)
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-add testing2')
assert re.match(r'Connected to radio', out)
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_COMMAND)
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --info')
assert re.match(r'Connected to radio', out)
assert re.search(r'testing2', out, re.MULTILINE)
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_COMMAND)
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-del --ch-index 1')
assert re.match(r'Connected to radio', out)
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_COMMAND)
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --info')
assert re.match(r'Connected to radio', out)
assert re.search(r'testing2', out, re.MULTILINE)
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_COMMAND)
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-del --ch-index 1')
assert re.match(r'Connected to radio', out)
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_COMMAND)
@pytest.mark.smokevirt
def test_smokevirt_ensure_ch_del_third_of_three_channels():
"""Test that when we delete the 3rd of 3 channels, that it deletes the correct channel."""
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-add testing1')
assert re.match(r'Connected to radio', out)
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_COMMAND)
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --info')
assert re.match(r'Connected to radio', out)
assert re.search(r'SECONDARY', out, re.MULTILINE)
assert re.search(r'testing1', out, re.MULTILINE)
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_COMMAND)
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-add testing2')
assert re.match(r'Connected to radio', out)
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_COMMAND)
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --info')
assert re.match(r'Connected to radio', out)
assert re.search(r'testing2', out, re.MULTILINE)
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_COMMAND)
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-del --ch-index 2')
assert re.match(r'Connected to radio', out)
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_COMMAND)
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --info')
assert re.match(r'Connected to radio', out)
assert re.search(r'testing1', out, re.MULTILINE)
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_COMMAND)
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-del --ch-index 1')
assert re.match(r'Connected to radio', out)
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_COMMAND)
@pytest.mark.smokevirt
def test_smokevirt_ch_set_modem_config():
"""Test --ch-set modem_config"""
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-set modem_config Bw31_25Cr48Sf512')
assert re.search(r'Warning: Need to specify', out, re.MULTILINE)
assert return_value == 1
# pause for the radio
time.sleep(PAUSE_AFTER_COMMAND)
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --info')
assert not re.search(r'Bw31_25Cr48Sf512', out, re.MULTILINE)
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_COMMAND)
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-set modem_config Bw31_25Cr48Sf512 --ch-index 0')
assert re.match(r'Connected to radio', out)
assert re.search(r'^Set modem_config to Bw31_25Cr48Sf512', out, re.MULTILINE)
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_COMMAND)
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --info')
assert re.search(r'Bw31_25Cr48Sf512', out, re.MULTILINE)
assert return_value == 0
@pytest.mark.smokevirt
def test_smokevirt_seturl_default():
"""Test --seturl with default value"""
# set some channel value so we no longer have a default channel
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --ch-set name foo --ch-index 0')
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_COMMAND)
# ensure we no longer have a default primary channel
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --info')
assert not re.search('CgUYAyIBAQ', out, re.MULTILINE)
assert return_value == 0
url = "https://www.meshtastic.org/d/#CgUYAyIBAQ"
return_value, out = subprocess.getstatusoutput(f"meshtastic --host localhost --seturl {url}")
assert re.match(r'Connected to radio', out)
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_COMMAND)
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --info')
assert re.search('CgUYAyIBAQ', out, re.MULTILINE)
assert return_value == 0
@pytest.mark.smokevirt
def test_smokevirt_seturl_invalid_url():
"""Test --seturl with invalid url"""
# Note: This url is no longer a valid url.
url = "https://www.meshtastic.org/c/#GAMiENTxuzogKQdZ8Lz_q89Oab8qB0RlZmF1bHQ="
return_value, out = subprocess.getstatusoutput(f"meshtastic --host localhost --seturl {url}")
assert re.match(r'Connected to radio', out)
assert re.search('Warning: There were no settings', out, re.MULTILINE)
assert return_value == 1
# pause for the radio
time.sleep(PAUSE_AFTER_COMMAND)
@pytest.mark.smokevirt
def test_smokevirt_configure():
"""Test --configure"""
_ , out = subprocess.getstatusoutput(f"meshtastic --host localhost --configure example_config.yaml")
assert re.match(r'Connected to radio', out)
assert re.search('^Setting device owner to Bob TBeam', out, re.MULTILINE)
assert re.search('^Fixing altitude at 304 meters', out, re.MULTILINE)
assert re.search('^Fixing latitude at 35.8', out, re.MULTILINE)
assert re.search('^Fixing longitude at -93.8', out, re.MULTILINE)
assert re.search('^Setting device position', out, re.MULTILINE)
assert re.search('^Set region to 1', out, re.MULTILINE)
assert re.search('^Set is_always_powered to true', out, re.MULTILINE)
assert re.search('^Set send_owner_interval to 2', out, re.MULTILINE)
assert re.search('^Set screen_on_secs to 31536000', out, re.MULTILINE)
assert re.search('^Set wait_bluetooth_secs to 31536000', out, re.MULTILINE)
assert re.search('^Writing modified preferences to device', out, re.MULTILINE)
# pause for the radio
time.sleep(PAUSE_AFTER_REBOOT)
@pytest.mark.smokevirt
def test_smokevirt_set_ham():
"""Test --set-ham
Note: Do a factory reset after this setting so it is very short-lived.
"""
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --set-ham KI1234')
assert re.search(r'Setting Ham ID', out, re.MULTILINE)
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_REBOOT)
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --info')
assert re.search(r'Owner: KI1234', out, re.MULTILINE)
assert return_value == 0
@pytest.mark.smokevirt
def test_smokevirt_set_wifi_settings():
"""Test --set wifi_ssid and --set wifi_password"""
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --set wifi_ssid "some_ssid" --set wifi_password "temp1234"')
assert re.match(r'Connected to radio', out)
assert re.search(r'^Set wifi_ssid to some_ssid', out, re.MULTILINE)
assert re.search(r'^Set wifi_password to temp1234', out, re.MULTILINE)
assert return_value == 0
# pause for the radio
time.sleep(PAUSE_AFTER_COMMAND)
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --get wifi_ssid --get wifi_password')
assert re.search(r'^wifi_ssid: some_ssid', out, re.MULTILINE)
assert re.search(r'^wifi_password: sekrit', out, re.MULTILINE)
assert return_value == 0
@pytest.mark.smokevirt
def test_smokevirt_factory_reset():
"""Test factory reset"""
return_value, out = subprocess.getstatusoutput('meshtastic --host localhost --set factory_reset true')
assert re.match(r'Connected to radio', out)
assert re.search(r'^Set factory_reset to true', out, re.MULTILINE)
assert re.search(r'^Writing modified preferences to device', out, re.MULTILINE)
assert return_value == 0
# NOTE: The virtual radio will not respond well after this command. Need to re-start the virtual program at this point.
# TODO: fix?

View File

@@ -19,7 +19,8 @@ def test_StreamInterface():
# Note: This takes a bit, so moving from unit to slow
@pytest.mark.unitslow
def test_StreamInterface_with_noProto(caplog, reset_globals):
@pytest.mark.usefixtures("reset_globals")
def test_StreamInterface_with_noProto(caplog):
"""Test that we can instantiate a StreamInterface based on nonProto
and we can read/write bytes from a mocked stream
"""
@@ -38,7 +39,8 @@ def test_StreamInterface_with_noProto(caplog, reset_globals):
## Tip: If you want to see the print output, run with '-s' flag:
## pytest -s meshtastic/tests/test_stream_interface.py::test_sendToRadioImpl
@pytest.mark.unitslow
def test_sendToRadioImpl(caplog, reset_globals):
@pytest.mark.usefixtures("reset_globals")
def test_sendToRadioImpl(caplog):
"""Test _sendToRadioImpl()"""
# def add_header(b):

View File

@@ -28,7 +28,24 @@ def test_TCPInterface(capsys):
@pytest.mark.unit
def test_TCPInterface_without_connecting(capsys):
def test_TCPInterface_exception():
"""Test that we can instantiate a TCPInterface"""
def throw_an_exception():
raise ValueError("Fake exception.")
with patch('meshtastic.tcp_interface.TCPInterface._socket_shutdown') as mock_shutdown:
mock_shutdown.side_effect = throw_an_exception
with patch('socket.socket') as mock_socket:
iface = TCPInterface(hostname='localhost', noProto=True)
iface.myConnect()
iface.close()
assert mock_socket.called
assert mock_shutdown.called
@pytest.mark.unit
def test_TCPInterface_without_connecting():
"""Test that we can instantiate a TCPInterface with connectNow as false"""
with patch('socket.socket'):
iface = TCPInterface(hostname='localhost', noProto=True, connectNow=False)

View File

@@ -14,7 +14,7 @@ from ..globals import Globals
@pytest.mark.unit
@patch('platform.system')
def test_Tunnel_on_non_linux_system(mock_platform_system, reset_globals):
def test_Tunnel_on_non_linux_system(mock_platform_system):
"""Test that we cannot instantiate a Tunnel on a non Linux system"""
a_mock = MagicMock()
a_mock.return_value = 'notLinux'
@@ -29,7 +29,7 @@ def test_Tunnel_on_non_linux_system(mock_platform_system, reset_globals):
@pytest.mark.unit
@patch('platform.system')
def test_Tunnel_without_interface(mock_platform_system, reset_globals):
def test_Tunnel_without_interface(mock_platform_system):
"""Test that we can not instantiate a Tunnel without a valid interface"""
a_mock = MagicMock()
a_mock.return_value = 'Linux'
@@ -41,7 +41,7 @@ def test_Tunnel_without_interface(mock_platform_system, reset_globals):
@pytest.mark.unitslow
@patch('platform.system')
def test_Tunnel_with_interface(mock_platform_system, caplog, reset_globals, iface_with_nodes):
def test_Tunnel_with_interface(mock_platform_system, caplog, iface_with_nodes):
"""Test that we can not instantiate a Tunnel without a valid interface"""
iface = iface_with_nodes
iface.myInfo.my_node_num = 2475227164
@@ -60,7 +60,7 @@ def test_Tunnel_with_interface(mock_platform_system, caplog, reset_globals, ifac
@pytest.mark.unitslow
@patch('platform.system')
def test_onTunnelReceive_from_ourselves(mock_platform_system, caplog, reset_globals, iface_with_nodes):
def test_onTunnelReceive_from_ourselves(mock_platform_system, caplog, iface_with_nodes):
"""Test onTunnelReceive"""
iface = iface_with_nodes
iface.myInfo.my_node_num = 2475227164
@@ -81,7 +81,7 @@ def test_onTunnelReceive_from_ourselves(mock_platform_system, caplog, reset_glob
@pytest.mark.unit
@patch('platform.system')
def test_onTunnelReceive_from_someone_else(mock_platform_system, caplog, reset_globals, iface_with_nodes):
def test_onTunnelReceive_from_someone_else(mock_platform_system, caplog, iface_with_nodes):
"""Test onTunnelReceive"""
iface = iface_with_nodes
iface.myInfo.my_node_num = 2475227164
@@ -99,9 +99,9 @@ def test_onTunnelReceive_from_someone_else(mock_platform_system, caplog, reset_g
assert re.search(r'in onTunnelReceive', caplog.text, re.MULTILINE)
@pytest.mark.unit
@pytest.mark.unitslow
@patch('platform.system')
def test_shouldFilterPacket_random(mock_platform_system, caplog, reset_globals, iface_with_nodes):
def test_shouldFilterPacket_random(mock_platform_system, caplog, iface_with_nodes):
"""Test _shouldFilterPacket()"""
iface = iface_with_nodes
iface.noProto = True
@@ -117,9 +117,9 @@ def test_shouldFilterPacket_random(mock_platform_system, caplog, reset_globals,
assert not ignore
@pytest.mark.unit
@pytest.mark.unitslow
@patch('platform.system')
def test_shouldFilterPacket_in_blacklist(mock_platform_system, caplog, reset_globals, iface_with_nodes):
def test_shouldFilterPacket_in_blacklist(mock_platform_system, caplog, iface_with_nodes):
"""Test _shouldFilterPacket()"""
iface = iface_with_nodes
iface.noProto = True
@@ -135,9 +135,9 @@ def test_shouldFilterPacket_in_blacklist(mock_platform_system, caplog, reset_glo
assert ignore
@pytest.mark.unit
@pytest.mark.unitslow
@patch('platform.system')
def test_shouldFilterPacket_icmp(mock_platform_system, caplog, reset_globals, iface_with_nodes):
def test_shouldFilterPacket_icmp(mock_platform_system, caplog, iface_with_nodes):
"""Test _shouldFilterPacket()"""
iface = iface_with_nodes
iface.noProto = True
@@ -156,7 +156,7 @@ def test_shouldFilterPacket_icmp(mock_platform_system, caplog, reset_globals, if
@pytest.mark.unit
@patch('platform.system')
def test_shouldFilterPacket_udp(mock_platform_system, caplog, reset_globals, iface_with_nodes):
def test_shouldFilterPacket_udp(mock_platform_system, caplog, iface_with_nodes):
"""Test _shouldFilterPacket()"""
iface = iface_with_nodes
iface.noProto = True
@@ -173,9 +173,9 @@ def test_shouldFilterPacket_udp(mock_platform_system, caplog, reset_globals, ifa
assert not ignore
@pytest.mark.unit
@pytest.mark.unitslow
@patch('platform.system')
def test_shouldFilterPacket_udp_blacklisted(mock_platform_system, caplog, reset_globals, iface_with_nodes):
def test_shouldFilterPacket_udp_blacklisted(mock_platform_system, caplog, iface_with_nodes):
"""Test _shouldFilterPacket()"""
iface = iface_with_nodes
iface.noProto = True
@@ -196,7 +196,7 @@ def test_shouldFilterPacket_udp_blacklisted(mock_platform_system, caplog, reset_
@pytest.mark.unit
@patch('platform.system')
def test_shouldFilterPacket_tcp(mock_platform_system, caplog, reset_globals, iface_with_nodes):
def test_shouldFilterPacket_tcp(mock_platform_system, caplog, iface_with_nodes):
"""Test _shouldFilterPacket()"""
iface = iface_with_nodes
iface.noProto = True
@@ -213,9 +213,9 @@ def test_shouldFilterPacket_tcp(mock_platform_system, caplog, reset_globals, ifa
assert not ignore
@pytest.mark.unit
@pytest.mark.unitslow
@patch('platform.system')
def test_shouldFilterPacket_tcp_blacklisted(mock_platform_system, caplog, reset_globals, iface_with_nodes):
def test_shouldFilterPacket_tcp_blacklisted(mock_platform_system, caplog, iface_with_nodes):
"""Test _shouldFilterPacket()"""
iface = iface_with_nodes
iface.noProto = True
@@ -234,9 +234,9 @@ def test_shouldFilterPacket_tcp_blacklisted(mock_platform_system, caplog, reset_
assert ignore
@pytest.mark.unit
@pytest.mark.unitslow
@patch('platform.system')
def test_ipToNodeId_none(mock_platform_system, caplog, reset_globals, iface_with_nodes):
def test_ipToNodeId_none(mock_platform_system, caplog, iface_with_nodes):
"""Test _ipToNodeId()"""
iface = iface_with_nodes
iface.noProto = True
@@ -250,9 +250,9 @@ def test_ipToNodeId_none(mock_platform_system, caplog, reset_globals, iface_with
assert nodeid is None
@pytest.mark.unit
@pytest.mark.unitslow
@patch('platform.system')
def test_ipToNodeId_all(mock_platform_system, caplog, reset_globals, iface_with_nodes):
def test_ipToNodeId_all(mock_platform_system, caplog, iface_with_nodes):
"""Test _ipToNodeId()"""
iface = iface_with_nodes
iface.noProto = True

View File

@@ -10,7 +10,11 @@ from meshtastic.util import (fixme, stripnl, pskToString, our_exit,
support_info, genPSK256, fromStr, fromPSK,
quoteBooleans, catchAndIgnore,
remove_keys_from_dict, Timeout, hexstr,
ipstr, readnet_u16, findPorts, convert_mac_addr)
ipstr, readnet_u16, findPorts, convert_mac_addr,
snake_to_camel, camel_to_snake, eliminate_duplicate_port,
is_windows11, active_ports_on_supported_devices)
from meshtastic.supported_device import SupportedDevice
@pytest.mark.unit
@@ -39,6 +43,7 @@ def test_fromStr():
assert fromStr('100.01') == 100.01
assert fromStr('123') == 123
assert fromStr('abc') == 'abc'
assert fromStr('123456789') == 123456789
@pytest.mark.unitslow
@@ -88,7 +93,7 @@ def test_pskToString_one_byte_zero_value():
assert pskToString(bytes([0x00])) == 'unencrypted'
@pytest.mark.unit
@pytest.mark.unitslow
def test_pskToString_one_byte_non_zero_value():
"""Test pskToString one byte that is non-zero"""
assert pskToString(bytes([0x01])) == 'default'
@@ -118,7 +123,7 @@ def test_our_exit_zero_return_value(capsys):
assert pytest_wrapped_e.value.code == 0
@pytest.mark.unit
@pytest.mark.unitslow
def test_our_exit_non_zero_return_value(capsys):
"""Test our_exit with a non-zero return value"""
with pytest.raises(SystemExit) as pytest_wrapped_e:
@@ -130,7 +135,7 @@ def test_our_exit_non_zero_return_value(capsys):
assert pytest_wrapped_e.value.code == 1
@pytest.mark.unit
@pytest.mark.unitslow
def test_fixme():
"""Test fixme()"""
with pytest.raises(Exception) as pytest_wrapped_e:
@@ -166,7 +171,7 @@ def test_remove_keys_from_dict_empty_keys_empty_dict():
assert not remove_keys_from_dict((), {})
@pytest.mark.unit
@pytest.mark.unitslow
def test_remove_keys_from_dict_empty_dict():
"""Test when dict is empty"""
assert not remove_keys_from_dict(('a'), {})
@@ -178,12 +183,29 @@ def test_remove_keys_from_dict_empty_keys():
assert remove_keys_from_dict((), {'a':1}) == {'a':1}
@pytest.mark.unit
@pytest.mark.unitslow
def test_remove_keys_from_dict():
"""Test remove_keys_from_dict()"""
assert remove_keys_from_dict(('b'), {'a':1, 'b':2}) == {'a':1}
@pytest.mark.unitslow
def test_remove_keys_from_dict_multiple_keys():
"""Test remove_keys_from_dict()"""
keys = ('a', 'b')
adict = {'a': 1, 'b': 2, 'c': 3}
assert remove_keys_from_dict(keys, adict) == {'c':3}
@pytest.mark.unit
def test_remove_keys_from_dict_nested():
"""Test remove_keys_from_dict()"""
keys = ('b')
adict = {'a': {'b': 1}, 'b': 2, 'c': 3}
exp = {'a': {}, 'c': 3}
assert remove_keys_from_dict(keys, adict) == exp
@pytest.mark.unitslow
def test_Timeout_not_found():
"""Test Timeout()"""
@@ -207,7 +229,7 @@ def test_hexstr():
assert hexstr(b'') == ''
@pytest.mark.unit
@pytest.mark.unitslow
def test_ipstr():
"""Test ipstr()"""
assert ipstr(b'1234') == '49.50.51.52'
@@ -220,15 +242,217 @@ def test_readnet_u16():
assert readnet_u16(b'123456', 2) == 13108
@pytest.mark.unit
@pytest.mark.unitslow
@patch('serial.tools.list_ports.comports', return_value=[])
def test_findPorts_when_none_found(patch_comports):
"""Test findPorts()"""
assert not findPorts()
patch_comports.assert_called()
@pytest.mark.unit
@pytest.mark.unitslow
@patch('serial.tools.list_ports.comports')
def test_findPorts_when_duplicate_found_and_duplicate_option_used(patch_comports):
"""Test findPorts()"""
class TempPort:
""" temp class for port"""
def __init__(self, device=None, vid=None):
self.device = device
self.vid = vid
fake1 = TempPort('/dev/cu.usbserial-1430', vid='fake1')
fake2 = TempPort('/dev/cu.wchusbserial1430', vid='fake2')
patch_comports.return_value = [fake1, fake2]
assert findPorts(eliminate_duplicates=True) == ['/dev/cu.wchusbserial1430']
patch_comports.assert_called()
@pytest.mark.unitslow
@patch('serial.tools.list_ports.comports')
def test_findPorts_when_duplicate_found_and_duplicate_option_used_ports_reversed(patch_comports):
"""Test findPorts()"""
class TempPort:
""" temp class for port"""
def __init__(self, device=None, vid=None):
self.device = device
self.vid = vid
fake1 = TempPort('/dev/cu.usbserial-1430', vid='fake1')
fake2 = TempPort('/dev/cu.wchusbserial1430', vid='fake2')
patch_comports.return_value = [fake2, fake1]
assert findPorts(eliminate_duplicates=True) == ['/dev/cu.wchusbserial1430']
patch_comports.assert_called()
@pytest.mark.unitslow
@patch('serial.tools.list_ports.comports')
def test_findPorts_when_duplicate_found_and_duplicate_option_not_used(patch_comports):
"""Test findPorts()"""
class TempPort:
""" temp class for port"""
def __init__(self, device=None, vid=None):
self.device = device
self.vid = vid
fake1 = TempPort('/dev/cu.usbserial-1430', vid='fake1')
fake2 = TempPort('/dev/cu.wchusbserial1430', vid='fake2')
patch_comports.return_value = [fake1, fake2]
assert findPorts() == ['/dev/cu.usbserial-1430', '/dev/cu.wchusbserial1430']
patch_comports.assert_called()
@pytest.mark.unitslow
def test_convert_mac_addr():
"""Test convert_mac_addr()"""
assert convert_mac_addr('/c0gFyhb') == 'fd:cd:20:17:28:5b'
assert convert_mac_addr('fd:cd:20:17:28:5b') == 'fd:cd:20:17:28:5b'
assert convert_mac_addr('') == ''
@pytest.mark.unit
def test_snake_to_camel():
"""Test snake_to_camel"""
assert snake_to_camel('') == ''
assert snake_to_camel('foo') == 'foo'
assert snake_to_camel('foo_bar') == 'fooBar'
assert snake_to_camel('fooBar') == 'fooBar'
@pytest.mark.unit
def test_camel_to_snake():
"""Test camel_to_snake"""
assert camel_to_snake('') == ''
assert camel_to_snake('foo') == 'foo'
assert camel_to_snake('Foo') == 'foo'
assert camel_to_snake('fooBar') == 'foo_bar'
assert camel_to_snake('fooBarBaz') == 'foo_bar_baz'
@pytest.mark.unit
def test_eliminate_duplicate_port():
"""Test eliminate_duplicate_port()"""
assert not eliminate_duplicate_port([])
assert eliminate_duplicate_port(['/dev/fake']) == ['/dev/fake']
assert eliminate_duplicate_port(['/dev/fake', '/dev/fake1']) == ['/dev/fake', '/dev/fake1']
assert eliminate_duplicate_port(['/dev/fake', '/dev/fake1', '/dev/fake2']) == ['/dev/fake', '/dev/fake1', '/dev/fake2']
assert eliminate_duplicate_port(['/dev/cu.usbserial-1430', '/dev/cu.wchusbserial1430']) == ['/dev/cu.wchusbserial1430']
assert eliminate_duplicate_port(['/dev/cu.wchusbserial1430', '/dev/cu.usbserial-1430']) == ['/dev/cu.wchusbserial1430']
assert eliminate_duplicate_port(['/dev/cu.SLAB_USBtoUART', '/dev/cu.usbserial-0001']) == ['/dev/cu.usbserial-0001']
assert eliminate_duplicate_port(['/dev/cu.usbserial-0001', '/dev/cu.SLAB_USBtoUART']) == ['/dev/cu.usbserial-0001']
assert eliminate_duplicate_port(['/dev/cu.usbmodem11301', '/dev/cu.wchusbserial11301']) == ['/dev/cu.wchusbserial11301']
assert eliminate_duplicate_port(['/dev/cu.wchusbserial11301', '/dev/cu.usbmodem11301']) == ['/dev/cu.wchusbserial11301']
assert eliminate_duplicate_port(['/dev/cu.usbmodem53230051441', '/dev/cu.wchusbserial53230051441']) == ['/dev/cu.wchusbserial53230051441']
assert eliminate_duplicate_port(['/dev/cu.wchusbserial53230051441', '/dev/cu.usbmodem53230051441']) == ['/dev/cu.wchusbserial53230051441']
@patch('platform.version', return_value='10.0.22000.194')
@patch('platform.release', return_value='10')
@patch('platform.system', return_value='Windows')
def test_is_windows11_true(patched_platform, patched_release, patched_version):
"""Test is_windows11()"""
assert is_windows11() is True
patched_platform.assert_called()
patched_release.assert_called()
patched_version.assert_called()
@patch('platform.version', return_value='10.0.a2200.foo') # made up
@patch('platform.release', return_value='10')
@patch('platform.system', return_value='Windows')
def test_is_windows11_true2(patched_platform, patched_release, patched_version):
"""Test is_windows11()"""
assert is_windows11() is False
patched_platform.assert_called()
patched_release.assert_called()
patched_version.assert_called()
@patch('platform.version', return_value='10.0.17763') # windows 10 home
@patch('platform.release', return_value='10')
@patch('platform.system', return_value='Windows')
def test_is_windows11_false(patched_platform, patched_release, patched_version):
"""Test is_windows11()"""
assert is_windows11() is False
patched_platform.assert_called()
patched_release.assert_called()
patched_version.assert_called()
@patch('platform.release', return_value='8.1')
@patch('platform.system', return_value='Windows')
def test_is_windows11_false_win8_1(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')
def test_active_ports_on_supported_devices_empty(mock_platform):
"""Test active_ports_on_supported_devices()"""
sds = set()
assert active_ports_on_supported_devices(sds) == set()
mock_platform.assert_called()
@pytest.mark.unit
@patch('subprocess.getstatusoutput')
@patch('platform.system', return_value='Linux')
def test_active_ports_on_supported_devices_linux(mock_platform, mock_sp):
"""Test active_ports_on_supported_devices()"""
mock_sp.return_value = (None, 'crw-rw-rw- 1 root wheel 0x9000000 Feb 8 22:22 /dev/ttyUSBfake')
fake_device = SupportedDevice(name='a', for_firmware='heltec-v2.1', baseport_on_linux='ttyUSB')
fake_supported_devices = [fake_device]
assert active_ports_on_supported_devices(fake_supported_devices) == {'/dev/ttyUSBfake'}
mock_platform.assert_called()
mock_sp.assert_called()
@pytest.mark.unit
@patch('subprocess.getstatusoutput')
@patch('platform.system', return_value='Darwin')
def test_active_ports_on_supported_devices_mac(mock_platform, mock_sp):
"""Test active_ports_on_supported_devices()"""
mock_sp.return_value = (None, 'crw-rw-rw- 1 root wheel 0x9000000 Feb 8 22:22 /dev/cu.usbserial-foo')
fake_device = SupportedDevice(name='a', for_firmware='heltec-v2.1', baseport_on_linux='cu.usbserial-')
fake_supported_devices = [fake_device]
assert active_ports_on_supported_devices(fake_supported_devices) == {'/dev/cu.usbserial-foo'}
mock_platform.assert_called()
mock_sp.assert_called()
@pytest.mark.unit
@patch('meshtastic.util.detect_windows_port', return_value={'COM2'})
@patch('platform.system', return_value='Windows')
def test_active_ports_on_supported_devices_win(mock_platform, mock_dwp):
"""Test active_ports_on_supported_devices()"""
fake_device = SupportedDevice(name='a', for_firmware='heltec-v2.1')
fake_supported_devices = [fake_device]
assert active_ports_on_supported_devices(fake_supported_devices) == {'COM2'}
mock_platform.assert_called()
mock_dwp.assert_called()
@pytest.mark.unit
@patch('subprocess.getstatusoutput')
@patch('platform.system', return_value='Darwin')
def test_active_ports_on_supported_devices_mac_no_duplicates_check(mock_platform, mock_sp):
"""Test active_ports_on_supported_devices()"""
mock_sp.return_value = (None, ('crw-rw-rw- 1 root wheel 0x9000005 Mar 8 10:05 /dev/cu.usbmodem53230051441\n'
'crw-rw-rw- 1 root wheel 0x9000003 Mar 8 10:06 /dev/cu.wchusbserial53230051441'))
fake_device = SupportedDevice(name='a', for_firmware='tbeam', baseport_on_mac='cu.usbmodem')
fake_supported_devices = [fake_device]
assert active_ports_on_supported_devices(fake_supported_devices, False) == {'/dev/cu.usbmodem53230051441', '/dev/cu.wchusbserial53230051441'}
mock_platform.assert_called()
mock_sp.assert_called()
@pytest.mark.unit
@patch('subprocess.getstatusoutput')
@patch('platform.system', return_value='Darwin')
def test_active_ports_on_supported_devices_mac_duplicates_check(mock_platform, mock_sp):
"""Test active_ports_on_supported_devices()"""
mock_sp.return_value = (None, ('crw-rw-rw- 1 root wheel 0x9000005 Mar 8 10:05 /dev/cu.usbmodem53230051441\n'
'crw-rw-rw- 1 root wheel 0x9000003 Mar 8 10:06 /dev/cu.wchusbserial53230051441'))
fake_device = SupportedDevice(name='a', for_firmware='tbeam', baseport_on_mac='cu.usbmodem')
fake_supported_devices = [fake_device]
assert active_ports_on_supported_devices(fake_supported_devices, True) == {'/dev/cu.wchusbserial53230051441'}
mock_platform.assert_called()
mock_sp.assert_called()

View File

@@ -22,12 +22,12 @@ from pubsub import pub
from pytap2 import TapDevice
from . import portnums_pb2
from .util import ipstr, readnet_u16
from .globals import Globals
from meshtastic import portnums_pb2
from meshtastic.util import ipstr, readnet_u16
from meshtastic.globals import Globals
def onTunnelReceive(packet, interface):
def onTunnelReceive(packet, interface): # pylint: disable=W0613
"""Callback for received tunneled messages from mesh."""
logging.debug(f'in onTunnelReceive()')
our_globals = Globals.getInstance()

View File

@@ -3,16 +3,20 @@
import traceback
from queue import Queue
import os
import re
import sys
import base64
import time
import platform
import logging
import threading
import subprocess
import serial
import serial.tools.list_ports
import pkg_resources
from meshtastic.supported_device import supported_devices
"""Some devices such as a seger jlink we never want to accidentally open"""
blacklistVids = dict.fromkeys([0x1366])
@@ -110,8 +114,9 @@ def catchAndIgnore(reason, closure):
logging.error(f"Exception thrown in {reason}: {ex}")
def findPorts():
def findPorts(eliminate_duplicates=False):
"""Find all ports that might have meshtastic devices
eliminate_duplicates will run the eliminate_duplicate_port() on the collection
Returns:
list -- a list of device paths
@@ -120,6 +125,8 @@ def findPorts():
filter(lambda port: port.vid is not None and port.vid not in blacklistVids,
serial.tools.list_ports.comports())))
l.sort()
if eliminate_duplicates:
l = eliminate_duplicate_port(l)
return l
@@ -189,37 +196,43 @@ def support_info():
print('or wish to make feature requests, visit:')
print('https://github.com/meshtastic/Meshtastic-python/issues')
print('When adding an issue, be sure to include the following info:')
print(' System: {0}'.format(platform.system()))
print(' Platform: {0}'.format(platform.platform()))
print(' Release: {0}'.format(platform.uname().release))
print(' Machine: {0}'.format(platform.uname().machine))
print(' Encoding (stdin): {0}'.format(sys.stdin.encoding))
print(' Encoding (stdout): {0}'.format(sys.stdout.encoding))
print(' meshtastic: v{0}'.format(pkg_resources.require('meshtastic')[0].version))
print(' Executable: {0}'.format(sys.argv[0]))
print(' Python: {0} {1} {2}'.format(platform.python_version(),
platform.python_implementation(), platform.python_compiler()))
print(f' System: {platform.system()}')
print(f' Platform: {platform.platform()}')
print(f' Release: {platform.uname().release}')
print(f' Machine: {platform.uname().machine}')
print(f' Encoding (stdin): {sys.stdin.encoding}')
print(f' Encoding (stdout): {sys.stdout.encoding}')
the_version = pkg_resources.get_distribution("meshtastic").version
print(f' meshtastic: v{the_version}')
print(f' Executable: {sys.argv[0]}')
print(f' Python: {platform.python_version()} {platform.python_implementation()} {platform.python_compiler()}')
print('')
print('Please add the output from the command: meshtastic --info')
def remove_keys_from_dict(keys, adict):
"""Return a dictionary without some keys in it."""
newdict = adict
"""Return a dictionary without some keys in it.
Will removed nested keys.
"""
for key in keys:
if key in adict:
del newdict[key]
return newdict
try:
del adict[key]
except:
pass
for val in adict.values():
if isinstance(val, dict):
remove_keys_from_dict(keys, val)
return adict
def hexstr(barray):
"""Print a string of hex digits"""
return ":".join('{:02x}'.format(x) for x in barray)
return ":".join(f'{x:02x}' for x in barray)
def ipstr(barray):
"""Print a string of ip digits"""
return ".".join('{}'.format(x) for x in barray)
return ".".join(f'{x}' for x in barray)
def readnet_u16(p, offset):
@@ -232,5 +245,301 @@ def convert_mac_addr(val):
val - base64 encoded value (ex: '/c0gFyhb'))
returns: a string formatted like a mac address (ex: 'fd:cd:20:17:28:5b')
"""
val_as_bytes = base64.b64decode(val)
return hexstr(val_as_bytes)
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)
return hexstr(val_as_bytes)
return val
def snake_to_camel(a_string):
"""convert snake_case to camelCase"""
# split underscore using split
temp = a_string.split('_')
# joining result
result = temp[0] + ''.join(ele.title() for ele in temp[1:])
return result
def camel_to_snake(a_string):
"""convert camelCase to snake_case"""
return ''.join(['_'+i.lower() if i.isupper() else i for i in a_string]).lstrip('_')
def detect_supported_devices():
"""detect supported devices"""
system = platform.system()
#print(f'system:{system}')
possible_devices = set()
if system == "Linux":
# if linux, run lsusb and list ports
# linux: use lsusb
# Bus 001 Device 091: ID 10c4:ea60 Silicon Labs CP210x UART Bridge
_, lsusb_output = subprocess.getstatusoutput('lsusb')
vids = get_unique_vendor_ids()
for vid in vids:
#print(f'looking for {vid}...')
search = f' {vid}:'
#print(f'search:"{search}"')
if re.search(search, lsusb_output, re.MULTILINE):
#print(f'Found vendor id that matches')
devices = get_devices_with_vendor_id(vid)
# check device id
for device in devices:
#print(f'device:{device} device.usb_product_id_in_hex:{device.usb_product_id_in_hex}')
if device.usb_product_id_in_hex:
search = f' {vid}:{device.usb_product_id_in_hex} '
#print(f'search:"{search}"')
if re.search(search, lsusb_output, re.MULTILINE):
# concatenate the devices with vendor id to possibles
possible_devices.add(device)
else:
# if there is a supported device witout a product id, then it
# might be a match... so, concatenate
possible_devices.add(device)
elif system == "Windows":
# if windows, run Get-PnpDevice
_, sp_output = subprocess.getstatusoutput('powershell.exe "[Console]::OutputEncoding = [Text.UTF8Encoding]::UTF8;'
'Get-PnpDevice -PresentOnly | Format-List"')
#print(f'sp_output:{sp_output}')
vids = get_unique_vendor_ids()
for vid in vids:
#print(f'looking for {vid.upper()}...')
search = f'DeviceID.*{vid.upper()}&'
#search = f'{vid.upper()}'
#print(f'search:"{search}"')
if re.search(search, sp_output, re.MULTILINE):
#print(f'Found vendor id that matches')
devices = get_devices_with_vendor_id(vid)
# check device id
for device in devices:
#print(f'device:{device} device.usb_product_id_in_hex:{device.usb_product_id_in_hex}')
if device.usb_product_id_in_hex:
search = f'DeviceID.*{vid.upper()}&PID_{device.usb_product_id_in_hex.upper()}'
#print(f'search:"{search}"')
if re.search(search, sp_output, re.MULTILINE):
# concatenate the devices with vendor id to possibles
possible_devices.add(device)
# do a check to see if there is a Windows driver issue
if detect_windows_needs_driver(device, False):
print("WARNING: Need to install driver.")
else:
# if there is a supported device witout a product id, then it
# might be a match... so, concatenate
possible_devices.add(device)
elif system == "Darwin":
# run: system_profiler SPUSBDataType
# Note: If in boot mode, the 19003 reports same product ID as 5005.
_, sp_output = subprocess.getstatusoutput('system_profiler SPUSBDataType')
vids = get_unique_vendor_ids()
for vid in vids:
#print(f'looking for {vid}...')
search = f'Vendor ID: 0x{vid}'
#print(f'search:"{search}"')
if re.search(search, sp_output, re.MULTILINE):
#print(f'Found vendor id that matches')
devices = get_devices_with_vendor_id(vid)
# check device id
for device in devices:
#print(f'device:{device} device.usb_product_id_in_hex:{device.usb_product_id_in_hex}')
if device.usb_product_id_in_hex:
search = f'Product ID: 0x{device.usb_product_id_in_hex}'
#print(f'search:"{search}"')
if re.search(search, sp_output, re.MULTILINE):
# concatenate the devices with vendor id to possibles
possible_devices.add(device)
else:
# if there is a supported device witout a product id, then it
# might be a match... so, concatenate
possible_devices.add(device)
return possible_devices
def detect_windows_needs_driver(sd, print_reason=False):
"""detect if Windows user needs to install driver for a supported device"""
need_to_install_driver = False
if sd:
system = platform.system()
#print(f'in detect_windows_needs_driver system:{system}')
if system == "Windows":
# if windows, see if we can find a DeviceId with the vendor id
# Get-PnpDevice | Where-Object{ ($_.DeviceId -like '*10C4*')} | Format-List
command = 'powershell.exe "[Console]::OutputEncoding = [Text.UTF8Encoding]::UTF8; Get-PnpDevice | Where-Object{ ($_.DeviceId -like '
command += f"'*{sd.usb_vendor_id_in_hex.upper()}*'"
command += ')} | Format-List"'
#print(f'command:{command}')
_, sp_output = subprocess.getstatusoutput(command)
#print(f'sp_output:{sp_output}')
search = f'CM_PROB_FAILED_INSTALL'
#print(f'search:"{search}"')
if re.search(search, sp_output, re.MULTILINE):
need_to_install_driver = True
# if the want to see the reason
if print_reason:
print(sp_output)
return need_to_install_driver
def eliminate_duplicate_port(ports):
"""Sometimes we detect 2 serial ports, but we really only need to use one of the ports.
ports is a list of ports
return a list with a single port to use, if it meets the duplicate port conditions
examples:
Ports: ['/dev/cu.usbserial-1430', '/dev/cu.wchusbserial1430'] => ['/dev/cu.wchusbserial1430']
Ports: ['/dev/cu.usbmodem11301', '/dev/cu.wchusbserial11301'] => ['/dev/cu.wchusbserial11301']
Ports: ['/dev/cu.SLAB_USBtoUART', '/dev/cu.usbserial-0001'] => ['/dev/cu.usbserial-0001']
"""
new_ports = []
if len(ports) != 2:
new_ports = ports
else:
ports.sort()
if 'usbserial' in ports[0] and 'wchusbserial' in ports[1]:
first = ports[0].replace("usbserial-", "")
second = ports[1].replace("wchusbserial", "")
if first == second:
new_ports.append(ports[1])
elif 'usbmodem' in ports[0] and 'wchusbserial' in ports[1]:
first = ports[0].replace("usbmodem", "")
second = ports[1].replace("wchusbserial", "")
if first == second:
new_ports.append(ports[1])
elif 'SLAB_USBtoUART' in ports[0] and 'usbserial' in ports[1]:
new_ports.append(ports[1])
else:
new_ports = ports
return new_ports
def is_windows11():
"""Detect if Windows 11"""
is_win11 = 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:
if int(patch) >= 22000:
is_win11 = True
except Exception as e:
print(f'problem detecting win11 e:{e}')
return is_win11
def get_unique_vendor_ids():
"""Return a set of unique vendor ids"""
vids = set()
for d in supported_devices:
if d.usb_vendor_id_in_hex:
vids.add(d.usb_vendor_id_in_hex)
return vids
def get_devices_with_vendor_id(vid):
"""Return a set of unique devices with the vendor id"""
sd = set()
for d in supported_devices:
if d.usb_vendor_id_in_hex == vid:
sd.add(d)
return sd
def active_ports_on_supported_devices(sds, eliminate_duplicates=False):
"""Return a set of active ports based on the supplied supported devices"""
ports = set()
baseports = set()
system = platform.system()
# figure out what possible base ports there are
for d in sds:
if system == "Linux":
baseports.add(d.baseport_on_linux)
elif system == "Darwin":
baseports.add(d.baseport_on_mac)
elif system == "Windows":
baseports.add(d.baseport_on_windows)
for bp in baseports:
if system == "Linux":
# see if we have any devices (ignoring any stderr output)
command = f'ls -al /dev/{bp}* 2> /dev/null'
#print(f'command:{command}')
_, ls_output = subprocess.getstatusoutput(command)
#print(f'ls_output:{ls_output}')
# if we got output, there are ports
if len(ls_output) > 0:
#print('got output')
# for each line of output
lines = ls_output.split('\n')
#print(f'lines:{lines}')
for line in lines:
parts = line.split(' ')
#print(f'parts:{parts}')
port = parts[-1]
#print(f'port:{port}')
ports.add(port)
elif system == "Darwin":
# see if we have any devices (ignoring any stderr output)
command = f'ls -al /dev/{bp}* 2> /dev/null'
#print(f'command:{command}')
_, ls_output = subprocess.getstatusoutput(command)
#print(f'ls_output:{ls_output}')
# if we got output, there are ports
if len(ls_output) > 0:
#print('got output')
# for each line of output
lines = ls_output.split('\n')
#print(f'lines:{lines}')
for line in lines:
parts = line.split(' ')
#print(f'parts:{parts}')
port = parts[-1]
#print(f'port:{port}')
ports.add(port)
elif system == "Windows":
# for each device in supported devices found
for d in sds:
# find the port(s)
com_ports = detect_windows_port(d)
#print(f'com_ports:{com_ports}')
# add all ports
for com_port in com_ports:
ports.add(com_port)
if eliminate_duplicates:
ports = eliminate_duplicate_port(list(ports))
ports.sort()
ports = set(ports)
return ports
def detect_windows_port(sd):
"""detect if Windows port"""
ports = set()
if sd:
system = platform.system()
if system == "Windows":
command = ('powershell.exe "[Console]::OutputEncoding = [Text.UTF8Encoding]::UTF8;'
'Get-PnpDevice -PresentOnly | Where-Object{ ($_.DeviceId -like ')
command += f"'*{sd.usb_vendor_id_in_hex.upper()}*'"
command += ')} | Format-List"'
#print(f'command:{command}')
_, sp_output = subprocess.getstatusoutput(command)
#print(f'sp_output:{sp_output}')
p = re.compile(r'\(COM(.*)\)')
for x in p.findall(sp_output):
#print(f'x:{x}')
ports.add(f'COM{x}')
return ports

2
proto

Submodule proto updated: 1d3b4806ab...99ce57802d

View File

@@ -1,11 +1,15 @@
[pytest]
addopts = -m "not int and not smoke1 and not smoke2 and not smokewifi and not examples"
addopts = -m "not int and not smoke1 and not smoke2 and not smokewifi and not examples and not smokevirt"
filterwarnings =
ignore::DeprecationWarning
markers =
unit: marks tests as unit tests
unitslow: marks slow unit tests
int: marks tests as integration tests
smokevirt: marks tests as smoke tests against virtual device
smoke1: runs smoke tests on a single device connected via USB
smoke2: runs smoke tests on a two devices connected via USB
smokewifi: runs smoke test on an esp32 device setup with wifi

View File

@@ -4,7 +4,6 @@ protobuf
dotmap
pexpect
pyqrcode
pygatt
tabulate
timeago
webencodings
@@ -18,3 +17,4 @@ pyyaml
pytap2
pdoc3
pypubsub
pygatt; platform_system == "Linux"

View File

@@ -12,7 +12,7 @@ with open("README.md", "r") as fh:
# This call to setup() does all the work
setup(
name="meshtastic",
version="1.2.49",
version="1.3alpha.8",
description="Python API & client shell for talking to Meshtastic devices",
long_description=long_description,
long_description_content_type="text/markdown",
@@ -22,18 +22,22 @@ setup(
license="MIT",
classifiers=[
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Development Status :: 4 - Beta",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
],
packages=["meshtastic"],
include_package_data=True,
install_requires=["pyserial>=3.4", "protobuf>=3.13.0",
"pypubsub>=4.0.3", "dotmap>=1.3.14", "pexpect>=4.6.0", "pyqrcode>=1.2.1",
"pygatt>=4.0.5", "tabulate>=0.8.9", "timeago>=1.0.15", "pyyaml"],
"tabulate>=0.8.9", "timeago>=1.0.15", "pyyaml",
"pygatt>=4.0.5 ; platform_system=='Linux'"],
extras_require={
'tunnel': ["pytap2>=2.0.0"]
},
python_requires='>=3.6',
python_requires='>=3.7',
entry_points={
"console_scripts": [
"meshtastic=meshtastic.__main__:main",

7
standalone_readme.txt Normal file
View File

@@ -0,0 +1,7 @@
readme.txt for single standalone executable zip files that can be
downloaded from https://github.com/meshtastic/Meshtastic-python/releases
If you do not want to install python and/or the python libraries, you can download one of these
files to run the Meshtastic command line interface (CLI) as a standalone executable.
See https://meshtastic.org/docs/software/python/python-standalone for more info.