def __init__(self, data: bytes): """Init.""" try: self._address = data[3:9] self.mac = reverse_mac(self._address) self.rssi = rssi_from_byte(data[-1]) self.raw_data = data[10:-1] self.flags = 6 self.name = None self.packet = None self.temperature = None self.humidity = None self.battery = None pos = 10 while pos < len(data) - 1: _LOGGER.debug("POS={}".format(pos)) length = data[pos] payload_offset = pos + 2 gap_type = data[pos + 1] payload_end = payload_offset + length - 1 payload = data[payload_offset:payload_end] _LOGGER.debug("Pos={} Type=0x{:02x} Len={} Payload={}".format( pos, gap_type, length, hex_string(payload))) if GAP_FLAGS == gap_type: self.flags = payload[0] _LOGGER.debug("Flags={:02x}".format(self.flags)) elif GAP_NAME_COMPLETE == gap_type: self.name = str(payload) _LOGGER.debug("Complete Name={}".format(self.name)) elif GAP_MFG_DATA == gap_type: # unit8 self.mfg_data = payload msg = "Manufacturer Data={}" _LOGGER.debug(msg.format(str(self.mfg_data))) pos += length + 1 if self.check_is_gvh5075_gvh5072(): mfg_data_5075 = hex_string(self.mfg_data[3:6]).replace(" ", "") self.packet = int(mfg_data_5075, 16) self.temperature = float(self.packet / 10000) self.humidity = float((self.packet % 1000) / 10) self.battery = int(self.mfg_data[6]) elif self.check_is_gvh5074(): mfg_data_5074 = hex_string(self.mfg_data[3:7]).replace(" ", "") temp_lsb = mfg_data_5074[2:4] + mfg_data_5074[0:2] hum_lsb = mfg_data_5074[6:8] + mfg_data_5074[4:6] self.packet = temp_lsb + hum_lsb self.humidity = float(int(hum_lsb, 16) / 100) # Negative temperature stored an two's complement temp_lsb_int = int(temp_lsb, 16) self.temperature = float(twos_complement(temp_lsb_int) / 100) self.battery = int(self.mfg_data[7]) except (ValueError, IndexError): pass
def __init__(self, data: bytes): """ init from ble advertising data preamble: 3 Access Address: 6 GAP Packet: N GAP Packet[1]: N GAP_Packet[2]: N GAP Packet: length: 1 (length of packet not including the length byte) type: 1 Payload: N """ self.rssi = rssi_from_byte(data[-1]) self.mac = BDAddress(data[3:9]) self.name = None self._raw_mfg_data = None gap_data = data[10:-1] # trim the data to just the ad data offset = 0 # parse the GAP reports in a loop while offset < len(gap_data): length = gap_data[offset] # process gap data starting with type byte (first byte after size) self._process_gap(gap_data[offset + 1:offset + 1 + length]) offset += 1 + length if len(gap_data) < 1: # catch packets that have no GAP data as # the mopeka sensor does send these and they # should not be considered an error. raise NoGapDataException("No GAP data") if self._raw_mfg_data is None: # Make sure we found the required MFG_DATA for Mopeka Sensor raise Exception("Incomplete Sensor Data")
def __init__(self, data: bytes): """Init.""" try: self._address = data[3:9] self.mac = reverse_mac(self._address) self.rssi = rssi_from_byte(data[-1]) self.raw_data = data[10:-1] self.flags = 6 self.name = None self.packet = None self.temperature = None self.humidity = None self.battery = None self.model = None pos = 10 while pos < len(data) - 1: _LOGGER.debug("POS={}".format(pos)) length = data[pos] payload_offset = pos + 2 gap_type = data[pos + 1] payload_end = payload_offset + length - 1 payload = data[payload_offset:payload_end] _LOGGER.debug( "Pos={} Type=0x{:02x} Len={} Payload={}".format( pos, gap_type, length, hex_string(payload) ) ) if GAP_FLAGS == gap_type: self.flags = payload[0] _LOGGER.debug("Flags={:02x}".format(self.flags)) elif GAP_NAME_COMPLETE == gap_type: self.name = payload.decode("ascii") _LOGGER.debug("Complete Name={}".format(self.name)) elif GAP_MFG_DATA == gap_type: # unit8 self.mfg_data = payload msg = "Manufacturer Data={}" _LOGGER.debug(msg.format(str(self.mfg_data))) pos += length + 1 if self.check_is_gvh5075_gvh5072(): mfg_data_5075 = hex_string(self.mfg_data[3:6]).replace(" ", "") self.packet = int(mfg_data_5075, 16) self.temperature = decode_temps(self.packet) self.humidity = float((self.packet % 1000) / 10) self.battery = int(self.mfg_data[6]) self.model = "Govee H5072/H5075" elif self.check_is_gvh5102(): mfg_data_5075 = hex_string(self.mfg_data[4:7]).replace(" ", "") self.packet = int(mfg_data_5075, 16) self.temperature = decode_temps(self.packet) self.humidity = float((self.packet % 1000) / 10) self.battery = int(self.mfg_data[7]) self.model = "Govee H5101/H5102" elif self.check_is_gvh5179(): temp, hum, batt = unpack_from("<HHB", self.mfg_data, 6) self.packet = hex(temp)[2:] + hex(hum)[2:] # Negative temperature stored an two's complement self.temperature = float(twos_complement(temp) / 100.0) self.humidity = float(hum / 100.0) self.battery = int(batt) self.model = "Govee H5179" elif self.check_is_gvh5074() or self.check_is_gvh5051(): temp, hum, batt = unpack_from("<HHB", self.mfg_data, 3) self.packet = hex(temp)[2:] + hex(hum)[2:] # Negative temperature stored an two's complement self.temperature = float(twos_complement(temp) / 100.0) self.humidity = float(hum / 100.0) self.battery = int(batt) self.model = "Govee H5074/H5051" except (ValueError, IndexError): pass
def __init__(self, data: bytes, brand: DeviceBrand): """Init.""" try: # Byte 0: Num reports to follow (we only expect 01) # Byte 1: GAP (Generic Access Profile) ADV type (can be 00=ADV_IND or # 04=SCAN_RSP) # Byte 2: GAP Addr type (Govees send 00=LE_PUBLIC_ADDRESS, Moats send # 01=LE_RANDOM_ADDRESS) # Bytes 3-8: MAC address (reverse byte order) self.mac = reverse_mac(data[3:9]) # Byte 9: Length (not counting header or final RSSI byte, so should be 11 # less than len(data)). # Bytes 10-[len(data)-1]: List of GAP Data # Byte 0: GAP Data length # Byte 1: GAP Data type # Bytes 2-[(GAP Data length)-2]: GAP Data payload # Byte [len(data)-1]: Signal Power (RSSI) self.rssi = rssi_from_byte(data[-1]) self.flags = 6 self.name = None self.packet = None self.temperature = None self.humidity = None self.battery = None self.battery_millivolts = None self.model = None pos = 10 while pos < len(data) - 1: # Byte 0: GAP Data length length = data[pos] # Byte 1: GAP Data Type gap_type = data[pos + 1] # Bytes 2-[GAP length - 2]: GAP Data payload payload_offset = pos + 2 payload_end = payload_offset + length - 1 payload = data[payload_offset:payload_end] _LOGGER.debug( "Pos=%d Type=%02x Len=%d Payload=%s", pos, gap_type, length, hex_string(payload), ) if GAP_FLAGS == gap_type: self.flags = payload[0] _LOGGER.debug("Flags=%02x", self.flags) elif GAP_NAME_COMPLETE == gap_type: self.name = payload.decode("ascii") _LOGGER.debug("Complete Name=%s", self.name) elif GAP_SERVICE_DATA == gap_type: self.mfg_data = payload _LOGGER.debug("Service Data=%s", hex_string(self.mfg_data)) elif GAP_MFG_DATA == gap_type: self.mfg_data = payload _LOGGER.debug("Manufacturer Data=%s", hex_string(self.mfg_data)) pos += length + 1 # Not all advertisements contain the measurement data. if (brand is DeviceBrand.MOAT) and self.check_is_moat_s2(): # Packet format (including temperature and humidity conversions) were # kindly provided by the Moat developer, Erik Laybourne. # timestamp = little_endian_to_unsigned_int(self.mfg_data[8:12]) self.temperature = -46.85 + 175.72 * ( little_endian_to_unsigned_int(self.mfg_data[12:14]) / 65536.0 ) self.humidity = -6.0 + 125.0 * ( little_endian_to_unsigned_int(self.mfg_data[14:16]) / 65536.0 ) self.battery_millivolts = little_endian_to_unsigned_int( self.mfg_data[16:18] ) self.battery = int( moat_s2_battery_voltage_to_percentage(self.battery_millivolts) ) self.packet = hex_string(self.mfg_data[8:18]).replace(" ", "") self.model = "Moat S2" elif brand is DeviceBrand.GOVEE: if self.check_is_gvh5075_gvh5072(): mfg_data_5075 = hex_string(self.mfg_data[3:6]).replace(" ", "") self.packet = int(mfg_data_5075, 16) self.temperature = decode_govee_temp(self.packet) self.humidity = (self.packet % 1000) / 10.0 self.battery = int(self.mfg_data[6]) self.model = "Govee H5072/H5075" elif self.check_is_gvh5102(): mfg_data_5075 = hex_string(self.mfg_data[4:7]).replace(" ", "") self.packet = int(mfg_data_5075, 16) self.temperature = decode_govee_temp(self.packet) self.humidity = (self.packet % 1000) / 10.0 self.battery = int(self.mfg_data[7]) self.model = "Govee H5101/H5102" else: is_5074_or_5051 = False if self.check_is_gvh5074(): self.model = "Govee H5074" is_5074_or_5051 = True elif self.check_is_gvh5051(): self.model = "Govee H5051" is_5074_or_5051 = True if is_5074_or_5051: mfg_data_5074 = hex_string(self.mfg_data[3:7]).replace(" ", "") temp_lsb = mfg_data_5074[2:4] + mfg_data_5074[0:2] hum_lsb = mfg_data_5074[6:8] + mfg_data_5074[4:6] self.packet = temp_lsb + hum_lsb self.humidity = float(int(hum_lsb, 16) / 100) # Negative temperature stored an two's complement temp_lsb_int = int(temp_lsb, 16) self.temperature = float(twos_complement(temp_lsb_int) / 100) self.battery = int(self.mfg_data[7]) else: return _LOGGER.debug( "Read=%s %f %f %d (%s) %r (%s)", self.mac, self.temperature, self.humidity, self.battery, self.packet, self.rssi, self.model, ) except (ValueError, IndexError): pass
def __init__(self, data: bytes): """Init.""" try: self._address = data[3:9] self.mac = reverse_mac(self._address) self.rssi = rssi_from_byte(data[-1]) self.raw_data = data[10:-1] self.flags = 6 self.name = None self.packet = None self.temperature = None self.humidity = None self.battery = None self.model = None pos = 10 while pos < len(data) - 1: _LOGGER.debug("POS={}".format(pos)) length = data[pos] payload_offset = pos + 2 gap_type = data[pos + 1] payload_end = payload_offset + length - 1 payload = data[payload_offset:payload_end] _LOGGER.debug("Pos={} Type=0x{:02x} Len={} Payload={}".format( pos, gap_type, length, hex_string(payload))) if GAP_FLAGS == gap_type: self.flags = payload[0] _LOGGER.debug("Flags={:02x}".format(self.flags)) elif GAP_NAME_COMPLETE == gap_type: self.name = payload.decode("ascii") _LOGGER.debug("Complete Name={}".format(self.name)) elif GAP_MFG_DATA == gap_type: # unit8 self.mfg_data = payload msg = "Manufacturer Data={}" _LOGGER.debug(msg.format(str(self.mfg_data))) pos += length + 1 if self.check_is_gvh5075_gvh5072(): mfg_data_5075 = hex_string(self.mfg_data[3:6]).replace(" ", "") self.packet = int(mfg_data_5075, 16) self.temperature = decode_temps(self.packet) self.humidity = float((self.packet % 1000) / 10) self.battery = int(self.mfg_data[6]) self.model = "Govee H5072/H5075" elif self.check_is_gvh5102(): mfg_data_5075 = hex_string(self.mfg_data[4:7]).replace(" ", "") self.packet = int(mfg_data_5075, 16) self.temperature = decode_temps(self.packet) self.humidity = float((self.packet % 1000) / 10) self.battery = int(self.mfg_data[7]) self.model = "Govee H5101/H5102" elif self.check_is_gvh5179(): temp, hum, batt = unpack_from("<HHB", self.mfg_data, 6) self.packet = hex(temp)[2:] + hex(hum)[2:] # Negative temperature stored an two's complement self.temperature = float(twos_complement(temp) / 100.0) self.humidity = float(hum / 100.0) self.battery = int(batt) self.model = "Govee H5179" elif self.check_is_gvh5074() or self.check_is_gvh5051(): temp, hum, batt = unpack_from("<HHB", self.mfg_data, 3) self.packet = hex(temp)[2:] + hex(hum)[2:] # Negative temperature stored an two's complement self.temperature = float(twos_complement(temp) / 100.0) self.humidity = float(hum / 100.0) self.battery = int(batt) self.model = "Govee H5074/H5051" elif self.check_is_ThermoBeacon(): # .. .. .. ID ID ID ID ID ID ID ID ID .. .. TEMPE HUMID .. .. .. .. # 15 ff 11 00 00 00 fe 01 00 00 e1 00 3b 0c 6e 01 a0 03 e2 30 01 00 bc self.name = "Thermobeacon" self.packet = hex_string(self.mfg_data) # Temperature (-20 to +65) # Temperate hex stored reversed. Multiply second byte by 256 then add to first # Negative temperature stored an two's complement temp_temp = twos_complement( (int(self.mfg_data[12]) + (int(self.mfg_data[13]) * 256))) # Data is actual value multiplied by 16 self.temperature = float(temp_temp / 16) # Humidity # Humidity hex stored reversed. Multiply second byte by 256 then add to first # Data is actual value multiplied by 16 self.humidity = float((int(self.mfg_data[14]) + (int(self.mfg_data[15]) * 256)) / 16) # Not identified battery data self.battery = 100 self.model = "ThermoBeacon" except (ValueError, IndexError): pass