Skip to content

Commit 3ce517f

Browse files
authored
Merge pull request #1489 from custom-components/XMOSB01XS
Add Xiaomi Human Presence Occupancy Sensor XMOSB01XS
2 parents 085748e + 12609d0 commit 3ce517f

File tree

6 files changed

+62
-26
lines changed

6 files changed

+62
-26
lines changed

custom_components/ble_monitor/ble_parser/xiaomi.py

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
0x045C: "V-SK152",
5454
0x040A: "WX08ZM",
5555
0x04E1: "XMMF01JQD",
56+
0x4683: "XMOSB01XS",
5657
0x1203: "XMWSDJ04MMC",
5758
0x1949: "XMWXKG01YL",
5859
0x2387: "XMWXKG01LM",
@@ -820,15 +821,19 @@ def obj4840(xobj):
820821
def obj484e(xobj, device_type=None):
821822
"""Occupancy Status"""
822823
(occupancy,) = struct.unpack("<B", xobj)
823-
if occupancy == 0:
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}
824+
# For ES3 and XMOSB01XS: This sensor is being treated as an occupancy sensor
825+
if device_type in ["ES3", "XMOSB01XS"]:
826+
if occupancy == 0:
827+
return {"occupancy": 0}
828828
else:
829-
return {}
829+
return {"occupancy": 1}
830830
else:
831-
return {"motion": 1, "motion timer": 1}
831+
# For other devices: This sensor is being treated as a motion sensor and will be using the timer in
832+
# of ble_monitor to set "no motion" state
833+
if occupancy == 0:
834+
return {}
835+
else:
836+
return {"motion": 1, "motion timer": 1}
832837

833838

834839
def obj484f(xobj):
@@ -849,6 +854,18 @@ def obj4850(xobj):
849854
return {"motion time": motion_time}
850855

851856

857+
def obj4851(xobj):
858+
"""From miot-spec: has-someone-duration: uint8: 2 - 2 minutes, 5 - 5 minutes (not used)"""
859+
(duration,) = struct.unpack("<I", xobj)
860+
return {"duration occupancy detected": duration}
861+
862+
863+
def obj4852(xobj):
864+
"""From miot-spec: no-one-duration: uint8: 2/5/10/30 - 2/5/10/30 minutes (not used)"""
865+
(duration,) = struct.unpack("<I", xobj)
866+
return {"duration no occupancy detected": duration}
867+
868+
852869
def obj4a01(xobj):
853870
"""Low Battery"""
854871
low_batt = xobj[0]
@@ -1332,6 +1349,8 @@ def obj6e16(xobj):
13321349
0x484e: obj484e,
13331350
0x484f: obj484f,
13341351
0x4850: obj4850,
1352+
0x4851: obj4851,
1353+
0x4852: obj4852,
13351354
0x4a01: obj4a01,
13361355
0x4a08: obj4a08,
13371356
0x4a0c: obj4a0c,

custom_components/ble_monitor/const.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2324,14 +2324,14 @@ class BLEMonitorBinarySensorEntityDescription(
23242324
'CQ60' : 'Chef iQ',
23252325
'SeeLevel II 709-BTP3' : 'Garnet Instruments Ltd',
23262326
'MI401' : 'Grundfos',
2327-
'Genisys75xx': 'Beckett',
2328-
'GenisysOil': 'Beckett',
2329-
'Iot7653': 'Beckett',
2330-
'Genisys7505': 'Beckett',
2331-
'LegacyGenisys': 'Beckett',
2332-
'Iot7652': 'Beckett',
2333-
'BeckettLinkPro': 'Beckett',
2334-
'GenisysGas': 'Beckett',
2327+
'Genisys75xx' : 'Beckett',
2328+
'GenisysOil' : 'Beckett',
2329+
'Iot7653' : 'Beckett',
2330+
'Genisys7505' : 'Beckett',
2331+
'LegacyGenisys' : 'Beckett',
2332+
'Iot7652' : 'Beckett',
2333+
'BeckettLinkPro' : 'Beckett',
2334+
'GenisysGas' : 'Beckett',
23352335
'HHCCJCY10' : 'HHCC',
23362336
'HolyIOT BLE tracker' : 'HolyIOT',
23372337
'JTYJGD03MI' : 'Honeywell',
@@ -2364,6 +2364,7 @@ class BLEMonitorBinarySensorEntityDescription(
23642364
'MJWSD06MMC' : 'Xiaomi',
23652365
'MMC-W505' : 'Xiaomi',
23662366
'SJWS01LM' : 'Xiaomi',
2367+
'XMOSB01XS' : 'Xiaomi',
23672368
'RS1BB' : 'Linptech',
23682369
'ES3' : 'Linptech',
23692370
}

custom_components/ble_monitor/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@
1414
"btsocket>=0.3.0",
1515
"pyric>=0.1.6.3"
1616
],
17-
"version": "13.7.1"
17+
"version": "13.8.0"
1818
}

