meshtastic

- Unnamed repository; edit this file 'description' to name the repository.
git clone git://git.acid.vegas/-c.git
Log | Files | Refs | Archive | README | LICENSE

commit 8eb954124f33845286e0f3bcb942ac42dd541ada
parent 1b0616daca22ba55a7ee0115533da0dabcfafad9
Author: acidvegas <acid.vegas@acid.vegas>
Date: Sat, 27 Apr 2024 05:13:26 -0400

Added dynamic interface with tcp and upcomming mqtt hooking. add outgoing messages aswell.

Diffstat:
Mmeshirc.py | 7++++---
Mmeshtastic_serial.py | 164+++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------

2 files changed, 115 insertions(+), 56 deletions(-)

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: