Skip to content

Commit 018cfca

Browse files
authored
Merge pull request #1482 from rankjie/master
Fix Linptech ES3 sensor motion clear delay (#1481)
2 parents 52f9ffb + 3ae5948 commit 018cfca

File tree

3 files changed

+38
-5
lines changed

3 files changed

+38
-5
lines changed

custom_components/ble_monitor/binary_sensor.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -281,10 +281,10 @@ def __init__(
281281
self._key = key
282282
self._fkey = identifier_normalize(key)
283283
self._state = None
284+
self._device_type = devtype
284285

285286
self._device_settings = self.get_device_settings()
286287
self._device_name = self._device_settings["name"]
287-
self._device_type = devtype
288288
self._device_firmware = firmware
289289
self._device_manufacturer = manufacturer \
290290
if manufacturer is not None \
@@ -380,7 +380,8 @@ def get_device_settings(self):
380380
# initial setup of device settings equal to integration settings
381381
dev_name = self._key
382382
dev_restore_state = self._config[CONF_RESTORE_STATE]
383-
dev_reset_timer = DEFAULT_DEVICE_RESET_TIMER
383+
# ES3 devices should have reset_timer disabled by default since they handle motion clear themselves
384+
dev_reset_timer = 0 if self._device_type == "ES3" else DEFAULT_DEVICE_RESET_TIMER
384385

385386
# in UI mode device name is equal to mac (but can be overwritten in UI)
386387
# in YAML mode device name is taken from config

custom_components/ble_monitor/ble_parser/xiaomi.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -817,12 +817,16 @@ def obj4840(xobj):
817817
return {"pressure not present time set": duration}
818818

819819

820-
def obj484e(xobj):
820+
def obj484e(xobj, device_type=None):
821821
"""Occupancy Status"""
822822
(occupancy,) = struct.unpack("<B", xobj)
823823
if occupancy == 0:
824-
# no motion is being taken care of by the timer in HA
825-
return {}
824+
# For ES3: if this is the only data object (no motion/illuminance), treat as motion clear
825+
# For other devices: no motion is being taken care of by the timer in HA
826+
if device_type == "ES3":
827+
return {"motion": 0}
828+
else:
829+
return {}
826830
else:
827831
return {"motion": 1, "motion timer": 1}
828832

@@ -1554,6 +1558,7 @@ def parse_xiaomi(self, data: bytes, mac: bytes):
15541558
"0x1001",
15551559
"0xf",
15561560
"0xb",
1561+
"0x484e",
15571562
"0x4e0c",
15581563
"0x4e0d",
15591564
"0x4e0e",

custom_components/ble_monitor/test/test_xiaomi_parser.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -984,6 +984,33 @@ def test_linptech_ES3_motion(self):
984984
assert sensor_msg["motion timer"] == 1
985985
assert sensor_msg["rssi"] == -58
986986

987+
def test_linptech_ES3_data_only_motion_clear(self):
988+
"""Test Xiaomi parser for linptech ES3 data-only frame (implicit motion clear)."""
989+
self.aeskeys = {}
990+
# This is a real data-only frame for ES3 with MAC A4:C1:38:A4:88:8A
991+
data_string = "043e29020100018a88a438c1a41d020106191695fe5859fb50328a88a438c1a4458a85b796000048fe13bac6"
992+
data = bytes(bytearray.fromhex(data_string))
993+
994+
aeskey = "fb352ea2139ab095877a9e2ae600c912"
995+
996+
is_ext_packet = True if data[3] == 0x0D else False
997+
mac = (data[8 if is_ext_packet else 7:14 if is_ext_packet else 13])[::-1]
998+
mac_address = mac.hex()
999+
p_mac = bytes.fromhex(mac_address.replace(":", "").lower())
1000+
p_key = bytes.fromhex(aeskey.lower())
1001+
self.aeskeys[p_mac] = p_key
1002+
# pylint: disable=unused-variable
1003+
ble_parser = BleParser(aeskeys=self.aeskeys)
1004+
sensor_msg, tracker_msg = ble_parser.parse_raw_data(data)
1005+
1006+
assert sensor_msg["firmware"] == "Xiaomi (MiBeacon V5 encrypted)"
1007+
assert sensor_msg["type"] == "ES3"
1008+
assert sensor_msg["mac"] == "A4C138A4888A"
1009+
assert sensor_msg["packet"] == 50
1010+
assert sensor_msg["data"]
1011+
assert sensor_msg["motion"] == 0 # Should be implicitly set to 0 for data-only frames
1012+
assert sensor_msg["rssi"] == -58
1013+
9871014
def test_MJZNZ018H_bed_occupancy(self):
9881015
"""Test Xiaomi parser for MJZNZ018H bed occupancy sensor."""
9891016
self.aeskeys = {}

0 commit comments

Comments
 (0)