mirror of
https://github.com/meshtastic/meshtastic.git
synced 2024-12-25 21:54:20 -08:00
Merge branch 'master' into update-python-cli
This commit is contained in:
commit
7be2f9d357
|
@ -20,6 +20,12 @@ export const Faq = {
|
||||||
content: "Everyone contributes in a different way. Join the [Meshtastic Discord](https://discord.gg/ktMAKGBnBs) and introduce yourself. We're all very friendly. If you'd like to pitch in some code, check out the [Development](/docs/developers) menu on the left. If you'd like to contribute financially, please visit our page on [Open Collective](https://opencollective.com/meshtastic) or you may choose to [sponsor a developer](https://github.com/sponsors/meshtastic). Check out [Contributing](/docs/contributing/) for more details.",
|
content: "Everyone contributes in a different way. Join the [Meshtastic Discord](https://discord.gg/ktMAKGBnBs) and introduce yourself. We're all very friendly. If you'd like to pitch in some code, check out the [Development](/docs/developers) menu on the left. If you'd like to contribute financially, please visit our page on [Open Collective](https://opencollective.com/meshtastic) or you may choose to [sponsor a developer](https://github.com/sponsors/meshtastic). Check out [Contributing](/docs/contributing/) for more details.",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
"firmware": [
|
||||||
|
{
|
||||||
|
title: "Can I update my node's firmware over the mesh?",
|
||||||
|
content: `No, Meshtastic does not support OTA updates over LoRa. Please visit [Flash Firmware](/docs/getting-started/flashing-firmware/) for update options.`,
|
||||||
|
},
|
||||||
|
],
|
||||||
"android": [
|
"android": [
|
||||||
{
|
{
|
||||||
title: "What versions of Android does the Meshtastic Android App require?",
|
title: "What versions of Android does the Meshtastic Android App require?",
|
||||||
|
@ -192,6 +198,10 @@ If you use your ham radio license with Meshtastic, consider both the privileges
|
||||||
|
|
||||||
<FaqAccordion rows={Faq.general} />
|
<FaqAccordion rows={Faq.general} />
|
||||||
|
|
||||||
|
## Firmware
|
||||||
|
|
||||||
|
<FaqAccordion rows={Faq.firmware} />
|
||||||
|
|
||||||
## Android Client
|
## Android Client
|
||||||
|
|
||||||
<FaqAccordion rows={Faq.android} />
|
<FaqAccordion rows={Faq.android} />
|
||||||
|
|
|
@ -15,11 +15,37 @@ To utilize this script you need to have an Adafruit IO account, a working mqtt b
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
You will need to modify the script with your Adafruit IO and mqtt credentials and mqtt publisher node id
|
You will need to modify the ini sample file with your Adafruit IO and mqtt credentials
|
||||||
|
Code repository is here [https://bitbucket.org/tavdog/mesh_aio_logger/src/main/]
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[GENERAL]
|
||||||
|
PRINT_CONFIG = false
|
||||||
|
; https://pynative.com/list-all-timezones-in-python/
|
||||||
|
TIMEZONE = US/Hawaii
|
||||||
|
CHANNEL_LIST = LongFast,Private
|
||||||
|
|
||||||
|
[MQTT]
|
||||||
|
SERVER = mqtt.server.net
|
||||||
|
PORT = 1883
|
||||||
|
USERNAME = a
|
||||||
|
PASSWORD = a
|
||||||
|
|
||||||
|
[AIO]
|
||||||
|
USER = a
|
||||||
|
KEY = a
|
||||||
|
;leave FEED_GROUP as Default is you don't want a separate group.
|
||||||
|
FEED_GROUP = Default
|
||||||
|
[LOG]
|
||||||
|
VOLTAGE = true
|
||||||
|
MESSAGE = true
|
||||||
|
POSITION = false
|
||||||
|
SNR = false
|
||||||
|
RSSI = false
|
||||||
|
```
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# Persistent mqtt client, watch for voltage, teleme, message packets and publish them to io
|
# Persistent mqtt client, watch for voltage, telemetry, message packets and publish them to io
|
||||||
# feeds will be created. free tier maxes out at 10
|
|
||||||
|
|
||||||
# Import Adafruit IO REST client.
|
# Import Adafruit IO REST client.
|
||||||
from Adafruit_IO import Client, Feed, Data, RequestError
|
from Adafruit_IO import Client, Feed, Data, RequestError
|
||||||
|
@ -29,107 +55,129 @@ import os
|
||||||
import sys
|
import sys
|
||||||
import json
|
import json
|
||||||
import time
|
import time
|
||||||
import random
|
|
||||||
|
|
||||||
import pytz
|
import pytz
|
||||||
|
import configparser
|
||||||
|
|
||||||
|
config = configparser.ConfigParser()
|
||||||
|
# Read the configuration file
|
||||||
|
config.read('config.ini')
|
||||||
|
|
||||||
|
if config.getboolean('GENERAL','PRINT_CONFIG'):
|
||||||
|
# Iterate through sections and options to print all config values
|
||||||
|
for section in config.sections():
|
||||||
|
print(f"[{section}]")
|
||||||
|
for key, value in config.items(section):
|
||||||
|
print(f"{key} = {value}")
|
||||||
|
print() # Add an empty line between sections for better readability
|
||||||
|
|
||||||
# set your timezone here
|
# set your timezone here
|
||||||
timezone = pytz.timezone("US/Hawaii")
|
TIMEZONE = config['GENERAL']['TIMEZONE']
|
||||||
|
CHANNEL_LIST = config['GENERAL']['CHANNEL_LIST']
|
||||||
|
MQTT_SERVER = config['MQTT']['SERVER']
|
||||||
|
MQTT_PORT = int(config['MQTT']['PORT'])
|
||||||
|
MQTT_USERNAME = config['MQTT']['USERNAME']
|
||||||
|
MQTT_PASSWORD = config['MQTT']['PASSWORD']
|
||||||
|
AIO_USER = config['AIO']['USER']
|
||||||
|
AIO_KEY = config['AIO']['KEY']
|
||||||
|
AIO_FEED_GROUP = config['AIO']['FEED_GROUP'] # leave as Default is you don't want a separate group.
|
||||||
|
LOG_SNR = config.getboolean('LOG','SNR')
|
||||||
|
LOG_VOLTAGE = config.getboolean('LOG','VOLTAGE')
|
||||||
|
LOG_RSSI = config.getboolean('LOG','RSSI')
|
||||||
|
LOG_MESSAGE = config.getboolean('LOG','MESSAGE')
|
||||||
|
LOG_POSITION = config.getboolean('LOG','POSITION')
|
||||||
|
|
||||||
# set your Adafruit IO creds here
|
###### END SETTINGS ######
|
||||||
AIO_USER = "tavdog"
|
|
||||||
AIO_KEY = "XXXXXXXXXXXXX"
|
|
||||||
|
|
||||||
# set your MQTT broker information
|
my_timezone = pytz.timezone(TIMEZONE)
|
||||||
MQTT_SERVER = "XXXXXXXXXX"
|
channel_array = CHANNEL_LIST.split(',')
|
||||||
MQTT_PORT = 1883 # default
|
print("\n")
|
||||||
MQTT_TOPIC_PREFIX = "mesh" # specified when you setup your node to publish mqtt
|
mqttClient = mqtt.Client("mesh_client_rssi")
|
||||||
MQTT_USERNAME = "xxxxxxxxx"
|
|
||||||
MQTT_PASSWORD = "xxxxxxxxx"
|
|
||||||
|
|
||||||
# your node ID that is publishingn to the MQTT broker
|
|
||||||
MQTT_NODE_ID = "!387e0248"
|
|
||||||
# your channel (node must be set to json output)
|
|
||||||
MQTT_CHANNEL = "LongFast"
|
|
||||||
|
|
||||||
print("\n\n")
|
|
||||||
client_id = str(random.randint(0, 100))
|
|
||||||
mqttClient = mqtt.Client("python_mesh_client_%s" % client_id) # this needs to be kind of unique. mqtt broker will not allow duplicate connections.
|
|
||||||
mqttClient.username_pw_set(MQTT_USERNAME, MQTT_PASSWORD)
|
mqttClient.username_pw_set(MQTT_USERNAME, MQTT_PASSWORD)
|
||||||
mqttClient.connect(MQTT_SERVER, MQTT_PORT)
|
|
||||||
mqttClient.loop_start()
|
|
||||||
print("Subscribing to %s/2/json/%s/%s" % (MQTT_TOPIC_PREFIX, MQTT_CHANNEL,MQTT_NODE_ID))
|
|
||||||
mqttClient.subscribe("%s/2/json/%s/%s" % (MQTT_TOPIC_PREFIX, MQTT_CHANNEL,MQTT_NODE_ID))
|
|
||||||
time.sleep(1)
|
|
||||||
|
|
||||||
# you can add more channels here
|
aio = Client(AIO_USER,AIO_KEY)
|
||||||
#mqttClient.subscribe("%s/2/json/MauiMesh/%s" % (MQTT_TOPIC_PREFIX, MQTT_NODE_ID))
|
|
||||||
|
|
||||||
|
# bootstrap your node_db here if you want
|
||||||
|
#node_db = dict()
|
||||||
|
node_db = { 947782216: 'G1 Kula',
|
||||||
|
1839130823: 'HR-Haleakala Repeater',
|
||||||
|
1969840052: 'MR8-Magnet Rak 8dbi',
|
||||||
|
}
|
||||||
|
|
||||||
aio = Client(AIO_USER, AIO_KEY)
|
# get feed object or create it if it doesn't exist yet.
|
||||||
|
|
||||||
node_db = dict()
|
|
||||||
# bootstrap your node_db here if you want. otherwise it will populate eventually but die on restart.
|
|
||||||
# node_db = { 634792740: 'Tavis Blue',
|
|
||||||
# 947782216: 'G1 Wailuku',
|
|
||||||
# 1839130823: 'Giggle Hill',
|
|
||||||
# 2330585902: 'Ayla Kekona',
|
|
||||||
# 634717328: 'Nani Hoku',
|
|
||||||
# 3667835576: 'Rachael'}
|
|
||||||
|
|
||||||
def get_feed(full_name,kind):
|
def get_feed(full_name,kind):
|
||||||
name = full_name.split('-')[0]
|
name = full_name.split('-')[0]
|
||||||
|
#print("getting feed: " + name)
|
||||||
|
if "\\x00" in name or "\\u0000" in name or "\x00" in name: # json don't like emojis
|
||||||
|
name = full_name.split('-')[1].replace(' ','-').replace('.','-')
|
||||||
try:
|
try:
|
||||||
feed = aio.feeds(f"mesh.{name.lower()}-{kind}")
|
feed = aio.feeds(f"{AIO_FEED_GROUP}.{name.lower()}-{kind}")
|
||||||
except:
|
except:
|
||||||
print("creating feed:" + kind)
|
print("creating feed:" + f"{AIO_FEED_GROUP}.{name.lower()}-{kind}")
|
||||||
# Create Feed
|
# Create Feed
|
||||||
new_feed = Feed(name=f"{name}_{kind}")
|
new_feed = Feed(name=f"{name}_{kind}")
|
||||||
feed = aio.create_feed(feed=new_feed,group_key="mesh")
|
feed = aio.create_feed(feed=new_feed,group_key=AIO_FEED_GROUP)
|
||||||
|
|
||||||
return feed
|
return feed
|
||||||
|
|
||||||
def publish_rssi(data,metadata):
|
def publish_rssi(data,metadata):
|
||||||
name = node_db[data['from']]
|
name = node_db[data['from']]
|
||||||
feed = get_feed(name,"rssi") # use a function here because we will create the feed if it doens't exist already.
|
feed = get_feed(name,"rssi") # use a function here because we will create the feed if it doens't exist already.
|
||||||
print(feed.key + " \t\t: " + str(data['rssi']))
|
#print(feed.key + " \t\t: " + str(data['rssi']))
|
||||||
aio.send_data(feed.key, data['rssi'],metadata)
|
aio.send_data(feed.key, data['rssi'],metadata)
|
||||||
|
|
||||||
def publish_snr(data,metadata):
|
def publish_snr(data,metadata):
|
||||||
name = node_db[data['from']]
|
name = node_db[data['from']]
|
||||||
feed = get_feed(name,"snr") # use a function here because we will create the feed if it doens't exist already.
|
feed = get_feed(name,"snr") # use a function here because we will create the feed if it doens't exist already.
|
||||||
print(feed.key + " \t\t: " + str(data['snr']))
|
#print(feed.key + " \t\t: " + str(data['snr']))
|
||||||
aio.send_data(feed.key, data['snr'],metadata)
|
aio.send_data(feed.key, data['snr'],metadata)
|
||||||
|
|
||||||
def publish_voltage(data,metadata):
|
def publish_voltage(data,metadata):
|
||||||
name = node_db[data['from']]
|
name = node_db[data['from']]
|
||||||
feed = get_feed(name,"voltage") # use a function here because we will create the feed if it doens't exist already. print(feed.key + " \t: " + str(data['payload'].get('voltage',"none")))
|
feed = get_feed(name,"voltage") # use a function here because we will create the feed if it doens't exist already. print(feed.key + " \t: " + str(data['payload'].get('voltage',"none")))
|
||||||
print(feed.key + " \t: " + str(data['payload']['voltage']))
|
#print(feed.key + " \t: " + str(data['payload']['voltage']))
|
||||||
aio.send_data(feed.key, data['payload'].get('voltage',0),metadata)
|
aio.send_data(feed.key, data['payload'].get('voltage',0),metadata)
|
||||||
|
|
||||||
def publish_packet(data):
|
def publish_packet(data):
|
||||||
|
|
||||||
feed = aio.feeds("mesh.messages")
|
feed = aio.feeds(AIO_FEED_GROUP+".messages")
|
||||||
if (data['from'] in node_db):
|
if (data['from'] in node_db):
|
||||||
data['fname'] = node_db[data['from']]
|
data['fname'] = node_db[data['from']]
|
||||||
|
if (data['to'] in node_db):
|
||||||
|
data['tname'] = node_db[data['to']]
|
||||||
# trim down the data. we really only want to see the message content
|
# trim down the data. we really only want to see the message content
|
||||||
|
if 'stamp' in data:
|
||||||
|
stamp = datetime.fromtimestamp(data['timestamp'],my_timezone).strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
else:
|
||||||
|
current_time = datetime.now() # Assuming UTC time
|
||||||
|
# Convert to the desired timezone (e.g., 'America/Los_Angeles' or your preferred timezone)
|
||||||
|
current_time = current_time.astimezone(my_timezone)
|
||||||
|
stamp = current_time.strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
|
||||||
trimmed = {
|
trimmed = {
|
||||||
'from' : data.get('fname',None) or data.get('from'),
|
'from' : data.get('fname',None) or data.get('from'),
|
||||||
'message' : data['payload']['text'],
|
'to' : data.get('tname',None) or data.get('to'),
|
||||||
'stamp' : datetime.fromtimestamp(data['timestamp'],timezone).strftime('%Y-%m-%d %H:%M:%S'),
|
'channel' : data['channel'],
|
||||||
|
'message' : data['payload'],
|
||||||
|
'stamp' : stamp,
|
||||||
}
|
}
|
||||||
aio.send_data(feed.key, json.dumps(trimmed, indent=4))
|
aio.send_data(feed.key, json.dumps(trimmed, indent=4))
|
||||||
|
print(trimmed)
|
||||||
|
|
||||||
def on_message(client, userdata, message):
|
def on_message(client, userdata, message):
|
||||||
print("\n")
|
try:
|
||||||
print("message received " ,str(message.payload.decode("utf-8")))
|
data = json.loads(str(message.payload.decode("utf-8")))
|
||||||
print("\n")
|
except Exception as e:
|
||||||
data = json.loads(str(message.payload.decode("utf-8")))
|
print(e)
|
||||||
|
return
|
||||||
|
|
||||||
# check the topic of the message
|
# check the topic of the message
|
||||||
if data['type'] == "text":
|
if data['type'] == "text" and LOG_MESSAGE:
|
||||||
# publish all message packets to the packet log
|
# publish all message packets to the message log
|
||||||
publish_packet(data)
|
print(data)
|
||||||
|
try:
|
||||||
|
publish_packet(data)
|
||||||
|
except Exception as e:
|
||||||
|
print("error in publish:",e)
|
||||||
|
|
||||||
# update node_db if needed
|
# update node_db if needed
|
||||||
if data['type'] == 'nodeinfo':
|
if data['type'] == 'nodeinfo':
|
||||||
|
@ -141,34 +189,49 @@ def on_message(client, userdata, message):
|
||||||
|
|
||||||
# "payload":{"altitude":113,"latitude_i":208759687,"longitude_i":-1565037665
|
# "payload":{"altitude":113,"latitude_i":208759687,"longitude_i":-1565037665
|
||||||
metadata = None
|
metadata = None
|
||||||
if data['type'] == 'position':
|
if data['type'] == 'position' and LOG_POSITION:
|
||||||
metadata = {
|
metadata = {
|
||||||
'lat': data['payload']['latitude_i'] / 10000000, #40.726190,
|
'lat': data['payload']['latitude_i'] / 10000000, #40.726190,
|
||||||
'lon': data['payload']['longitude_i'] / 10000000, #-74.005334,
|
'lon': data['payload']['longitude_i'] / 10000000, #-74.005334,
|
||||||
'ele': data['payload'].get('altitude',None),
|
'ele': data['payload'].get('altitude',None),
|
||||||
'created_at': datetime.now(timezone),
|
'created_at': str(datetime.now(my_timezone)),
|
||||||
}
|
}
|
||||||
# add to the node_db if we know who they are already
|
# add to the node_db if we know who they are already
|
||||||
|
|
||||||
|
# if metadata:
|
||||||
|
# print(metadata)
|
||||||
|
|
||||||
if data['from'] in node_db:
|
if data['from'] in node_db:
|
||||||
try:
|
try:
|
||||||
if 'rssi' in data and data['rssi'] != 0:
|
if LOG_RSSI and 'rssi' in data and data['rssi'] != 0:
|
||||||
# do the doings
|
|
||||||
publish_rssi( data, metadata )
|
publish_rssi( data, metadata )
|
||||||
if 'snr' in data and data['snr'] != 0:
|
if LOG_SNR and 'snr' in data and data['snr'] != 0:
|
||||||
publish_snr( data, metadata )
|
publish_snr( data, metadata )
|
||||||
if 'payload' in data and data['payload'].get('voltage',0) != 0:
|
if LOG_VOLTAGE and 'payload' in data and 'voltage' in data['payload'] and data['payload'].get('voltage',0) != 0:
|
||||||
publish_voltage( data, metadata )
|
publish_voltage( data, metadata )
|
||||||
|
pass
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("Error sending to IO:", str(e))
|
print("Error sending to IO:", str(e))
|
||||||
|
|
||||||
|
|
||||||
mqttClient.on_message = on_message
|
mqttClient.on_message = on_message
|
||||||
|
|
||||||
# loop forever
|
#def on_log(client, userdata, level, buf):
|
||||||
|
#print("log: ",buf)
|
||||||
|
|
||||||
|
#mqttClient.on_log=on_log
|
||||||
|
|
||||||
while(True):
|
while(True):
|
||||||
pass
|
if (not mqttClient.is_connected()) :
|
||||||
|
print("Connecting to mqtt server")
|
||||||
|
mqttClient.connect(MQTT_SERVER, MQTT_PORT)
|
||||||
|
mqttClient.loop_start()
|
||||||
|
for channel in channel_array:
|
||||||
|
print("Subscribing to %s" % channel)
|
||||||
|
mqttClient.subscribe("mesh/2/json/%s/#" % (channel))
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
time.sleep(.01)
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue