async def find_ble_device(name, timeout=5): """Quickly find BLE device address by friendly device name. This is an alternative to bleak.discover. Instead of waiting a long time to scan everything, it returns as soon as it finds any device with the requested name. Arguments: name (str): Friendly device name. timeout (float): When to give up searching. Returns: str: Matching device address. Raises: TimeoutError: Device was not found within the timeout. """ # Flag raised by detection of a device device_discovered = False def set_device_discovered(*args): nonlocal device_discovered device_discovered = True # Create scanner object and register callback to raise discovery flag scanner = BleakScanner() scanner.register_detection_callback(set_device_discovered) # Start the scanner await scanner.start() INTERVAL = 0.1 # Sleep until a device of interest is discovered. We cheat by using the # cross-platform get_discovered_devices() ahead of time, instead of waiting # for the whole discover() process to complete. We call it every time # a new device is detected by the register_detection_callback. for i in range(round(timeout / INTERVAL)): # If device_discovered flag is raised, check if it's the right one. if device_discovered: # Unset the flag so we only check if raised again. device_discovered = False # Check if any of the devices found so far has the expected name. devices = await scanner.get_discovered_devices() for dev in devices: # If the name matches, stop scanning and return address. if name == dev.name: await scanner.stop() return dev.address # Await until we check again. await asyncio.sleep(INTERVAL) # If we are here, scanning has timed out. await scanner.stop() raise TimeoutError("Could not find {0} in {1} seconds".format( name, timeout))
async def connect_and_send(data): scanner = BleakScanner() scanner.register_detection_callback(detect_printer) await scanner.start() for x in range(50): await asyncio.sleep(0.1) if device: break await scanner.stop() if not device: raise BleakError(f"No device named GB01 could be found.") async with BleakClient(device) as client: # Set up callback to handle messages from the printer await client.start_notify(NotifyCharacteristic, notification_handler) while (data): # Cut the command stream up into pieces small enough for the printer to handle await client.write_gatt_char(PrinterCharacteristic, data[:PacketLength]) data = data[PacketLength:] while not transmit: # Pause transmission per printer request. # Note: doing it this way does not appear to actually work. await asyncio.sleep(0)
async def scan_devices_task(self): empty_scans = 0 while True: # 10 empty scans in a row means that bluetooth restart is required if empty_scans >= 10: empty_scans = 0 await restart_bluetooth() try: async with handle_ble_exceptions(): scanner = BleakScanner() scanner.register_detection_callback( self.device_detection_callback, ) try: await aio.wait_for(scanner.start(), 10) except aio.TimeoutError: _LOGGER.error('Scanner start failed with timeout') await aio.sleep(3) devices = scanner.discovered_devices await scanner.stop() if not devices: empty_scans += 1 else: empty_scans = 0 _LOGGER.debug(f'found {len(devices)} devices: {devices}') except KeyboardInterrupt: raise except aio.IncompleteReadError: raise except ListOfConnectionErrors as e: _LOGGER.exception(e) empty_scans += 1 await aio.sleep(1)
async def run(): scanner = BleakScanner() scanner.register_detection_callback(detection_callback) await scanner.start() await asyncio.sleep(float(60)) await scanner.stop() devices = await scanner.get_discovered_devices()
async def update(): scanner = BleakScanner(filter={'DuplicateData':False}) scanner.register_detection_callback(accelerometer_callback) while True: await scanner.start() await asyncio.sleep(5) await scanner.stop()
async def run(): scanner = BleakScanner() scanner.register_detection_callback(detection_callback) while True: await scanner.start() await asyncio.sleep(0.5) await scanner.stop()
async def main(): scanner = BleakScanner() scanner.register_detection_callback(simple_callback) while True: await scanner.start() await asyncio.sleep(5.0) await scanner.stop()
async def scan(): scanner = BleakScanner() scanner.register_detection_callback(detection_callback) await scanner.start() await asyncio.sleep(3.0) await scanner.stop() return await scanner.get_discovered_devices()
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Fjäråskupan from a config entry.""" scanner = BleakScanner() state = EntryState(scanner, {}) hass.data.setdefault(DOMAIN, {}) hass.data[DOMAIN][entry.entry_id] = state async def detection_callback( ble_device: BLEDevice, advertisement_data: AdvertisementData) -> None: if not device_filter(ble_device, advertisement_data): return _LOGGER.debug("Detection: %s %s - %s", ble_device.name, ble_device, advertisement_data) data = state.devices.get(ble_device.address) if data: data.device.detection_callback(ble_device, advertisement_data) data.coordinator.async_set_updated_data(data.device.state) else: device = Device(ble_device) device.detection_callback(ble_device, advertisement_data) async def async_update_data(): """Handle an explicit update request.""" await device.update() return device.state coordinator: DataUpdateCoordinator[State] = DataUpdateCoordinator( hass, logger=_LOGGER, name="Fjaraskupan Updater", update_interval=timedelta(seconds=120), update_method=async_update_data, ) coordinator.async_set_updated_data(device.state) device_info: DeviceInfo = { "identifiers": {(DOMAIN, ble_device.address)}, "manufacturer": "Fjäråskupan", "name": "Fjäråskupan", } device_state = DeviceState(device, coordinator, device_info) state.devices[ble_device.address] = device_state async_dispatcher_send(hass, f"{DISPATCH_DETECTION}.{entry.entry_id}", device_state) scanner.register_detection_callback(detection_callback) await scanner.start() hass.config_entries.async_setup_platforms(entry, PLATFORMS) return True
async def run(): # https://github.com/hbldh/bleak/issues/230#issuecomment-652822031 # scanner = BleakScanner(AdvertisementFilter=) scanner = BleakScanner(filter={'DuplicateData': False}) scanner.register_detection_callback(accelerometer_callback) while True: await scanner.start() await asyncio.sleep(5) await scanner.stop()
async def run(): global start scanner = BleakScanner() scanner.register_detection_callback(detection_callback) start = time.time() await scanner.start() await asyncio.sleep(float(duration)) await scanner.stop() end = time.time()
async def run(): scanner = BleakScanner() scanner.register_detection_callback(detection_callback) await scanner.start() await asyncio.sleep(2.0) await scanner.stop() devices = await scanner.get_discovered_devices() for d in devices: print(d)
async def start(self): def _callback(device: BLEDevice, adv: AdvertisementData): adv = process_advertisement(device, adv) if adv: for callback in self._callbacks: callback(adv.dict()) scanner = BleakScanner() scanner.register_detection_callback(_callback) await scanner.start()
async def asyncRun(self, loop): scanner = BleakScanner() scanner.register_detection_callback(self.detection_handler) await scanner.start() print('scanning...') while not self.__found: await asyncio.sleep(0.2) await scanner.stop() print('scanning...done')
async def main(): scanner = BleakScanner() # print('scanner: created') scanner.register_detection_callback(detection_callback) # print('scanner: registered') await scanner.start() # print('scanner: started') await asyncio.sleep(5.0) # print('wait end') await scanner.stop() # print('scanner: stopped') print(json.dumps(meters))
async def asyncRun(): global found scanner = BleakScanner() scanner.register_detection_callback(detection_handler) await scanner.start() print('scanning...') while not found: await asyncio.sleep(0.2) await scanner.stop() print('scanning...done')
async def list(self, timeout=5) -> list: # logger.debug("list devices...") self.devices_list = {} # 清空 try: scanner = BleakScanner() self.scanner = scanner scanner.register_detection_callback(self._detection_ble_callback) await scanner.start() await asyncio.sleep(5.0) await self.scanner.stop() except BleakError as e: # 提醒开启蓝牙 logger.error(e) await self.node_instance.pub_notification(str(e), type="ERROR") return []
async def run(): global deviceFound global deviceAddress global deviceConnected global fw_number scanner = BleakScanner() scanner.register_detection_callback(simple_callback) while True: await scanner.start() await asyncio.sleep(5.0) await scanner.stop() if deviceFound: deviceFound = False break async with BleakClient(deviceAddress) as client: deviceConnected = True svcs = await client.get_services() print("Services:") for service in svcs: print(service) fw_number = await client.read_gatt_char(OPENWIND_FW_CHARACTERISTIC_UUID ) print("Model Number: {0}".format("".join(map(chr, fw_number)))) sn_number = await client.read_gatt_char(OPENWIND_SN_CHARACTERISTIC_UUID ) if float(fw_number) >= 1.27: print("Model Number: {0}".format(sn_number.hex())) else: print("Model Number: {0}".format("".join(map(chr, sn_number)))) client.set_disconnected_callback(OW_DISCONNECT_CALLBACK) write_value = bytearray([0x2C]) await client.write_gatt_char(OPENWIND_MOV_ENABLE_CHARACTERISTIC_UUID, write_value) await asyncio.sleep(1.0) await client.start_notify(OPENWIND_WIND_CHARACTERISTIC_UUID, WIND_DATA_CALLBACK) while await client.is_connected(): await asyncio.sleep(5.0)
async def _scan(self): logging.info("Scanning...") scanner = BleakScanner() scanner.register_detection_callback(self._detection_callback) logging.debug("searching HuGo device") waiting = 0.5 # to be cough power_up window in case of power save countdown = int(90 / waiting) #~1.5 min self.server = None while countdown: await scanner.start() await asyncio.sleep(waiting) await scanner.stop() if self.server: logging.debug("server found %s", self.server ) logging.info("Scanning DONE") return True countdown -= 1 logging.info("Scanning FAILED") return False
async def calibrate(): # Scan for TotTag devices until three are found scanner = BleakScanner() scanner.register_detection_callback(detection_callback) await scanner.start() while not tottag_0 or not tottag_1 or not tottag_2: await asyncio.sleep(1.0) await scanner.stop() # Initiate calibration for the two slave devices file_1, client_1, data_characteristic_1 = await initiate_calibration( tottag_1, 1) file_2, client_2, data_characteristic_2 = await initiate_calibration( tottag_2, 2) # Initiate calibration for the master device file_0, client_0, data_characteristic_0 = await initiate_calibration( tottag_0, 0) # Wait until a set amount of data has been received while num_ranges_received != (3 * NUM_CALIBRATION_RANGES_TO_READ): await asyncio.sleep(1.0) # Disconnect from all TotTag devices if client_0 and data_characteristic_0: await client_0.stop_notify(data_characteristic_0) await client_0.disconnect() if client_1 and data_characteristic_1: await client_1.stop_notify(data_characteristic_1) await client_1.disconnect() if client_2 and data_characteristic_2: await client_2.stop_notify(data_characteristic_2) await client_2.disconnect() # Close all calibration data log files if file_0: file_0.close() if file_1: file_1.close() if file_2: file_2.close()
async def list(self, timeout=5) -> list: # logger.debug("list devices...") self.devices_list = {} # 清空 try: scanner = BleakScanner() self.scanner = scanner scanner.register_detection_callback(self._detection_ble_callback) await scanner.start() await asyncio.sleep(5.0) await scanner.stop() # devices = await scanner.get_discovered_devices() # devices = await BleakScanner.discover() # todo except # 一直阻塞 ''' return [ str(d.address) for d in devices if self.device_flag in str(d).lower() ] ''' except BleakError as e: # 提醒开启蓝牙 logger.error(e) await self.node_instance.pub_notification(str(e), type="ERROR") return []
async def main(service_uuids): Bleak mac_ver = platform.mac_ver()[0].split(".") if mac_ver[0] and int(mac_ver[0]) >= 12 and not service_uuids: # In macOS 12 Monterey the service_uuids need to be specified. As a # workaround for this example program, we scan for all known UUIDs to # increse the chance of at least something showing up. However, in a # "real" program, only the device-specific advertised UUID should be # used. Devices that don't advertize at least one service UUID cannot # currently be detected. logger.warning( "Scanning using all known service UUIDs to work around a macOS 12 bug. Some devices may not be detected. Please report this to Apple using the Feedback Assistant app and reference <https://github.com/hbldh/bleak/issues/635>." ) for item in uuid16_dict: service_uuids.append("{0:04x}".format(item)) service_uuids.extend(uuid128_dict.keys()) scanner = BleakScanner() scanner.register_detection_callback(simple_callback) while True: await scanner.start() await asyncio.sleep(5.0) await scanner.stop()
class Scanner: def __init__(self) -> None: """Initialize the scanner.""" self.__scanner = BleakScanner() self._listeners: Dict[str, List[Callable]] = {} self._known_devices: dict[str, Device] = {} @property def known_devices(self) -> list[Device]: return list(self._known_devices.values()) def on(self, event_name: str, callback: Callable) -> Callable: """Register an event callback.""" listeners: list = self._listeners.setdefault(event_name, []) listeners.append(callback) def unsubscribe() -> None: """Unsubscribe listeners.""" if callback in listeners: listeners.remove(callback) return unsubscribe def emit(self, event_name: str, data: dict) -> None: """Run all callbacks for an event.""" for listener in self._listeners.get(event_name, []): listener(data) async def start(self): def _callback(device: BLEDevice, advertisement: AdvertisementData): log_advertisement_message(device, advertisement) known_device = self._known_devices.get(device.address) if known_device: known_device.update(device=device, advertisement=advertisement) self.emit(device.address, {"device": known_device}) else: known_device = determine_known_device( device=device, advertisement=advertisement) if known_device: self._known_devices[device.address] = known_device self.emit(DEVICE_DISCOVERED, {"device": known_device}) self.__scanner.register_detection_callback(_callback) await self.__scanner.start() async def stop(self): await self.__scanner.stop() @staticmethod async def find_known_device_by_address(device_identifier: str, timeout: float = 10.0 ) -> Optional[Device]: """Find a device (with metadata) by Bluetooth address or UUID address (macOS).""" device_identifier = device_identifier.lower() stop_scanning_event = asyncio.Event() def stop_if_detected(device: BLEDevice, advertisement: AdvertisementData): if device.address.lower( ) == device_identifier and determine_known_device( device, advertisement): stop_scanning_event.set() async with BleakScanner( timeout=timeout, detection_callback=stop_if_detected) as scanner: try: await asyncio.wait_for(stop_scanning_event.wait(), timeout=timeout) except asyncio.TimeoutError: return None device = next( determine_known_device(d) for d in await scanner.get_discovered_devices() if d.address.lower() == device_identifier) return device
class O2DeviceManager: def __init__(self): self.pipe_down = [] self.devices = {} self.scanner = BleakScanner() self.scanner.register_detection_callback(self.on_detection) async def start_discovery(self): await self.scanner.start() async def stop_discovery(self): await self.scanner.stop() def on_detection(self, device, advertisement_data): if device.address not in self.devices: name = device.name or device.address uuids = device.metadata[ "uuids"] if "uuids" in device.metadata else None if self.verbose > 4 and device.address not in self.pipe_down: print(f"Considering {device.address} {name} {uuids}") self.pipe_down.append(device.address) valid = False if uuids is not None and BLE_MATCH_UUID in uuids and BLE_SERVICE_UUID in uuids: valid = True else: # We might not have the list of UUIDs yet, so also check by name names = ("Checkme_O2", "CheckO2", "SleepU", "SleepO2", "O2Ring", "WearO2", "KidsO2", "BabyO2", "Oxylink") for n in names: if n in name: if self.verbose > 1: print(f"Found device by name: {n}") valid = True break if not valid: return print(f"Adding device {device.address}") dev = O2BTDevice(address_or_ble_device=device, timeout=10.0, disconnected_callback=O2BTDevice.on_disconnect) dev.mac_address = device.address dev.manager = self dev.name = name dev.notified = False dev.rssi = device.rssi if device.rssi is not None else -999 dev.write = None dev.disconnect_pending = False dev.pkt = None dev.pkt_queue = queue.Queue() self.devices[device.address] = dev dev.connect() else: dev = self.devices[device.address] if device.name is not None and dev.name == device.address: dev.name = device.name if device.rssi is not None: dev.rssi = device.rssi if not dev.disconnect_pending and dev.is_connected and not dev.notified and "uuids" in device.metadata: print(f"[{device.address}] Discovered: {dev.name}") if self.verbose > 1: if len(device.metadata["uuids"]) > 0: print(f"UUIDs: {' '.join(device.metadata['uuids'])}") else: print(f"UUIDs: (none)") dev.notified = True
class BleHandler: def __init__(self, settings: EncryptionSettings, bleAdapterAddress: str = None): # bleAdapterAddress is the MAC address of the adapter you want to use. self.settings = settings self.bleAdapterAddress = bleAdapterAddress # Connection self.activeClient: ActiveClient or None = None # Scanning self.scanner = BleakScanner(adapter=bleAdapterAddress) self.scanningActive = False self.scanAborted = False scanDelegate = BleakScanDelegate(self.settings) self.scanner.register_detection_callback(scanDelegate.handleDiscovery) # Event bus self.subscriptionIds = [] self.validator = Validator() self.subscriptionIds.append( BleEventBus.subscribe(SystemBleTopics.abortScanning, lambda x: self.abortScan())) # To be moved to active client or notification handler. self.notificationLoopActive = False async def shutDown(self): for subscriptionId in self.subscriptionIds: BleEventBus.unsubscribe(subscriptionId) await self.disconnect() await self.stopScanning() async def is_connected_guard(self): connected = await self.is_connected() if not connected: _LOGGER.debug( f"Could not perform action since the client is not connected!." ) raise CrownstoneBleException("Not connected.") async def is_connected(self, address=None) -> bool: """ Check if connected to a BLE device. @param address: When not None, check if connected to given address. @return: True when connected. """ if self.activeClient is None: return False if address is not None: if self.activeClient.address.lower() != address.lower(): return False connected = await self.activeClient.client.is_connected() if connected: return True def resetClient(self): self.activeClient = None async def connect(self, address) -> bool: # TODO: Check if activeClient is already set. self.activeClient = ActiveClient(address, lambda: self.resetClient(), self.bleAdapterAddress) _LOGGER.info(f"Connecting to {address}") # this can throw an error when the connection fails. # these BleakErrors are nicely human readable. # TODO: document/convert these errors. connected = await self.activeClient.client.connect() serviceSet = await self.activeClient.client.get_services() self.activeClient.services = {} self.activeClient.characteristics = {} for key, service in serviceSet.services.items(): self.activeClient.services[service.uuid] = key for key, characteristic in serviceSet.characteristics.items(): self.activeClient.characteristics[ characteristic.uuid] = characteristic.handle self.activeClient.notificationCallbacks = {} self.activeClient.notificationSubscriptions = {} return connected # print(self.activeClient.client.services.characteristics) async def disconnect(self): if self.activeClient is not None: await self.activeClient.client.disconnect() self.activeClient = None async def waitForPeripheralToDisconnect(self, timeout: int = 10): if self.activeClient is not None: if await self.activeClient.isConnected(): waiting = True def disconnectListener(data): nonlocal waiting waiting = False listenerId = BleEventBus.subscribe( SystemBleTopics.forcedDisconnect, disconnectListener) timer = 0 while waiting and timer < 10: await asyncio.sleep(0.1) timer += 0.1 self.settings.exitSetup() BleEventBus.unsubscribe(listenerId) self.activeClient = None async def scan(self, duration=3): await self.startScanning() while duration > 0 and self.scanAborted == False: await asyncio.sleep(0.1) duration -= 0.1 await self.stopScanning() async def startScanning(self): _LOGGER.debug(f"startScanning scanningActive={self.scanningActive}") if not self.scanningActive: self.scanAborted = False self.scanningActive = True await self.scanner.start() async def stopScanning(self): _LOGGER.debug(f"stopScanning scanningActive={self.scanningActive}") if self.scanningActive: self.scanningActive = False self.scanAborted = False await self.scanner.stop() def abortScan(self): _LOGGER.debug("abortScan") self.scanAborted = True def hasService(self, serviceUUID) -> bool: _LOGGER.debug(f"hasService serviceUUID={serviceUUID}") return serviceUUID in self.activeClient.services def hasCharacteristic(self, characteristicUUID) -> bool: _LOGGER.debug( f"hasCharacteristic characteristicUUID={characteristicUUID}") return characteristicUUID in self.activeClient.characteristics async def writeToCharacteristic(self, serviceUUID, characteristicUUID, content): _LOGGER.debug( f"writeToCharacteristic serviceUUID={serviceUUID} characteristicUUID={characteristicUUID} content={content}" ) await self.is_connected_guard() encryptedContent = EncryptionHandler.encrypt(content, self.settings) payload = self._preparePayload(encryptedContent) await self.activeClient.client.write_gatt_char(characteristicUUID, payload, response=True) async def writeToCharacteristicWithoutEncryption(self, serviceUUID, characteristicUUID, content): _LOGGER.debug( f"writeToCharacteristicWithoutEncryption serviceUUID={serviceUUID} characteristicUUID={characteristicUUID} content={content}" ) await self.is_connected_guard() payload = self._preparePayload(content) await self.activeClient.client.write_gatt_char(characteristicUUID, payload, response=True) async def readCharacteristic(self, serviceUUID, characteristicUUID): _LOGGER.debug( f"readCharacteristic serviceUUID={serviceUUID} characteristicUUID={characteristicUUID}" ) data = await self.readCharacteristicWithoutEncryption( serviceUUID, characteristicUUID) if self.settings.isEncryptionEnabled(): return EncryptionHandler.decrypt(data, self.settings) async def readCharacteristicWithoutEncryption(self, serviceUUID, characteristicUUID): _LOGGER.debug( f"readCharacteristicWithoutEncryption serviceUUID={serviceUUID} characteristicUUID={characteristicUUID}" ) await self.is_connected_guard() return await self.activeClient.client.read_gatt_char(characteristicUUID ) async def setupSingleNotification(self, serviceUUID, characteristicUUID, writeCommand): _LOGGER.debug( f"setupSingleNotification serviceUUID={serviceUUID} characteristicUUID={characteristicUUID}" ) await self.is_connected_guard() # setup the collecting of the notification data. _LOGGER.debug(f"setupSingleNotification: subscribe for notifications.") notificationDelegate = NotificationDelegate(self._killNotificationLoop, self.settings) await self.activeClient.subscribeNotifications( characteristicUUID, notificationDelegate.handleNotification) # execute something that will trigger the notifications _LOGGER.debug(f"setupSingleNotification: writeCommand().") await writeCommand() # wait for the results to come in. self.notificationLoopActive = True loopCount = 0 polInterval = 0.1 while self.notificationLoopActive and loopCount < (12.5 / polInterval): await asyncio.sleep(polInterval) loopCount += 1 if notificationDelegate.result is None: self.activeClient.unsubscribeNotifications(characteristicUUID) raise CrownstoneBleException( BleError.NO_NOTIFICATION_DATA_RECEIVED, "No notification data received.") self.activeClient.unsubscribeNotifications(characteristicUUID) return notificationDelegate.result async def setupNotificationStream(self, serviceUUID, characteristicUUID, writeCommand, resultHandler, timeout): _LOGGER.debug( f"setupNotificationStream serviceUUID={serviceUUID} characteristicUUID={characteristicUUID} timeout={timeout}" ) await self.is_connected_guard() # setup the collecting of the notification data. _LOGGER.debug(f"setupNotificationStream: subscribe for notifications.") notificationDelegate = NotificationDelegate(None, self.settings) await self.activeClient.subscribeNotifications( characteristicUUID, notificationDelegate.handleNotification) # execute something that will trigger the notifications _LOGGER.debug(f"setupNotificationStream: writeCommand().") await writeCommand() # wait for the results to come in. self.notificationLoopActive = True loopCount = 0 successful = False polInterval = 0.1 while self.notificationLoopActive and loopCount < (timeout / polInterval): await asyncio.sleep(polInterval) _LOGGER.debug( f"loopActive={self.notificationLoopActive} loopCount={loopCount}" ) loopCount += 1 if notificationDelegate.result is not None: command = resultHandler(notificationDelegate.result) notificationDelegate.reset() if command == ProcessType.ABORT_ERROR: _LOGGER.debug("abort") self.notificationLoopActive = False self.activeClient.unsubscribeNotifications( characteristicUUID) raise CrownstoneBleException( BleError.ABORT_NOTIFICATION_STREAM_W_ERROR, "Aborting the notification stream because the resultHandler raised an error." ) elif command == ProcessType.FINISHED: _LOGGER.debug("finished") self.notificationLoopActive = False successful = True elif command == ProcessType.CONTINUE: _LOGGER.debug("continue") if not successful: self.activeClient.unsubscribeNotifications(characteristicUUID) raise CrownstoneBleException( BleError.NOTIFICATION_STREAM_TIMEOUT, "Notification stream not finished within timeout.") # remove subscription from this characteristic self.activeClient.unsubscribeNotifications(characteristicUUID) def _killNotificationLoop(self): _LOGGER.debug("_killNotificationLoop") self.notificationLoopActive = False def _preparePayload(self, data: list or bytes or bytearray): return bytearray(data)
# Install: pip3 install bleak # Run: python3 ./bleak.py import asyncio from bleak import BleakScanner scanner = BleakScanner() loop = asyncio.get_event_loop() """ def detection_callback(*args): # Ideally we can read adv data with-in this callback, # But I tried and found nothing useful inside "args" print(args[0]) scanner.register_detection_callback(detection_callback) """ async def run(): """ await scanner.start() await asyncio.sleep(1) await scanner.stop() devices = await scanner.get_discovered_devices() for d in devices: print(d) """ while (1): # timeout < 1 seems not very stable on my system d = await scanner.find_device_by_address("C0:98:E5:49:53:54", timeout=2)
class MainWindow(QMainWindow): def __init__(self, parent=None): QMainWindow.__init__(self, parent) # const value self.dequeMax = 1000 self.fftMax = 1000 self.notchCutOff = 60 self.notchQualityFactor = 15 self.lowPassCutOff = 50 self.lowPassOrder = 8 self.BL = 1000 self.frequencyRange = 50 self.samplingRate = 500 self.two_16 = pow(2, 16) self.two_8 = pow(2, 8) self.max_uv = 407 self.two_resolution = 8388607 self.rawGraphFrame = 25 self.update_num = 20 self.timerCounter = 0 self.fftHeight = 10 self.windowed = np.hamming(self.fftMax) # value self.measure_time = 0 self.ch1_1_value = 0 self.ch1_2_value = 0 self.ch1_3_value = 0 self.ch2_1_value = 0 self.ch2_2_value = 0 self.ch2_3_value = 0 self.read_state = parsingState.header1 self.ptr = 0 self.ptrFilter = 0 self.ptrTime = 0 # UI self.pgWidget = QWidget() self.setCentralWidget(self.pgWidget) self.vBoxLayout = QVBoxLayout(self.pgWidget) self.vScrollArea = QScrollArea(self.pgWidget) self.vScrollArea.setWidgetResizable(True) self.scrollAreaWidgetContents = QWidget() self.scrollAreaWidgetContents.setGeometry(QRect(0, 0, 500, 500)) self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents") self.vBoxLayout2 = QVBoxLayout(self.scrollAreaWidgetContents) self.setWindowTitle('NeuroSpec Demo') self.setWindowIcon(QIcon("./images/neuro.png")) self.setGeometry(QRect(0, 0, 1000, 1000)) self.vScrollArea.setWidget(self.scrollAreaWidgetContents) self.vBoxLayout.addWidget(self.vScrollArea) # grpah self.ax = pg.PlotWidget() self.ax.setMaximumHeight(300) self.ax.setMinimumHeight(250) self.ax.setDownsampling(mode='peak') self.ax.setClipToView(True) self.ax.setTitle("Left Raw Data", color='r') self.ax.setLabel('left', '[uV]', color='red', size=30) self.ax.setRange(xRange=[-10, 0], yRange=[-50, 50]) self.ax2 = pg.PlotWidget() self.ax2.setMaximumHeight(300) self.ax2.setMinimumHeight(250) self.ax2.setDownsampling(mode='peak') self.ax2.setClipToView(True) self.ax2.setTitle("Right Raw Data") self.ax2.setLabel('left', '[uV]', color='red', size=30) self.ax2.setRange(xRange=[-10, 0], yRange=[-50, 50]) self.ax3 = pg.PlotWidget() self.ax3.setMaximumHeight(300) self.ax3.setMinimumHeight(250) self.ax3.setDownsampling(mode='peak') self.ax3.setClipToView(True) self.ax3.setLabel( 'left', "<span style=\"color:red;font-size:10px\">uV</span>") self.ax3.setTitle("Left Filtering Data", color='r') self.ax3.setRange(xRange=[-10, 0], yRange=[-50, 50]) self.ax4 = pg.PlotWidget() self.ax4.setMaximumHeight(300) self.ax4.setMinimumHeight(250) self.ax4.setDownsampling(mode='peak') self.ax4.setTitle("Right Filtering Data") self.ax4.setClipToView(True) self.ax4.setRange(xRange=[-10, 0], yRange=[-50, 50]) self.ax4.setLabel('left', '[uV]', color='red', size=30) self.ax4.setLabel('bottom', 'Time', color='red', size=30) self.vBoxLayout2.addWidget(self.ax) self.vBoxLayout2.addWidget(self.ax2) self.vBoxLayout2.addWidget(self.ax3) self.vBoxLayout2.addWidget(self.ax4) self.pen = pg.mkPen(color=(255, 0, 0)) self.line = self.ax.plot(pen=self.pen) self.line2 = self.ax2.plot() self.line3 = self.ax3.plot(pen=self.pen) self.line4 = self.ax4.plot() self.data = np.zeros(500) self.data_x = np.linspace(0, 499, 500) * 0.002 self.data2 = np.zeros(500) self.data2_x = np.linspace(0, 499, 500) * 0.002 self.data3 = np.zeros(500) self.data3_x = np.linspace(0, 499, 500) * 0.002 self.data4 = np.zeros(500) self.data4_x = np.linspace(0, 499, 500) * 0.002 # 3d fft_freq = fftpack.fftfreq(self.BL, 1 / self.samplingRate) pos_mask = np.where(fft_freq > 0) self.frequencies = fft_freq[pos_mask] self.frequencies = np.delete(self.frequencies, range(self.frequencyRange * 2, 499), axis=0) #self.idx_band = dict() #for band in eeg_bands: # self.idx_band[band] = np.logical_and(self.frequencies >= eeg_bands[band][0], # self.frequencies <= eeg_bands[band][1]) self.figure3d = gl.GLViewWidget() self.figure3d.show() self.figure3d.setMaximumHeight(300) self.figure3d.setMinimumHeight(250) self.figure3d.setCameraPosition(distance=80, azimuth=12, elevation=15) self.gz = gl.GLGridItem() self.gz.rotate(90, 0, 1, 0) self.gz.setSize(10, 10, 1) self.gz.translate(0, 5, 5) self.figure3d.addItem(self.gz) self.gx = gl.GLGridItem() self.gx.setSize(50, 10, 1) self.gx.translate(25, 5, 0) self.figure3d.addItem(self.gx) self.vBoxLayout2.addWidget(self.figure3d) self.cMap = plt.get_cmap('jet') self.freq_ix = dict() self.freqBand = dict() self.ampBand = dict() self.Time = dict() self.Fre = dict() self.CvMax = dict() self.surfPlot = dict() for band in eeg_bands: self.freq_ix[band] = np.where( (self.frequencies >= eeg_bands[band][0]) & (self.frequencies <= eeg_bands[band][1]))[0] self.freqBand[band] = self.frequencies[self.freq_ix[band]] self.ampBand[band] = np.zeros((len(self.freq_ix[band]), 10)) self.Time[band] = np.linspace(0, 9, 10) self.Fre[band] = self.freqBand[band] self.cMap = plt.get_cmap(cMapList[band]) self.CvMax[band] = self.cMap(self.ampBand[band] / self.fftHeight) self.surfPlot[band] = gl.GLSurfacePlotItem(x=self.Fre[band], y=self.Time[band], z=self.ampBand[band], colors=self.CvMax[band], smooth=True, glOptions='opaque') self.figure3d.addItem(self.surfPlot[band]) self.surfPlot['Theta'].translate(-0.5, 0, 0) self.surfPlot['Alpha'].translate(-1, 0, 0) self.surfPlot['Beta'].translate(-1.5, 0, 0) self.surfPlot['Gamma'].translate(-2, 0, 0) # database self.fData = deque(np.zeros(self.dequeMax), maxlen=self.dequeMax) self.fData2 = deque(np.zeros(self.dequeMax), maxlen=self.dequeMax) self.fData3 = deque(np.zeros(self.fftMax), maxlen=self.fftMax) self.buffer = [] self.ch1_int_buffer = [] self.ch2_int_buffer = [] # bluetooth self.scanner = BleakScanner() self.macAddress = " " self.Read_UUID = "0000fff1-0000-1000-8000-00805f9b34fb" self.Read_UUID2 = "0000fff2-0000-1000-8000-00805f9b34fb" self.client = None # "74:F0:7D:C0:52:0C" self.panaxAddress = "PAPA" self.find_device = False self.noexcept = False self.conBool = False self.autoScan() # event # create actions, file menu action self.save = QAction("&저장", self) self.save.setIcon(QIcon("./images/saving.png")) self.save.setShortcut("Ctrl+S") self.save.setStatusTip("Save txt file") self.save.triggered.connect(self.close) self.load = QAction("L&oad", self) self.load.setIcon(QIcon("./images/loading.png")) self.load.setShortcut("Ctrl+O") self.load.setStatusTip("Load txt file") self.load.triggered.connect(self.close) self.exitAction = QAction("E&xit", self) self.exitAction.setIcon(QIcon("./images/Quit.png")) self.exitAction.setShortcut("Ctrl+Q") self.exitAction.setStatusTip("Exit the application") self.exitAction.triggered.connect(self.close) # control menu action self.start = QAction("S&tart", self) self.start.setIcon(QIcon("./images/start_red.png")) self.start.setStatusTip("측정을 시작합니다") self.start.triggered.connect(self.startDialog) self.paused = QAction("P&ause", self) self.paused.setIcon(QIcon("./images/pause.png")) self.paused.setStatusTip("측정을 정지합니다") self.paused.triggered.connect(self.stopMeasure) self.openClose = QAction("O&penclose", self) self.openClose.setIcon(QIcon("./images/openclose.png")) self.openClose.setStatusTip("개안폐안개안 40초씩 측정합니다") self.openClose.triggered.connect(self.measureClear) self.stop = QAction("&Stop", self) self.stop.setIcon(QIcon("./images/all_stop.png")) self.stop.setStatusTip("측정을 멈춤니다") self.stop.triggered.connect(self.plotInit) # view menu action self.surface = QAction("&Surface", self) self.surface.setIcon(QIcon("./images/surface.png")) self.surface.setStatusTip("입체로 보기-전압에 따른 색상") self.band = QAction("&Band", self) self.band.setIcon(QIcon("./images/band.png")) self.band.setStatusTip("입체로 보기-대역에 따른 색상") self.bar = QAction("&Bar", self) self.bar.setIcon(QIcon("./images/bar.png")) self.bar.setStatusTip("막대 그래프 보기") self.pie = QAction("&Pie", self) self.pie.setIcon(QIcon("./images/pie.png")) self.pie.setStatusTip("파이 그래프 보기") self.lineplot = QAction("&Line", self) self.lineplot.setIcon(QIcon("./images/lineplot.png")) self.lineplot.setStatusTip("입체로 보기-전압에 따른 색상") # configuration menu action self.rawDataConfig = QAction("&Rawconfig", self) self.rawDataConfig.setIcon(QIcon("./images/config2.png")) self.rawDataConfig.setStatusTip("Raw Graph 설정") self.tdConfig = QAction("&3Dconfig", self) self.tdConfig.setIcon(QIcon("./images/config2.png")) self.tdConfig.setStatusTip("3D graph 설정") # about self.aboutAction = QAction("&About", self) self.aboutAction.setStatusTip("Show the application's About box") self.aboutAction.triggered.connect(self.about) # createMenus fileMenu = self.menuBar().addMenu("&File") fileMenu.addAction(self.save) fileMenu.addAction(self.load) fileMenu.addAction(self.exitAction) ControlMenu = self.menuBar().addMenu("&Control") ControlMenu.addAction(self.start) ControlMenu.addAction(self.paused) ControlMenu.addAction(self.openClose) ControlMenu.addAction(self.stop) viewMenu = self.menuBar().addMenu("&View") viewMenu.addAction(self.surface) viewMenu.addAction(self.band) viewMenu.addAction(self.bar) viewMenu.addAction(self.pie) viewMenu.addAction(self.lineplot) configMenu = self.menuBar().addMenu("&Config") configMenu.addAction(self.rawDataConfig) configMenu.addAction(self.tdConfig) helpMenu = self.menuBar().addMenu("&Help") helpMenu.addAction(self.aboutAction) # createToolBar pgToolBar = self.addToolBar("&PG") pgToolBar.setObjectName("PGToolBar") pgToolBar.addAction(self.load) pgToolBar.addAction(self.save) pgToolBar.addSeparator() pgToolBar.addAction(self.start) pgToolBar.addAction(self.paused) pgToolBar.addAction(self.openClose) pgToolBar.addAction(self.stop) # spacer pgToolBar.addSeparator() pgToolBar.addAction(self.surface) pgToolBar.addAction(self.band) # spacer pgToolBar.addSeparator() pgToolBar.addAction(self.bar) pgToolBar.addAction(self.pie) pgToolBar.addAction(self.lineplot) # spacer pgToolBar.addSeparator() pgToolBar.addAction(self.rawDataConfig) pgToolBar.addAction(self.tdConfig) # spacer pgToolBar.addSeparator() pgToolBar.addAction(self.aboutAction) # createStatusBar locationLabel = QLabel("status bar") locationLabel.setAlignment(Qt.AlignHCenter) locationLabel.setMinimumSize(locationLabel.sizeHint()) self.statusBar().addWidget(locationLabel) def readSettings(self): settings = QSettings("Panaxtos", "newneuroSpec_Demo") # modify self.restoreGeometry(settings.value("geometry")) self.restoreState(settings.value("state")) def writeSettings(self): settings = QSettings("Qt5Programming Inc.", "Shape") self.saveGeometry() settings.setValue("geometry", self.saveGeometry()) settings.setValue("state", self.saveState()) def autoScan(self): asyncio.ensure_future(self.scan_start(), loop=loop) print('btnCallback returns...') def detection_callback(*args): print(args) async def scan_start(self): self.scanner.register_detection_callback(self.detection_callback) run_time = 0 start_time = time() while run_time < 16: await self.scanner.start() await asyncio.sleep(5.0) await self.scanner.stop() devices = await self.scanner.get_discovered_devices() if not devices: print("bluetooth를 켜주세요") for d in devices: if d.name == self.panaxAddress: self.find_device = True self.macAddress = d.address break if self.find_device: print("find panaxtos") await self.connect_panax(self.macAddress, loop) break end_time = time() run_time = end_time - start_time async def connect_panax(self, address, loop): self.client = BleakClient(address, loop=loop) while not self.noexcept: try: await self.client.connect() await self.isConnected() print(self.conBool) self.noexcept = True except Exception as e: print(e) async def isConnected(self): self.conBool = await self.client.is_connected() async def disconnect_panax(self): await self.client.disconnect() async def start_panax(self): if self.conBool: await self.client.start_notify(self.Read_UUID, self.tx_data_received) await self.client.start_notify(self.Read_UUID2, self.tx_data_received2) def measureStart(self): asyncio.ensure_future(self.isConnected()) asyncio.ensure_future(self.start_panax(), loop=loop) # event def close_event(self, event): self.writeSettings() def startDialog(self): sd = startdialog.Ui_dialog(self) if sd.exec(): self.measure_time = sd.time_info() self.measureStart() def measureClear(self): self.pgWidget.plotClear() def stopMeasure(self): asyncio.ensure_future(self.disconnect_panax()) def plotInit(self): self.pgWidget.plotInit() def about(self): QMessageBox.about( self, "About Shape", "<h2>newNeuroSpec Demo 1.0</h2>" "<p>Copyright © 2020 Panaxtos Inc." "<p>Shape is a small application that " "demonstrates QAction, QMainWindow, QMenuBar, " "QStatusBar, QToolBar, and many other " "데모데모") # data_received -> parsing -> int -> 20 -> print def tx_data_received2(self, sender, data): print("tx2!!!!") def tx_data_received(self, sender, data): #print("RX!!!> {}".format(data)) data_len = len(data) b = [] c = [] for rep in range(data_len): self.buffer.append(data[rep]) b.append(data[rep]) for rep in range(len(b)): if b[rep] == 255: c.append('0xFF') elif b[rep] == 119: c.append('0x77') else: c.append(data[rep]) print(c) self.read_data() # state2 if len(self.ch1_int_buffer) >= self.update_num and len( self.ch2_int_buffer) >= self.update_num: self.print_graph() self.timerCounter += 1 if self.timerCounter >= self.rawGraphFrame: self.timerCounter -= self.rawGraphFrame self.print3DGraph() def read_data(self): while len(self.buffer) > 0: temp = self.buffer.pop(0) if self.read_state == parsingState.header1: if temp == 255: self.read_state = parsingState.header2 elif self.read_state == parsingState.header2: if temp == 119: self.read_state = parsingState.header3 elif self.read_state == parsingState.header3: if temp == 255: self.read_state = parsingState.ch1_1 elif self.read_state == parsingState.ch1_1: self.ch1_1_value = temp self.read_state = parsingState.ch1_2 elif self.read_state == parsingState.ch1_2: self.ch1_2_value = temp self.read_state = parsingState.ch1_3 elif self.read_state == parsingState.ch1_3: self.ch1_3_value = temp self.read_state = parsingState.ch2_1 elif self.read_state == parsingState.ch2_1: self.ch2_1_value = temp self.read_state = parsingState.ch2_2 elif self.read_state == parsingState.ch2_2: self.ch2_2_value = temp self.read_state = parsingState.ch2_3 elif self.read_state == parsingState.ch2_3: self.ch2_3_value = temp ch1_int = (self.ch1_1_value * self.two_16) + ( self.ch1_2_value * self.two_8) + self.ch1_3_value ch1_int = tc.twos_comp(ch1_int, 24) ch1_int = (ch1_int * self.max_uv) / self.two_resolution self.ch1_int_buffer.append(ch1_int) ch2_int = (self.ch2_1_value * self.two_16) + ( self.ch2_2_value * self.two_8) + self.ch2_3_value ch2_int = tc.twos_comp(ch2_int, 24) ch2_int = (ch2_int * self.max_uv) / self.two_resolution self.ch2_int_buffer.append(ch2_int) self.read_state = parsingState.header1 def print_graph(self): ch1 = [] ch2 = [] for rep in range(0, self.update_num): temp = self.ch1_int_buffer.pop(0) temp2 = self.ch2_int_buffer.pop(0) ch1.append(temp) ch2.append(temp2) self.fData.extend(ch1) self.fData2.extend(ch2) self.data[self.ptr:self.ptr + self.update_num] = ch1 self.data2[self.ptr:self.ptr + self.update_num] = ch2 self.ptr += self.update_num if self.ptr >= self.data.shape[0]: tmp = self.data tmp2 = self.data2 self.data = np.zeros(self.data.shape[0] * 2) self.data2 = np.zeros(self.data2.shape[0] * 2) self.data[:tmp.shape[0]] = tmp self.data2[:tmp2.shape[0]] = tmp2 self.data_x = np.linspace(0, self.data.shape[0] - 1, self.data.shape[0]) * 0.002 self.data2_x = np.linspace(0, self.data.shape[0] - 1, self.data.shape[0]) * 0.002 self.line.setData(x=self.data_x[:self.ptr], y=self.data[:self.ptr]) self.line.setPos(-self.ptr * 0.002, 0) self.line2.setData(x=self.data2_x[:self.ptr], y=self.data2[:self.ptr]) self.line2.setPos(-self.ptr * 0.002, 0) notch_ch1 = nf.notch_filter(self.fData, self.notchCutOff, self.samplingRate, self.notchQualityFactor) notch_ch2 = nf.notch_filter(self.fData2, self.notchCutOff, self.samplingRate, self.notchQualityFactor) notch_ch1 = nf.notch_filter(notch_ch1, self.notchCutOff, self.samplingRate, self.notchQualityFactor) notch_ch2 = nf.notch_filter(notch_ch2, self.notchCutOff, self.samplingRate, self.notchQualityFactor) notchLowPass_ch1 = lf.butter_lowpass_filter(notch_ch1, self.lowPassCutOff, self.samplingRate, self.lowPassOrder) notchLowPass_ch2 = lf.butter_lowpass_filter(notch_ch2, self.lowPassCutOff, self.samplingRate, self.lowPassOrder) filtering_ch1 = lf.butter_lowpass_filter(notchLowPass_ch1, self.lowPassCutOff, self.samplingRate, self.lowPassOrder) filtering_ch2 = lf.butter_lowpass_filter(notchLowPass_ch2, self.lowPassCutOff, self.samplingRate, self.lowPassOrder) self.fData3.extend(filtering_ch1[-self.update_num:]) self.data3[self.ptrFilter:self.ptrFilter + self.update_num] = filtering_ch1[-self.update_num:] self.data4[self.ptrFilter:self.ptrFilter + self.update_num] = filtering_ch2[-self.update_num:] self.ptrFilter += self.update_num if self.ptrFilter >= self.data3.shape[0]: tmp = self.data3 tmp2 = self.data4 self.data3 = np.zeros(self.data3.shape[0] * 2) self.data4 = np.zeros(self.data3.shape[0] * 2) self.data3[:tmp.shape[0]] = tmp self.data4[:tmp2.shape[0]] = tmp2 self.data3_x = np.linspace(0, self.data3.shape[0] - 1, self.data3.shape[0]) * 0.002 self.data4_x = np.linspace(0, self.data3.shape[0] - 1, self.data3.shape[0]) * 0.002 self.line3.setData(x=self.data3_x[:self.ptrFilter], y=self.data3[:self.ptrFilter]) self.line3.setPos(-self.ptrFilter * 0.002, 0) self.line4.setData(x=self.data4_x[:self.ptrFilter], y=self.data4[:self.ptrFilter]) self.line4.setPos(-self.ptrFilter * 0.002, 0) def print3DGraph(self): ch1_raw_data = self.fData3 ch1_raw_data = ch1_raw_data * self.windowed ch1_fft = np.absolute(np.fft.rfft(ch1_raw_data)) ch1_fft = np.delete(ch1_fft, 0, axis=0) ch1_fft = np.delete(ch1_fft, range(self.frequencyRange * 2, int(self.fftMax / 2)), axis=0) ''' bar graph . band power band_power_array = np.zeros(5) band_power_array[0] = simps(ch1_fft_vals[idx_band['Delta']], dx = 0.5) band_power_array[1] = simps(ch1_fft_vals[idx_band['Theta']], dx = 0.5) band_power_array[2] = simps(ch1_fft_vals[idx_band['Alpha']], dx = 0.5) band_power_array[3] = simps(ch1_fft_vals[idx_band['Beta']], dx = 0.5) band_power_array[4] = simps(ch1_fft_vals[idx_band['Gamma']], dx = 0.5) ''' ch1_fft = np.log(ch1_fft) for band in eeg_bands: self.ampBand[band][:, self.ptrTime] = ch1_fft[ self.freq_ix[band][0]:self.freq_ix[band][-1] + 1] if self.ptrTime >= self.ampBand[band].shape[1] - 1: tmp = self.ampBand[band] self.ampBand[band] = np.zeros( (len(self.freq_ix[band]), self.ampBand[band].shape[1] + 10)) self.ampBand[band][:, :tmp.shape[1]] = tmp self.Time[band] = np.linspace(0, tmp.shape[1] + 9, tmp.shape[1] + 10) if band == 'Delta': self.gx.setSize(50, 10 + self.ptrTime, 1) self.gx.translate(0, 1, 0) self.gz.setSize(10, 10 + self.ptrTime, 1) self.gz.translate(0, 1, 0) self.surfPlot[band].translate(0, -4, 0) self.cMap = plt.get_cmap('jet') self.CvMax[band] = self.cMap(self.ampBand[band] / self.fftHeight) self.surfPlot[band].setData(y=self.Time[band], z=self.ampBand[band], colors=self.CvMax[band]) self.ptrTime += 1
class MainWindow(QMainWindow): def __init__(self, parent=None): # super().__init__() QMainWindow.__init__(self, parent) # const value self.dequeMax = 1000 self.notchCutOff = 60 self.notchQualityFactor = 15 self.lowPassCutOff = 50 self.lowPassOrder = 8 self.samplingRate = 500 self.two_16 = pow(2, 16) self.two_8 = pow(2, 8) self.max_uv = 407 self.two_resolution = 8388607 self.rawGraphFrame = 25 self.update_num = 20 self.timerCounter = 0 # value self.measure_time = 0 self.timerCount = 0 self.printIndex = 0 self.headerCount = 0 self.ch1_1_value = 0 self.ch1_2_value = 0 self.ch1_3_value = 0 self.ch2_1_value = 0 self.ch2_2_value = 0 self.ch2_3_value = 0 self.dataIndex = 0 self.read_state = parsingState.header1 self.ptr = 0 self.ptrFilter = 0 self.ptrTime = 0 self.boolPaused = True # UI self.pgWidget = QWidget() self.setCentralWidget(self.pgWidget) self.setGeometry(QRect(250, 120, 1600, 820)) self.dockingWidget = QDockWidget("개발용 텍스트") self.listWidget = QListWidget() self.listWidget.setFont("Courier") self.dockingWidget.setWidget(self.listWidget) self.dockingWidget.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea) self.dockingWidget.setFloating(False) self.addDockWidget(Qt.RightDockWidgetArea, self.dockingWidget) self.vBoxLayout2 = QVBoxLayout(self.pgWidget) self.lLabel = QLabel("블루투스 자동연결을 실행합니다.") self.lLabel.resize(200, 45) self.lLabel.setMaximumHeight(45) self.lLabel.setMinimumHeight(30) self.lLabel.setStyleSheet("color: blue;" "border-style: solid;" "border-width: 1px;" "border-color: #c1f7fe;" "border-radius: 3px;" "background-color: #F7FFFE;") font1 = self.lLabel.font() font1.setPointSize(30) font1.setFamily('Times New Roman') font1.setBold(True) self.lLabel.setFont(font1) self.setWindowTitle('Brint Monitor') self.setWindowIcon(QIcon("./images/brainGreen.png")) # grpah self.ax3 = pg.PlotWidget() self.ax3.setMaximumHeight(340) self.ax3.setMinimumHeight(250) self.ax3.setDownsampling(mode='peak') self.ax3.setTitle("좌측 뇌파", color='w') self.ax3.setClipToView(True) self.ax3.setLabel('left', "뇌파 [uV]", color='white') self.ax3.setLabel('bottom', '시간 [초]', color='white') self.ax3.setRange(xRange=[-10, 0], yRange=[-150, 150]) self.ax4 = pg.PlotWidget() self.ax4.setMaximumHeight(340) self.ax4.setMinimumHeight(250) self.ax4.setDownsampling(mode='peak') self.ax4.setTitle("우측 뇌파", color='w') self.ax4.setClipToView(True) self.ax4.setRange(xRange=[-10, 0], yRange=[-150, 150]) self.ax4.setLabel('left', '뇌파 [uV]', color='white') self.ax4.setLabel('bottom', '시간 [초]', color='white', size=30) self.vBoxLayout2.addWidget(self.ax3) self.vBoxLayout2.addWidget(self.ax4) self.pen = pg.mkPen(color=(255, 0, 0)) self.line3 = self.ax3.plot(pen=self.pen) self.line4 = self.ax4.plot() self.data3 = np.zeros(500) self.data3_x = np.linspace(0, 499, 500) * 0.002 self.data4 = np.zeros(500) self.data4_x = np.linspace(0, 499, 500) * 0.002 self.vBoxLayout2.addWidget(self.lLabel) # database self.fData = deque(np.zeros(self.dequeMax), maxlen=self.dequeMax) self.fData2 = deque(np.zeros(self.dequeMax), maxlen=self.dequeMax) self.buffer = [] self.ch1_int_buffer = [] self.ch2_int_buffer = [] # xml self.user = ET.Element("userName") # bluetooth self.scanner = BleakScanner() self.macAddress = " " self.Read_UUID = "0000fff1-0000-1000-8000-00805f9b34fb" self.Rx_UUID = "a9da6040-0823-4995-94ec-9ce41ca28833" self.Tx_UUID = "a73e9a10-628f-4494-a099-12efaf72258f" self.client = None # "74:F0:7D:C0:52:0C" self.panaxAddress = "PAPA" # "BGX-76DE" self.find_device = False self.noexcept = False self.conBool = False self.autoScan() # event # create actions, file menu action self.save = QAction("&Save", self) self.save.setIcon(QIcon("./images/saveBlue.png")) self.save.setShortcut("Ctrl+S") self.save.setStatusTip("Save .xml file") self.save.triggered.connect(self.save_rx) self.exitAction = QAction("E&xit", self) self.exitAction.setIcon(QIcon("./images/Quit.png")) self.exitAction.setShortcut("Ctrl+Q") self.exitAction.setStatusTip("Exit the application") self.exitAction.triggered.connect(self.close) # control menu action self.start = QAction("S&tart", self) self.start.setIcon(QIcon("./images/recordRed.png")) self.start.setStatusTip("측정을 시작합니다") self.start.triggered.connect(self.startDialog) self.paused = QAction("P&ause", self) self.paused.setIcon(QIcon("./images/pauseBlue.png")) self.paused.setStatusTip("측정을 정지합니다") self.paused.triggered.connect(self.pausedMeasure) self.stop = QAction("&Stop", self) self.stop.setIcon(QIcon("./images/stopBlue.png")) self.stop.setStatusTip("측정을 멈춤니다") self.stop.triggered.connect(self.rx_stop) # view menu action # about self.aboutAction = QAction("&About", self) self.aboutAction.setStatusTip("Show the application's About box") self.aboutAction.triggered.connect(self.about) # createMenus fileMenu = self.menuBar().addMenu("&File") fileMenu.addAction(self.save) # fileMenu.addAction(self.load) fileMenu.addAction(self.exitAction) ControlMenu = self.menuBar().addMenu("&Control") ControlMenu.addAction(self.start) ControlMenu.addAction(self.paused) ControlMenu.addAction(self.stop) helpMenu = self.menuBar().addMenu("&Help") helpMenu.addAction(self.aboutAction) # createToolBar pgToolBar = self.addToolBar("&PG") pgToolBar.setObjectName("PGToolBar") pgToolBar.addAction(self.save) pgToolBar.addSeparator() pgToolBar.addAction(self.start) pgToolBar.addAction(self.paused) pgToolBar.addAction(self.stop) pgToolBar.addSeparator() pgToolBar.addAction(self.aboutAction) pgToolBar2 = self.addToolBar("PG2") pgToolBar2.setObjectName("PGToolBar2") # createStatusBar ''' lLabel = QLabel("status bar") #lLabel.setAlignment(Qt.AlignHCenter) #lLabel.setMinimumSize(lLabel.sizeHint()) self.locationLabel = QLabel("test") self.locationLabel.setMinimumWidth(100) self.statusBar().setMinimumHeight(50) self.statusBar().addWidget(lLabel) self.statusBar().addWidget(self.locationLabel) ''' def readSettings(self): settings = QSettings("Panaxtos", "newneuroSpec_Demo") # modify self.restoreGeometry(settings.value("geometry")) self.restoreState(settings.value("state")) def writeSettings(self): settings = QSettings("Qt5Programming Inc.", "Shape") self.saveGeometry() settings.setValue("geometry", self.saveGeometry()) settings.setValue("state", self.saveState()) def autoScan(self): asyncio.ensure_future(self.scan_start(), loop=loop) print('btnCallback returns...') def detection_callback(*args): print(args) async def scan_start(self): self.scanner.register_detection_callback(self.detection_callback) self.lLabel.setText("PANAXTOS 기기를 Scan 중....") run_time = 0 start_time = time() while run_time < 16: await self.scanner.start() await asyncio.sleep(5.0) await self.scanner.stop() devices = await self.scanner.get_discovered_devices() if not devices: self.lLabel.setText("bluetooth를 켜주세요") for d in devices: if d.name == self.panaxAddress: self.find_device = True self.macAddress = d.address break if self.find_device: print("True") self.lLabel.setText("기기를 찾았습니다. connection을 진행합니다.") await self.connect_panax(self.macAddress, loop) break end_time = time() run_time = end_time - start_time if not self.find_device: self.lLabel.setText("기기가 켜져있는지 확인해주시고 프로그램을 다시 시작해주세요") async def connect_panax(self, address, loop): self.client = BleakClient(address, loop=loop) while not self.noexcept: try: await self.client.connect() await self.isConnected() self.start.setEnabled(True) self.noexcept = True self.lLabel.setText("연결이 완료 되었습니다. 측정시작 버튼을 눌러주세요") except Exception as e: print(e) self.lLabel.setText("Connection 중") async def isConnected(self): self.conBool = await self.client.is_connected() async def disconnect_panax(self): # await self.client.disconnect() await self.client.stop_notify(self.Read_UUID) async def start_panax(self): if self.conBool: await self.client.start_notify(self.Read_UUID, self.tx_data_received) # await self.client.start_notify(self.Rx_UUID, self.rx_data_received) # await asyncio.sleep(0.2) # await self.client.stop_notify(self.Rx_UUID) # await self.client.start_notify(self.Tx_UUID, self.tx_data_received) async def start_rx(self): if self.conBool: await self.client.start_notify(self.Rx_UUID, self.rx_data_received) async def stop_rx(self): await self.client.stop_notify(self.Read_UUID) def measureStart(self): asyncio.ensure_future(self.isConnected()) asyncio.ensure_future(self.start_panax(), loop=loop) self.start.setDisabled(True) self.save.setEnabled(True) self.lLabel.setText("뇌파를 측정중입니다.") # event def save_xml(self): xml_write.indent(self.user) now = datetime.datetime.now() nowDate = now.strftime('%Y-%m-%d.%H.%M') nowXml = nowDate + '.xml' ET.ElementTree(self.user).write(nowXml) nowXml = "저장이 완료 되었습니다" + nowXml self.lLabel.setText(nowXml) def save_rx(self): asyncio.ensure_future(self.start_rx(), loop=loop) def rx_stop(self): asyncio.ensure_future(self.stop_rx(), loop=loop) def startDialog(self): sd = startdialog.Ui_dialog(self) if sd.exec(): self.measure_time = sd.time_info() self.measureStart() def stopDialog(self): sd = stopdialog.Ui_dialog(self) if sd.exec(): self.plotInit() def pausedMeasure(self): self.boolPaused = not self.boolPaused if not self.boolPaused: asyncio.ensure_future(self.disconnect_panax()) self.paused.setIcon(QIcon("./images/playBlue.png")) self.paused.setStatusTip("측정을 재개합니다") self.lLabel.setText("측정을 정지합니다.") else: self.measureStart() self.paused.setIcon(QIcon("./images/pauseBlue.png")) self.paused.setStatusTip("측정을 정지합니다") self.lLabel.setText("측정을 재개합니다.") def plotInit(self): self.lLabel.setText("측정을 초기화 하였습니다. 다시 시작하려면 시작버튼을 누르시오") if self.boolPaused: self.pausedMeasure() self.fData.clear() self.fData2.clear() self.fData.extend(np.zeros(500)) self.fData2.extend(np.zeros(500)) self.ptr = 0 self.ptrTime = 0 self.ptrFilter = 0 self.timerCount = 0 self.line3.setData(np.empty(1)) self.line4.setData(np.empty(1)) self.ax3.setRange(xRange=[-10, 0], yRange=[-150, 150]) self.ax4.setRange(xRange=[-10, 0], yRange=[-150, 150]) self.boolPaused = not self.boolPaused self.paused.setIcon(QIcon("./images/pauseBlue.png")) self.paused.setStatusTip("측정을 정지합니다") self.start.setEnabled(True) self.paused.setDisabled(True) self.stop.setDisabled(True) def about(self): QMessageBox.about( self, "About Shape", "<h2>Brint Monitor 1.0</h2>" "<p>Copyright © 2020 Panaxtos Inc." "<p>Shape is a small application that " "demonstrates QAction, QMainWindow, QMenuBar, " "QStatusBar, QToolBar, and many other ") # data_received -> parsing -> int -> 20 -> print def rx_data_received(self, sender, data): print("RX {}".format(data)) def tx_data_received(self, sender, data): data_len = len(data) for rep in range(data_len): self.buffer.append(data[rep]) self.print_data() '''data_len = len(data) for rep in range(data_len): self.buffer.append(data[rep]) self.read_data() if len(self.ch1_int_buffer) >= self.update_num and len(self.ch2_int_buffer) >= self.update_num: self.print_graph() ''' def print_data(self): while len(self.buffer) > 0: temp = self.buffer.pop(0) if self.read_state == parsingState.header1: if temp == 255: self.read_state = parsingState.header2 elif self.read_state == parsingState.header2: if temp == 119: self.read_state = parsingState.header3 elif self.read_state == parsingState.header3: self.headerCount = temp self.read_state = parsingState.ch1_1 elif self.read_state == parsingState.ch1_1: self.ch1_1_value = temp self.read_state = parsingState.ch1_2 elif self.read_state == parsingState.ch1_2: self.ch1_2_value = temp self.read_state = parsingState.ch1_3 elif self.read_state == parsingState.ch1_3: self.ch1_3_value = temp self.read_state = parsingState.ch2_1 elif self.read_state == parsingState.ch2_1: self.ch2_1_value = temp self.read_state = parsingState.ch2_2 elif self.read_state == parsingState.ch2_2: self.ch2_2_value = temp self.read_state = parsingState.ch2_3 elif self.read_state == parsingState.ch2_3: self.ch2_3_value = temp stringFF = hex(255) string77 = hex(119) hCount = "0x{:02x}".format(self.headerCount) ch1_1 = "0x{:02x}".format(self.ch1_1_value) ch1_2 = "0x{:02x}".format(self.ch1_2_value) ch1_3 = "0x{:02x}".format(self.ch1_3_value) ch2_1 = "0x{:02x}".format(self.ch2_1_value) ch2_2 = "0x{:02x}".format(self.ch2_2_value) ch2_3 = "0x{:02x}".format(self.ch2_3_value) ss = stringFF + " " + string77 + " " + hCount + " " + ch1_1 + " " + ch1_2 + " " + ch1_3 + " " + ch2_1 \ + " " + ch2_2 + " " + ch2_3 self.listWidget.addItem(ss) self.printIndex += 1 if self.printIndex > 40: self.listWidget.takeItem(0) self.read_state = parsingState.header1 def read_data(self): while len(self.buffer) > 0: temp = self.buffer.pop(0) if self.read_state == parsingState.header1: if temp == 255: self.read_state = parsingState.header2 elif self.read_state == parsingState.header2: if temp == 119: self.read_state = parsingState.header3 elif self.read_state == parsingState.header3: if temp == 255: self.read_state = parsingState.ch1_1 elif self.read_state == parsingState.ch1_1: self.ch1_1_value = temp self.read_state = parsingState.ch1_2 elif self.read_state == parsingState.ch1_2: self.ch1_2_value = temp self.read_state = parsingState.ch1_3 elif self.read_state == parsingState.ch1_3: self.ch1_3_value = temp self.read_state = parsingState.ch2_1 elif self.read_state == parsingState.ch2_1: self.ch2_1_value = temp self.read_state = parsingState.ch2_2 elif self.read_state == parsingState.ch2_2: self.ch2_2_value = temp self.read_state = parsingState.ch2_3 elif self.read_state == parsingState.ch2_3: self.ch2_3_value = temp ch1_int = (self.ch1_1_value * self.two_16) + ( self.ch1_2_value * self.two_8) + self.ch1_3_value ch1_int = tc.twos_comp(ch1_int, 24) ch1_int = (ch1_int * self.max_uv) / self.two_resolution self.ch1_int_buffer.append(ch1_int) ch2_int = (self.ch2_1_value * self.two_16) + ( self.ch2_2_value * self.two_8) + self.ch2_3_value ch2_int = tc.twos_comp(ch2_int, 24) ch2_int = (ch2_int * self.max_uv) / self.two_resolution self.ch2_int_buffer.append(ch2_int) self.dataIndex += 1 data = xml_write.makeXML(self.dataIndex, ch1_int, ch2_int) self.user.append(data) self.read_state = parsingState.header1 def print_graph(self): ch1 = [] ch2 = [] for rep in range(0, self.update_num): temp = self.ch1_int_buffer.pop(0) temp2 = self.ch2_int_buffer.pop(0) ch1.append(temp) ch2.append(temp2) self.fData.extend(ch1) self.fData2.extend(ch2) notch_ch1 = nf.notch_filter(self.fData, self.notchCutOff, self.samplingRate, self.notchQualityFactor) notch_ch2 = nf.notch_filter(self.fData2, self.notchCutOff, self.samplingRate, self.notchQualityFactor) notch_ch1 = nf.notch_filter(notch_ch1, self.notchCutOff, self.samplingRate, self.notchQualityFactor) notch_ch2 = nf.notch_filter(notch_ch2, self.notchCutOff, self.samplingRate, self.notchQualityFactor) notchLowPass_ch1 = lf.butter_lowpass_filter(notch_ch1, self.lowPassCutOff, self.samplingRate, self.lowPassOrder) notchLowPass_ch2 = lf.butter_lowpass_filter(notch_ch2, self.lowPassCutOff, self.samplingRate, self.lowPassOrder) filtering_ch1 = lf.butter_lowpass_filter(notchLowPass_ch1, self.lowPassCutOff, self.samplingRate, self.lowPassOrder) filtering_ch2 = lf.butter_lowpass_filter(notchLowPass_ch2, self.lowPassCutOff, self.samplingRate, self.lowPassOrder) # self.fData3.extend(filtering_ch1[-self.update_num:]) # self.fData4.extend(filtering_ch2[-self.update_num:]) self.data3[self.ptrFilter:self.ptrFilter + self.update_num] = filtering_ch1[-self.update_num:] self.data4[self.ptrFilter:self.ptrFilter + self.update_num] = filtering_ch2[-self.update_num:] self.ptrFilter += self.update_num if self.ptrFilter >= self.data3.shape[0]: tmp = self.data3 tmp2 = self.data4 self.data3 = np.zeros(self.data3.shape[0] * 2) self.data4 = np.zeros(self.data3.shape[0] * 2) self.data3[:tmp.shape[0]] = tmp self.data4[:tmp2.shape[0]] = tmp2 self.data3_x = np.linspace(0, self.data3.shape[0] - 1, self.data3.shape[0]) * 0.002 self.data4_x = np.linspace(0, self.data3.shape[0] - 1, self.data3.shape[0]) * 0.002 self.line3.setData(x=self.data3_x[:self.ptrFilter], y=self.data3[:self.ptrFilter]) self.line3.setPos(-self.ptrFilter * 0.002, 0) self.line4.setData(x=self.data4_x[:self.ptrFilter], y=self.data4[:self.ptrFilter]) self.line4.setPos(-self.ptrFilter * 0.002, 0) self.timerCount += 0.04 if self.timerCount > self.measure_time: self.pausedMeasure() self.paused.setDisabled(True) self.lLabel.setText("측정이 끝났습니다. 수고하셨습니다.")
class BasePlugin: def __init__(self): self.ble_queue = queue.Queue() self.message_queue = queue.Queue() self.message_thread = threading.Thread( name="QueueThread", target=BasePlugin.handle_message, args=(self, )) self.ble_thread = threading.Thread(name="BleThread", target=BasePlugin.handle_ble, args=(self, )) self.keeping_track = {} self.scanner = BleakScanner() self.scanner.register_detection_callback(self.simple_callback) self.loop = asyncio.get_event_loop() self.macs = [] def handle_message(self): try: Domoticz.Debug("Entering message handler") while True: values = self.message_queue.get(block=True) if values is None: Domoticz.Debug("Exiting message handler") self.message_queue.task_done() break # create sensor if needed if values.address not in self.macs and Parameters[ "Mode1"] == 'auto': self.macs.append(values.address) sensor_number = self.macs.index(values.address) + 2 Domoticz.Device(Name=values.address, Unit=sensor_number, TypeName="Temp+Hum", Used=1).Create() self.save_macs_to_database(self.macs) # update values for known devices if values.address in self.macs: sensor_number = self.macs.index(values.address) + 2 # humidity stat hum = values.humidity hum_stat = 0 if hum < 30: hum_stat = 2 elif 30 <= hum < 45: hum_stat = 0 elif 45 <= hum < 70: hum_stat = 1 elif hum >= 70: hum_stat = 3 # Signal level signal_level = int(5 - (-50 - values.rssi) / 10) Domoticz.Debug("signal level: %.0f, %d" % (values.rssi, signal_level)) value = "%.1f;%d;%d" % (values.temperature, values.humidity, hum_stat) Devices[sensor_number].Update( nValue=0, sValue=value, SignalLevel=signal_level, BatteryLevel=values.battery_level) self.message_queue.task_done() except Exception as err: Domoticz.Error("handleMessage: " + str(err)) def simple_callback(self, device: BLEDevice, advertisement_data: AdvertisementData): try: if XIAOMI_LYWSD03MMC_PROFILE_CUSTOM in advertisement_data.service_data: service_data = advertisement_data.service_data[ XIAOMI_LYWSD03MMC_PROFILE_CUSTOM] values = XiaomiLYWSD03MMC() values.address = ':'.join('{:02x}'.format(x) for x in service_data[0:6]) values.temperature = float( int.from_bytes( service_data[6:8], byteorder='big', signed=True)) / 10 values.humidity = service_data[8] values.battery_level = service_data[9] values.battery_voltage = int.from_bytes(service_data[10:12], byteorder='big') values.frame = service_data[12] values.rssi = device.rssi # was the frame already received send = False track = self.keeping_track.get(values.address) if track: if values.frame != track.frame: send = True else: send = True if send: self.keeping_track[values.address] = values Domoticz.Debug('device.address: %s' % device.address) Domoticz.Debug('keeping_track: %d' % len(self.keeping_track)) self.message_queue.put(values) except Exception as err: Domoticz.Error("simple_callback: " + str(err)) async def run(self): try: await self.scanner.start() await asyncio.sleep(5.0) await self.scanner.stop() except Exception as err: Domoticz.Error("run: " + str(err)) def handle_ble(self): try: Domoticz.Debug("Scanner") while True: if self.ble_queue and not self.ble_queue.empty(): message = self.ble_queue.get(block=True) if message is None: Domoticz.Debug("Exiting ble handler") self.ble_queue.task_done() break if Devices[1].nValue == 1: Domoticz.Debug("Scan") self.loop.run_until_complete(self.run()) else: Domoticz.Debug("Scanner is disabled") time.sleep(5.0) except Exception as err: Domoticz.Error("handle_ble: " + str(err)) # function to create corresponding sensors in Domoticz if there are Mi Flower Mates which don't have them yet. def create_sensors(self): # create the domoticz sensors if necessary if len(Devices) - 1 < len(self.macs): Domoticz.Debug("Creating new sensors") # Create the sensors. Later we get the data. for idx, mac in enumerate(self.macs): Domoticz.Debug("Creating new sensor:" + str(mac)) sensor_number = idx + 2 if sensor_number not in Devices: Domoticz.Device(Name=str(mac), Unit=sensor_number, TypeName="Temp+Hum", Used=1).Create() Domoticz.Log("Created device: " + Devices[sensor_number].Name) @staticmethod def parse_csv(csv): listvals = [] for value in csv.split(","): listvals.append(value) return listvals @staticmethod def load_macs_from_database(): # first, let's get the list of devices we already know about database = shelve.open('XiaomiLLYWSD03MMC') try: knownSensors = database['macs'] oldLength = len(knownSensors) Domoticz.Debug("Already know something:" + str(oldLength)) Domoticz.Log("Already known devices:" + str(knownSensors)) except: knownSensors = [] database['macs'] = knownSensors Domoticz.Debug("No existing sensors in system?") database.close() return knownSensors @staticmethod def save_macs_to_database(macs): # first, let's get the list of devices we already know about database = shelve.open('XiaomiLLYWSD03MMC') database['macs'] = macs database.close() return macs def onStart(self): Domoticz.Debug("Xiaomi LYWSD03MMC - devices made so far (max 255): " + str(len(Devices))) if Parameters["Mode6"] != "0": Domoticz.Debugging(int(Parameters["Mode6"])) DumpConfigToLog() # create master toggle switch if 1 not in Devices: Domoticz.Log( "Creating the master Xiaomi LYWSD03MMC poll switch. Flip it to poll the sensors." ) # default SwitchType, not enabled by default Domoticz.Device(Name="scanner", Unit=1, Type=244, Subtype=62, Image=9, Used=1).Create() # get the mac addresses of the sensors if Parameters["Mode1"] == 'auto': Domoticz.Log("Automatic mode is selected") self.macs = self.load_macs_from_database() else: Domoticz.Log("Manual mode is selected") self.macs = self.parse_csv(Parameters["Mode2"]) self.create_sensors() self.message_thread.start() self.ble_thread.start() Domoticz.Heartbeat(5) def onCommand(self, Unit, Command, Level, Hue): Domoticz.Log("onCommand called for Unit " + str(Unit) + ": Parameter '" + str(Command) + "', Level: " + str(Level)) if Command == 'On': value = 1 else: value = 0 Domoticz.Debug("value: %d" % value) Devices[Unit].Update(nValue=value, sValue='') def onHeartbeat(self): Domoticz.Debug("Heartbeat test message") def onStop(self): # Not needed in an actual plugin for thread in threading.enumerate(): if thread.name != threading.current_thread().name: Domoticz.Log( "'" + thread.name + "' is running, it must be shutdown otherwise Domoticz will abort on plugin exit." ) # signal queue thread to exit self.ble_queue.put(None) self.message_queue.put(None) Domoticz.Log("Clearing message queue...") self.message_queue.join() self.ble_queue.join() # Wait until queue thread has exited Domoticz.Log("Threads still active: " + str(threading.active_count()) + ", should be 1.") while (threading.active_count() > 1): for thread in threading.enumerate(): if (thread.name != threading.current_thread().name): Domoticz.Log( "'" + thread.name + "' is still running, waiting otherwise Domoticz will abort on plugin exit." ) time.sleep(1.0)
async def run(): """Asyncio runner.""" mqttLogger = logging.getLogger("mqttClient") mqttLogger.setLevel(logging.DEBUG) # Connect to the MQTT broker client = mqtt.Client() def on_connect(client, userdata, flags, rc): set_online(client) client.enable_logger(logger=mqttLogger) client.on_connect = on_connect client.will_set(discoveryPayload["avty_t"], discoveryPayload["pl_not_avail"], 0, retain=False) client.connect("192.168.1.10", 1883, 60) scanner = BleakScanner() def display_status(): def add_line(): return "-" * 66 + "\n" buf = "\n" buf += add_line() buf += "| Name | P (B)| T (C) | B (%)| Update |\n" buf += "|================================================================|\n" for dev in devList: buf += str(devList[dev]) buf += "|================================================================|\n" logger.info(buf) def decodeMftData(name, mftData): pressure = mftData[1] / 32 temp = mftData[2] - 55 battery = mftData[0] * 0.01 + 1.23 if battery >= 3.0: battery = 3.0 # 3.0V is 100%, 2.6V is 0% battery = int(100 * (battery - 2.6) / 0.4) return presSensor(name, pressure, temp, battery, datetime.datetime.now()) def detection_callback(*args): msg = args[0] if msg.member == "InterfacesAdded": # print('================InterfacesAdded========================') devList[msg.body[0]] = decodeMftData( msg.body[1]["org.bluez.Device1"]["Name"], msg.body[1]["org.bluez.Device1"]["ManufacturerData"][172], ) postConfig(client, msg.body[0]) postStatus(client, msg.body[0]) elif msg.member == "PropertiesChanged": # print('================PropertiesChanged========================') # print(msg.body) if msg.path in devList: if "ManufacturerData" in msg.body[1]: # get name name = devList[msg.path].name # update the data devList[msg.path] = decodeMftData( name, msg.body[1]["ManufacturerData"][172]) else: # update the timestamp devList[msg.path].u = datetime.datetime.now() postStatus(client, msg.path) else: print("Properties changed for unknown devices") # for att in dir(msg): # print(att,getattr(msg,att)) pass elif msg.member == "InterfacesRemoved": # we don't care return else: print("================else========================") print("{}, {}, {}".format(datetime.datetime.now(), msg.member, msg.body)) pass display_status() scanner.register_detection_callback(detection_callback) await scanner.set_scanning_filter( filters={"UUIDs": ["0000fbb0-0000-1000-8000-00805f9b34fb"]}) await scanner.start() print("scan started", datetime.datetime.now()) while True: # run mqtt client loop client.loop() await asyncio.sleep(1)