Skip to content

Add support for PTX-F1-Display and enhance debugging tools#1533

Open
M1k0t0 wants to merge 8 commits intocustom-components:masterfrom
M1k0t0:master
Open

Add support for PTX-F1-Display and enhance debugging tools#1533
M1k0t0 wants to merge 8 commits intocustom-components:masterfrom
M1k0t0:master

Conversation

@M1k0t0
Copy link
Copy Markdown

@M1k0t0 M1k0t0 commented Mar 21, 2026

No description provided.

M1k0t0 added 8 commits March 21, 2026 04:09
Add device mapping for PTX-F1-Display (https://home.miot-spec.com/spec/090615.remote.x1swd) Internal type: 0x64C5.
Unable to retrieve the battery percentage right now since the packet I dumped does not include it.

Add debug logging for duplicate packets, lower-priority advertisements, and missing decryption keys to aid troubleshooting.
Add doc file for the Xiaomi PTX F1 4-Button Wireless Switch (Display Version).
Add tools/anonymize_mibeacon_v5.py: a CLI script to anonymize Xiaomi MiBeacon V4/V5 encrypted BLE advertisements.
Copilot AI review requested due to automatic review settings March 21, 2026 09:22
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds recognition and parsing for the Xiaomi PTX-F1-Display (MiBeacon V5 encrypted) device type, plus supporting docs/assets and a helper tool intended to aid debugging/trace generation.

Changes:

  • Add new Xiaomi device type ID mapping and decode paths for PTX-F1-Display (including button events and new data objects).
  • Extend constants and tests to cover PTX-F1-Display parsing behavior.
  • Add device documentation + image asset, and a new MiBeacon anonymization tool script.

Reviewed changes

Copilot reviewed 5 out of 6 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
custom_components/ble_monitor/ble_parser/xiaomi.py Adds PTX-F1-Display type mapping, new object converters, and extra debug logging in duplicate/priority/key-error paths.
custom_components/ble_monitor/const.py Registers PTX-F1-Display in measurement/manufacturer dictionaries for entity creation.
custom_components/ble_monitor/test/test_xiaomi_parser.py Adds PTX-F1-Display tests for a button press payload and a humidity payload.
docs/_devices/Xiaomi_PTX_F1_Display.md Adds a new device documentation entry for PTX F1 Display.
docs/assets/images/PTX_F1_Display.webp Adds the device image used by the docs page.
tools/anonymize_mibeacon_v5.py Adds a standalone script to anonymize MiBeacon V4/V5 encrypted raw advertisements (MAC/key replacement + re-encryption).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 1508 to 1510
if self.filter_duplicates is True:
_LOGGER.debug("Duplicate packet received, not processing. Data: %s", data.hex())
return None
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new debug log computes data.hex() unconditionally; .hex() is evaluated even when debug logging is disabled, which can add measurable overhead in a hot path (duplicate filtering). Consider guarding with _LOGGER.isEnabledFor(logging.DEBUG) (or equivalent) before building the hex string; same applies to the other newly added debug statements below.

Copilot uses AI. Check for mistakes.
Comment on lines +736 to +737
temp = xobj[0]
return {"temperature": temp}
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

obj605d assumes the temperature payload is at least 1 byte and silently ignores any extra bytes. To avoid mis-parsing (and to prevent surprises if the object definition changes), add an explicit length check (and return {} / None on unexpected lengths), similar to other obj* converters in this file.

Suggested change
temp = xobj[0]
return {"temperature": temp}
if len(xobj) == 1:
temp = xobj[0]
return {"temperature": temp}
else:
return {}

Copilot uses AI. Check for mistakes.
'XMWXKG01YL' : [["rssi"], ["two btn switch left", "two btn switch right"], []],
'XMWXKG01LM' : [["battery", "rssi"], ["one btn switch"], []],
'PTX' : [["battery", "rssi"], ["one btn switch"], []],
'PTX-F1-Display' : [["temperature", "humidity", "battery", "rssi"], ["four btn switch 1", "four btn switch 2", "four btn switch 3", "four btn switch 4"], []],
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This new device entry advertises a battery measurement, but the accompanying device documentation in this PR says battery percentage can't be retrieved and doesn't list battery as a broadcasted property. Either remove battery here or update the docs/parser so the battery value is actually produced, to avoid creating a permanent 'unknown' battery entity.

Suggested change
'PTX-F1-Display' : [["temperature", "humidity", "battery", "rssi"], ["four btn switch 1", "four btn switch 2", "four btn switch 3", "four btn switch 4"], []],
'PTX-F1-Display' : [["temperature", "humidity", "rssi"], ["four btn switch 1", "four btn switch 2", "four btn switch 3", "four btn switch 4"], []],

Copilot uses AI. Check for mistakes.
'XMWXKG01YL' : 'Xiaomi',
'XMWXKG01LM' : 'Xiaomi',
'PTX' : 'Xiaomi',
"PTX-F1-Display" : 'Xiaomi',
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The device key uses double quotes while the surrounding entries consistently use single quotes. For consistency (and to minimize noisy diffs in the future), switch this key to single quotes.

Suggested change
"PTX-F1-Display" : 'Xiaomi',
'PTX-F1-Display' : 'Xiaomi',

Copilot uses AI. Check for mistakes.
Comment on lines +17 to +23
note: returns 'short press', 'double press' or 'long press'
- property: four btn switch 2
note: returns 'short press', 'double press' or 'long press'
- property: four btn switch 3
note: returns 'short press', 'double press' or 'long press'
- property: four btn switch 4
note: returns 'short press', 'double press' or 'long press'
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The notes for four btn switch 1 imply the property returns press types (short/double/long), but the parser emits four btn switch X: "toggle" and puts the press type in button switch (see the added tests in this PR). Please update these property notes (and the other 3 button notes below) to match the actual emitted fields/values.

Suggested change
note: returns 'short press', 'double press' or 'long press'
- property: four btn switch 2
note: returns 'short press', 'double press' or 'long press'
- property: four btn switch 3
note: returns 'short press', 'double press' or 'long press'
- property: four btn switch 4
note: returns 'short press', 'double press' or 'long press'
note: always "toggle"; actual press type ('short press', 'double press', 'long press') is reported via the 'button switch' property
- property: four btn switch 2
note: always "toggle"; actual press type ('short press', 'double press', 'long press') is reported via the 'button switch' property
- property: four btn switch 3
note: always "toggle"; actual press type ('short press', 'double press', 'long press') is reported via the 'button switch' property
- property: four btn switch 4
note: always "toggle"; actual press type ('short press', 'double press', 'long press') is reported via the 'button switch' property

Copilot uses AI. Check for mistakes.
assert sensor_msg["humidity"] == 39
assert sensor_msg["rssi"] == -60
assert sensor_msg["local_name"] == ""

Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR adds parsing support for the new temperature data object (0x605d / obj605d), but there isn't a corresponding test covering temperature decoding for PTX-F1-Display. Adding a temperature sample test would help prevent regressions and validate the expected units/format.

Suggested change
def test_Xiaomi_PTX_F1_Display_temperature(self):
"""Test Xiaomi parser for PTX-F1-Display temperature."""
self.aeskeys = {}
data_string = "043E29020100006655443322111D020106191695FE5859C56433665544332211D67B54550C01001D8F98BBC4".replace(" ", "")
data = bytes(bytearray.fromhex(data_string))
aeskey = "00112233445566778899aabbccddeeff"
is_ext_packet = True if data[3] == 0x0D else False
mac = (data[8 if is_ext_packet else 7:14 if is_ext_packet else 13])[::-1]
mac_address = mac.hex()
p_mac = bytes.fromhex(mac_address.replace(":", "").lower())
p_key = bytes.fromhex(aeskey.lower())
self.aeskeys[p_mac] = p_key
# pylint: disable=unused-variable
ble_parser = BleParser(aeskeys=self.aeskeys)
sensor_msg, tracker_msg = ble_parser.parse_raw_data(data)
assert sensor_msg["firmware"] == "Xiaomi (MiBeacon V5 encrypted)"
assert sensor_msg["type"] == "PTX-F1-Display"
assert sensor_msg["mac"] == "112233445566"
assert sensor_msg["packet"] == 51
assert sensor_msg["data"]
# Validate that temperature is decoded and numeric; exact value depends on obj605d payload.
assert "temperature" in sensor_msg
assert isinstance(sensor_msg["temperature"], (int, float))
assert sensor_msg["rssi"] == -60
assert sensor_msg["local_name"] == ""

Copilot uses AI. Check for mistakes.
Comment on lines +19 to +23
def _parse_ad_structures(raw: bytes) -> tuple[list[AdStructure], int, int, bool]:
is_ext_packet = raw[3] == 0x0D
adpayload_start = 29 if is_ext_packet else 14
adpayload_size = raw[adpayload_start - 1]
structures: list[AdStructure] = []
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The script directly indexes into raw/service_data (e.g., raw[3], raw[adpayload_start-1], service_data[4]) without validating the minimum lengths first. For malformed/short --raw inputs this will raise IndexError and produce an unhelpful traceback; consider adding explicit length checks up front and raising a clear ValueError describing the expected format/length.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants