Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add ptx and xmpiro2sx #74

Merged
merged 2 commits into from
Mar 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/xiaomi_ble/devices.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,10 @@ class DeviceEntry:
name="Motion Sensor",
model="RTCGQ02LM",
),
0x3531: DeviceEntry(
name="Motion Sensor",
model="XMPIRO2SXS",
),
0x0863: DeviceEntry(
name="Flood Detector",
model="SJWS01LM",
Expand Down Expand Up @@ -197,6 +201,10 @@ class DeviceEntry:
name="Wireless Switch",
model="YLAI003",
),
0x38BB: DeviceEntry(
name="Wireless Switch",
model="PTX_YK1_QMIMB",
),
0x0153: DeviceEntry(
name="Remote Control",
model="YLYK01YL",
Expand Down
66 changes: 60 additions & 6 deletions src/xiaomi_ble/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -990,8 +990,8 @@


# The following data objects are device specific. For now only added for
# LYWSD02MMC, MJWSD05MMC, XMWSDJ04MMC, XMWXKG01YL, LINPTECH MS1BB(MI), HS1BB(MI), K9BB
# https://miot-spec.org/miot-spec-v2/instances?status=all
# LYWSD02MMC, MJWSD05MMC, XMWSDJ04MMC, XMWXKG01YL, LINPTECH MS1BB(MI), HS1BB(MI),
# K9BB, PTX https://miot-spec.org/miot-spec-v2/instances?status=all
def obj4803(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
Expand Down Expand Up @@ -1071,6 +1071,42 @@
return {}


def obj4a0c(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Single press"""
device.fire_event(
key=EventDeviceKeys.BUTTON,
event_type="press",
event_properties=None,
)
return {}


def obj4a0d(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Double press"""
device.fire_event(

Check warning on line 1090 in src/xiaomi_ble/parser.py

View check run for this annotation

Codecov / codecov/patch

src/xiaomi_ble/parser.py#L1090

Added line #L1090 was not covered by tests
key=EventDeviceKeys.BUTTON,
event_type="double_press",
event_properties=None,
)
return {}

Check warning on line 1095 in src/xiaomi_ble/parser.py

View check run for this annotation

Codecov / codecov/patch

src/xiaomi_ble/parser.py#L1095

Added line #L1095 was not covered by tests


def obj4a0e(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Long press"""
device.fire_event(

Check warning on line 1102 in src/xiaomi_ble/parser.py

View check run for this annotation

Codecov / codecov/patch

src/xiaomi_ble/parser.py#L1102

Added line #L1102 was not covered by tests
key=EventDeviceKeys.BUTTON,
event_type="long_press",
event_properties=None,
)
return {}

Check warning on line 1107 in src/xiaomi_ble/parser.py

View check run for this annotation

Codecov / codecov/patch

src/xiaomi_ble/parser.py#L1107

Added line #L1107 was not covered by tests


def obj4a0f(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
Expand Down Expand Up @@ -1189,12 +1225,20 @@
return {"mode": mode}


def obj4e01(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Low Battery"""
device.update_predefined_binary_sensor(BinarySensorDeviceClass.BATTERY, xobj[0])
return {}

Check warning on line 1233 in src/xiaomi_ble/parser.py

View check run for this annotation

Codecov / codecov/patch

src/xiaomi_ble/parser.py#L1232-L1233

Added lines #L1232 - L1233 were not covered by tests


def obj4e0c(
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Button press"""
press = xobj[0]
if device_type == "XMWXKG01YL":
press = xobj[0]
if press == 1:
# left button
device.fire_event(
Expand Down Expand Up @@ -1222,6 +1266,7 @@
event_properties=None,
)
elif device_type == "K9BB-1BTN":
press = xobj[0]

Check warning on line 1269 in src/xiaomi_ble/parser.py

View check run for this annotation

Codecov / codecov/patch

src/xiaomi_ble/parser.py#L1269

Added line #L1269 was not covered by tests
if press == 1:
device.fire_event(
key=EventDeviceKeys.BUTTON,
Expand Down Expand Up @@ -1253,8 +1298,8 @@
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Double Press"""
press = xobj[0]
if device_type == "XMWXKG01YL":
press = xobj[0]

Check warning on line 1302 in src/xiaomi_ble/parser.py

View check run for this annotation

Codecov / codecov/patch

src/xiaomi_ble/parser.py#L1302

Added line #L1302 was not covered by tests
if press == 1:
# left button
device.fire_event(
Expand Down Expand Up @@ -1294,8 +1339,8 @@
xobj: bytes, device: XiaomiBluetoothDeviceData, device_type: str
) -> dict[str, Any]:
"""Long Press"""
press = xobj[0]
if device_type == "XMWXKG01YL":
press = xobj[0]

Check warning on line 1343 in src/xiaomi_ble/parser.py

View check run for this annotation

Codecov / codecov/patch

src/xiaomi_ble/parser.py#L1343

Added line #L1343 was not covered by tests
if press == 1:
# left button
device.fire_event(
Expand Down Expand Up @@ -1376,6 +1421,9 @@
0x4818: obj4818,
0x4A01: obj4a01,
0x4A08: obj4a08,
0x4A0C: obj4a0c,
0x4A0D: obj4a0d,
0x4A0E: obj4a0e,
0x4A0F: obj4a0f,
0x4A12: obj4a12,
0x4A13: obj4a13,
Expand All @@ -1385,6 +1433,7 @@
0x4C03: obj4c03,
0x4C08: obj4c08,
0x4C14: obj4c14,
0x4E01: obj4e01,
0x4E1C: obj4e1c,
0x4E0C: obj4e0c,
0x4E0D: obj4e0d,
Expand Down Expand Up @@ -1713,7 +1762,12 @@
break
this_start = payload_start + 3
dobject = payload[this_start:next_start]
if obj_length != 0:
if (
dobject
and obj_length != 0
or hex(obj_typecode)
in ["0x4a0c", "0x4a0d", "0x4a0e", "0x4e0c", "0x4e0d", "0x4e0e"]
):
resfunc = xiaomi_dataobject_dict.get(obj_typecode, None)
if resfunc:
self.unhandled.update(resfunc(dobject, self, device_type))
Expand Down
140 changes: 140 additions & 0 deletions tests/test_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -2887,5 +2887,145 @@ def test_Xiaomi_RTCGQ02LM_timeout_motion():
)


def test_Xiaomi_XMPIRO2SXS():
"""Test Xiaomi parser for Xiaomi Human Body Sensor 2S XMPIRO2SXS."""
data_string = b"HY15\x0bdy\x91\x173\x1e\xf4\x02\x00\x00\xc5\xd2\xf6\xac"
advertisement = bytes_to_service_info(data_string, address="DC:8E:95:2D:EA:43")
bindkey = "685d647dc5e7bc9bcfcf5a1357bd2114"

device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Motion Sensor EA43 (XMPIRO2SXS)",
devices={
None: SensorDeviceInfo(
name="Motion Sensor EA43",
manufacturer="Xiaomi",
model="XMPIRO2SXS",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_ILLUMINANCE: SensorDescription(
device_key=KEY_ILLUMINANCE,
device_class=DeviceClass.ILLUMINANCE,
native_unit_of_measurement=Units.LIGHT_LUX,
),
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_ILLUMINANCE: SensorValue(
name="Illuminance", device_key=KEY_ILLUMINANCE, native_value=51
),
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
binary_entity_descriptions={
KEY_BINARY_MOTION: BinarySensorDescription(
device_key=KEY_BINARY_MOTION,
device_class=BinarySensorDeviceClass.MOTION,
),
},
binary_entity_values={
KEY_BINARY_MOTION: BinarySensorValue(
device_key=KEY_BINARY_MOTION, name="Motion", native_value=True
),
},
)


def test_Xiaomi_PTX_press():
"""Test Xiaomi parser for Xiaomi PTX YK1 QMIMB."""
bindkey = "a74510b40386d35ae6227a7451efc76e"
data_string = b"XY\xbb8\x04\xad\xb9\xa58\xc1\xa4\xdc\x10\xb5\x04\x00\x00,\x12/\xb6"
advertisement = bytes_to_service_info(data_string, address="A4:C1:38:A5:B9:AD")

device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Wireless Switch B9AD (PTX_YK1_QMIMB)",
devices={
None: SensorDeviceInfo(
name="Wireless Switch B9AD",
manufacturer="Xiaomi",
model="PTX_YK1_QMIMB",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
events={
KEY_EVENT_BUTTON: Event(
device_key=KEY_EVENT_BUTTON,
name="Button",
event_type="press",
event_properties=None,
),
},
)


def test_Xiaomi_XMWXKG01LM_press():
"""Test Xiaomi parser for Xiaomi XMWXKG01LM ."""
bindkey = "7202a2d4201bbf82ea5bb3705657c32a"
data_string = b"XY\x87#5\x057$<\xc2\x18\xd6w\x94\x02\x00\x00\xcb-\xe3\t"
advertisement = bytes_to_service_info(data_string, address="18:C2:3C:24:37:05")

device = XiaomiBluetoothDeviceData(bindkey=bytes.fromhex(bindkey))
assert device.supported(advertisement)
assert device.bindkey_verified
assert device.update(advertisement) == SensorUpdate(
title="Button 3705 (XMWXKG01LM)",
devices={
None: SensorDeviceInfo(
name="Button 3705",
manufacturer="Xiaomi",
model="XMWXKG01LM",
hw_version=None,
sw_version="Xiaomi (MiBeacon V5 encrypted)",
)
},
entity_descriptions={
KEY_SIGNAL_STRENGTH: SensorDescription(
device_key=KEY_SIGNAL_STRENGTH,
device_class=DeviceClass.SIGNAL_STRENGTH,
native_unit_of_measurement="dBm",
),
},
entity_values={
KEY_SIGNAL_STRENGTH: SensorValue(
name="Signal Strength", device_key=KEY_SIGNAL_STRENGTH, native_value=-60
),
},
events={
KEY_EVENT_BUTTON: Event(
device_key=KEY_EVENT_BUTTON,
name="Button",
event_type="press",
event_properties=None,
),
},
)


def test_can_create():
XiaomiBluetoothDeviceData()
Loading