Beispiel #1
0
    def _data_dict_from_bleak(device):
        data_dict = {}
        for key, value in device.metadata.items():
            if key == "manufacturer_data":
                # The manufacturer data value is a dictionary.
                # Re-concatenate it into bytes
                all_mfr_data = bytearray()
                for mfr_id, mfr_data in value.items():
                    all_mfr_data.extend(mfr_id.to_bytes(2, byteorder="little"))
                    all_mfr_data.extend(mfr_data)
                data_dict[0xFF] = all_mfr_data
            elif key == "uuids":
                uuids16 = bytearray()
                uuids128 = bytearray()
                for uuid in value:
                    bleio_uuid = UUID(uuid)
                    # If this is a Standard UUID in 128-bit form, convert it to a 16-bit UUID.
                    if bleio_uuid.is_standard_uuid:
                        uuids16.extend(bleio_uuid.uuid128[12:14])
                    else:
                        uuids128.extend(bleio_uuid.uuid128)

            if uuids16:
                # Complete list of 16-bit UUIDs.
                data_dict[0x03] = uuids16
            if uuids128:
                # Complete list of 128-bit UUIDs
                data_dict[0x07] = uuids128

        if not ScanEntry._RE_IGNORABLE_NAME.fullmatch(device.name):
            # Complete name
            data_dict[0x09] = device.name.encode("utf-8")

        return data_dict
    def service_uuids(self):
        """List of all the service UUIDs in the advertisement."""
        uuid_values = []

        concat_uuids = self._packet.get(AdvertisingPacket.ALL_16_BIT_SERVICE_UUIDS)
        concat_uuids = concat_uuids if concat_uuids else self._packet.get(
            AdvertisingPacket.SOME_16_BIT_SERVICE_UUIDS)

        if concat_uuids:
            for i in range(0, len(concat_uuids), 2):
                uuid_values.extend(struct.unpack("<H", concat_uuids[i:i+2]))

        concat_uuids = self._packet.get(AdvertisingPacket.ALL_128_BIT_SERVICE_UUIDS)
        concat_uuids = concat_uuids if concat_uuids else self._packet.get(
            AdvertisingPacket.SOME_128_BIT_SERVICE_UUIDS)

        if concat_uuids:
            for i in range(0, len(concat_uuids), 16):
                uuid_values.append(concat_uuids[i:i+16])

        return [UUID(value) for value in uuid_values]
