Skip to content

Commit

Permalink
new version
Browse files Browse the repository at this point in the history
  • Loading branch information
PiDiBi committed Jun 30, 2024
1 parent 6ede389 commit cea0897
Show file tree
Hide file tree
Showing 15 changed files with 875 additions and 287 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -160,3 +160,4 @@ cython_debug/
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
/*.db
15 changes: 15 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Python Debugger: Current File",
"type": "debugpy",
"request": "launch",
"program": "${file}",
"console": "integratedTerminal"
}
]
}
4 changes: 4 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,8 @@
"python.testing.pytestEnabled": true,
"python.testing.cwd": "${workspaceFolder}/tests",
"python.testing.autoTestDiscoverOnSaveEnabled": true,
"flake8.args": [
"--max-line-length=120 --ignore E402,W503"
],
"editor.defaultFormatter": "ms-python.black-formatter"
}
93 changes: 71 additions & 22 deletions basic_bot.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
from meshtastic.stream_interface import StreamInterface
from message_processor import MessageProcessor
from geopy.geocoders import Nominatim
import maidenhead as mh
from dadjokes import Dadjoke


class BasicBot(MessageProcessor):
def __init__(self):
super().__init__()
self.trap_list = ["ping", "ack", "testing", "pong", "lheard", "sitrep", "joke"]
def __init__(self, interface: StreamInterface):
super(BasicBot, self).__init__(interface)
self.trap_list = ["ping", "ack", "lheard", "sitrep", "joke", "whereami"]
pass

def auto_response(self, message, snr, rssi, hop, message_from_id, location:list[float], node_list:list[str]):
print(f"BasicBot: Got message: {message}")

def auto_response(
self, message, snr, rssi, hop, message_from_id, location: list[float]
):
bot_response = None
message = message.lower().strip()
if "ping" in message:
#Check if the user added @foo to the message
# Check if the user added @foo to the message
bot_response = "PONG, " + f"SNR:{snr} RSSI:{rssi} HOP {hop}"
if " " in message:
bot_response += " and copy: " + message.split(" ")[1]
Expand All @@ -26,37 +29,83 @@ def auto_response(self, message, snr, rssi, hop, message_from_id, location:list[

elif "lheard" in message or "sitrep" in message:
# make a nice list for the user
short_node_list = []
for x in node_list[:5]:
short_node_list.append(f"{x[0]} SNR:{x[2]}")
if not self.node_list:
return "Error Processing Node List"

short_node_list = self.get_node_list()

bot_response = "Last 5 nodes heard:\n" + str("\n".join(short_node_list))
node_list = []
for x in short_node_list:
node_list.append(f"{x[0]} [SNR:{x[2]}]")

bot_response = "Last 5 nodes heard:\n" + str("\n".join(node_list))

elif "whereami" in message:
bot_response = self.where_am_i(location[0], location[1])

elif "joke" in message:
bot_response = self.tell_joke()

return bot_response

def tell_joke(self):
# tell a dad joke, does it need an explanationn :)
dadjoke = Dadjoke()
return dadjoke.joke

def where_am_i(self, lat=0, lon=0):
whereIam = ""
if float(lat) == 0 and float(lon) == 0:
return super().NO_DATA_NOGPS
# initialize Nominatim API
return self.NO_DATA_NOGPS

geolocator = Nominatim(user_agent="mesh-bot")

location = geolocator.reverse(lat + ", " + lon)
address = location.raw['address']
address_components = ['house_number', 'road', 'city', 'state', 'postcode', 'county', 'country']
whereIam += ' '.join([address.get(component, '') for component in address_components if component in address])
location = geolocator.reverse(str(lat) + ", " + str(lon))
address = location.raw["address"]
address_components = [
"house_number",
"road",
"city",
"state",
"postcode",
"county",
"country",
]
whereIam += " ".join(
[
address.get(component, "")
for component in address_components
if component in address
]
)
grid = mh.to_maiden(float(lat), float(lon))
whereIam += " Grid: " + grid

return whereIam
return whereIam

def get_node_list(self, limit=5):

result = []

for index, node in enumerate(self.node_list):
# ignore own
if node["num"] == self.myNodeNum:
continue

node_name = MessageProcessor.get_name_from_number(
self.interface, node["num"]
)
snr = node.get("snr", 0)

# issue where lastHeard is not always present
last_heard = node.get("lastHeard", 0)

# make a list of nodes with last heard time and SNR
item = (node_name, last_heard, snr)
result.append(item)

if index >= limit:
break

result.sort(key=lambda x: x[1], reverse=True)

return result
94 changes: 94 additions & 0 deletions db_operations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import sqlite3
import threading
from datetime import datetime

thread_local = threading.local()


def get_db_connection():
if not hasattr(thread_local, "connection"):
thread_local.connection = sqlite3.connect("messages.db")
return thread_local.connection


def initialize_database():
conn = get_db_connection()
c = conn.cursor()

c.execute(
"""CREATE TABLE IF NOT EXISTS message (
id INTEGER PRIMARY KEY AUTOINCREMENT,
message_id INTEGER NOT NULL,
sender TEXT NOT NULL,
sender_short_name TEXT NOT NULL,
sender_long_name TEXT NOT NULL,
reply_id INTEGER NOT NULL,
channel INTEGER NOT NULL,
date TEXT NOT NULL,
content TEXT NOT NULL
);"""
)
c.execute(
"""CREATE TABLE IF NOT EXISTS channels (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
url TEXT NOT NULL
);"""
)
conn.commit()
print("Database schema initialized.")


def add_channel(name, url):
conn = get_db_connection()
c = conn.cursor()
c.execute("INSERT INTO channels (name, url) VALUES (?, ?)", (name, url))
conn.commit()


def get_channels():
conn = get_db_connection()
c = conn.cursor()
c.execute("SELECT name, url FROM channels")
return c.fetchall()


def add_message(
message_id,
sender_id,
sender_short_name,
sender_long_name,
reply_id,
channel,
content,
):
conn = get_db_connection()
c = conn.cursor()
date = datetime.now().strftime("%Y-%m-%d %H:%M")

c.execute(
"INSERT INTO message (message_id, sender, sender_short_name, sender_long_name, reply_id, "
"channel, date, content) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
(
message_id,
sender_id,
sender_short_name,
sender_long_name,
reply_id,
channel,
date,
content,
),
)
return conn.commit()


def get_messages(top: int = 5):
conn = get_db_connection()
c = conn.cursor()
c.execute(
"SELECT id, sender_short_name, sender_long_name, date, channel, "
"content FROM message ORDER BY date DESC LIMIT ?",
(top,),
)
return c.fetchall()
3 changes: 2 additions & 1 deletion log.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from datetime import datetime


def log_timestamp():
return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
44 changes: 26 additions & 18 deletions mesh_bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,55 +2,63 @@
# Meshtastic Autoresponder MESH Bot

import signal
import time
from typing import List
from pubsub import pub
from datetime import datetime
from basic_bot import BasicBot
from db_operations import initialize_database
from message_processor import MessageProcessor
from serial_mesh import SerialMeshHelper

import meshtastic.serial_interface
import meshtastic.tcp_interface
import meshtastic.ble_interface

from store_forward_bot import StoreForwardBot
from weather_bot import WeatherBot

# Uncomment the interface you want to use depending on your device connection
interface = meshtastic.serial_interface.SerialInterface() #serial interface
#interface=meshtastic.tcp_interface.TCPInterface(hostname="10.0.4.36") # IP of your device
#interface=meshtastic.ble_interface.BLEInterface("10:06:1C:49:90:36") # BLE interface - find it using meshtastic --ble-scan
interface = meshtastic.serial_interface.SerialInterface() # serial interface
# interface=meshtastic.tcp_interface.TCPInterface(hostname="10.0.4.36") # IP of your device
# BLE interface - find it using meshtastic --ble-scan
# interface=meshtastic.ble_interface.BLEInterface("10:06:1C:49:90:36")

#interface = None
initialize_database()

myinfo = interface.getMyNodeInfo()
myNodeNum = myinfo['num']
print(f"System: My Node Number is {myNodeNum}")
bb = BasicBot(interface)
wb = WeatherBot(interface)
sfb = StoreForwardBot(interface)

bb = BasicBot()
wb = WeatherBot()
# node_list = interface.nodes.values()
# print(f"System: Node List {node_list}")

message_processors:List[MessageProcessor] = [bb, wb]
message_processors: List[MessageProcessor] = [bb, wb, sfb]
sh = SerialMeshHelper(interface, message_processors)

# if not serial or serial.myNodeNum == -1:
# print("System: Critical Error script abort. Could not get myNodeNum")
# exit()


def exit_handler(signum, frame):
print("\nSystem: Closing Autoresponder")
interface.close()
exit (0)
exit(0)


print("\nMeshtastic Autoresponder MESH Bot CTL+C to exit\n")

print ("\nMeshtastic Autoresponder MESH Bot CTL+C to exit\n")
pub.subscribe(sh.onReceive, 'meshtastic.receive')
print (f"System: Autoresponder Started for device {sh.get_name_from_number(sh.myNodeNum)}")
# subscribe to process messages
pub.subscribe(sh.onReceive, "meshtastic.receive")
# subscrie to store to DB
pub.subscribe(sfb.onReceive, "meshtastic.receive")

print(
f"System: Autoresponder Started for device {MessageProcessor.get_name_from_number(interface, sh.myNodeNum)}"
)

while True:
# Catch CTL+C to exit
signal.signal(signal.SIGINT, exit_handler)
pass



# EOF
Loading

0 comments on commit cea0897

Please sign in to comment.