Skip to content

Commit 3120c4e

Browse files
authored
Merge pull request #1319 from custom-components/xiaomi_ptx
Add PTX wireless switch
2 parents ca10221 + 6ac5df6 commit 3120c4e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+108
-2
lines changed

custom_components/ble_monitor/ble_parser/xiaomi.py

+38-1
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
0x18E3: "ZX1",
7676
0x11C2: "SV40",
7777
0x3F0F: "RS1BB",
78+
0x38BB: "PTX",
7879
}
7980

8081
# Structured objects for data conversions
@@ -769,6 +770,30 @@ def obj4a08(xobj):
769770
return {"motion": 1, "motion timer": 1, "illuminance": illu}
770771

771772

773+
def obj4a0c(xobj):
774+
"""Single click PTX"""
775+
return {
776+
"one btn switch": "toggle",
777+
"button switch": "single press",
778+
}
779+
780+
781+
def obj4a0d(xobj):
782+
"""Double click PTX"""
783+
return {
784+
"one btn switch": "toggle",
785+
"button switch": "double press",
786+
}
787+
788+
789+
def obj4a0e(xobj):
790+
"""Long click PTX"""
791+
return {
792+
"one btn switch": "toggle",
793+
"button switch": "long press",
794+
}
795+
796+
772797
def obj4a0f(xobj):
773798
"""Door/window broken open"""
774799
dev_forced = xobj[0]
@@ -852,6 +877,12 @@ def obj4c14(xobj):
852877
return {"mode": mode}
853878

854879

880+
def obj4e01(xobj):
881+
"""Low Battery"""
882+
low_batt = xobj[0]
883+
return {"low battery": low_batt}
884+
885+
855886
def obj4e0c(xobj, device_type):
856887
"""Click"""
857888
if device_type == "XMWXKG01YL":
@@ -1065,6 +1096,9 @@ def obj5a16(xobj):
10651096
0x4818: obj4818,
10661097
0x4a01: obj4a01,
10671098
0x4a08: obj4a08,
1099+
0x4a0c: obj4a0c,
1100+
0x4a0d: obj4a0d,
1101+
0x4a0e: obj4a0e,
10681102
0x4a0f: obj4a0f,
10691103
0x4a12: obj4a12,
10701104
0x4a13: obj4a13,
@@ -1074,6 +1108,7 @@ def obj5a16(xobj):
10741108
0x4c03: obj4c03,
10751109
0x4c08: obj4c08,
10761110
0x4c14: obj4c14,
1111+
0x4e01: obj4e01,
10771112
0x4e0c: obj4e0c,
10781113
0x4e0d: obj4e0d,
10791114
0x4e0e: obj4e0e,
@@ -1267,7 +1302,9 @@ def parse_xiaomi(self, data: bytes, mac: str):
12671302
_LOGGER.debug("Invalid payload data length, payload: %s", payload.hex())
12681303
break
12691304
dobject = payload[payload_start + 3:next_start]
1270-
if dobject and obj_length != 0 or hex(obj_typecode) in ["0x4e0c", "0x4e0d", "0x4e0e"]:
1305+
if dobject and obj_length != 0 or hex(obj_typecode) in [
1306+
"0x4a0c", "0x4a0d", "0x4a0e", "0x4e0c", "0x4e0d", "0x4e0e"
1307+
]:
12711308
resfunc = xiaomi_dataobject_dict.get(obj_typecode, None)
12721309
if resfunc:
12731310
if hex(obj_typecode) in ["0x8", "0x100e", "0x1001", "0xf", "0xb", "0x4e0c", "0x4e0d", "0x4e0e"]:

custom_components/ble_monitor/const.py

+2
Original file line numberDiff line numberDiff line change
@@ -1674,6 +1674,7 @@ class BLEMonitorBinarySensorEntityDescription(
16741674
'HS1BB(MI)' : [["illuminance", "battery", "rssi"], [], ["motion"]],
16751675
'XMWXKG01YL' : [["rssi"], ["two btn switch left", "two btn switch right"], []],
16761676
'XMWXKG01LM' : [["battery", "rssi"], ["one btn switch"], []],
1677+
'PTX' : [["battery", "rssi"], ["one btn switch"], []],
16771678
'YLAI003' : [["rssi", "battery"], ["button"], []],
16781679
'YLYK01YL' : [["rssi"], ["remote"], ["remote single press", "remote long press"]],
16791680
'YLYK01YL-FANCL' : [["rssi"], ["fan remote"], []],
@@ -1812,6 +1813,7 @@ class BLEMonitorBinarySensorEntityDescription(
18121813
'HS1BB(MI)' : 'Linptech',
18131814
'XMWXKG01YL' : 'Xiaomi',
18141815
'XMWXKG01LM' : 'Xiaomi',
1816+
'PTX' : 'Xiaomi',
18151817
'SV40' : 'Lockin',
18161818
'SU001-T' : 'Petoneer',
18171819
'ATC' : 'ATC',

custom_components/ble_monitor/manifest.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@
1414
"btsocket>=0.2.0",
1515
"pyric>=0.1.6.3"
1616
],
17-
"version": "12.9.2"
17+
"version": "12.9.3"
1818
}

custom_components/ble_monitor/test/test_xiaomi_parser.py

+27
Original file line numberDiff line numberDiff line change
@@ -1037,3 +1037,30 @@ def test_XMWXKG01LM_long_click(self):
10371037
assert sensor_msg["one btn switch"] == "toggle"
10381038
assert sensor_msg["button switch"] == "long press"
10391039
assert sensor_msg["rssi"] == -64
1040+
1041+
def test_Xiaomi_PTX(self):
1042+
"""Test Xiaomi parser for PTX BLE wireless switch."""
1043+
self.aeskeys = {}
1044+
data_string = "043E2802010000adb9a538c1a41c020106181695fe5859bb3804adb9a538c1a4dc10b50400002c122fb6CC"
1045+
data = bytes(bytearray.fromhex(data_string))
1046+
1047+
aeskey = "a74510b40386d35ae6227a7451efc76e"
1048+
1049+
is_ext_packet = True if data[3] == 0x0D else False
1050+
mac = (data[8 if is_ext_packet else 7:14 if is_ext_packet else 13])[::-1]
1051+
mac_address = mac.hex()
1052+
p_mac = bytes.fromhex(mac_address.replace(":", "").lower())
1053+
p_key = bytes.fromhex(aeskey.lower())
1054+
self.aeskeys[p_mac] = p_key
1055+
# pylint: disable=unused-variable
1056+
ble_parser = BleParser(aeskeys=self.aeskeys)
1057+
sensor_msg, tracker_msg = ble_parser.parse_raw_data(data)
1058+
1059+
assert sensor_msg["firmware"] == "Xiaomi (MiBeacon V5 encrypted)"
1060+
assert sensor_msg["type"] == "PTX"
1061+
assert sensor_msg["mac"] == "A4C138A5B9AD"
1062+
assert sensor_msg["packet"] == 4
1063+
assert sensor_msg["data"]
1064+
assert sensor_msg["one btn switch"] == "toggle"
1065+
assert sensor_msg["button switch"] == "single press"
1066+
assert sensor_msg["rssi"] == -52

docs/_devices/Xiaomi_PTX.md

+20

docs/_devices/Xiaomi_XMWXKG01LM.md

+20

docs/assets/images/PTX.png

6.06 KB

0 commit comments

Comments
 (0)