class HIDServer:
    """
    Provide devices for HID over BLE.

    :param str name: Name to advertise for server. If None, use default Peripheral name.

    Example::

        from adafruit_ble.hid_server import HIDServer

        hid = HIDServer()
    """

    # These are used multiple times, so make them class constants.
    _REPORT_UUID = UUID(_REPORT_UUID_NUM)
    _REPORT_REF_DESCR_UUID = UUID(_REPORT_REF_DESCR_UUID_NUM)

    #pylint: disable=line-too-long
    HID_DESCRIPTOR = (
        b'\x05\x01'  # Usage Page (Generic Desktop Ctrls)
        b'\x09\x06'  # Usage (Keyboard)
        b'\xA1\x01'  # Collection (Application)
        b'\x85\x01'  #   Report ID (1)
        b'\x05\x07'  #   Usage Page (Kbrd/Keypad)
        b'\x19\xE0'  #   Usage Minimum (\xE0)
        b'\x29\xE7'  #   Usage Maximum (\xE7)
        b'\x15\x00'  #   Logical Minimum (0)
        b'\x25\x01'  #   Logical Maximum (1)
        b'\x75\x01'  #   Report Size (1)
        b'\x95\x08'  #   Report Count (8)
        b'\x81\x02'  #   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
        b'\x81\x01'  #   Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
        b'\x19\x00'  #   Usage Minimum (\x00)
        b'\x29\x65'  #   Usage Maximum (\x65)
        b'\x15\x00'  #   Logical Minimum (0)
        b'\x25\x65'  #   Logical Maximum (101)
        b'\x75\x08'  #   Report Size (8)
        b'\x95\x06'  #   Report Count (6)
        b'\x81\x00'  #   Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
        b'\x05\x08'  #   Usage Page (LEDs)
        b'\x19\x01'  #   Usage Minimum (Num Lock)
        b'\x29\x05'  #   Usage Maximum (Kana)
        b'\x15\x00'  #   Logical Minimum (0)
        b'\x25\x01'  #   Logical Maximum (1)
        b'\x75\x01'  #   Report Size (1)
        b'\x95\x05'  #   Report Count (5)
        b'\x91\x02'  #   Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
        b'\x95\x03'  #   Report Count (3)
        b'\x91\x01'  #   Output (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
        b'\xC0'  # End Collection
        b'\x05\x01'  # Usage Page (Generic Desktop Ctrls)
        b'\x09\x02'  # Usage (Mouse)
        b'\xA1\x01'  # Collection (Application)
        b'\x09\x01'  #   Usage (Pointer)
        b'\xA1\x00'  #   Collection (Physical)
        b'\x85\x02'  #     Report ID (2)
        b'\x05\x09'  #     Usage Page (Button)
        b'\x19\x01'  #     Usage Minimum (\x01)
        b'\x29\x05'  #     Usage Maximum (\x05)
        b'\x15\x00'  #     Logical Minimum (0)
        b'\x25\x01'  #     Logical Maximum (1)
        b'\x95\x05'  #     Report Count (5)
        b'\x75\x01'  #     Report Size (1)
        b'\x81\x02'  #     Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
        b'\x95\x01'  #     Report Count (1)
        b'\x75\x03'  #     Report Size (3)
        b'\x81\x01'  #     Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
        b'\x05\x01'  #     Usage Page (Generic Desktop Ctrls)
        b'\x09\x30'  #     Usage (X)
        b'\x09\x31'  #     Usage (Y)
        b'\x15\x81'  #     Logical Minimum (-127)
        b'\x25\x7F'  #     Logical Maximum (127)
        b'\x75\x08'  #     Report Size (8)
        b'\x95\x02'  #     Report Count (2)
        b'\x81\x06'  #     Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
        b'\x09\x38'  #     Usage (Wheel)
        b'\x15\x81'  #     Logical Minimum (-127)
        b'\x25\x7F'  #     Logical Maximum (127)
        b'\x75\x08'  #     Report Size (8)
        b'\x95\x01'  #     Report Count (1)
        b'\x81\x06'  #     Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
        b'\xC0'  #   End Collection
        b'\xC0'  # End Collection
        b'\x05\x0C'  # Usage Page (Consumer)
        b'\x09\x01'  # Usage (Consumer Control)
        b'\xA1\x01'  # Collection (Application)
        b'\x85\x03'  #   Report ID (3)
        b'\x75\x10'  #   Report Size (16)
        b'\x95\x01'  #   Report Count (1)
        b'\x15\x01'  #   Logical Minimum (1)
        b'\x26\x8C\x02'  #   Logical Maximum (652)
        b'\x19\x01'  #   Usage Minimum (Consumer Control)
        b'\x2A\x8C\x02'  #   Usage Maximum (AC Send)
        b'\x81\x00'  #   Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
        b'\xC0'  # End Collection
        b'\x05\x01'  # Usage Page (Generic Desktop Ctrls)
        # b'\x09\x05'        # Usage (Game Pad)
        # b'\xA1\x01'        # Collection (Application)
        # b'\x85\x05'        #   Report ID (5)
        # b'\x05\x09'        #   Usage Page (Button)
        # b'\x19\x01'        #   Usage Minimum (\x01)
        # b'\x29\x10'        #   Usage Maximum (\x10)
        # b'\x15\x00'        #   Logical Minimum (0)
        # b'\x25\x01'        #   Logical Maximum (1)
        # b'\x75\x01'        #   Report Size (1)
        # b'\x95\x10'        #   Report Count (16)
        # b'\x81\x02'        #   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
        # b'\x05\x01'        #   Usage Page (Generic Desktop Ctrls)
        # b'\x15\x81'        #   Logical Minimum (-127)
        # b'\x25\x7F'        #   Logical Maximum (127)
        # b'\x09\x30'        #   Usage (X)
        # b'\x09\x31'        #   Usage (Y)
        # b'\x09\x32'        #   Usage (Z)
        # b'\x09\x35'        #   Usage (Rz)
        # b'\x75\x08'        #   Report Size (8)
        # b'\x95\x04'        #   Report Count (4)
        # b'\x81\x02'        #   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
        # b'\xC0'            # End Collection
    )
    #pylint: enable=line-too-long

    REPORT_ID_KEYBOARD = 1
    """Keyboard device indicator, for use with `send_report()`."""
    REPORT_ID_MOUSE = 2
    """Mouse device indicator, for use with `send_report()`."""
    REPORT_ID_CONSUMER_CONTROL = 3
    """Consumer control device indicator, for use with `send_report()`."""
    REPORT_ID_GAMEPAD = 5
    """Gamepad device indicator, for use with `send_report()`."""

    _INPUT_REPORT_SIZES = {
        REPORT_ID_KEYBOARD: 8,
        REPORT_ID_MOUSE: 4,
        REPORT_ID_CONSUMER_CONTROL: 2,
        # REPORT_ID_GAMEPAD : 6,
    }

    _OUTPUT_REPORT_SIZES = {
        REPORT_ID_KEYBOARD: 1,
    }

    def __init__(self, name=None, tx_power=0):
        self._periph = Peripheral(name)

        # iOS requires Device Information Service. Android does not.
        DeviceInformationService.add_to_peripheral(
            self._periph,
            software_revision=adafruit_ble.__version__,
            manufacturer="Adafruit Industries")

        hid_service = Service.add_to_peripheral(self._periph,
                                                UUID(_HID_SERVICE_UUID_NUM))

        self._input_chars = {}
        for report_id in sorted(self._INPUT_REPORT_SIZES.keys()):
            input_char = Characteristic.add_to_service(
                hid_service,
                self._REPORT_UUID,
                properties=Characteristic.READ | Characteristic.NOTIFY,
                read_perm=Attribute.ENCRYPT_NO_MITM,
                write_perm=Attribute.NO_ACCESS,
                max_length=self._INPUT_REPORT_SIZES[report_id],
                fixed_length=True)
            Descriptor.add_to_characteristic(
                input_char,
                self._REPORT_REF_DESCR_UUID,
                read_perm=Attribute.ENCRYPT_NO_MITM,
                write_perm=Attribute.NO_ACCESS,
                initial_value=struct.pack('<BB', report_id,
                                          _REPORT_TYPE_INPUT))
            self._input_chars[report_id] = input_char

        self._output_chars = {}
        for report_id in sorted(self._OUTPUT_REPORT_SIZES.keys()):
            output_char = Characteristic.add_to_service(
                hid_service,
                self._REPORT_UUID,
                properties=(Characteristic.READ | Characteristic.WRITE
                            | Characteristic.WRITE_NO_RESPONSE),
                read_perm=Attribute.ENCRYPT_NO_MITM,
                write_perm=Attribute.ENCRYPT_NO_MITM,
                max_length=self._OUTPUT_REPORT_SIZES[report_id],
                fixed_length=True)
            Descriptor.add_to_characteristic(
                output_char,
                self._REPORT_REF_DESCR_UUID,
                read_perm=Attribute.ENCRYPT_NO_MITM,
                write_perm=Attribute.NO_ACCESS,
                initial_value=struct.pack('<BB', report_id,
                                          _REPORT_TYPE_OUTPUT))
            self._output_chars[report_id] = output_char

        # Protocol mode: boot or report. Make it read-only for now because
        # it can't be changed

        Characteristic.add_to_service(hid_service,
                                      UUID(_PROTOCOL_MODE_UUID_NUM),
                                      properties=Characteristic.READ
                                      | Characteristic.WRITE_NO_RESPONSE,
                                      read_perm=Attribute.OPEN,
                                      write_perm=Attribute.OPEN,
                                      max_length=1,
                                      fixed_length=True,
                                      initial_value=_PROTOCOL_MODE_REPORT)

        Characteristic.add_to_service(
            hid_service,
            UUID(_BOOT_KEYBOARD_INPUT_REPORT_UUID_NUM),
            properties=Characteristic.READ | Characteristic.NOTIFY,
            read_perm=Attribute.ENCRYPT_NO_MITM,
            write_perm=Attribute.NO_ACCESS,
            max_length=self._INPUT_REPORT_SIZES[self.REPORT_ID_KEYBOARD],
            fixed_length=True)

        Characteristic.add_to_service(
            hid_service,
            UUID(_BOOT_KEYBOARD_OUTPUT_REPORT_UUID_NUM),
            properties=(Characteristic.READ | Characteristic.WRITE
                        | Characteristic.WRITE_NO_RESPONSE),
            read_perm=Attribute.ENCRYPT_NO_MITM,
            write_perm=Attribute.ENCRYPT_NO_MITM,
            max_length=self._OUTPUT_REPORT_SIZES[self.REPORT_ID_KEYBOARD],
            fixed_length=True)

        # This is the USB HID descriptor (not to be confused with a BLE Descriptor).
        Characteristic.add_to_service(hid_service,
                                      UUID(_REPORT_MAP_UUID_NUM),
                                      properties=Characteristic.READ,
                                      read_perm=Attribute.ENCRYPT_NO_MITM,
                                      write_perm=Attribute.NO_ACCESS,
                                      max_length=len(self.HID_DESCRIPTOR),
                                      fixed_length=True,
                                      initial_value=self.HID_DESCRIPTOR)

        # bcdHID (version), bCountryCode (0 not localized), Flags: RemoteWake, NormallyConnectable
        # bcd1.1, country = 0, flag = normal connect
        Characteristic.add_to_service(hid_service,
                                      UUID(_HID_INFORMATION_UUID_NUM),
                                      properties=Characteristic.READ,
                                      read_perm=Attribute.ENCRYPT_NO_MITM,
                                      write_perm=Attribute.NO_ACCESS,
                                      initial_value=b'\x01\x01\x00\x02')

        # 0 = suspend; 1 = exit suspend
        Characteristic.add_to_service(
            hid_service,
            UUID(_HID_CONTROL_POINT_UUID_NUM),
            properties=Characteristic.WRITE_NO_RESPONSE,
            read_perm=Attribute.NO_ACCESS,
            write_perm=Attribute.ENCRYPT_NO_MITM)

        self._advertisement = ServerAdvertisement(
            self._periph,
            tx_power=tx_power,
            appearance=_APPEARANCE_HID_KEYBOARD)

    def start_advertising(self):
        """Start advertising the service. When a client connects, advertising will stop.
        When the client disconnects, restart advertising by calling ``start_advertising()`` again.
        """
        self._periph.start_advertising(
            self._advertisement.advertising_data_bytes,
            scan_response=self._advertisement.scan_response_bytes)

    def stop_advertising(self):
        """Stop advertising the service."""
        self._periph.stop_advertising()

    @property
    def connected(self):
        """True if someone connected to the server."""
        return self._periph.connected

    def disconnect(self):
        """Disconnect from peer."""
        self._periph.disconnect()

    def _check_connected(self):
        if not self.connected:
            raise OSError("Not connected")

    def send_report(self, report_id, report):
        """Send a report to the specified device"""
        try:
            self._input_chars[report_id].value = report
        except OSError:
            self._check_connected()
            raise
    def __init__(self, name=None, tx_power=0):
        self._periph = Peripheral(name)

        # iOS requires Device Information Service. Android does not.
        DeviceInformationService.add_to_peripheral(
            self._periph,
            software_revision=adafruit_ble.__version__,
            manufacturer="Adafruit Industries")

        hid_service = Service.add_to_peripheral(self._periph,
                                                UUID(_HID_SERVICE_UUID_NUM))

        self._input_chars = {}
        for report_id in sorted(self._INPUT_REPORT_SIZES.keys()):
            input_char = Characteristic.add_to_service(
                hid_service,
                self._REPORT_UUID,
                properties=Characteristic.READ | Characteristic.NOTIFY,
                read_perm=Attribute.ENCRYPT_NO_MITM,
                write_perm=Attribute.NO_ACCESS,
                max_length=self._INPUT_REPORT_SIZES[report_id],
                fixed_length=True)
            Descriptor.add_to_characteristic(
                input_char,
                self._REPORT_REF_DESCR_UUID,
                read_perm=Attribute.ENCRYPT_NO_MITM,
                write_perm=Attribute.NO_ACCESS,
                initial_value=struct.pack('<BB', report_id,
                                          _REPORT_TYPE_INPUT))
            self._input_chars[report_id] = input_char

        self._output_chars = {}
        for report_id in sorted(self._OUTPUT_REPORT_SIZES.keys()):
            output_char = Characteristic.add_to_service(
                hid_service,
                self._REPORT_UUID,
                properties=(Characteristic.READ | Characteristic.WRITE
                            | Characteristic.WRITE_NO_RESPONSE),
                read_perm=Attribute.ENCRYPT_NO_MITM,
                write_perm=Attribute.ENCRYPT_NO_MITM,
                max_length=self._OUTPUT_REPORT_SIZES[report_id],
                fixed_length=True)
            Descriptor.add_to_characteristic(
                output_char,
                self._REPORT_REF_DESCR_UUID,
                read_perm=Attribute.ENCRYPT_NO_MITM,
                write_perm=Attribute.NO_ACCESS,
                initial_value=struct.pack('<BB', report_id,
                                          _REPORT_TYPE_OUTPUT))
            self._output_chars[report_id] = output_char

        # Protocol mode: boot or report. Make it read-only for now because
        # it can't be changed

        Characteristic.add_to_service(hid_service,
                                      UUID(_PROTOCOL_MODE_UUID_NUM),
                                      properties=Characteristic.READ
                                      | Characteristic.WRITE_NO_RESPONSE,
                                      read_perm=Attribute.OPEN,
                                      write_perm=Attribute.OPEN,
                                      max_length=1,
                                      fixed_length=True,
                                      initial_value=_PROTOCOL_MODE_REPORT)

        Characteristic.add_to_service(
            hid_service,
            UUID(_BOOT_KEYBOARD_INPUT_REPORT_UUID_NUM),
            properties=Characteristic.READ | Characteristic.NOTIFY,
            read_perm=Attribute.ENCRYPT_NO_MITM,
            write_perm=Attribute.NO_ACCESS,
            max_length=self._INPUT_REPORT_SIZES[self.REPORT_ID_KEYBOARD],
            fixed_length=True)

        Characteristic.add_to_service(
            hid_service,
            UUID(_BOOT_KEYBOARD_OUTPUT_REPORT_UUID_NUM),
            properties=(Characteristic.READ | Characteristic.WRITE
                        | Characteristic.WRITE_NO_RESPONSE),
            read_perm=Attribute.ENCRYPT_NO_MITM,
            write_perm=Attribute.ENCRYPT_NO_MITM,
            max_length=self._OUTPUT_REPORT_SIZES[self.REPORT_ID_KEYBOARD],
            fixed_length=True)

        # This is the USB HID descriptor (not to be confused with a BLE Descriptor).
        Characteristic.add_to_service(hid_service,
                                      UUID(_REPORT_MAP_UUID_NUM),
                                      properties=Characteristic.READ,
                                      read_perm=Attribute.ENCRYPT_NO_MITM,
                                      write_perm=Attribute.NO_ACCESS,
                                      max_length=len(self.HID_DESCRIPTOR),
                                      fixed_length=True,
                                      initial_value=self.HID_DESCRIPTOR)

        # bcdHID (version), bCountryCode (0 not localized), Flags: RemoteWake, NormallyConnectable
        # bcd1.1, country = 0, flag = normal connect
        Characteristic.add_to_service(hid_service,
                                      UUID(_HID_INFORMATION_UUID_NUM),
                                      properties=Characteristic.READ,
                                      read_perm=Attribute.ENCRYPT_NO_MITM,
                                      write_perm=Attribute.NO_ACCESS,
                                      initial_value=b'\x01\x01\x00\x02')

        # 0 = suspend; 1 = exit suspend
        Characteristic.add_to_service(
            hid_service,
            UUID(_HID_CONTROL_POINT_UUID_NUM),
            properties=Characteristic.WRITE_NO_RESPONSE,
            read_perm=Attribute.NO_ACCESS,
            write_perm=Attribute.ENCRYPT_NO_MITM)

        self._advertisement = ServerAdvertisement(
            self._periph,
            tx_power=tx_power,
            appearance=_APPEARANCE_HID_KEYBOARD)
