# 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 # ----------------------------------------------------------------------------- from enum import IntEnum import struct from ..gatt_client import ProfileServiceProxy from ..att import ATT_Error from ..gatt import ( GATT_HEART_RATE_SERVICE, GATT_HEART_RATE_MEASUREMENT_CHARACTERISTIC, GATT_BODY_SENSOR_LOCATION_CHARACTERISTIC, GATT_HEART_RATE_CONTROL_POINT_CHARACTERISTIC, TemplateService, Characteristic, CharacteristicValue, DelegatedCharacteristicAdapter, PackedCharacteristicAdapter, ) # ----------------------------------------------------------------------------- class HeartRateService(TemplateService): UUID = GATT_HEART_RATE_SERVICE HEART_RATE_CONTROL_POINT_FORMAT = 'B' CONTROL_POINT_NOT_SUPPORTED = 0x80 RESET_ENERGY_EXPENDED = 0x01 class BodySensorLocation(IntEnum): OTHER = 0 CHEST = 1 WRIST = 2 FINGER = 3 HAND = 4 EAR_LOBE = 5 FOOT = 6 class HeartRateMeasurement: def __init__( self, heart_rate, sensor_contact_detected=None, energy_expended=None, rr_intervals=None, ): if heart_rate < 0 or heart_rate > 0xFFFF: raise ValueError('heart_rate out of range') if energy_expended is not None and ( energy_expended < 0 or energy_expended > 0xFFFF ): raise ValueError('energy_expended out of range') if rr_intervals: for rr_interval in rr_intervals: if rr_interval < 0 or rr_interval * 1024 > 0xFFFF: raise ValueError('rr_intervals out of range') self.heart_rate = heart_rate self.sensor_contact_detected = sensor_contact_detected self.energy_expended = energy_expended self.rr_intervals = rr_intervals @classmethod def from_bytes(cls, data): flags = data[0] offset = 1 if flags & 1: hr = struct.unpack_from('