diff --git a/ble_interface.py b/ble_interface.py new file mode 100644 index 0000000..b58a99e --- /dev/null +++ b/ble_interface.py @@ -0,0 +1,101 @@ +from asyncio import AbstractEventLoop +import asyncio +from typing import Dict + +from bleak import BleakClient, BleakScanner +from bleak.backends.characteristic import BleakGATTCharacteristic +from bleak.backends.service import BleakGATTService +from bleak.uuids import normalize_uuid_str + +import logging +import struct + +import myUUIDs + +class ble_interface: + CURRENT = "current" + VOLTAGE = "volts" + CONFIGURATION = "conf" + RFRSH_DELAY = "refresh_delay" + ON_CONNECT = "on_connect" + + def __init__(self, address, loop: AbstractEventLoop, logger : logging.Logger) -> None: + self.address = address + self.loop = loop + self.client: BleakClient + self.log = logger + self.callbacks = {} + self.services: Dict[str, BleakGATTService] = {} + self.handlers = { + self.CURRENT: self.__current_meas_handler, + } + + async def __connect(self): + device = await BleakScanner.find_device_by_address(self.address, cb=dict(use_bdaddr=False)) + if device is None: + self.log.error("could not find device with address") + return + + async with BleakClient(device) as client: + self.log.info("Connected") + self.client = client + + self.call_callback(self.ON_CONNECT) + + services = client.services.services + + self.parse_services(services) + + await asyncio.Event().wait() + + def parse_services(self, services: Dict[int, BleakGATTService]): + for i in services: + s = services[i] + if(s.uuid == normalize_uuid_str(myUUIDs.METROLOGY_SERVICE)): + if(s.characteristics[0].uuid == normalize_uuid_str(myUUIDs.ELECTRIC_CURRENT_CHAR)): + self.services[self.CURRENT] = s + + if(s.characteristics[0].uuid == normalize_uuid_str(myUUIDs.VOLTAGE_CHAR)): + self.services[self.VOLTAGE] = s + + + if(s.uuid == normalize_uuid_str(myUUIDs.CONFIGURATION_SERVICE)): + self.services[self.CONFIGURATION] = s + + def connect(self): + self.log.info("connecting") + + self.loop.create_task(self.__connect()) + + def subscribe_to(self, to: str): + self.loop.create_task(self.__subscribe_to(to)) + + async def __subscribe_to(self, to: str): + self.log.info("subscribing to %s", to) + for c in self.services[to].characteristics: + await self.client.start_notify(c, self.handlers[to]) + + def add_callback(self, id: str, callback): + if(not id in self.callbacks): + self.callbacks[id] = [] + self.callbacks[id].append(callback) + + def call_callback(self, id: str, *params): + if(id in self.callbacks): + for c in self.callbacks[id]: + if(params): + c(self, *params) + else: + c(self) + + def __current_meas_handler(self, char: BleakGATTCharacteristic, data: bytearray): + val = struct.unpack("