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 ce9519afdadc9284ad5284d1d6b5ea58dd6c5aa1
parent c244fa63fea90aef5f53323cdc50ac32a7a650ba
Author: acidvegas <acid.vegas@acid.vegas>
Date: Sun, 28 Apr 2024 04:51:02 -0400

Meshtastic events all handled mostly now and outputting to stdout, debugging and testing finished

Diffstat:
MREADME.md | 3++-
Mmeshtastic_serial.py | 141++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------

2 files changed, 99 insertions(+), 45 deletions(-)

diff --git a/README.md b/README.md
@@ -1,4 +1,4 @@
-# Meshtastic
+# Meshtastic Utilities
 > Experiments with Meshtastic, MQTT, Lora, & more....
 
 ## WORK-IN-PROGRESS
@@ -25,6 +25,7 @@ The goal is to experiment with the possibilities of Python as a means of interfa
 ## Updates
 - Threw in an IRC skeleton where the serial controller will interface with. Will have to consider how handle asyncronous comms over serial...
 - Working reconnection on disconnection!
+- Most events are handled and outputted to stdout, debugging and testing finished
 ___
 
 ###### Mirrors for this repository: [acid.vegas](https://git.acid.vegas/meshtastic) • [SuperNETs](https://git.supernets.org/acidvegas/meshtastic) • [GitHub](https://github.com/acidvegas/meshtastic) • [GitLab](https://gitlab.com/acidvegas/meshtastic) • [Codeberg](https://codeberg.org/acidvegas/meshtastic)
 \ No newline at end of file
diff --git a/meshtastic_serial.py b/meshtastic_serial.py
@@ -1,5 +1,5 @@
 #!/usr/bin/env python
-# Meshtastic Serial Interface - Developed by Acidvegas in Python (https://git.acid.vegas/meshtastic)
+# Meshtastic Serial Interface - Developed by Acidvegas in Python (https://git.acid.vegas)
 
 import argparse
 import logging
@@ -10,7 +10,7 @@ try:
 	import meshtastic
 	from meshtastic.serial_interface import SerialInterface
 	from meshtastic.util             import findPorts
-	from meshtastic.tcp_interface    import TCPInterface
+	from meshtastic.tcp_interface    import TCPInterface		
 except ImportError:
 	raise ImportError('meshtastic library not found (pip install meshtastic)')
 
@@ -21,7 +21,7 @@ except ImportError:
 
 
 # Initialize logging
-logging.basicConfig(level=logging.INFO, format='%(asctime)s | %(levelname)9s | %(funcName)s | %(message)s')
+logging.basicConfig(level=logging.INFO, format='%(asctime)s | %(levelname)9s | %(funcName)s | %(message)s', datefmt='%Y-%m-%d %I:%M:%S')
 
 
 def now():
@@ -40,11 +40,11 @@ class MeshtasticClient(object):
 	def connect(self, option: str, value: str):
 		'''
   		Connect to the Meshtastic interface
-
-		:param option: The interface option to connect to
+    
+    	:param option: The interface option to connect to
 		:param value:  The value of the interface option
-	 	'''
-
+     	'''
+  
 		while True:
 			try:
 				if option == 'serial':
@@ -54,7 +54,7 @@ class MeshtasticClient(object):
 					else:
 						raise Exception('No serial devices found')
 					self.interface = SerialInterface(value)
-
+		
 				elif option == 'tcp':
 					self.interface = TCPInterface(value)
 
@@ -91,19 +91,16 @@ class MeshtasticClient(object):
 		'''Create the Meshtastic callback subscriptions'''
 
 		pub.subscribe(self.event_connect,    'meshtastic.connection.established')
+		pub.subscribe(self.event_data,       'meshtastic.receive.data.portnum')
 		pub.subscribe(self.event_disconnect, 'meshtastic.connection.lost')
 		pub.subscribe(self.event_node,       'meshtastic.node')
-		pub.subscribe(self.event_packet,     'meshtastic.receive')
+		pub.subscribe(self.event_position,   'meshtastic.receive.position')
+		pub.subscribe(self.event_text,       'meshtastic.receive.text')
+		pub.subscribe(self.event_user,       'meshtastic.receive.user')
 
 		logging.debug('Listening for Meshtastic events...')
-
-		# The meshtastic.receive topics can be broken down further:
-		# pub.subscribe(self.on_text,      'meshtastic.receive.text')
-		# pub.subscribe(self.on_position,  'meshtastic.receive.position')
-		# pub.subscribe(self.on_user,      'meshtastic.receive.user')
-		# pub.subscribe(self.on_data,      'meshtastic.receive.data.portnum')
-
-
+		
+		
 	def event_connect(self, interface, topic=pub.AUTO_TOPIC):
 		'''
 		Callback function for connection established
@@ -116,6 +113,17 @@ class MeshtasticClient(object):
 		logging.info(f'Found a total of {len(self.nodes):,} nodes')
 
 
+	def event_data(self, packet: dict, interface):
+		'''
+		Callback function for data updates
+
+		:param packet: Data information
+		:param interface: Meshtastic interface
+		'''
+
+		logging.info(f'Data update: {data}')
+
+
 	def event_disconnect(self, interface, topic=pub.AUTO_TOPIC):
 		'''
 		Callback function for connection lost
@@ -125,15 +133,13 @@ class MeshtasticClient(object):
 		'''
 
 		logging.warning('Lost connection to radio!')
