From adc9e2a7f6469dcc674ac32bbdd4b47679b80ee5 Mon Sep 17 00:00:00 2001 From: Anton Sarukhanov <code@ant.sr> Date: Sun, 10 Jun 2018 17:40:41 -0400 Subject: [PATCH] Add device nickname setter/getter. Improve logging. --- turntouch/__init__.py | 72 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 65 insertions(+), 7 deletions(-) diff --git a/turntouch/__init__.py b/turntouch/__init__.py index d3e0e27..a97771c 100644 --- a/turntouch/__init__.py +++ b/turntouch/__init__.py @@ -1,7 +1,7 @@ """ Python library for the Turn Touch bluetooth smart home remote.""" import logging from bluepy import btle -from typing import List +from typing import List, Union logger = logging.getLogger('TurnTouch') @@ -10,12 +10,65 @@ logger = logging.getLogger('TurnTouch') class TurnTouch(btle.Peripheral): """A Turn Touch smart home remote.""" + BUTTON_STATUS_CHARACTERISTIC_UUID = '99c31525-dc4f-41b1-bb04-4e4deb81fadd' + BATTERY_LEVEL_CHARACTERISTIC_UUID = '2a19' + DEVICE_NAME_CHARACTERISTIC_UUID = '99c31526-dc4f-41b1-bb04-4e4deb81fadd' + DEVICE_NAME_LENGTH = 32 -def scan(device_index: int = 0, + def __init__(self, + device_address: Union[str, btle.ScanEntry], + interface: int = None, + enable_notifications: bool = True): + """Connect to the Turn Touch remote. + Set appropriate address type (overriding btle default). + :param device_address Union[str, btle.ScanEntry]: + MAC address (or btle.ScanEntry object) of this device + :param interface int: Index of the bluetooth device (eg. 0 for hci0) + :param enable_notifications bool: Start listening for button presses""" + try: + logger.info("Connecting to device {address}...'".format( + address=self.addr)) + super(TurnTouch, self).__init__( + device_address, btle.ADDR_TYPE_RANDOM, interface) + logger.info("Successfully connected to device {address}.'".format( + address=self.addr)) + except btle.BTLEException: + raise TurnTouchException("Failed to connect to device {address}" + .format(address=device_address)) + + @property + def name(self) -> str: + """Read the nickname of this remote.""" + name_bytes = self.getCharacteristics( + uuid=self.DEVICE_NAME_CHARACTERISTIC_UUID)[0].read() + logger.debug("Read name of device {address}: '{name}'".format( + address=self.addr, name=name_bytes)) + return name_bytes.decode('utf-8').rstrip('\0') + + @name.setter + def name(self, name: str): + """Set the nickname of this remote.""" + if len(name) > self.DEVICE_NAME_LENGTH: + raise(TurnTouchException("Name must be {limit} characters or less." + .format(limit=self.DEVICE_NAME_LENGTH))) + name_characteristic = self.getCharacteristics( + uuid=self.DEVICE_NAME_CHARACTERISTIC_UUID)[0] + name_bytes = name.encode('utf-8').ljust(self.DEVICE_NAME_LENGTH, b'\0') + name_characteristic.write(name_bytes, withResponse=True) + logger.debug("Set name for device {address} to '{name}'".format( + address=self.addr, name=name_bytes)) + + +class TurnTouchException(Exception): + """An error related to the Turn Touch bluetooth smart home remote.""" + pass + + +def scan(interface: int = 0, timeout: float = 10, only_one: bool = False) -> List[TurnTouch]: """Scan for Turn Touch devices. - :param device_index int: Index of the bluetooth device (eg. 0 for hci0) + :param interface int: Index of the bluetooth device (eg. 0 for hci0) :param timeout float: Scanning timeout, in seconds :param only_one float: Stop scanning after one Turn Touch is found :return list: Found TurnTouch devices""" @@ -42,7 +95,7 @@ def scan(device_index: int = 0, If only searching for one device, short-circuit the scan by raising a DeviceFoundException once we've found a Turn Touch device.""" if is_new_device and is_turn_touch(device): - logger.info("Discovered device {address}".format( + logger.debug("Discovered device {address}".format( address=device.addr)) if self.only_one: raise DeviceFoundException() @@ -53,11 +106,16 @@ def scan(device_index: int = 0, name = device.getValueText(BLE_COMPLETE_DEVICE_NAME) return name == TT_DEVICE_NAME or short_name == TT_SHORT_DEVICE_NAME - scanner = btle.Scanner(device_index) + scanner = btle.Scanner(interface) + logger.info("Scanning for Turn Touch devices...") try: scanner.withDelegate(ScanDelegate(only_one)).scan(timeout) except DeviceFoundException: pass finally: - return [TurnTouch(device) for device in scanner.scanned.values() - if is_turn_touch(device)] + devices = [device + for device in scanner.scanned.values() + if is_turn_touch(device)] + logger.info("Scan finished. Found {count} device(s)." + .format(count=len(devices))) + return [TurnTouch(device) for device in devices] -- GitLab