Beispiel #5
0
    def add_to_peripheral(peripheral, *, model_number=None, serial_number=None,
                          firmware_revision=None, hardware_revision='',
                          software_revision='', manufacturer=''):
        """
        Add a Service with fixed Device Information Service characteristics to the given Peripheral.
        All values are optional.

          :param str model_number: Device model number. If None use `sys.platform`.
          :param str serial_number: Device serial number. If None use a hex representation of
             ``microcontroller.cpu.id``.
          :param str firmware_revision: Device firmware revision.
             If None use ``os.uname().version``.
          :param str hardware_revision: Device hardware revision.
          :param str software_revision: Device software revision.
          :param str manufacturer: Device manufacturer name
          :return: the created Service

        Example::

            peripheral = Peripheral()
            dis = DeviceInformationService.add_to_peripheral(
                peripheral, software_revision="1.2.4", manufacturer="Acme Corp")
        """

        # Avoid creating constants with names if not necessary. Just takes up space.
        # Device Information Service UUID = 0x180A
        # Module Number UUID = 0x2A24
        # Serial Number UUID = 0x2A25
        # Firmware Revision UUID = 0x2A26
        # Hardware Revision UUID = 0x2A27
        # Software Revision UUID = 0x2A28
        # Manufacturer Name UUID = 0x2A29

        service = Service.add_to_peripheral(peripheral, UUID(0x180A))

        if model_number is None:
            import sys
            model_number = sys.platform
        if serial_number is None:
            import microcontroller
            import binascii
            serial_number = binascii.hexlify(microcontroller.cpu.uid).decode('utf-8') # pylint: disable=no-member

        if firmware_revision is None:
            import os
            firmware_revision = os.uname().version

        # Values must correspond to UUID numbers.
        for uuid_num, value in zip(
                range(0x2A24, 0x2A29+1),
                (model_number, serial_number,
                 firmware_revision, hardware_revision, software_revision,
                 manufacturer)):

            Characteristic.add_to_service(
                service, UUID(uuid_num), properties=Characteristic.READ,
                read_perm=Attribute.OPEN, write_perm=Attribute.NO_ACCESS,
                fixed_length=True, max_length=len(value),
                initial_value=value)

        return service