-		logging.info('Reconnecting in 10 seconds...')
 
 		time.sleep(10)
 
 		 # TODO: Consider storing the interface option and value in a class variable since we don't want to reference the args object inside the class
 		self.connect('serial' if args.serial else 'tcp', args.serial if args.serial else args.tcp)
 
-
-
+	
 	def event_node(self, node):
 		'''
 		Callback function for node updates
@@ -146,30 +152,78 @@ class MeshtasticClient(object):
 		logging.info(f'Node found: {node["user"]["id"]} - {node["user"]["shortName"].ljust(4)} - {node["user"]["longName"]}')
 
 
-	def event_packet(self, packet: dict):
+	def event_position(self, packet: dict, interface):
+		'''
+		Callback function for position updates
+
+		:param packet: Position information
+		:param interface: Meshtastic interface
+		'''
+
+		sender    = packet['from']
+		msg       = packet['decoded']['payload'].hex()
+		id        = self.nodes[sender]['user']['id']       if sender in self.nodes else '!unk   '
+		name      = self.nodes[sender]['user']['longName'] if sender in self.nodes else 'UNK'
+		longitude = packet['decoded']['position']['longitudeI'] / 1e7
+		latitude  = packet['decoded']['position']['latitudeI'] / 1e7
+		altitude  = packet['decoded']['position']['altitude']
+		snr	      = packet['rxSnr']
+		rssi	  = packet['rxRssi']
+
+		logging.info(f'{id} - {name}: {longitude}, {latitude}, {altitude}m (SNR: {snr}, RSSI: {rssi}) - {msg}')
+
+
+	def event_text(self, packet: dict, interface):
 		'''
 		Callback function for received packets
 
 		:param packet: Packet received
 		'''
 
-		# Handle incoming text messages
-		if packet['decoded']['portnum'] == 'TEXT_MESSAGE_APP':
-			sender = packet['from']
-			msg    = packet['decoded']['payload'].decode('utf-8')
+		sender = packet['from']
+		msg    = packet['decoded']['payload'].decode('utf-8')
+		id     = self.nodes[sender]['user']['id']       if sender in self.nodes else '!unk   '
+		name   = self.nodes[sender]['user']['longName'] if sender in self.nodes else 'UNK'
+		logging.info(f'{id} - {name}: {msg}')
 
-			# Message from self
-			if sender == self.interface.myInfo.my_node_num:
-				print(f'{now()} {self.nodes[sender]}: {msg}') # Can do custom formatting here or ignore the message, just an example
 
-			# Message from others
-			if sender in self.nodes:
-				print(f'{now()} {self.nodes[sender]}: {msg}')
+	def event_user(self, packet: dict, interface):
+		'''
+		Callback function for user updates
 
-			# Unknown sender
-			else:
-				# TODO: Trigger request for node update here
-				print(f'{now()} UNK: {msg}')
+		:param user: User information
+		'''
+
+		'''
+		{
+			'from' : 862341900,
+			'to'   : 4294967295,
+			'decoded' : {
+				'portnum'      : 'NODEINFO_APP',
+				'payload'      : b'\n\t!33664b0c\x12\x08HELLDIVE\x1a\x04H3LL"\x06d\xe83fK\x0c(+8\x03',
+				'wantResponse' : True,
+				'user' : {
+					'id'        : '!33664b0c',
+					'longName'  : 'HELLDIVE',
+					'shortName' : 'H3LL',
+					'macaddr'   : 'ZOgzZksM',
+					'hwModel'   : 'HELTEC_V3',
+					'role'      : 'ROUTER_CLIENT',
+					'raw'       : 'rm this'
+				}
+			},
+			'id'       : 1612906268,
+			'rxTime'   : 1714279638,
+			'rxSnr'    : 6.25,
+			'hopLimit' : 3,
+			'rxRssi'   : -38,
+			'hopStart' : 3,
+			'raw'      : 'rm this'
+		}
+		'''
+
+		# Not sure what to do with this yet...
+		pass
 
 
 
@@ -178,11 +232,11 @@ if __name__ == '__main__':
 	parser.add_argument('--serial', help='Use serial interface') # Typically /dev/ttyUSB0 or /dev/ttyACM0
 	parser.add_argument('--tcp',    help='Use TCP interface')    # Can be an IP address or hostname (meshtastic.local)
 	args = parser.parse_args()
-
+ 
 	# Ensure one interface is specified
 	if (not args.serial and not args.tcp) or (args.serial and args.tcp):
 		raise SystemExit('Must specify either --serial or --tcp interface')
-
+	
 	# Initialize the Meshtastic client
 	mesh = MeshtasticClient()
 
@@ -197,11 +251,10 @@ if __name__ == '__main__':
 		while True:
 			time.sleep(60)
 	except KeyboardInterrupt:
-		pass # Exit the loop on Ctrl+C
+		try:
+			mesh.interface.close()
+		except:
+			pass
 	finally:
-		if mesh.interface:
-			try:
-				mesh.interface.close()
-				logging.info('Connection to radio interface closed!')
-			except:
-				pass
+		logging.info('Connection to radio lost')
+