Compare commits

...

15 Commits

Author SHA1 Message Date
SpudGunMan
1324f83f17 enhance schedule
allow basic Joke and Weather messages with out extra config
# value can also be joke (everyXmin) or weather (hour) for special scheduled messages
# custom for module/scheduler.py custom schedule examples
2025-10-20 12:07:22 -07:00
SpudGunMan
08ae8c31a0 enhance:fix
I added some things to help any future aarg, sorry I broke it again I also
2025-10-20 11:45:49 -07:00
SpudGunMan
957e803951 cleanup 2025-10-20 11:35:26 -07:00
SpudGunMan
2de3441d67 enhance
with time stamp and also better CSV answers for review
2025-10-20 09:54:44 -07:00
SpudGunMan
2b420022f9 timeFix
will be make year 2000
2025-10-20 09:54:25 -07:00
SpudGunMan
d01f143adf Update system.py 2025-10-20 09:24:18 -07:00
SpudGunMan
5f5aeeadac Update system.py 2025-10-20 09:22:45 -07:00
SpudGunMan
d57826613c moved
to slurp repo
2025-10-20 09:19:18 -07:00
SpudGunMan
af09dc0cf9 improveUse 2025-10-19 22:43:51 -07:00
SpudGunMan
011bac41f2 lower 2025-10-19 22:32:06 -07:00
SpudGunMan
20467ea886 enhance 2025-10-19 22:30:40 -07:00
SpudGunMan
bbfd71f011 "IP-Network" 2025-10-19 22:27:40 -07:00
SpudGunMan
e1ff87a197 enhance Ping 2025-10-19 22:26:09 -07:00
SpudGunMan
a859f830bb coreFix
Enhance Packet hop and MQTT detection
2025-10-19 22:16:19 -07:00
SpudGunMan
d99698e7f3 Update blackjack.py 2025-10-19 20:25:36 -07:00
11 changed files with 232 additions and 316 deletions

View File

@@ -522,7 +522,9 @@ enabled = False # enable or disable the scheduler module
interface = 1 # channel to send the message to
channel = 2
message = "MeshBot says Hello! DM for more info."
value = # value can be min,hour,day,mon,tue,wed,thu,fri,sat,sun
value = # value can be min,hour,day,mon,tue,wed,thu,fri,sat,sun.
# value can also be joke (everyXmin) or weather (hour) for special scheduled messages
# custom for module/scheduler.py custom schedule examples
interval = # interval to use when time is not set (e.g. every 2 days)
time = # time of day in 24:00 hour format when value is 'day' and interval is not set
```

View File

@@ -277,7 +277,9 @@ channel = 2
message = "MeshBot says Hello! DM for more info."
# enable overides the above and uses the motd as the message
schedulerMotd = False
# value can be min,hour,day,mon,tue,wed,thu,fri,sat,sun. or custom for module/scheduler.py
# value can be min,hour,day,mon,tue,wed,thu,fri,sat,sun.
# value can also be joke (everyXmin) or weather (hour) for special scheduled messages
# custom for module/scheduler.py custom schedule examples
value =
# interval to use when time is not set (e.g. every 2 days)
interval =

View File

@@ -1,224 +0,0 @@
// Example to receive and decode Meshtastic UDP packets
// Make sure to install the meashtastic library and generate the .pb.h and .pb.c files from the Meshtastic .proto definitions
// https://github.com/meshtastic/protobufs/tree/master/meshtastic
// Example to receive and decode Meshtastic UDP packets
#include <WiFi.h>
#include <WiFiUdp.h>
// #include <AESLib.h> // or another AES library
#include "pb_decode.h"
#include "meshtastic/mesh.pb.h" // MeshPacket, Position, etc.
#include "meshtastic/portnums.pb.h" // Port numbers enum
#include "meshtastic/telemetry.pb.h" // Telemetry message
const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";
const char* default_key = "1PG7OiApB1nwvP+rz05pAQ=="; // Your network key here
uint8_t aes_key[16]; // Buffer for decoded key
const char* MCAST_GRP = "224.0.0.69";
const uint16_t MCAST_PORT = 4403;
unsigned long udpPacketCount = 0;
WiFiUDP udp;
IPAddress multicastIP;
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println("Scanning for WiFi networks...");
int n = WiFi.scanNetworks();
if (n == 0) {
Serial.println("No networks found.");
} else {
Serial.print(n);
Serial.println(" networks found:");
for (int i = 0; i < n; ++i) {
Serial.print(i + 1);
Serial.print(": ");
Serial.print(WiFi.SSID(i));
Serial.print(" (RSSI ");
Serial.print(WiFi.RSSI(i));
Serial.print(")");
Serial.println((WiFi.encryptionType(i) == WIFI_AUTH_OPEN) ? " [OPEN]" : " [SECURED]");
delay(10);
}
}
Serial.println("Connecting to WiFi...");
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
unsigned long startAttemptTime = millis();
const unsigned long wifiTimeout = 20000;
while (WiFi.status() != WL_CONNECTED && millis() - startAttemptTime < wifiTimeout) {
delay(500);
Serial.print(".");
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("\nWiFi connected.");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
multicastIP.fromString(MCAST_GRP);
if (udp.beginMulticast(multicastIP, MCAST_PORT)) {
Serial.println("UDP multicast listener started.");
} else {
Serial.println("Failed to start UDP multicast listener.");
}
} else {
Serial.print("\nFailed to connect to WiFi. SSID: ");
Serial.println(ssid);
Serial.println("Check SSID, range, and password.");
}
}
void printHex(const uint8_t* buf, size_t len) {
for (size_t i = 0; i < len; i++) {
Serial.printf("%02X ", buf[i]);
}
Serial.println();
}
void printAscii(const uint8_t* buf, size_t len) {
for (size_t i = 0; i < len; i++) {
char c = static_cast<char>(buf[i]);
Serial.print(isprint(c) ? c : '.');
}
Serial.println();
}
void decodeKey() {
// Convert base64 key to raw bytes
// You may need to add a base64 decoding function/library
// Example: decode_base64(default_key, aes_key, sizeof(aes_key));
}
void decryptPayload(const uint8_t* encrypted, size_t len, uint8_t* decrypted) {
// Use AESLib or similar to decrypt
// Example: aes128_dec_single(decrypted, encrypted, aes_key);
}
void loop() {
int packetSize = udp.parsePacket();
if (!packetSize) {
delay(50);
return;
}
udpPacketCount++;
Serial.print("UDP packets seen: ");
Serial.println(udpPacketCount);
uint8_t buffer[512];
int len = udp.read(buffer, sizeof(buffer));
if (len <= 0) {
Serial.println("Failed to read UDP packet.");
delay(50);
return;
}
// Always show raw payload
Serial.print("Raw UDP payload (hex): ");
printHex(buffer, len);
Serial.print("Raw UDP payload (ASCII): ");
printAscii(buffer, len);
// Decode outer MeshPacket
meshtastic_MeshPacket pkt = meshtastic_MeshPacket_init_zero;
pb_istream_t stream = pb_istream_from_buffer(buffer, len);
if (!pb_decode(&stream, meshtastic_MeshPacket_fields, &pkt)) {
Serial.println("Failed to decode meshtastic_MeshPacket.");
delay(50);
return;
}
// Basic MeshPacket fields
Serial.print("id: "); Serial.println(pkt.id);
Serial.print("rx_time: "); Serial.println(pkt.rx_time);
Serial.print("rx_snr: "); Serial.println(pkt.rx_snr, 2);
Serial.print("rx_rssi: "); Serial.println(pkt.rx_rssi);
Serial.print("hop_limit: "); Serial.println(pkt.hop_limit);
Serial.print("priority: "); Serial.println(pkt.priority);
Serial.print("from: "); Serial.println(pkt.from);
Serial.print("to: "); Serial.println(pkt.to);
Serial.print("channel: "); Serial.println(pkt.channel);
// Only proceed if we have a decoded Data variant
if (pkt.which_payload_variant != meshtastic_MeshPacket_decoded_tag) {
Serial.println("Packet does not contain decoded Data (maybe encrypted or other variant).");
delay(50);
return;
}
const meshtastic_Data& data = pkt.decoded;
Serial.print("Portnum: "); Serial.println(data.portnum);
Serial.print("Payload size: "); Serial.println(data.payload.size);
if (data.payload.size == 0) {
Serial.println("No inner payload bytes.");
delay(50);
return;
}
// Decode by portnum
switch (data.portnum) {
case meshtastic_PortNum_TEXT_MESSAGE_APP: {
// Current schemas do not use a separate user.pb.h. Text payload is plain bytes.
Serial.print("Decoded text message: ");
printAscii(data.payload.bytes, data.payload.size);
break;
}
case meshtastic_PortNum_POSITION_APP: {
meshtastic_Position pos = meshtastic_Position_init_zero;
pb_istream_t ps = pb_istream_from_buffer(data.payload.bytes, data.payload.size);
if (pb_decode(&ps, meshtastic_Position_fields, &pos)) {
Serial.print("Position lat="); Serial.print(pos.latitude_i / 1e7, 7);
Serial.print(" lon="); Serial.print(pos.longitude_i / 1e7, 7);
Serial.print(" alt="); Serial.println(pos.altitude);
} else {
Serial.println("Failed to decode Position payload.");
}
break;
}
case meshtastic_PortNum_TELEMETRY_APP: {
meshtastic_Telemetry tel = meshtastic_Telemetry_init_zero;
pb_istream_t ts = pb_istream_from_buffer(data.payload.bytes, data.payload.size);
if (pb_decode(&ts, meshtastic_Telemetry_fields, &tel)) {
// Print a few common fields if present
if (tel.which_variant == meshtastic_Telemetry_device_metrics_tag) {
const meshtastic_DeviceMetrics& m = tel.variant.device_metrics;
Serial.print("Telemetry battery_level="); Serial.print(m.battery_level);
Serial.print(" voltage="); Serial.print(m.voltage);
Serial.print(" air_util_tx="); Serial.println(m.air_util_tx);
} else {
Serial.println("Telemetry decoded, different variant. Raw bytes:");
printHex(data.payload.bytes, data.payload.size);
}
} else {
Serial.println("Failed to decode Telemetry payload.");
}
break;
}
default: {
Serial.print("Unhandled portnum "); Serial.print((int)data.portnum);
Serial.println(", showing payload as hex:");
printHex(data.payload.bytes, data.payload.size);
break;
}
}
delay(50);
}

View File

@@ -1444,12 +1444,14 @@ def onReceive(packet, interface):
# extract interface details from inbound packet
rxType = type(interface).__name__
# Valies assinged to the packet
rxNode, message_from_id, snr, rssi, hop, hop_away, channel_number = 0, 0, 0, 0, 0, 0, 0
# Values assinged to the packet
rxNode = message_from_id = snr = rssi = hop = hop_away = channel_number = hop_start = hop_count = hop_limit = 0
pkiStatus = (False, 'ABC')
replyIDset = False
emojiSeen = False
simulator_flag = False
isDM = False
channel_name = "unknown"
playingGame = False
if DEBUGpacket:
@@ -1494,9 +1496,33 @@ def onReceive(packet, interface):
elif multiple_interface and interface8_type == 'ble': rxNode = 8
elif multiple_interface and interface9_type == 'ble': rxNode = 9
# check if the packet has a channel flag use it
# check if the packet has a channel flag use it ## FIXME needs to be channel hash lookup
if packet.get('channel'):
channel_number = packet.get('channel', 0)
channel_number = packet.get('channel')
# get channel name from channel number from connected devices
for device in channel_list:
if device["interface_id"] == rxNode:
device_channels = device['channels']
for chan_name, info in device_channels.items():
if info['number'] == channel_number:
channel_name = chan_name
break
# get channel hashes for the interface
device = next((d for d in channel_list if d["interface_id"] == rxNode), None)
if device:
# Find the channel name whose hash matches channel_number
for chan_name, info in device['channels'].items():
if info['hash'] == channel_number:
print(f"Matched channel hash {info['hash']} to channel name {chan_name}")
channel_name = chan_name
break
# check if the packet has a simulator flag
simulator_flag = packet.get('decoded', {}).get('simulator', False)
if isinstance(simulator_flag, dict):
# assume Software Simulator
simulator_flag = True
# set the message_from_id
message_from_id = packet['from']
@@ -1521,7 +1547,13 @@ def onReceive(packet, interface):
message_bytes = packet['decoded']['payload']
message_string = message_bytes.decode('utf-8')
via_mqtt = packet['decoded'].get('viaMqtt', False)
transport_mechanism = packet['decoded'].get('transport_mechanism', 'unknown')
transport_mechanism = (
packet.get('transport_mechanism')
or packet.get('transportMechanism')
or (packet.get('decoded', {}).get('transport_mechanism'))
or (packet.get('decoded', {}).get('transportMechanism'))
or 'unknown'
)
rx_time = packet['decoded'].get('rxTime', time.time())
# check if the packet is from us
@@ -1548,40 +1580,36 @@ def onReceive(packet, interface):
# check if the packet has a hop count flag use it
if packet.get('hopsAway'):
hop_away = packet.get('hopsAway', 0)
if packet.get('hopStart'):
hop_start = packet.get('hopStart', 0)
if packet.get('hopLimit'):
hop_limit = packet.get('hopLimit', 0)
# calculate hop count
hop = ""
if hop_limit > 0 and hop_start >= hop_limit:
hop_count = hop_away + (hop_start - hop_limit)
elif hop_limit > 0 and hop_start < hop_limit:
hop_count = hop_away + (hop_limit - hop_start)
else:
# if the packet does not have a hop count try other methods
if packet.get('hopLimit'):
hop_limit = packet.get('hopLimit', 0)
else:
hop_limit = 0
if packet.get('hopStart'):
hop_start = packet.get('hopStart', 0)
else:
hop_start = 0
if enableHopLogs:
logger.debug(f"System: Packet HopDebugger: hop_away:{hop_away} hop_limit:{hop_limit} hop_start:{hop_start}")
hop_count = hop_away
if hop_away == 0 and hop_limit == 0 and hop_start == 0:
hop = "Last Hop"
hop_count = 0
if hop_start == hop_limit:
hop = "Direct"
hop_count = 0
elif hop_start == 0 and hop_limit > 0 or via_mqtt or transport_mechanism == "TRANSPORT_MQTT":
hop = "MQTT"
hop_count = 0
else:
# set hop to Direct if the message was sent directly otherwise set the hop count
if hop_away > 0:
hop_count = hop_away
else:
hop_count = hop_start - hop_limit
#print (f"calculated hop count: {hop_start} - {hop_limit} = {hop_count}")
hop = f"{hop_count} hops"
if hop_start == hop_limit and "lora" in str(transport_mechanism).lower():
hop = "Direct"
if ((hop_start == 0 and hop_limit >= 0) or via_mqtt or ("mqtt" in str(transport_mechanism).lower())):
hop = "MQTT"
if "unknown" in str(transport_mechanism).lower() and (snr == 0 and rssi == 0):
hop = "IP-Network"
if enableHopLogs:
logger.debug(f"System: Packet HopDebugger: hop_away:{hop_away} hop_limit:{hop_limit} hop_start:{hop_start} calculated_hop_count:{hop_count} final_hop_value:{hop} via_mqtt:{via_mqtt} transport_mechanism:{transport_mechanism}")
# check with stringSafeChecker if the message is safe
if stringSafeCheck(message_string) is False:

View File

@@ -97,7 +97,7 @@ def bbs_delete_message(messageID = 0, fromNode = 0):
def bbs_post_message(subject, message, fromNode, threadID=0, replytoID=0):
# post a message to the bbsdb
now = today.strftime('%Y-%m-%d %H:%M:%S')
now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
thread = threadID
replyto = replytoID
# post a message to the bbsdb and assign a messageID

View File

@@ -297,7 +297,7 @@ def playBlackJack(nodeID, message, last_cmd=None):
#resend the hand
msg += show_some(p_cards, d_cards, p_hand)
return msg
elif message.lower() == "blackjack":
elif "blackjack" in message.lower():
return f"\nTo place a bet, enter the amount you wish to wager."
else:
try:

View File

@@ -756,7 +756,7 @@ def get_nws_marine(zone, days=3):
marine_pz_data = marine_pz_data.text
#validate data
todayDate = today.strftime("%Y%m%d")
todayDate = datetime.now().strftime("%Y%m%d")
if marine_pz_data.startswith("Expires:"):
expires = marine_pz_data.split(";;")[0].split(":")[1]
expires_date = expires[:8]

View File

@@ -1,15 +1,16 @@
# modules/scheduler.py 2025 meshing-around
# Scheduler setup for Mesh Bot
import asyncio
import schedule
from modules.log import logger
from modules.system import send_message, BroadcastScheduler
from modules.system import send_message
# methods available for custom scheduler messages
from mesh_bot import tell_joke, welcome_message, MOTD, handle_wxc, handle_moon, handle_sun, handle_riverFlow, handle_tide, handle_satpass
async def setup_scheduler(
schedulerMotd, MOTD, schedulerMessage, schedulerChannel, schedulerInterface,
schedulerValue, schedulerTime, schedulerInterval, logger, BroadcastScheduler
):
schedulerValue, schedulerTime, schedulerInterval, logger, BroadcastScheduler):
# methods available for custom scheduler messages
from mesh_bot import tell_joke, welcome_message, handle_wxc, handle_moon, handle_sun, handle_riverFlow, handle_tide, handle_satpass
schedulerValue = schedulerValue.lower().strip()
schedulerTime = schedulerTime.strip()
schedulerInterval = schedulerInterval.strip()
@@ -23,7 +24,8 @@ async def setup_scheduler(
scheduler_message = schedulerMessage
# Basic Scheduler Options
if 'custom' not in schedulerValue:
basicOptions = ['day', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun', 'hour', 'min']
if any(option.lower() in schedulerValue.lower() for option in basicOptions):
# Basic scheduler job to run the schedule see examples below for custom schedules
if schedulerValue.lower() == 'day':
if schedulerTime != '':
@@ -49,16 +51,28 @@ async def setup_scheduler(
elif 'min' in schedulerValue.lower():
schedule.every(int(schedulerInterval)).minutes.do(lambda: send_message(scheduler_message, schedulerChannel, 0, schedulerInterface))
logger.debug(f"System: Starting the basic scheduler to send '{scheduler_message}' on schedule '{schedulerValue}' every {schedulerInterval} interval at time '{schedulerTime}' on Device:{schedulerInterface} Channel:{schedulerChannel}")
else:
# Default schedule if no valid configuration is provided
elif 'joke' in schedulerValue.lower():
# Schedule to send a joke every specified interval
schedule.every(int(schedulerInterval)).minutes.do(lambda: send_message(tell_joke(), schedulerChannel, 0, schedulerInterface))
logger.debug(f"System: Starting the joke scheduler to send a joke every {schedulerInterval} minutes on Device:{schedulerInterface} Channel:{schedulerChannel}")
elif 'weather' in schedulerValue.lower():
# Schedule to send weather updates every specified interval
schedule.every(int(schedulerInterval)).hours.do(lambda: send_message(handle_wxc(0, schedulerInterface, 'wx'), schedulerChannel, 0, schedulerInterface))
logger.debug(f"System: Starting the weather scheduler to send weather updates every {schedulerInterval} hours on Device:{schedulerInterface} Channel:{schedulerChannel}")
elif 'custom' in schedulerValue.lower():
# Custom scheduler job to run the schedule see examples below
# custom scheduler job to run the schedule see examples below
logger.debug(f"System: Starting the scheduler to send reminder every Monday at noon on Device:{schedulerInterface} Channel:{schedulerChannel}")
logger.debug(f"System: Starting the custom scheduler default to send reminder every Monday at noon on Device:{schedulerInterface} Channel:{schedulerChannel}")
schedule.every().monday.at("12:00").do(lambda: logger.info("System: Scheduled Broadcast Enabled Reminder"))
# send a joke every 15 minutes
#schedule.every(15).minutes.do(lambda: send_message(tell_joke(), schedulerChannel, 0, schedulerInterface))
# Place your custom schedule code below this line, helps with merges
# Place your custom schedule code above this line
# Start the Broadcast Scheduler
await BroadcastScheduler()
except Exception as e:

View File

@@ -61,8 +61,9 @@ class SurveyModule:
'answers': [],
'location': location if surveyRecordLocation and location is not None else 'N/A'
}
msg = f"'{survey_name}'📝survey\nSend answer' or 'end'\n"
msg = f"'{survey_name}'📝survey\n"
msg += self.show_question(user_id)
msg += f"\nSend answer' or 'end'"
return msg
except Exception as e:
logger.error(f"Error starting survey for user {user_id}: {e}")
@@ -96,12 +97,12 @@ class SurveyModule:
filename = os.path.join(self.response_dir, f'{survey_name}_responses.csv')
try:
with open(filename, 'a', encoding='utf-8') as f:
row = list(map(str, self.responses[user_id]['answers']))
if surveyRecordID:
row.insert(0, str(user_id))
if surveyRecordLocation:
location = self.responses[user_id].get('location')
row.insert(1 if surveyRecordID else 0, str(location) if location is not None else "N/A")
# Always write: timestamp, userID, position, answers...
timestamp = datetime.datetime.now().strftime('%d%m%Y%H%M%S')
user_id_str = str(user_id)
location = self.responses[user_id].get('location', "N/A")
answers = list(map(str, self.responses[user_id]['answers']))
row = [timestamp, user_id_str, str(location)] + answers
f.write(','.join(row) + '\n')
logger.info(f"Survey: Responses for user {user_id} saved for survey '{survey_name}' to {filename}.")
except Exception as e:
@@ -125,7 +126,8 @@ class SurveyModule:
return "Please answer with a letter (A, B, C, ...)."
option_index = ord(answer_char) - 65
if 0 <= option_index < len(question['options']):
self.responses[user_id]['answers'].append(str(option_index))
# Valid answer record letter, not index
self.responses[user_id]['answers'].append(answer_char)
self.responses[user_id]['current_question'] += 1
return f"Recorded..\n" + self.show_question(user_id)
else:

View File

@@ -7,6 +7,7 @@ import meshtastic.ble_interface
import time
import asyncio
import random
import base64
# not ideal but needed?
import contextlib # for suppressing output on watchdog
import io # for suppressing output on watchdog
@@ -315,6 +316,24 @@ if ble_count > 1:
logger.critical(f"System: Multiple BLE interfaces detected. Only one BLE interface is allowed. Exiting")
exit()
def xor_hash(data: bytes) -> int:
"""Compute an XOR hash from bytes."""
result = 0
for char in data:
result ^= char
return result
def generate_hash(name: str, key: str) -> int:
"""generate the channel number by hashing the channel name and psk"""
if key == "AQ==":
key = "1PG7OiApB1nwvP+rz05pAQ=="
replaced_key = key.replace("-", "+").replace("_", "/")
key_bytes = base64.b64decode(replaced_key.encode("utf-8"))
h_name = xor_hash(bytes(name, "utf-8"))
h_key = xor_hash(key_bytes)
result: int = h_name ^ h_key
return result
# Initialize interfaces
logger.debug(f"System: Initializing Interfaces")
interface1 = interface2 = interface3 = interface4 = interface5 = interface6 = interface7 = interface8 = interface9 = None
@@ -365,6 +384,44 @@ for i in range(1, 10):
else:
globals()[f'myNodeNum{i}'] = 777
# Fetch channel list from each device
channel_list = []
for i in range(1, 10):
if globals().get(f'interface{i}') and globals().get(f'interface{i}_enabled'):
try:
node = globals()[f'interface{i}'].getNode('^local')
channels = node.channels
channel_dict = {}
for channel in channels:
if hasattr(channel, 'role') and channel.role:
channel_name = getattr(channel.settings, 'name', '').strip()
channel_number = getattr(channel, 'index', 0)
# Only add channels with a non-empty name
if channel_name:
channel_dict[channel_name] = channel_number
channel_list.append({
"interface_id": i,
"channels": channel_dict
})
logger.debug(f"System: Fetched Channel List from Device{i}")
except Exception as e:
logger.error(f"System: Error fetching channel list from Device{i}: {e}")
# add channel hash to channel_list
for device in channel_list:
interface_id = device["interface_id"]
interface = globals().get(f'interface{interface_id}')
for channel_name, channel_number in device["channels"].items():
psk_base64 = "AQ==" # default PSK
channel_hash = generate_hash(channel_name, psk_base64)
# add hash to the channel entry in channel_list under key 'hash'
for entry in channel_list:
if entry["interface_id"] == interface_id:
entry["channels"][channel_name] = {
"number": channel_number,
"hash": channel_hash
}
#### FUN-ctions ####
def cleanup_memory():
@@ -454,7 +511,6 @@ def get_name_from_number(number, type='long', nodeInt=1):
name = str(decimal_to_hex(number)) # If name not found, use the ID as string
return name
def get_num_from_short_name(short_name, nodeInt=1):
interface = globals()[f'interface{nodeInt}']
# Get the node number from the short name, converting all to lowercase for comparison (good practice?)

View File

@@ -216,11 +216,14 @@ def onReceive(packet, interface):
rxType = type(interface).__name__
# Valies assinged to the packet
rxNode, message_from_id, snr, rssi, hop, hop_away, channel_number = 0, 0, 0, 0, 0, 0, 0
rxNode = message_from_id = snr = rssi = hop = hop_away = channel_number = hop_start = hop_count = hop_limit = 0
pkiStatus = (False, 'ABC')
replyIDset = False
emojiSeen = False
simulator_flag = False
isDM = False
channel_name = "unknown"
playingGame = False
if DEBUGpacket:
# Debug print the interface object
@@ -265,9 +268,33 @@ def onReceive(packet, interface):
elif multiple_interface and interface8_type == 'ble': rxNode = 8
elif multiple_interface and interface9_type == 'ble': rxNode = 9
# check if the packet has a channel flag use it
# check if the packet has a channel flag use it ## FIXME needs to be channel hash lookup
if packet.get('channel'):
channel_number = packet.get('channel', 0)
channel_number = packet.get('channel')
# get channel name from channel number from connected devices
for device in channel_list:
if device["interface_id"] == rxNode:
device_channels = device['channels']
for chan_name, info in device_channels.items():
if info['number'] == channel_number:
channel_name = chan_name
break
# get channel hashes for the interface
device = next((d for d in channel_list if d["interface_id"] == rxNode), None)
if device:
# Find the channel name whose hash matches channel_number
for chan_name, info in device['channels'].items():
if info['hash'] == channel_number:
print(f"Matched channel hash {info['hash']} to channel name {chan_name}")
channel_name = chan_name
break
# check if the packet has a simulator flag
simulator_flag = packet.get('decoded', {}).get('simulator', False)
if isinstance(simulator_flag, dict):
# assume Software Simulator
simulator_flag = True
# set the message_from_id
message_from_id = packet['from']
@@ -282,6 +309,7 @@ def onReceive(packet, interface):
message_bytes = packet['decoded']['payload']
message_string = message_bytes.decode('utf-8')
via_mqtt = packet['decoded'].get('viaMqtt', False)
transport_mechanism = packet['decoded'].get('transport_mechanism', 'unknown')
# check if the packet is from us
if message_from_id == myNodeNum1 or message_from_id == myNodeNum2:
@@ -294,46 +322,54 @@ def onReceive(packet, interface):
# check if the packet has a publicKey flag use it
if packet.get('publicKey'):
pkiStatus = (packet.get('pkiEncrypted', False), packet.get('publicKey', 'ABC'))
pkiStatus = packet.get('pkiEncrypted', False), packet.get('publicKey', 'ABC')
# check if the packet has replyId flag // currently unused in the code
if packet.get('replyId'):
replyIDset = packet.get('replyId', False)
# check if the packet has emoji flag set it // currently unused in the code
if packet.get('emoji'):
emojiSeen = packet.get('emoji', False)
# check if the packet has a hop count flag use it
if packet.get('hopsAway'):
hop_away = packet.get('hopsAway', 0)
else:
# if the packet does not have a hop count try other methods
if packet.get('hopLimit'):
hop_limit = packet.get('hopLimit', 0)
else:
hop_limit = 0
if packet.get('hopStart'):
hop_start = packet.get('hopStart', 0)
else:
hop_start = 0
if enableHopLogs:
logger.debug(f"System: Packet HopDebugger: hop_away:{hop_away} hop_limit:{hop_limit} hop_start:{hop_start}")
if packet.get('hopStart'):
hop_start = packet.get('hopStart', 0)
if packet.get('hopLimit'):
hop_limit = packet.get('hopLimit', 0)
# calculate hop count
hop = ""
if hop_limit > 0 and hop_start >= hop_limit:
hop_count = hop_away + (hop_start - hop_limit)
elif hop_limit > 0 and hop_start < hop_limit:
hop_count = hop_away + (hop_limit - hop_start)
else:
hop_count = hop_away
if hop_away == 0 and hop_limit == 0 and hop_start == 0:
hop = "Last Hop"
hop_count = 0
if hop_start == hop_limit:
hop = "Direct"
hop_count = 0
elif hop_start == 0 and hop_limit > 0 or via_mqtt:
hop = "MQTT"
hop_count = 0
else:
# set hop to Direct if the message was sent directly otherwise set the hop count
if hop_away > 0:
hop_count = hop_away
else:
hop_count = hop_start - hop_limit
#print (f"calculated hop count: {hop_start} - {hop_limit} = {hop_count}")
hop = f"{hop_count} hops"
if hop_start == hop_limit and "lora" in str(transport_mechanism).lower():
hop = "Direct"
if ((hop_start == 0 and hop_limit >= 0) or via_mqtt or ("mqtt" in str(transport_mechanism).lower())):
hop = "MQTT"
if "unknown" in str(transport_mechanism).lower() and (snr == 0 and rssi == 0):
hop = "IP-Network"
if enableHopLogs:
logger.debug(f"System: Packet HopDebugger: hop_away:{hop_away} hop_limit:{hop_limit} hop_start:{hop_start} calculated_hop_count:{hop_count} final_hop_value:{hop} via_mqtt:{via_mqtt} transport_mechanism:{transport_mechanism}")
# check with stringSafeChecker if the message is safe
if stringSafeCheck(message_string) is False:
logger.warning(f"System: Possibly Unsafe Message from {get_name_from_number(message_from_id, 'long', rxNode)}")
if help_message in message_string or welcome_message in message_string or "CMD?:" in message_string:
# ignore help and welcome messages
logger.warning(f"Got Own Welcome/Help header. From: {get_name_from_number(message_from_id, 'long', rxNode)}")