Beispiel #6
0
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
"""
`adafruit_ble.uart`
====================================================

BLE UART-style communication. Common definitions.

* Author(s): Dan Halbert for Adafruit Industries

"""
from _bleio import UUID

NUS_SERVICE_UUID = UUID("6E400001-B5A3-F393-E0A9-E50E24DCCA9E")
"""Nordic UART Service UUID"""
NUS_RX_CHAR_UUID = UUID("6E400002-B5A3-F393-E0A9-E50E24DCCA9E")
"""Nordic UART Service RX Characteristic UUID"""
NUS_TX_CHAR_UUID = UUID("6E400003-B5A3-F393-E0A9-E50E24DCCA9E")
"""Nordic UART Service TX Characteristic UUID"""
Beispiel #7
0
class CurrentTimeClient:
    """
    Set up a peripheral that solicits centrals for Current Time Service.

    :param str name: Name to advertise for server. If None, use default Advertisement name.

    Example::

        from adafruit_ble.current_time_client import CurrentTimeClient
        import time

        cts_client = CurrentTimeClient()
        cts_client.start_advertising()
        while not cts_client.connected:
            pass
        # The first time a property is read, the client
        # will do discovery and pairing.
        while True:
            print(cts_client.current_time)
            time.sleep(5)

    To try the example above, open Settings->Bluetooth on your iOS device.
    After the program starts advertising, ``CIRCUITPYxxxx` will show up as a Bluetooth
    device for possible connection. Tap it, and then accept the pairing request.
    Then the time should print.
    """

    CTS_UUID = UUID(0x1805)
    CURRENT_TIME_UUID = UUID(0x2A2B)
    LOCAL_TIME_INFORMATION_UUID = UUID(0x2A0F)

    def __init__(self, name=None, tx_power=0):
        self._periph = Peripheral(name)
        self._advertisement = SolicitationAdvertisement(self._periph.name,
                                                        (self.CTS_UUID,), tx_power=tx_power)
        self._current_time_char = self._local_time_char = None


    def start_advertising(self):
        """Start advertising to solicit a central that supports Current Time Service."""
        self._periph.start_advertising(self._advertisement.advertising_data_bytes,
                                       scan_response=self._advertisement.scan_response_bytes)

    def stop_advertising(self):
        """Stop advertising the service."""
        self._periph.stop_advertising()

    @property
    def connected(self):
        """True if a central connected to this peripheral."""
        return self._periph.connected

    def disconnect(self):
        """Disconnect from central."""
        self._periph.disconnect()

    def _check_connected(self):
        if not self.connected:
            raise OSError("Not connected")
        # Do discovery and pairing if not already done.
        if not self._current_time_char:
            self._discover()
            self._periph.pair()

    def _discover(self):
        """Discover service information."""
        services = self._periph.discover_remote_services((self.CTS_UUID,))
        if not services:
            raise OSError("Unable to discover service")
        for characteristic in services[0].characteristics:
            if characteristic.uuid == self.CURRENT_TIME_UUID:
                self._current_time_char = characteristic
            elif characteristic.uuid == self.LOCAL_TIME_INFORMATION_UUID:
                self._local_time_char = characteristic
        if not self._current_time_char or not self._local_time_char:
            raise OSError("Remote service missing needed characteristic")

    @property
    def current_time(self):
        """Get a tuple describing the current time from the server:
        (year, month, day, hour, minute, second, weekday, subsecond, adjust_reason)
        """
        self._check_connected()
        if self._current_time_char:
            # year, month, day, hour, minute, second, weekday, subsecond, adjust_reason
            values = struct.unpack('<HBBBBBBBB', self._current_time_char.value)
            return values
        else:
            raise OSError("Characteristic not discovered")


    @property
    def local_time_information(self):
        """Get a tuple of location information from the server:
        (timezone, dst_offset)
        """
        self._check_connected()
        if self._local_time_char:
            # timezone, dst_offset
            values = struct.unpack('<bB', self._local_time_char.value)
            return values
        else:
            raise OSError("Characteristic not discovered")

    @property
    def struct_time(self):
        """Return the current time as a `time.struct_time` Day of year and whether DST is in effect
        is not available from Current Time Service, so these are set to -1.
        """
        _, month, day, hour, minute, second, weekday, _, _ = self.current_time
        # Bluetooth weekdays count from 1. struct_time counts from 0.
        return time.struct_time((month, day, hour, minute, second, weekday - 1, -1))