Skip to content

Commit 57d43b8

Browse files
committed
Merge branch 'master' into node-favorites
2 parents 4f6d183 + 9a72e36 commit 57d43b8

71 files changed

Lines changed: 7440 additions & 2519 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/CONTRIBUTING.md

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

.github/meshtastic_logo.png

105 KB
Loading

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,5 @@ examples/__pycache__
1717
meshtastic.spec
1818
.hypothesis/
1919
coverage.xml
20-
.ipynb_checkpoints
20+
.ipynb_checkpoints
21+
.cursor/

.vscode/launch.json

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
55
"version": "0.2.0",
66
"configurations": [
7+
78
{
89
"name": "meshtastic BLE",
910
"type": "debugpy",
@@ -261,7 +262,14 @@
261262
"module": "meshtastic",
262263
"justMyCode": true,
263264
"args": ["--nodes", "--show-fields", "AKA,Pubkey,Role,Role,Role,Latitude,Latitude,deviceMetrics.voltage"]
264-
}
265-
265+
},
266+
{
267+
"name": "meshtastic --export-config",
268+
"type": "debugpy",
269+
"request": "launch",
270+
"module": "meshtastic",
271+
"justMyCode": true,
272+
"args": ["--export-config", "config.json"]
273+
},
266274
]
267275
}
File renamed without changes.

README.md

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
# Meshtastic Python
1+
<div align="center" markdown="1">
2+
3+
<img src=".github/meshtastic_logo.png" alt="Meshtastic Logo" width="80"/>
4+
5+
<h1 align="center"> Meshtastic Python
6+
</h1>
7+
<p style="font-size:15px;" align="center">A Python library and client for use with Meshtastic devices. </p>
28

