diff --git a/custom_components/ble_monitor/ble_parser/__init__.py b/custom_components/ble_monitor/ble_parser/__init__.py index b5b89b6a1..58735af9f 100644 --- a/custom_components/ble_monitor/ble_parser/__init__.py +++ b/custom_components/ble_monitor/ble_parser/__init__.py @@ -289,6 +289,13 @@ def parse_advertisement( # Jaalee sensor_data = parse_jaalee(self, man_spec_data, mac, rssi) break + elif len(man_spec_data_list) == 2: + # Teltonika Eye (can send both iBeacon and Teltonika data in one message) + second_man_spec_data = man_spec_data_list[1] + second_comp_id = (second_man_spec_data[3] << 8) | second_man_spec_data[2] + if second_comp_id == 0x089A: + sensor_data = parse_teltonika(self, second_man_spec_data, local_name, mac, rssi) + break else: # iBeacon sensor_data, tracker_data = parse_ibeacon(self, man_spec_data, mac, rssi) @@ -325,6 +332,10 @@ def parse_advertisement( man_spec_data = b"".join(man_spec_data_list) sensor_data = parse_hormann(self, man_spec_data, mac, rssi) break + elif comp_id == 0x089A: + # Teltonika + sensor_data = parse_teltonika(self, man_spec_data, local_name, mac, rssi) + break elif comp_id == 0x094F and data_len == 0x15: # Mikrotik sensor_data = parse_mikrotik(self, man_spec_data, mac, rssi) diff --git a/custom_components/ble_monitor/ble_parser/teltonika.py b/custom_components/ble_monitor/ble_parser/teltonika.py index a990214bc..24808545b 100644 --- a/custom_components/ble_monitor/ble_parser/teltonika.py +++ b/custom_components/ble_monitor/ble_parser/teltonika.py @@ -11,8 +11,11 @@ def parse_teltonika(self, data, complete_local_name, source_mac, rssi): """Teltonika parser""" result = {"firmware": "Teltonika"} teltonika_mac = source_mac + device_id = (data[3] << 8) | data[2] - if complete_local_name == "PUCK_T1": + if device_id == 0x089A: + device_type = "EYE sensor" + elif complete_local_name == "PUCK_T1": device_type = "Blue Puck T" elif complete_local_name == "PUCK_TH": device_type = "Blue Puck RHT" @@ -62,6 +65,49 @@ def parse_teltonika(self, data, complete_local_name, source_mac, rssi): # Battery (batt,) = unpack("h", sensor_data[0:2]) + result.update({"temperature": temp / 100}) + sensor_data = sensor_data[2:] + if flags & (1 << 1): # bit 1 + # Humidity + humi = sensor_data[0] + result.update({"humidity": humi}) + sensor_data = sensor_data[1:] + if flags & (1 << 2): # bit 2 + # Magnetic sensor presence + if flags & (1 << 3): # bit 3 + # magnetic field is detected + result.update({"magnetic field": 1}) + else: + # magnetic field is not detected + result.update({"magnetic field": 0}) + if flags & (1 << 4): # bit 4 + # Movement sensor counter + # Most significant bit indicates movement state + # 15 least significant bits represent count of movement events. + movement = sensor_data[0] & (1 << 7) + count = ((sensor_data[0] & 0b01111111) << 8) + sensor_data[1] + result.update({"movement": movement, "count": count}) + sensor_data = sensor_data[2:] + if flags & (1 << 5): # bit 5 + # Movement sensor angle + # Most significant byte – pitch (-90/+90) + # Two least significant bytes – roll (-180/+180) + (pitch, roll,) = unpack(">bh", sensor_data[0:3]) + result.update({"roll": roll, "pitch": pitch}) + sensor_data = sensor_data[3:] + if flags & (1 << 6): # bit 6 + # Low battery indication sensor presence + result.update({"low battery": 1}) + if flags & (1 << 7): # bit 7 + # Battery voltage value presence + volt = round(2.0 + sensor_data[0] * 0.01, 3) + result.update({"voltage": volt}) data_size -= packet_size packet_start += packet_size diff --git a/custom_components/ble_monitor/test/test_teltonika.py b/custom_components/ble_monitor/test/test_teltonika.py index 405ff33eb..2009f68c7 100644 --- a/custom_components/ble_monitor/test/test_teltonika.py +++ b/custom_components/ble_monitor/test/test_teltonika.py @@ -102,3 +102,26 @@ def test_ela_blue_puck_T_with_batt(self): assert sensor_msg["temperature"] == 27.12 assert sensor_msg["battery"] == 13 assert sensor_msg["rssi"] == -35 + + def test_teltonika_eye(self): + """Test Teltonika parser for Teltonika Eye sensor.""" + data_string = "043E46020103001897035ECFD03a0201061AFF4C000215FFFFFFFF0B8C404510C655AAB636EBEFBB700055020C094D50315F313233343536370EFF9A0801B708B4120CCB0BFFC767DD" + data = bytes(bytearray.fromhex(data_string)) + # pylint: disable=unused-variable + ble_parser = BleParser() + sensor_msg, tracker_msg = ble_parser.parse_raw_data(data) + + assert sensor_msg["firmware"] == "Teltonika" + assert sensor_msg["type"] == "EYE sensor" + assert sensor_msg["mac"] == "D0CF5E039718" + assert sensor_msg["packet"] == "no packet id" + assert sensor_msg["data"] + assert sensor_msg["temperature"] == 22.28 + assert sensor_msg["humidity"] == 18 + assert sensor_msg["magnetic field"] == 0 + assert sensor_msg["movement"] == 0 + assert sensor_msg["count"] == 3275 + assert sensor_msg["roll"] == -57 + assert sensor_msg["pitch"] == 11 + assert sensor_msg["voltage"] == 3.03 + assert sensor_msg["rssi"] == -35