diff --git a/meshirc.py b/meshirc.py
@@ -171,9 +171,10 @@ class Bot():
await self.action(target, 'explodes')
elif msg == '!ping':
await self.sendmsg(target, 'Pong!')
- elif msg.startswith('!say') and len(msg.split()) > 1: # Only allow !say if there is something to say
- option = ' '.join(msg.split()[1:]) # Everything after !say is stored here
- await self.sendmsg(target, option)
+ elif msg.startswith('!mesh') and len(msg.split()) > 1:
+ message = ' '.join(msg.split()[1:])
+ # Implement outgoing meshtastic message here
+ #await self.sendmesh(message)
self.last = time.time() # Update the last command time if it starts with ! character to prevent command flooding
diff --git a/meshtastic_serial.py b/meshtastic_serial.py
@@ -10,6 +10,7 @@ try:
import meshtastic
from meshtastic.serial_interface import SerialInterface
from meshtastic.util import findPorts
+ from meshtastic.tcp_interface import TCPInterface
except ImportError:
raise ImportError('meshtastic library not found (pip install meshtastic)')
@@ -29,12 +30,41 @@ def now():
return time.strftime('%Y-%m-%d %H:%M:%S')
-class Meshtastic(object):
- def __init__(self, serial: str):
- self.interface = None # We will define the interface in the run() function
- self.nodes = {} # Nodes will populate with the on_node() callback
- self.serial = serial # Serial device to use for the Meshtastic interface
-
+class MeshtasticClient(object):
+ def __init__(self):
+ self.interface = None # We will define the interface in the connect() function
+ self.nodes = {} # Nodes will populate with the event_node() callback
+
+
+ def connect(self, option: str, value: str):
+ '''
+ Connect to the Meshtastic interface
+
+ :param option: The interface option to connect to
+ :param value: The value of the interface option
+ '''
+
+ if option == 'serial':
+ if devices := findPorts():
+ if not os.path.exists(args.serial) or not args.serial in devices:
+ raise SystemExit(f'Invalid serial device: {args.serial} (Available: {devices})') # Show available devices if the specified device is invalid
+ else:
+ raise SystemExit('No serial devices found')
+ self.interface = SerialInterface(value)
+
+ elif option == 'tcp':
+ self.interface = TCPInterface(value)
+
+ elif option == 'mqtt':
+ raise NotImplementedError('MQTT interface not implemented yet')
+
+ else:
+ raise SystemExit('Invalid interface option')
+
+ logging.info(f'Connected to radio over {option} from {value}:')
+
+ logging.debug(self.interface.nodes[self.interface.myInfo.my_node_num]) # Print the node info of the connected radio
+
def disconnect(self):
'''Disconnect from the Meshtastic interface'''
@@ -50,35 +80,33 @@ class Meshtastic(object):
logging.info('Meshtastic interface closed')
else:
logging.warning('No Meshtastic interface to close')
+
+ logging.info('Disconnected from radio')
+
+
+ def send(self, message: str):
+ '''
+ Send a message to the Meshtastic interface
+ :param message: The message to send
+ '''
- def run(self):
- '''Start the Meshtastic interface and subscribe to the callback functions'''
-
- if devices := findPorts():
- if not os.path.exists(args.serial) or not args.serial in devices:
- raise SystemExit(f'Invalid serial device: {args.serial} (Available: {devices})') # Show available devices if the specified device is invalid
- else:
- raise SystemExit('No serial devices found')
+ if len(message) > 255:
+ logging.warning('Message exceeds 255 characters')
+ message = message[:255]
- # Initialize the Meshtastic interface
- self.interface = SerialInterface(self.serial)
+ self.interface.sendText(message)
- # Interface over TCP instead of serial:
- #from meshtastic.tcp_interface import TCPInterface
- #self.interface = TCPInterface(args.tcp)
+ logging.info(f'Sent broadcast message: {message}')
- logging.info('Meshtastic interface started over serial on {self.serial}')
- # Get the current node information
- me = self.interface.nodes[self.interface.myInfo.my_node_num]
- logging.debug(me)
-
- # Create the Meshtastic callback subscriptions
+ def listen(self):
+ '''Create the Meshtastic callback subscriptions'''
+
pub.subscribe(self.event_connect, 'meshtastic.connection.established')
pub.subscribe(self.event_disconnect, 'meshtastic.connection.lost')
- pub.subscribe(self.on_node, 'meshtastic.node.updated')
- pub.subscribe(self.on_packet, 'meshtastic.receive')
+ pub.subscribe(self.event_node, 'meshtastic.node.updated')
+ pub.subscribe(self.event_packet, 'meshtastic.receive')
logging.debug('Listening for Meshtastic events...')
@@ -97,7 +125,9 @@ class Meshtastic(object):
:param topic: PubSub topic
'''
- logging.info('Connection established')
+ me = interface.nodes[interface.myInfo.my_node_num]['user']['longName']
+
+ logging.info(f'Connected to \'{me}\' radio')
def event_disconnect(self, interface, topic=pub.AUTO_TOPIC):
@@ -108,10 +138,33 @@ class Meshtastic(object):
:param topic: PubSub topic
'''
- logging.warning('Connection lost')
+ logging.warning('Lost connection to radio')
+
+
+ def event_node(self, interface, topic=pub.AUTO_TOPIC):
+ '''
+ Callback function for node updates
+ :param interface: Meshtastic interface
+ :param topic: PubSub topic
+ '''
+
+ if not interface.nodes:
+ logging.warning('No nodes found')
+ return
- def on_packet(self, packet: dict):
+ for node in interface.nodes.values():
+ short = node['user']['shortName']
+ long = node['user']['longName'].encode('ascii', 'ignore').decode().rstrip()
+ num = node['num']
+ id = node['user']['id']
+ mac = node['user']['macaddr']
+ hw = node['user']['hwModel']
+
+ self.nodes[num] = long # we store the node updates in a dictionary so we can parse the names of who sent incomming messages
+
+
+ def event_packet(self, packet: dict):
'''
Callback function for received packets
@@ -135,43 +188,48 @@ class Meshtastic(object):
else:
# TODO: Trigger request for node update here
print(f'{now()} UNK: {msg}')
-
-
- def on_node(self, interface, topic=pub.AUTO_TOPIC):
+
+
+ def event_position(self, packet: dict):
'''
- Callback function for node updates
+ Callback function for received position packets
- :param interface: Meshtastic interface
- :param topic: PubSub topic
+ :param packet: Packet received
'''
- if not interface.nodes:
- logging.warning('No nodes found')
- return
+ # Handle incoming position messages
+ pass
- for node in interface.nodes.values():
- short = node['user']['shortName']
- long = node['user']['longName'].encode('ascii', 'ignore').decode().rstrip()
- num = node['num']
- id = node['user']['id']
- mac = node['user']['macaddr']
- hw = node['user']['hwModel']
- self.nodes[num] = long # we store the node updates in a dictionary so we can parse the names of who sent incomming messages
+
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Meshtastic Interface')
- parser.add_argument('--serial', default='/dev/ttyACM0', help='Use serial interface')
- parser.add_argument('--tcp', default='meshtastic.local'. help='Use TCP interface')
+
+ # Interface options
+ parser.add_argument('--serial', help='Use serial interface')
+ parser.add_argument('--tcp', help='Use TCP interface')
+ parser.add_argument('--mqtt', help='Use MQTT interface')
+
args = parser.parse_args()
+
+ if not args.serial and not args.tcp and not args.mqtt:
+ raise SystemExit('No interface specified')
+
+ if (args.serial and args.tcp) or (args.serial and args.mqtt) or (args.tcp and args.mqtt):
+ raise SystemExit('Only one interface option can be specified (--serial, --tcp, or --mqtt)')
+
+ # Initialize the Meshtastic client
+ mesh = MeshtasticClient()
- # Define the Meshtastic client
- mesh = Meshtastic(args.serial)
+ # Determine the interface option and value
+ option = 'serial' if args.serial else 'tcp' if args.tcp else 'mqtt'
+ value = args.serial if args.serial else args.tcp if args.tcp else args.mqtt
- # Initialize the Meshtastic interface
- mesh.run()
+ # Start the Meshtastic interface
+ mesh.connect(option, value)
# Keep-alive loop
try:
| |