39
[![codecov](https://codecov.io/gh/meshtastic/python/branch/master/graph/badge.svg?token=TIWPJL73KV)](https://codecov.io/gh/meshtastic/python)
410
![PyPI - Downloads](https://img.shields.io/pypi/dm/meshtastic)
@@ -7,17 +13,20 @@
713
[![Fiscal Contributors](https://opencollective.com/meshtastic/tiers/badge.svg?label=Fiscal%20Contributors&color=deeppink)](https://opencollective.com/meshtastic/)
814
![GPL-3.0](https://img.shields.io/badge/License-GPL%20v3-blue.svg)
915

16+
</div>
17+
18+
<div align="center">
19+
<a href="https://meshtastic.org/docs/software/python/cli/installation">Getting Started Guide</a>
20+
-
21+
<a href="https://python.meshtastic.org">API Documentation</a>
22+
</div>
23+
1024
## Overview
1125

12-
A Python client for use with Meshtastic devices.
1326
This small library (and example application) provides an easy API for sending and receiving messages over mesh radios.
1427
It also provides access to any of the operations/data available in the device user interface or the Android application.
1528
Events are delivered using a publish-subscribe model, and you can subscribe to only the message types you are interested in.
1629

17-
**[Getting Started Guide](https://meshtastic.org/docs/software/python/cli/installation)**
18-
19-
**[API Documentation](https://python.meshtastic.org)**
20-
2130
## Call for Contributors
2231

2332
This library and CLI has gone without a consistent maintainer for a while, and there's many improvements that could be made. We're all volunteers here and help is extremely appreciated, whether in implementing your own needs or helping maintain the library and CLI in general.

bin/regen-protobufs.sh

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@ set -e
66
#gsed -i 's/import "\//import ".\//g' ./protobufs/meshtastic/*
77
#gsed -i 's/package meshtastic;//g' ./protobufs/meshtastic/*
88

9+
POETRYDIR=$(poetry env info --path)
10+
11+
if [[ -z "${POETRYDIR}" ]]; then
12+
poetry install
13+
fi
14+
915
# protoc looks for mypy plugin in the python path
1016
source $(poetry env info --path)/bin/activate
1117

@@ -22,6 +28,7 @@ OUTDIR=${TMPDIR}/out
2228
PYIDIR=${TMPDIR}/out
2329
mkdir -p "${OUTDIR}" "${INDIR}" "${PYIDIR}"
2430
cp ./protobufs/meshtastic/*.proto "${INDIR}"
31+
cp ./protobufs/nanopb.proto "${INDIR}"
2532

2633
# OS-X sed is apparently a little different and expects an arg for -i
2734
if [[ $OSTYPE == 'darwin'* ]]; then
@@ -36,6 +43,8 @@ $SEDCMD 's/^package meshtastic;/package meshtastic.protobuf;/' "${INDIR}/"*.prot
3643
# fix the imports to match
3744
$SEDCMD 's/^import "meshtastic\//import "meshtastic\/protobuf\//' "${INDIR}/"*.proto
3845

46+
$SEDCMD 's/^import "nanopb.proto"/import "meshtastic\/protobuf\/nanopb.proto"/' "${INDIR}/"*.proto
47+
3948
# Generate the python files
4049
./nanopb-0.4.8/generator-bin/protoc -I=$TMPDIR/in --python_out "${OUTDIR}" "--mypy_out=${PYIDIR}" $INDIR/*.proto
4150

example_config.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ owner_short: BOB
44

55
channel_url: https://www.meshtastic.org/e/#CgMSAQESCDgBQANIAVAe
66

7+
canned_messages: Hi|Bye|Yes|No|Ok
8+
ringtone: 24:d=32,o=5,b=565:f6,p,f6,4p,p,f6,p,f6,2p,p,b6,p,b6,p,b6,p,b6,p,b,p,b,p,b,p,b,p,b,p,b,p,b,p,b,1p.,2p.,p
9+
710
location:
811
lat: 35.88888
912
lon: -93.88888

examples/waypoint.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
parser_create.add_argument('id', help="id of the waypoint")
2626
parser_create.add_argument('name', help="name of the waypoint")
2727
parser_create.add_argument('description', help="description of the waypoint")
28+
parser_create.add_argument('icon', help="icon of the waypoint")
2829
parser_create.add_argument('expire', help="expiration date of the waypoint as interpreted by datetime.fromisoformat")
2930
parser_create.add_argument('latitude', help="latitude of the waypoint")
3031
parser_create.add_argument('longitude', help="longitude of the waypoint")
@@ -44,6 +45,7 @@
4445
waypoint_id=int(args.id),
4546
name=args.name,
4647
description=args.description,
48+
icon=args.icon,
4749
expire=int(datetime.datetime.fromisoformat(args.expire).timestamp()),
4850
latitude=float(args.latitude),
4951
longitude=float(args.longitude),

meshtastic/__init__.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
- `meshtastic.receive.data.portnum(packet)` (where portnum is an integer or well known PortNum enum)
3636
- `meshtastic.node.updated(node = NodeInfo)` - published when a node in the DB changes (appears, location changed, username changed, etc...)
3737
- `meshtastic.log.line(line)` - a raw unparsed log line from the radio
38+
- `meshtastic.clientNotification(notification, interface) - a ClientNotification sent from the radio
3839
3940
We receive position, user, or data packets from the mesh. You probably only care about `meshtastic.receive.data`. The first argument for
4041
that publish will be the packet. Text or binary data packets (from `sendData` or `sendText`) will both arrive this way. If you print packet
@@ -128,6 +129,7 @@ def onConnection(interface, topic=pub.AUTO_TOPIC): # called when we (re)connect
128129

129130
publishingThread = DeferredExecution("publishing")
130131

132+
logger = logging.getLogger(__name__)
131133

132134
class ResponseHandler(NamedTuple):
133135
"""A pending response callback, waiting for a response to one of our messages"""
@@ -159,31 +161,31 @@ def _onTextReceive(iface, asDict):
159161
#
160162
# Usually btw this problem is caused by apps sending binary data but setting the payload type to
161163
# text.
162-
logging.debug(f"in _onTextReceive() asDict:{asDict}")
164+
logger.debug(f"in _onTextReceive() asDict:{asDict}")
163165
try:
164166
asBytes = asDict["decoded"]["payload"]
165167
asDict["decoded"]["text"] = asBytes.decode("utf-8")
166168
except Exception as ex:
167-
logging.error(f"Malformatted utf8 in text message: {ex}")
169+
logger.error(f"Malformatted utf8 in text message: {ex}")
168170
_receiveInfoUpdate(iface, asDict)
169171

170172

171173
def _onPositionReceive(iface, asDict):
172174
"""Special auto parsing for received messages"""
173-
logging.debug(f"in _onPositionReceive() asDict:{asDict}")
175+
logger.debug(f"in _onPositionReceive() asDict:{asDict}")
174176
if "decoded" in asDict:
175177
if "position" in asDict["decoded"] and "from" in asDict:
176178
p = asDict["decoded"]["position"]
177-
logging.debug(f"p:{p}")
179+
logger.debug(f"p:{p}")
178180
p = iface._fixupPosition(p)
179-
logging.debug(f"after fixup p:{p}")
181+
logger.debug(f"after fixup p:{p}")
180182
# update node DB as needed
181183
iface._getOrCreateByNum(asDict["from"])["position"] = p
182184

183185

184186
def _onNodeInfoReceive(iface, asDict):
185187
"""Special auto parsing for received messages"""
186-
logging.debug(f"in _onNodeInfoReceive() asDict:{asDict}")
188+
logger.debug(f"in _onNodeInfoReceive() asDict:{asDict}")
187189
if "decoded" in asDict:
188190
if "user" in asDict["decoded"] and "from" in asDict:
189191
p = asDict["decoded"]["user"]
@@ -197,7 +199,7 @@ def _onNodeInfoReceive(iface, asDict):
197199

198200
def _onTelemetryReceive(iface, asDict):
199201
"""Automatically update device metrics on received packets"""
200-
logging.debug(f"in _onTelemetryReceive() asDict:{asDict}")
202+
logger.debug(f"in _onTelemetryReceive() asDict:{asDict}")
201203
if "from" not in asDict:
202204
return
203205

@@ -221,7 +223,7 @@ def _onTelemetryReceive(iface, asDict):
221223
updateObj = telemetry.get(toUpdate)
222224
newMetrics = node.get(toUpdate, {})
223225
newMetrics.update(updateObj)
224-
logging.debug(f"updating {toUpdate} metrics for {asDict['from']} to {newMetrics}")
226+
logger.debug(f"updating {toUpdate} metrics for {asDict['from']} to {newMetrics}")
225227
node[toUpdate] = newMetrics
226228

227229
def _receiveInfoUpdate(iface, asDict):
@@ -233,7 +235,7 @@ def _receiveInfoUpdate(iface, asDict):
233235

234236
def _onAdminReceive(iface, asDict):
235237
"""Special auto parsing for received messages"""
236-
logging.debug(f"in _onAdminReceive() asDict:{asDict}")
238+
logger.debug(f"in _onAdminReceive() asDict:{asDict}")
237239
if "decoded" in asDict and "from" in asDict and "admin" in asDict["decoded"]:
238240
adminMessage = asDict["decoded"]["admin"]["raw"]
239241
iface._getOrCreateByNum(asDict["from"])["adminSessionPassKey"] = adminMessage.session_passkey

0 commit comments

Comments
 (0)