2 Коміти 56a50038f2 ... b31cd92caf

Автор SHA1 Опис Дата
  subDesTagesMitExtraKaese b31cd92caf fix unhandled exceptions on mqtt disconnect 1 рік тому
  subDesTagesMitExtraKaese 20a4601f8d replace paho-mqtt with aiomqtt 1 рік тому
4 змінених файлів з 37 додано та 45 видалено
  1. 1 1
      README.md
  2. 10 4
      bleclient.py
  3. 24 38
      main.py
  4. 2 2
      requirements.txt

+ 1 - 1
README.md

@@ -10,7 +10,7 @@ The application establishes a connection to the MQTT broker and the BLE device,
 
 - Python 3.7+
 - [Bleak](https://github.com/hbldh/bleak) - A BLE library for Python
-- [Paho MQTT](https://github.com/eclipse/paho.mqtt.python) - A MQTT library for Python
+- [aiomqtt](https://github.com/sbtinstruments/aiomqtt) - A MQTT library for Python
 
 ## Installation
 

+ 10 - 4
bleclient.py

@@ -14,7 +14,7 @@ class BleClient:
 
     def __init__(self, mac_address: str):
         self.client = BleakClient(mac_address)
-        self.on_details_received = lambda v: print(v)  # Callback function to handle received details
+        self.details_queue = asyncio.Queue()  # Queue to store the received details
 
     async def __aenter__(self):
         await self.client.connect()  # Connect to the BLE device
@@ -36,7 +36,7 @@ class BleClient:
         #print("write ", self.WRITE_UUID, data.hex())
         await self.client.write_gatt_char(self.WRITE_UUID, data)  # Write the data to the BLE device
 
-    def notification_handler(self, characteristic: BleakGATTCharacteristic, data: bytearray):
+    async def notification_handler(self, characteristic: BleakGATTCharacteristic, data: bytearray):
         if characteristic.uuid != self.NOTIFY_UUID:
             return
         self.buffer += data  # Append the received data to the buffer
@@ -55,7 +55,7 @@ class BleClient:
             return
         if len(self.buffer) == 91:
             response = BleClient.parse_details_response(self.buffer)  # Parse the details response from the buffer
-            self.on_details_received(response)  # Call the callback function with the parsed response
+            self.details_queue.put_nowait(response)  # Add the parsed response to the queue
             self.buffer = bytearray()
         if len(self.buffer) >= 91:
             print(f"received too many bytes ({len(self.buffer)})")
@@ -78,7 +78,13 @@ class BleClient:
         return "".join(map(chr, device_name))
 
     async def request_details(self):
-        await self.write(0xFE043030002bbf1a)  # Send a request for details to the BLE device
+        self.details_queue = asyncio.Queue()  # Clear the queue
+        i = 0
+        while self.details_queue.empty() and i < 10:
+            i += 1
+            await self.write(0xFE043030002bbf1a)  # Send a request for details to the BLE device
+            await asyncio.sleep(0.1)  # Wait for the response to be received
+        return await self.details_queue.get()  # Return the first item in the queue
 
     @staticmethod
     def solar_panel_charge_state(v: int):

+ 24 - 38
main.py

@@ -5,23 +5,15 @@ import asyncio
 import signal
 import json
 
-import paho.mqtt.client as mqtt
+import aiomqtt
 from bleak.exc import BleakError, BleakDeviceNotFoundError
 
 from bleclient import BleClient
 
-client = mqtt.Client()
 send_config = True
+reconnect_interval = 5  # In seconds
 
-def details_handler(details):
-    if details:
-        print(f"Battery: {details['battery_percentage']}% ({details['battery_voltage']}V)")
-        mqtt_publish(details, client)
-    else:
-        print("No values recieved")
-
-
-def mqtt_publish(details, client):
+async def mqtt_publish(details: dict[str, any], client: aiomqtt.Client):
     global send_config
     # Define the base topic for MQTT Discovery
     base_topic = "homeassistant"
@@ -70,47 +62,41 @@ def mqtt_publish(details, client):
 
         # Publish the MQTT Discovery payload
         if send_config:
-            client.publish(topic, payload=json.dumps(payload), retain=True)
+            print(f"Publishing MQTT Discovery payload for {key}")
+            await client.publish(topic, payload=json.dumps(payload), retain=True)
 
         # Publish the entity state
-        client.publish(state_topic, payload=str(value))
+        await client.publish(state_topic, payload=str(value))
     send_config = False
     
 async def main(address, host, port, username, password):
-    client.username_pw_set(username, password)  # Set MQTT username and password
-
     async def run_mppt():
         while True:
             try:
-                client.connect(host, port)  # Connect to the MQTT broker
-                break  # Connection successful, exit the loop
-
-            except asyncio.CancelledError:
-                raise  # Re-raise the CancelledError to stop the task
-            except Exception as e:
-                print(f"An error occurred while connecting to MQTT broker: {e}")
-                await asyncio.sleep(5)  # Wait for 5 seconds before retrying
-
-        while True:
-            try:
-                async with BleClient(address) as mppt:
-                    mppt.on_details_received = details_handler
-                    await mppt.request_details()
-
+                async with aiomqtt.Client(hostname=host, port=port, username=username, password=password) as client:
+                    print(f"Connecting to MQTT broker at {host}:{port}")
                     while True:
-                        await asyncio.sleep(20.0)
                         try:
-                            await mppt.request_details()
+                            async with BleClient(address) as mppt:
+                                while True:
+                                    details = await mppt.request_details()
+                                    if details:
+                                        print(f"Battery: {details['battery_percentage']}% ({details['battery_voltage']}V)")
+                                        await mqtt_publish(details, client)
+                                    else:
+                                        print("No values recieved")
+                                    await asyncio.sleep(20.0)
+
+                        except BleakDeviceNotFoundError:
+                            print(f"BLE device with address {address} was not found")
+                            await asyncio.sleep(5)  # Wait for 5 seconds before retrying
                         except BleakError as e:
                             print(f"BLE error occurred: {e}")
-                            # Handle the BLE error accordingly, e.g., reconnect or terminate the task
-                            break
-
+            except aiomqtt.MqttError as error:
+                print(f'Error "{error}". Reconnecting in {reconnect_interval} seconds.')
+                await asyncio.sleep(reconnect_interval)
             except asyncio.CancelledError:
                 raise  # Re-raise the CancelledError to stop the task
-            except BleakDeviceNotFoundError:
-                print(f"BLE device with address {address} was not found")
-                await asyncio.sleep(5)  # Wait for 5 seconds before retrying
             except Exception as e:
                 print(f"An error occurred during BLE communication: {e}")
                 await asyncio.sleep(5)  # Wait for 5 seconds before retrying

+ 2 - 2
requirements.txt

@@ -1,2 +1,2 @@
-paho-mqtt==1.6.1
-bleak==0.20.2
+aiomqtt==1.2.1
+bleak==0.21.1