# Copyright 2021-2022 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ----------------------------------------------------------------------------- # Imports # ----------------------------------------------------------------------------- import struct from typing import Optional from bumble.gatt import ( GATT_DEVICE_INFORMATION_SERVICE, GATT_FIRMWARE_REVISION_STRING_CHARACTERISTIC, GATT_HARDWARE_REVISION_STRING_CHARACTERISTIC, GATT_MANUFACTURER_NAME_STRING_CHARACTERISTIC, GATT_MODEL_NUMBER_STRING_CHARACTERISTIC, GATT_SERIAL_NUMBER_STRING_CHARACTERISTIC, GATT_SOFTWARE_REVISION_STRING_CHARACTERISTIC, GATT_SYSTEM_ID_CHARACTERISTIC, GATT_REGULATORY_CERTIFICATION_DATA_LIST_CHARACTERISTIC, TemplateService, Characteristic, ) from bumble.gatt_adapters import ( DelegatedCharacteristicProxyAdapter, UTF8CharacteristicProxyAdapter, ) from bumble.gatt_client import CharacteristicProxy, ProfileServiceProxy, ServiceProxy # ----------------------------------------------------------------------------- class DeviceInformationService(TemplateService): UUID = GATT_DEVICE_INFORMATION_SERVICE @staticmethod def pack_system_id(oui, manufacturer_id): return struct.pack('> 40, system_id & 0xFFFFFFFFFF) def __init__( self, manufacturer_name: Optional[str] = None, model_number: Optional[str] = None, serial_number: Optional[str] = None, hardware_revision: Optional[str] = None, firmware_revision: Optional[str] = None, software_revision: Optional[str] = None, system_id: Optional[tuple[int, int]] = None, # (OUI, Manufacturer ID) ieee_regulatory_certification_data_list: Optional[bytes] = None, # TODO: pnp_id ): characteristics: list[Characteristic[bytes]] = [ Characteristic( uuid, Characteristic.Properties.READ, Characteristic.READABLE, bytes(field, 'utf-8'), ) for (field, uuid) in ( (manufacturer_name, GATT_MANUFACTURER_NAME_STRING_CHARACTERISTIC), (model_number, GATT_MODEL_NUMBER_STRING_CHARACTERISTIC), (serial_number, GATT_SERIAL_NUMBER_STRING_CHARACTERISTIC), (hardware_revision, GATT_HARDWARE_REVISION_STRING_CHARACTERISTIC), (firmware_revision, GATT_FIRMWARE_REVISION_STRING_CHARACTERISTIC), (software_revision, GATT_SOFTWARE_REVISION_STRING_CHARACTERISTIC), ) if field is not None ] if system_id is not None: characteristics.append( Characteristic( GATT_SYSTEM_ID_CHARACTERISTIC, Characteristic.Properties.READ, Characteristic.READABLE, self.pack_system_id(*system_id), ) ) if ieee_regulatory_certification_data_list is not None: characteristics.append( Characteristic( GATT_REGULATORY_CERTIFICATION_DATA_LIST_CHARACTERISTIC, Characteristic.Properties.READ, Characteristic.READABLE, ieee_regulatory_certification_data_list, ) ) super().__init__(characteristics) # ----------------------------------------------------------------------------- class DeviceInformationServiceProxy(ProfileServiceProxy): SERVICE_CLASS = DeviceInformationService manufacturer_name: Optional[CharacteristicProxy[str]] model_number: Optional[CharacteristicProxy[str]] serial_number: Optional[CharacteristicProxy[str]] hardware_revision: Optional[CharacteristicProxy[str]] firmware_revision: Optional[CharacteristicProxy[str]] software_revision: Optional[CharacteristicProxy[str]] system_id: Optional[CharacteristicProxy[tuple[int, int]]] ieee_regulatory_certification_data_list: Optional[CharacteristicProxy[bytes]] def __init__(self, service_proxy: ServiceProxy): self.service_proxy = service_proxy for field, uuid in ( ('manufacturer_name', GATT_MANUFACTURER_NAME_STRING_CHARACTERISTIC), ('model_number', GATT_MODEL_NUMBER_STRING_CHARACTERISTIC), ('serial_number', GATT_SERIAL_NUMBER_STRING_CHARACTERISTIC), ('hardware_revision', GATT_HARDWARE_REVISION_STRING_CHARACTERISTIC), ('firmware_revision', GATT_FIRMWARE_REVISION_STRING_CHARACTERISTIC), ('software_revision', GATT_SOFTWARE_REVISION_STRING_CHARACTERISTIC), ): if characteristics := service_proxy.get_characteristics_by_uuid(uuid): characteristic = UTF8CharacteristicProxyAdapter(characteristics[0]) else: characteristic = None self.__setattr__(field, characteristic) if characteristics := service_proxy.get_characteristics_by_uuid( GATT_SYSTEM_ID_CHARACTERISTIC ): self.system_id = DelegatedCharacteristicProxyAdapter( characteristics[0], encode=lambda v: DeviceInformationService.pack_system_id(*v), decode=DeviceInformationService.unpack_system_id, ) else: self.system_id = None if characteristics := service_proxy.get_characteristics_by_uuid( GATT_REGULATORY_CERTIFICATION_DATA_LIST_CHARACTERISTIC ): self.ieee_regulatory_certification_data_list = characteristics[0] else: self.ieee_regulatory_certification_data_list = None