From d8dd3107d2bf9ec7bf70d0bb7eefe995f8e87509 Mon Sep 17 00:00:00 2001 From: geoffwhittington Date: Wed, 17 May 2023 21:06:11 -0400 Subject: [PATCH] Add nodes command (#44) Co-authored-by: Geoff Whittington --- plugin_loader.py | 2 ++ plugins/base_plugin.py | 4 +-- plugins/nodes_plugin.py | 77 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 plugins/nodes_plugin.py diff --git a/plugin_loader.py b/plugin_loader.py index 4672048..b2c247c 100644 --- a/plugin_loader.py +++ b/plugin_loader.py @@ -13,6 +13,7 @@ def load_plugins(): from plugins.telemetry_plugin import Plugin as TelemetryPlugin from plugins.weather_plugin import Plugin as WeatherPlugin from plugins.help_plugin import Plugin as HelpPlugin + from plugins.nodes_plugin import Plugin as NodesPlugin global plugins if active_plugins: @@ -26,6 +27,7 @@ def load_plugins(): TelemetryPlugin(), WeatherPlugin(), HelpPlugin(), + NodesPlugin(), ] for plugin in plugins: diff --git a/plugins/base_plugin.py b/plugins/base_plugin.py index d8abe5b..021ba79 100644 --- a/plugins/base_plugin.py +++ b/plugins/base_plugin.py @@ -28,7 +28,7 @@ class BasePlugin(ABC): def get_matrix_commands(self): return [self.plugin_name] - async def send_matrix_message(self, room_id, message): + async def send_matrix_message(self, room_id, message, formatted=True): from matrix_utils import connect_matrix matrix_client = await connect_matrix() @@ -38,7 +38,7 @@ class BasePlugin(ABC): message_type="m.room.message", content={ "msgtype": "m.text", - "format": "org.matrix.custom.html", + "format": "org.matrix.custom.html" if formatted else None, "body": message, "formatted_body": markdown.markdown(message), }, diff --git a/plugins/nodes_plugin.py b/plugins/nodes_plugin.py new file mode 100644 index 0000000..ab14c84 --- /dev/null +++ b/plugins/nodes_plugin.py @@ -0,0 +1,77 @@ +import re +import statistics +from plugins.base_plugin import BasePlugin +from datetime import datetime + + +def get_relative_time(timestamp): + now = datetime.now() + dt = datetime.fromtimestamp(timestamp) + + # Calculate the time difference between the current time and the given timestamp + delta = now - dt + + # Extract the relevant components from the time difference + days = delta.days + seconds = delta.seconds + + # Convert the time difference into a relative timeframe + if days > 7: + return dt.strftime( + "%b %d, %Y" + ) # Return the timestamp in a specific format if it's older than 7 days + elif days >= 1: + return f"{days} days ago" + elif seconds >= 3600: + hours = seconds // 3600 + return f"{hours} hours ago" + elif seconds >= 60: + minutes = seconds // 60 + return f"{minutes} minutes ago" + else: + return "Just now" + + +class Plugin(BasePlugin): + plugin_name = "nodes" + + @property + def description(self): + return """Show mesh radios and node data + +$shortname $longname / $devicemodel / $battery $voltage / $snr / $lastseen +""" + + def generate_response(self): + from meshtastic_utils import connect_meshtastic + + meshtastic_client = connect_meshtastic() + + response = f"Nodes: {len(meshtastic_client.nodes)}\n" + + for node, info in meshtastic_client.nodes.items(): + if "snr" in info: + snr = f"{info['snr']} dB" + else: + snr = "" + response += f"{info['user']['shortName']} {info['user']['longName']} / {info['user']['hwModel']} / {info['deviceMetrics']['batteryLevel']}% {info['deviceMetrics']['voltage']}V / {snr} / {get_relative_time(info['lastHeard'])}\n" + + return response + + async def handle_meshtastic_message( + self, packet, formatted_message, longname, meshnet_name + ): + return False + + async def handle_room_message(self, room, event, full_message): + from matrix_utils import connect_matrix + + full_message = full_message.strip() + if not self.matches(full_message): + return False + + response = await self.send_matrix_message( + room_id=room.room_id, message=self.generate_response(), formatted=False + ) + + return True