예제 #1
1
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))
예제 #2
0
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)
예제 #3
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)
예제 #4
0
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()
예제 #6
0
async def run():
    scanner = BleakScanner()
    scanner.register_detection_callback(detection_callback)
    while True:
        await scanner.start()
        await asyncio.sleep(0.5)
        await scanner.stop()
예제 #7
0
async def main():
    scanner = BleakScanner()
    scanner.register_detection_callback(simple_callback)

    while True:
        await scanner.start()
        await asyncio.sleep(5.0)
        await scanner.stop()
예제 #8
0
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()
예제 #9
0
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()
예제 #11
0
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()
예제 #12
0
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)
예제 #13
0
파일: scanner.py 프로젝트: irremotus/govee
    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()
예제 #14
0
    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')
예제 #15
0
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))
예제 #16
0
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')
예제 #17
0
 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 []
예제 #18
0
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)
예제 #19
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
예제 #20
0
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()
예제 #23
0
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
예제 #24
0
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
예제 #25
0
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)
예제 #26
0
# 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)
예제 #27
0
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 &copy; 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
예제 #28
0
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 &copy; 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("측정이 끝났습니다. 수고하셨습니다.")
예제 #29
0
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)