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)
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
# 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"""
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))