Merge branch 'master' into update-python-cli

This commit is contained in:
rcarteraz 2024-03-03 09:30:51 -07:00 committed by GitHub
commit 7be2f9d357
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 141 additions and 68 deletions

View file

@ -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} />

View file

@ -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)
``` ```