Merge pull request #263 from SpudGunMan/lab

Lab
This commit is contained in:
Kelly
2025-11-11 13:03:39 -08:00
committed by GitHub
6 changed files with 73 additions and 128 deletions

View File

@@ -323,6 +323,7 @@ value =
# interval to use when time is not set (e.g. every 2 days)
interval =
# time of day in 24:00 hour format when value is 'day' and interval is not set
# Process run :00,:20,:40 try and vary the 20 minute offsets to avoid collision
time =
[radioMon]

View File

@@ -15,6 +15,7 @@ def setup_custom_schedules(send_message, tell_joke, welcome_message, handle_wxc,
5. Make sure to uncomment (delete the single #) the example schedules down at the end of the file to enable them
Python is sensitive to indentation so be careful when editing this file.
https://thonny.org is included on pi's image and is a simple IDE to use for editing python files.
6. System Tasks run every 20min try and avoid overlapping schedules to reduce API rapid fire issues. use like 8:05
Available functions you can import and use, be sure they are enabled modules in config.ini:
- tell_joke() - Returns a random joke

View File

@@ -457,8 +457,8 @@ if [[ $(echo "${embedded}" | grep -i "^n") ]]; then
printf "sudo systemctl disable %s.service\n" "$service" >> install_notes.txt
printf "sudo systemctl disable %s.service\n" "$service" >> install_notes.txt
printf "\n older chron statment to run the report generator hourly:\n" >> install_notes.txt
printf "0 * * * * /usr/bin/python3 $program_path/etc/report_generator5.py" >> install_notes.txt
printf " to edit crontab run 'crontab -e'\n" >> install_notes.txt
#printf "0 * * * * /usr/bin/python3 $program_path/etc/report_generator5.py" >> install_notes.txt
#printf " to edit crontab run 'crontab -e'\n" >> install_notes.txt
printf "\nmesh_bot_reporting.timer installed to run daily at 4:20 am\n" >> install_notes.txt
printf "Check timer status: systemctl status mesh_bot_reporting.timer\n" >> install_notes.txt
printf "List all timers: systemctl list-timers\n" >> install_notes.txt
@@ -483,21 +483,6 @@ else
# add service dependency for meshtasticd into service file
#replace="s|After=network.target|After=network.target meshtasticd.service|g"
# Set up the meshing around service
sudo cp /opt/meshing-around/etc/$service.service /etc/systemd/system/$service.service
sudo systemctl daemon-reload
sudo systemctl enable $service.service
sudo systemctl start $service.service
sudo systemctl daemon-reload
# # check if the cron job already exists
# if ! crontab -l | grep -q "$chronjob"; then
# # add the cron job to run the report_generator5.py script
# (crontab -l 2>/dev/null; echo "$chronjob") | crontab -
# printf "\nAdded cron job to run report_generator5.py\n"
# else
# printf "\nCron job already exists, skipping\n"
# fi
# document the service install
printf "Reference following commands:\n\n" > install_notes.txt
printf "sudo systemctl status %s.service\n" "$service" >> install_notes.txt
@@ -508,8 +493,8 @@ else
printf "sudo systemctl stop %s.service\n" "$service" >> install_notes.txt
printf "sudo systemctl disable %s.service\n" "$service" >> install_notes.txt
printf "older crontab to run the report generator hourly:" >> install_notes.txt
printf "0 * * * * /usr/bin/python3 $program_path/etc/report_generator5.py" >> install_notes.txt
printf " to edit crontab run 'crontab -e'" >> install_notes.txt
#printf "0 * * * * /usr/bin/python3 $program_path/etc/report_generator5.py" >> install_notes.txt
#printf " to edit crontab run 'crontab -e'" >> install_notes.txt
printf "\nmesh_bot_reporting.timer installed to run daily at 4:20 am\n" >> install_notes.txt
printf "Check timer status: systemctl status mesh_bot_reporting.timer\n" >> install_notes.txt
printf "List all timers: systemctl list-timers\n" >> install_notes.txt

View File

@@ -1909,25 +1909,18 @@ def onReceive(packet, interface):
# 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')
# 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
try:
channel_name, _ = resolve_channel_name(channel_number, rxNode, interface)
except Exception as e:
channel_name = "unknown"
logger.debug(f"System: channel resolution error: {e}")
#debug channel info
# if "unknown" in str(channel_name):
# logger.debug(f"System: Received Packet on Channel:{channel_number} on Interface:{rxNode}")
# else:
# logger.debug(f"System: Received Packet on Channel:{channel_number} Name:{channel_name} on Interface:{rxNode}")
# check if the packet has a simulator flag
simulator_flag = packet.get('decoded', {}).get('simulator', False)
if isinstance(simulator_flag, dict):

View File

@@ -422,29 +422,58 @@ def build_channel_cache(force_refresh: bool = False):
# lightweight call to fetch local node and its channels once
node = globals()[f'interface{i}'].getNode('^local')
channels = getattr(node, "channels", []) or []
# try to use the node-provided channel/hash table if available
try:
ch_hash_table = node.get_channels_with_hash()
except Exception:
logger.warning(f"System: update meshtastic API 2.7.4 +")
ch_hash_table = {}
channel_dict = {}
for channel in channels:
if getattr(channel, "role", False):
channel_name = getattr(channel.settings, "name", "").strip()
channel_number = getattr(channel, "index", 0)
if channel_name:
channel_dict[channel_name] = {"number": channel_number}
if not channel_name:
continue
ch_hash = None
# ch_hash_table may map by name or by index; try both defensively
if isinstance(ch_hash_table, dict):
# by name
if channel_name in ch_hash_table:
entry = ch_hash_table[channel_name]
if isinstance(entry, dict):
ch_hash = entry.get("hash") or entry.get("pskHash") or entry.get("hashValue")
elif isinstance(entry, (list, tuple)) and len(entry) >= 2:
ch_hash = entry[1]
else:
ch_hash = entry
# by index
elif channel_number in ch_hash_table:
entry = ch_hash_table[channel_number]
if isinstance(entry, dict):
ch_hash = entry.get("hash") or entry.get("pskHash") or entry.get("hashValue")
elif isinstance(entry, (list, tuple)) and len(entry) >= 2:
ch_hash = entry[1]
else:
ch_hash = entry
# fallback to generate_hash with default PSK if no table/hash available
if ch_hash is None:
try:
ch_hash = generate_hash(channel_name, "AQ==")
except Exception:
ch_hash = 0
channel_dict[channel_name] = {"number": channel_number, "hash": ch_hash}
if channel_dict:
cache.append({"interface_id": i, "channels": channel_dict})
logger.debug(f"System: Fetched Channel List from Device{i} (cached)")
except Exception as e:
logger.debug(f"System: Error fetching channel list from Device{i}: {e}")
# compute and attach channel hash (PSK default) once
for device in cache:
for channel_name, info in list(device["channels"].items()):
psk_base64 = "AQ=="
try:
channel_hash = generate_hash(channel_name, psk_base64)
except Exception:
channel_hash = 0
device["channels"][channel_name] = {"number": info.get("number", 0), "hash": channel_hash}
# hashes are attached above using node.get_channels_with_hash() when available
_channel_cache = cache
return _channel_cache
@@ -455,12 +484,9 @@ def refresh_channel_cache():
channel_list = build_channel_cache()
#### FUN-ctions ####
def resolve_channel_name(channel_number, rxNode=1, interface_obj=None, allow_node_lookup: bool = False):
def resolve_channel_name(channel_number, rxNode=1, interface_obj=None):
"""
Resolve a channel number/hash to a human name.
Prefers the cached channel_list (build_channel_cache) and only does node API lookups
if allow_node_lookup is True.
Returns (channel_name, matched_index_or_hash)
Resolve a channel number/hash to its name using cached channel list.
"""
try:
# ensure cache exists (cheap)
@@ -480,63 +506,9 @@ def resolve_channel_name(channel_number, rxNode=1, interface_obj=None, allow_nod
return (chan_name, info)
except Exception:
continue
break
# If caller allows, try heavier node-level lookups as a fallback
if not allow_node_lookup:
return ("unknown", channel_number)
if interface_obj is None:
interface_obj = globals().get(f'interface{rxNode}')
# Try node-level API
node = None
if interface_obj:
if hasattr(interface_obj, "get_node") and callable(interface_obj.get_node):
node = interface_obj.get_node()
elif hasattr(interface_obj, "node"):
node = getattr(interface_obj, "node")
channels = None
if node is not None and hasattr(node, "get_channels_with_hash"):
try:
channels = node.get_channels_with_hash()
except Exception:
channels = None
# Fallback: generate channel list from raw payload
if not channels:
try:
from meshtastic.util import generate_channel_hash
except Exception:
generate_channel_hash = None
channels_raw = {}
if node is not None:
if isinstance(node, dict):
channels_raw = node.get("channels", {}) or {}
else:
channels_raw = getattr(node, "channels", None) or getattr(node, "channels_payload", None) or {}
if channels_raw is None:
channels_raw = {}
if generate_channel_hash and channels_raw:
try:
channels = generate_channel_hash(channels_raw)
except Exception:
channels = None
# If we have a channels sequence, try to match by hash or index
if channels and isinstance(channels, (list, tuple)):
for ch in channels:
try:
if str(ch.get('hash')) == str(channel_number) or str(ch.get('index')) == str(channel_number):
return (ch.get('name', 'unknown'), ch.get('index') or ch.get('hash'))
except Exception:
continue
except Exception:
pass
return ("unknown", channel_number)
break # stop searching other devices
except Exception as e:
logger.debug(f"System: Error resolving channel name from cache: {e}")
def cleanup_memory():

View File

@@ -279,24 +279,17 @@ def onReceive(packet, interface):
# 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')
# 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
try:
channel_name, _ = resolve_channel_name(channel_number, rxNode, interface)
except Exception as e:
channel_name = "unknown"
logger.debug(f"System: channel resolution error: {e}")
# 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
#debug channel info
# if "unknown" in str(channel_name):
# logger.debug(f"System: Received Packet on Channel:{channel_number} on Interface:{rxNode}")
# else:
# logger.debug(f"System: Received Packet on Channel:{channel_number} Name:{channel_name} on Interface:{rxNode}")
# check if the packet has a simulator flag
simulator_flag = packet.get('decoded', {}).get('simulator', False)