From 1568d026f2289490452624012b080b5ee2a0a66c Mon Sep 17 00:00:00 2001 From: SpudGunMan Date: Sun, 9 Nov 2025 15:23:26 -0800 Subject: [PATCH 01/12] flood packet work debug on --- mesh_bot.py | 26 ++++++++------------------ pong_bot.py | 24 +++++++----------------- 2 files changed, 15 insertions(+), 35 deletions(-) diff --git a/mesh_bot.py b/mesh_bot.py index e9eac0b..41910ca 100755 --- a/mesh_bot.py +++ b/mesh_bot.py @@ -1909,25 +1909,15 @@ 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 + logger.debug(f"System: Packet Received on Interface {rxNode} Channel: {channel_number} Name: {channel_name}") + # check if the packet has a simulator flag simulator_flag = packet.get('decoded', {}).get('simulator', False) if isinstance(simulator_flag, dict): diff --git a/pong_bot.py b/pong_bot.py index 1b1b0a7..32ec0af 100755 --- a/pong_bot.py +++ b/pong_bot.py @@ -279,24 +279,14 @@ 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 + logger.debug(f"System: Packet Received on Interface {rxNode} Channel: {channel_number} Name: {channel_name}") # check if the packet has a simulator flag simulator_flag = packet.get('decoded', {}).get('simulator', False) From 658fb33b699e173f7b740baff6ca6a17de8e83f8 Mon Sep 17 00:00:00 2001 From: SpudGunMan Date: Sun, 9 Nov 2025 15:29:52 -0800 Subject: [PATCH 02/12] enhance log --- mesh_bot.py | 5 ++++- pong_bot.py | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/mesh_bot.py b/mesh_bot.py index 41910ca..30dfd78 100755 --- a/mesh_bot.py +++ b/mesh_bot.py @@ -1916,7 +1916,10 @@ def onReceive(packet, interface): logger.debug(f"System: channel resolution error: {e}") #debug channel info - logger.debug(f"System: Packet Received on Interface {rxNode} Channel: {channel_number} Name: {channel_name}") + if channel_name == "unknown": + logger.debug(f"System: Unknown Channel {channel_number} on Interface {rxNode}") + else: + logger.debug(f"System: Resolved Channel {channel_number} to Name: {channel_name} on Interface {rxNode}") # check if the packet has a simulator flag simulator_flag = packet.get('decoded', {}).get('simulator', False) diff --git a/pong_bot.py b/pong_bot.py index 32ec0af..01d6188 100755 --- a/pong_bot.py +++ b/pong_bot.py @@ -286,7 +286,10 @@ def onReceive(packet, interface): logger.debug(f"System: channel resolution error: {e}") #debug channel info - logger.debug(f"System: Packet Received on Interface {rxNode} Channel: {channel_number} Name: {channel_name}") + if channel_name == "unknown": + logger.debug(f"System: Unknown Channel {channel_number} on Interface {rxNode}") + else: + logger.debug(f"System: Resolved Channel {channel_number} to Name: {channel_name} on Interface {rxNode}") # check if the packet has a simulator flag simulator_flag = packet.get('decoded', {}).get('simulator', False) From e64e60358d519ab1b845d659967437c91d18c08a Mon Sep 17 00:00:00 2001 From: SpudGunMan Date: Sun, 9 Nov 2025 15:33:32 -0800 Subject: [PATCH 03/12] enhance --- mesh_bot.py | 4 ++-- pong_bot.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mesh_bot.py b/mesh_bot.py index 30dfd78..849eb30 100755 --- a/mesh_bot.py +++ b/mesh_bot.py @@ -1917,9 +1917,9 @@ def onReceive(packet, interface): #debug channel info if channel_name == "unknown": - logger.debug(f"System: Unknown Channel {channel_number} on Interface {rxNode}") + logger.debug(f"System: Received Packet on Channel:{channel_number} on Interface:{rxNode}") else: - logger.debug(f"System: Resolved Channel {channel_number} to Name: {channel_name} on Interface {rxNode}") + 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) diff --git a/pong_bot.py b/pong_bot.py index 01d6188..590e1e8 100755 --- a/pong_bot.py +++ b/pong_bot.py @@ -287,9 +287,9 @@ def onReceive(packet, interface): #debug channel info if channel_name == "unknown": - logger.debug(f"System: Unknown Channel {channel_number} on Interface {rxNode}") + logger.debug(f"System: Received Packet on Channel:{channel_number} on Interface:{rxNode}") else: - logger.debug(f"System: Resolved Channel {channel_number} to Name: {channel_name} on Interface {rxNode}") + 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) From 289ada1fc0fe34395a9b255f6e901aecb400e5ca Mon Sep 17 00:00:00 2001 From: SpudGunMan Date: Sun, 9 Nov 2025 15:45:09 -0800 Subject: [PATCH 04/12] refactor --- mesh_bot.py | 2 +- pong_bot.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mesh_bot.py b/mesh_bot.py index 849eb30..5704f6b 100755 --- a/mesh_bot.py +++ b/mesh_bot.py @@ -1916,7 +1916,7 @@ def onReceive(packet, interface): logger.debug(f"System: channel resolution error: {e}") #debug channel info - if channel_name == "unknown": + 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}") diff --git a/pong_bot.py b/pong_bot.py index 590e1e8..cdf1835 100755 --- a/pong_bot.py +++ b/pong_bot.py @@ -286,7 +286,7 @@ def onReceive(packet, interface): logger.debug(f"System: channel resolution error: {e}") #debug channel info - if channel_name == "unknown": + 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}") From c8800d837f2cc5111c79b538e6d474077c0d7195 Mon Sep 17 00:00:00 2001 From: SpudGunMan Date: Sun, 9 Nov 2025 15:51:52 -0800 Subject: [PATCH 05/12] Update config.template --- config.template | 1 + 1 file changed, 1 insertion(+) diff --git a/config.template b/config.template index ff5c325..d34faba 100644 --- a/config.template +++ b/config.template @@ -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] From 0e074a68851de865683b878e6ac45bcb837df3ef Mon Sep 17 00:00:00 2001 From: SpudGunMan Date: Sun, 9 Nov 2025 15:54:00 -0800 Subject: [PATCH 06/12] Update custom_scheduler.template --- etc/custom_scheduler.template | 1 + 1 file changed, 1 insertion(+) diff --git a/etc/custom_scheduler.template b/etc/custom_scheduler.template index f3f6876..1d1c469 100644 --- a/etc/custom_scheduler.template +++ b/etc/custom_scheduler.template @@ -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 From 3774b8407bef3c40808f79a440bc43614f12f5fe Mon Sep 17 00:00:00 2001 From: SpudGunMan Date: Sun, 9 Nov 2025 16:29:36 -0800 Subject: [PATCH 07/12] refactor with new API --- modules/system.py | 120 ++++++++++++++++++---------------------------- 1 file changed, 46 insertions(+), 74 deletions(-) diff --git a/modules/system.py b/modules/system.py index 8bb0d2b..6f9d5bf 100644 --- a/modules/system.py +++ b/modules/system.py @@ -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() if hasattr(node, "get_channels_with_hash") else {} + except Exception: + logger.warning(f"System: meshtastic API 2.7.4+ required for channel hash fetching. Falling back to default PSK hashing.") + 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(): From e72b3c191ebcb946bcbffb4e705b6b285c827963 Mon Sep 17 00:00:00 2001 From: SpudGunMan Date: Sun, 9 Nov 2025 16:43:22 -0800 Subject: [PATCH 08/12] Update system.py --- modules/system.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/system.py b/modules/system.py index 6f9d5bf..bfbc831 100644 --- a/modules/system.py +++ b/modules/system.py @@ -424,9 +424,9 @@ def build_channel_cache(force_refresh: bool = False): 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() if hasattr(node, "get_channels_with_hash") else {} + ch_hash_table = node.get_channels_with_hash() except Exception: - logger.warning(f"System: meshtastic API 2.7.4+ required for channel hash fetching. Falling back to default PSK hashing.") + logger.warning(f"System: meshtastic API 2.7.4+ required for channel hash fetching. Attempting PSK hashing.") ch_hash_table = {} channel_dict = {} From c3e8f4a93eb417d0471dc55ae7ffad661c33e9e5 Mon Sep 17 00:00:00 2001 From: SpudGunMan Date: Sun, 9 Nov 2025 16:59:52 -0800 Subject: [PATCH 09/12] fix install embedded --- install.sh | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/install.sh b/install.sh index 0a5149a..1af3301 100755 --- a/install.sh +++ b/install.sh @@ -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 From 5f4f832af65aeb75773fef6a34a8c52cacb2c0a0 Mon Sep 17 00:00:00 2001 From: SpudGunMan Date: Sun, 9 Nov 2025 17:00:26 -0800 Subject: [PATCH 10/12] Update install.sh --- install.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/install.sh b/install.sh index 1af3301..c4f4f78 100755 --- a/install.sh +++ b/install.sh @@ -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 @@ -493,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 From d0e33f943fca53187886ffb8be3d9b158786651c Mon Sep 17 00:00:00 2001 From: SpudGunMan Date: Tue, 11 Nov 2025 12:53:24 -0800 Subject: [PATCH 11/12] logs --- mesh_bot.py | 8 ++++---- pong_bot.py | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/mesh_bot.py b/mesh_bot.py index 5704f6b..0e28652 100755 --- a/mesh_bot.py +++ b/mesh_bot.py @@ -1916,10 +1916,10 @@ def onReceive(packet, interface): 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}") + # 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) diff --git a/pong_bot.py b/pong_bot.py index cdf1835..0b2d918 100755 --- a/pong_bot.py +++ b/pong_bot.py @@ -286,10 +286,10 @@ def onReceive(packet, interface): 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}") + # 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) From 17d92dc78d0efd2db5df066c778549065995d11f Mon Sep 17 00:00:00 2001 From: SpudGunMan Date: Tue, 11 Nov 2025 13:02:03 -0800 Subject: [PATCH 12/12] Update system.py --- modules/system.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/system.py b/modules/system.py index bfbc831..7e7d7ed 100644 --- a/modules/system.py +++ b/modules/system.py @@ -426,7 +426,7 @@ def build_channel_cache(force_refresh: bool = False): try: ch_hash_table = node.get_channels_with_hash() except Exception: - logger.warning(f"System: meshtastic API 2.7.4+ required for channel hash fetching. Attempting PSK hashing.") + logger.warning(f"System: update meshtastic API 2.7.4 +") ch_hash_table = {} channel_dict = {}