custom_components/ble_monitor/test/test_xiaomi_parser.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -957,7 +957,7 @@ def test_linptech_ES3_illuminance(self):
957957
assert sensor_msg["illuminance"] == 173.0
958958
assert sensor_msg["rssi"] == -58
959959

960-
def test_linptech_ES3_motion(self):
960+
def test_linptech_ES3_occupancy(self):
961961
"""Test Xiaomi parser for linptech ES3."""
962962
self.aeskeys = {}
963963
data_string = "043E290201000176c3c738c1a41D020106191695fe5859fb50da76c3c738c1a4aabc4c16000000c60c1646C6"
@@ -980,12 +980,11 @@ def test_linptech_ES3_motion(self):
980980
assert sensor_msg["mac"] == "A4C138C7C376"
981981
assert sensor_msg["packet"] == 218
982982
assert sensor_msg["data"]
983-
assert sensor_msg["motion"] == 1
984-
assert sensor_msg["motion timer"] == 1
983+
assert sensor_msg["occupancy"] == 1
985984
assert sensor_msg["rssi"] == -58
986985

987986
def test_linptech_ES3_data_only_motion_clear(self):
988-
"""Test Xiaomi parser for linptech ES3 data-only frame (implicit motion clear)."""
987+
"""Test Xiaomi parser for linptech ES3 data-only frame."""
989988
self.aeskeys = {}
990989
# This is a real data-only frame for ES3 with MAC A4:C1:38:A4:88:8A
991990
data_string = "043e29020100018a88a438c1a41d020106191695fe5859fb50328a88a438c1a4458a85b796000048fe13bac6"
@@ -1008,7 +1007,7 @@ def test_linptech_ES3_data_only_motion_clear(self):
10081007
assert sensor_msg["mac"] == "A4C138A4888A"
10091008
assert sensor_msg["packet"] == 50
10101009
assert sensor_msg["data"]
1011-
assert sensor_msg["motion"] == 0 # Should be implicitly set to 0 for data-only frames
1010+
assert sensor_msg["occupancy"] == 0 # Should be implicitly set to 0 for data-only frames
10121011
assert sensor_msg["rssi"] == -58
10131012

10141013
def test_MJZNZ018H_bed_occupancy(self):

docs/_devices/Linptech ES3.md

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,15 @@ image: Linptech_ES3.png
66
physical_description:
77
broadcasted_properties:
88
- illuminance
9-
- motion
9+
- occupancy
1010
- battery
1111
- rssi
1212
broadcasted_property_notes:
1313
- property: illuminance
1414
note: is measured in lux.
15-
- property: motion
16-
note: Motion state is ‘motion detected’ or ‘clear’.
1715
broadcast_rate: See Notes
1816
active_scan:
1917
encryption_key: Yes
2018
custom_firmware:
21-
notes: >
22-
- You can use the [reset_timer](configuration_params#reset_timer) option if you want to use a different time to set the sensor to `motion clear`.
19+
notes:
2320
---

docs/_devices/Xiaomi_XMOSB01XS.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
---
2+
manufacturer: Xiaomi
3+
name: Xiaomi Mijia Human Body Occupancy Sensor
4+
model: XMOSB01XS
5+
image: Xiaomi_XMOSB01XS.png
6+
physical_description:
7+
broadcasted_properties:
8+
- illuminance
9+
- occupancy
10+
- battery
11+
- rssi
12+
broadcasted_property_notes:
13+
- property: illuminance
14+
note: is measured in lux.
15+
broadcast_rate: See Notes
16+
active_scan:
17+
encryption_key: Yes
18+
custom_firmware:
19+
notes:
20+
---

0 commit comments

Comments
 (0)