Skip to content

Commit

Permalink
Allow config for map plugin (#46)
Browse files Browse the repository at this point in the history
* Allow config for map plugin
  • Loading branch information
geoffwhittington authored May 27, 2023
1 parent 640113d commit 6e5082c
Show file tree
Hide file tree
Showing 6 changed files with 182 additions and 28 deletions.
23 changes: 17 additions & 6 deletions plugin_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

logger = get_logger(name="Plugins")

active_plugins = []
sorted_active_plugins = []


def load_plugins():
Expand All @@ -14,10 +14,12 @@ def load_plugins():
from plugins.weather_plugin import Plugin as WeatherPlugin
from plugins.help_plugin import Plugin as HelpPlugin
from plugins.nodes_plugin import Plugin as NodesPlugin
from plugins.drop_plugin import Plugin as DropPlugin
from plugins.debug_plugin import Plugin as DebugPlugin

global plugins
if active_plugins:
return active_plugins
global sorted_active_plugins
if sorted_active_plugins:
return sorted_active_plugins

plugins = [
HealthPlugin(),
Expand All @@ -28,11 +30,20 @@ def load_plugins():
WeatherPlugin(),
HelpPlugin(),
NodesPlugin(),
DropPlugin(),
DebugPlugin(),
]

active_plugins = []
for plugin in plugins:
if plugin.config["active"]:
logger.info(f"Loaded {plugin.plugin_name}")
plugin.priority = (
plugin.config["priority"]
if "priority" in plugin.config
else plugin.priority
)
logger.info(f"Loaded {plugin.plugin_name} ({plugin.priority})")
active_plugins.append(plugin)

return active_plugins
sorted_active_plugins = sorted(active_plugins, key=lambda plugin: plugin.priority)
return sorted_active_plugins
13 changes: 13 additions & 0 deletions plugins/base_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
class BasePlugin(ABC):
plugin_name = None
max_data_rows_per_node = 100
priority = 10

@property
def description(self):
Expand All @@ -25,6 +26,18 @@ def __init__(self) -> None:
if "plugins" in relay_config and self.plugin_name in relay_config["plugins"]:
self.config = relay_config["plugins"][self.plugin_name]

def strip_raw(self, data):
if type(data) is not dict:
return data

if "raw" in data:
del data["raw"]

for k, v in data.items():
data[k] = self.strip_raw(v)

return data

def get_matrix_commands(self):
return [self.plugin_name]

Expand Down
17 changes: 17 additions & 0 deletions plugins/debug_plugin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from plugins.base_plugin import BasePlugin


class Plugin(BasePlugin):
plugin_name = "debug"
priority = 1

async def handle_meshtastic_message(
self, packet, formatted_message, longname, meshnet_name
):
packet = self.strip_raw(packet)

self.logger.debug(f"Packet received: {packet}")
return False

async def handle_room_message(self, room, event, full_message):
return False
108 changes: 108 additions & 0 deletions plugins/drop_plugin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import re
from haversine import haversine
from plugins.base_plugin import BasePlugin
from meshtastic_utils import connect_meshtastic
from meshtastic import mesh_pb2


class Plugin(BasePlugin):
plugin_name = "drop"
special_node = "!NODE_MSGS!"

def get_position(self, meshtastic_client, node_id):
for node, info in meshtastic_client.nodes.items():
if info["user"]["id"] == node_id:
return info["position"]
return None

async def handle_meshtastic_message(
self, packet, formatted_message, longname, meshnet_name
):
meshtastic_client = connect_meshtastic()
nodeInfo = meshtastic_client.getMyNodeInfo()

# Attempt message drop to packet originator if not relay
if "fromId" in packet and packet["fromId"] != nodeInfo["user"]["id"]:
position = self.get_position(meshtastic_client, packet["fromId"])
if position and "latitude" in position and "longitude" in position:
packet_location = (
position["latitude"],
position["longitude"],
)

self.logger.debug(f"Packet originates from: {packet_location}")
messages = self.get_node_data(self.special_node)
unsent_messages = []
for message in messages:
# You cannot pickup what you dropped
if (
"originator" in message
and message["originator"] == packet["fromId"]
):
unsent_messages.append(message)
continue

try:
distance_km = haversine(
(packet_location[0], packet_location[1]),
message["location"],
)
except:
distance_km = 1000
radius_km = (
self.config["radius_km"] if "radius_km" in self.config else 5
)
if distance_km <= radius_km:
target_node = packet["fromId"]
self.logger.debug(f"Sending dropped message to {target_node}")
meshtastic_client.sendText(
text=message["text"], destinationId=target_node
)
else:
unsent_messages.append(message)
self.set_node_data(self.special_node, unsent_messages)
total_unsent_messages = len(unsent_messages)
if total_unsent_messages > 0:
self.logger.debug(f"{total_unsent_messages} message(s) remaining")

# Attempt to drop a message
if (
"decoded" in packet
and "portnum" in packet["decoded"]
and packet["decoded"]["portnum"] == "TEXT_MESSAGE_APP"
):
text = packet["decoded"]["text"] if "text" in packet["decoded"] else None
if f"!{self.plugin_name}" not in text:
return False

match = re.search(r"!drop\s+(.+)$", text)
if not match:
return False

drop_message = match.group(1)

position = {}
for node, info in meshtastic_client.nodes.items():
if info["user"]["id"] == packet["fromId"]:
position = info["position"]

if "latitude" not in position or "longitude" not in position:
self.logger.debug(
"Position of dropping node is not known. Skipping ..."
)
return True

self.store_node_data(
self.special_node,
{
"location": (position["latitude"], position["longitude"]),
"text": drop_message,
"originator": packet["fromId"],
},
)
self.logger.debug(f"Dropped a message: {drop_message}")
return True

async def handle_room_message(self, room, event, full_message):
if self.matches(full_message):
return True
37 changes: 27 additions & 10 deletions plugins/map_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def anonymize_location(lat, lon, radius=1000):
return new_lat, new_lon


def get_map(locations, zoom=None, image_size=None, radius=10000):
def get_map(locations, zoom=None, image_size=None, anonymize=True, radius=10000):
"""
Anonymize a location to 10km by default
"""
Expand All @@ -31,12 +31,17 @@ def get_map(locations, zoom=None, image_size=None, radius=10000):
context.set_zoom(zoom)

for location in locations:
new_location = anonymize_location(
lat=float(location["lat"]),
lon=float(location["lon"]),
radius=radius,
)
radio = staticmaps.create_latlng(new_location[0], new_location[1])
if anonymize:
new_location = anonymize_location(
lat=float(location["lat"]),
lon=float(location["lon"]),
radius=radius,
)
radio = staticmaps.create_latlng(new_location[0], new_location[1])
else:
radio = staticmaps.create_latlng(
float(location["lat"]), float(location["lon"])
)
context.add_object(staticmaps.Marker(radio, size=10))

# render non-anti-aliased png
Expand Down Expand Up @@ -120,15 +125,18 @@ async def handle_room_message(self, room, event, full_message):
try:
zoom = int(zoom)
except:
zoom = 8
zoom = self.config["zoom"] if "zoom" in self.config else 8

if zoom < 0 or zoom > 30:
zoom = 8

try:
image_size = (int(image_size[0]), int(image_size[1]))
except:
image_size = (1000, 1000)
image_size = (
self.config["image_width"] if "image_width" in self.config else 1000,
self.config["image_height"] if "image_height" in self.config else 1000,
)

if image_size[0] > 1000 or image_size[1] > 1000:
image_size = (1000, 1000)
Expand All @@ -143,7 +151,16 @@ async def handle_room_message(self, room, event, full_message):
}
)

pillow_image = get_map(locations=locations, zoom=zoom, image_size=image_size)
anonymize = self.config["anonymize"] if "anonymize" in self.config else True
radius = self.config["radius"] if "radius" in self.config else 1000

pillow_image = get_map(
locations=locations,
zoom=zoom,
image_size=image_size,
anonymize=anonymize,
radius=radius,
)

await send_image(matrix_client, room.room_id, pillow_image)

Expand Down
12 changes: 0 additions & 12 deletions plugins/mesh_relay_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,6 @@ class Plugin(BasePlugin):
plugin_name = "mesh_relay"
max_data_rows_per_node = 50

def strip_raw(self, data):
if type(data) is not dict:
return data

if "raw" in data:
del data["raw"]

for k, v in data.items():
data[k] = self.strip_raw(v)

return data

def normalize(self, dict_obj):
"""
Packets are either a dict, string dict or string
Expand Down

0 comments on commit 6e5082c

Please sign in to comment.