From 64608b434cd61632b80de071137dbe9e2dbec9de Mon Sep 17 00:00:00 2001 From: Stuart Clark Date: Fri, 15 May 2026 11:33:49 +0100 Subject: [PATCH] Fix OpenDisplay sensor data parsing to support new 14-byte format --- .../opendisplay/ble/protocol_base.py | 2 +- .../opendisplay/ble/protocol_open_display.py | 36 ++++++++++++------- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/custom_components/opendisplay/ble/protocol_base.py b/custom_components/opendisplay/ble/protocol_base.py index 7c5adf4..081f281 100644 --- a/custom_components/opendisplay/ble/protocol_base.py +++ b/custom_components/opendisplay/ble/protocol_base.py @@ -13,7 +13,7 @@ class AdvertisingData: battery_mv: int battery_pct: int - temperature: int | None + temperature: float | None hw_type: int fw_version: int version: int # Config/protocol version diff --git a/custom_components/opendisplay/ble/protocol_open_display.py b/custom_components/opendisplay/ble/protocol_open_display.py index fe0304e..1556cdc 100644 --- a/custom_components/opendisplay/ble/protocol_open_display.py +++ b/custom_components/opendisplay/ble/protocol_open_display.py @@ -146,9 +146,9 @@ def protocol_name(self) -> str: def parse_advertising_data(self, data: bytes) -> AdvertisingData: """Parse OpenDisplay manufacturer data for device state updates. - OpenDisplay firmware uses the same advertising format as ATC firmware: - [version, hw_type_low, hw_type_high, fw_low, fw_high, reserved_low, reserved_high, - battery_low, battery_high, temperature, counter] + OpenDisplay firmware has two advertising formats: + - Legacy (11 bytes): Same layout as ATC (battery at 7-8, temp at 9) + - Current/v1 (14 bytes): Firmware 1.0+ (temp at 11, battery at 12-13) Args: data: Manufacturer-specific advertising data @@ -166,7 +166,7 @@ def parse_advertising_data(self, data: bytes) -> AdvertisingData: if len(data) < 5: raise ValueError(f"OpenDisplay advertising requires at least 5 bytes, got {len(data)}") - # Parse core fields (same layout as ATC) + # Parse core fields version = data[0] hw_type = int.from_bytes(data[1:3], "little") fw_version = int.from_bytes(data[3:5], "little") @@ -176,15 +176,27 @@ def parse_advertising_data(self, data: bytes) -> AdvertisingData: battery_pct = 0 temperature = None - # Battery voltage at bytes 7-8 (same as ATC) - if len(data) >= 9: - battery_mv = int.from_bytes(data[7:9], "little") - battery_pct = self._calculate_battery_percentage(battery_mv) if battery_mv > 0 else 0 + if len(data) >= 14: + # Current v1 format (Firmware 1.0+) + # Temperature at index 11: 0.5 C resolution, -40 C offset + temperature = (data[11] / 2.0) - 40.0 - # Temperature at byte 9 (signed int8, same as ATC) - if len(data) >= 10: - import struct - temperature = struct.unpack(" 0: + battery_pct = self._calculate_battery_percentage(battery_mv) + else: + # Legacy format: Battery voltage at bytes 7-8 (same as ATC) + if len(data) >= 9: + battery_mv = int.from_bytes(data[7:9], "little") + + if battery_mv > 0: + battery_pct = self._calculate_battery_percentage(battery_mv) + + # Temperature at byte 9 (signed int8, same as ATC) + if len(data) >= 10: + import struct + temperature = float(struct.unpack("