Skip to content

Commit 2cdbc83

Browse files
joeknollpre-commit-ci[bot]bdraco
authored
feat: add support for TP972S (#122)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: J. Nick Koston <nick@home-assistant.io>
1 parent e4bbd2a commit 2cdbc83

File tree

2 files changed

+310
-44
lines changed

2 files changed

+310
-44
lines changed

src/thermopro_ble/parser.py

Lines changed: 80 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@
1111

1212
import logging
1313
from math import tanh
14-
from struct import Struct, error as struct_error
14+
from struct import Struct
1515

1616
from bluetooth_data_tools import short_address
1717
from bluetooth_sensor_state_data import BluetoothData
1818
from sensor_state_data import SensorLibrary
1919

20-
from habluetooth import BluetoothServiceInfoBleak
20+
from habluetooth import BluetoothServiceInfo, BluetoothServiceInfoBleak
2121

2222
_LOGGER = logging.getLogger(__name__)
2323

@@ -30,6 +30,7 @@
3030

3131
UNPACK_TEMP_HUMID = Struct("<hB").unpack
3232
UNPACK_SPIKE_TEMP = Struct("<BHHH").unpack
33+
UNPACK_SPIKE_PRO_TEMP = Struct("<BHHfffHHH").unpack
3334

3435

3536
# TP96x battery values appear to be a voltage reading, probably in millivolts.
@@ -47,7 +48,36 @@ def tp96_battery(voltage: int) -> float:
4748
class ThermoProBluetoothDeviceData(BluetoothData):
4849
"""Date update for ThermoPro Bluetooth devices."""
4950

50-
def _start_update(self, service_info: BluetoothServiceInfoBleak) -> None:
51+
def _update_sensors(
52+
self,
53+
probe_one_indexed: int,
54+
internal_temp: int,
55+
ambient_temp: int,
56+
battery_percent: float,
57+
) -> None:
58+
self.update_predefined_sensor(
59+
SensorLibrary.TEMPERATURE__CELSIUS,
60+
internal_temp,
61+
key=f"internal_temperature_probe_{probe_one_indexed}",
62+
name=f"Probe {probe_one_indexed} Internal Temperature",
63+
)
64+
self.update_predefined_sensor(
65+
SensorLibrary.TEMPERATURE__CELSIUS,
66+
ambient_temp,
67+
key=f"ambient_temperature_probe_{probe_one_indexed}",
68+
name=f"Probe {probe_one_indexed} Ambient Temperature",
69+
)
70+
self.set_precision(0)
71+
self.update_predefined_sensor(
72+
SensorLibrary.BATTERY__PERCENTAGE,
73+
battery_percent,
74+
key=f"battery_probe_{probe_one_indexed}",
75+
name=f"Probe {probe_one_indexed} Battery",
76+
)
77+
78+
def _start_update(
79+
self, service_info: BluetoothServiceInfo | BluetoothServiceInfoBleak
80+
) -> None:
5181
"""Update from BLE advertisement data."""
5282
_LOGGER.debug("Parsing thermopro BLE advertisement data: %s", service_info)
5383
name = service_info.name
@@ -74,56 +104,63 @@ def _start_update(self, service_info: BluetoothServiceInfoBleak) -> None:
74104
+ changed_manufacturer_data[last_id]
75105
)
76106

77-
if len(data) < 6:
107+
data_length = len(data)
108+
109+
if data_length == 23 and name.startswith("TP97"):
110+
# TP972 has a 23-byte format
111+
# It has an internal temp probe and an ambient temp probe
112+
(
113+
probe_zero_indexed,
114+
ambient_temp,
115+
battery_voltage,
116+
_, # looks to be part of some temp range (min)
117+
internal_temp,
118+
_, # looks to be part of some temp range (max)
119+
_,
120+
_,
121+
_, # looks like a static id
122+
) = UNPACK_SPIKE_PRO_TEMP(data)
123+
124+
probe_one_indexed = probe_zero_indexed + 1
125+
internal_temp = int(internal_temp) - 54
126+
ambient_temp = int(ambient_temp) - 54
127+
battery_percent = tp96_battery(battery_voltage)
128+
self._update_sensors(
129+
probe_one_indexed, internal_temp, ambient_temp, battery_percent
130+
)
78131
return
79132

80-
if name.startswith(("TP96", "TP97")):
81-
# TP96 has a different format
133+
if data_length == 7 and name.startswith(("TP96", "TP97")):
134+
# TP96 has a different format that is shared with TP970
82135
# It has an internal temp probe and an ambient temp probe
83-
try:
84-
(
85-
probe_zero_indexed,
86-
internal_temp,
87-
battery_voltage,
88-
ambient_temp,
89-
) = UNPACK_SPIKE_TEMP(data)
90-
except struct_error:
91-
_LOGGER.error("Error parsing data from probe: %s", data)
92-
return
136+
(
137+
probe_zero_indexed,
138+
internal_temp,
139+
battery_voltage,
140+
ambient_temp,
141+
) = UNPACK_SPIKE_TEMP(data)
93142

94143
probe_one_indexed = probe_zero_indexed + 1
95144
internal_temp = internal_temp - 30
96145
ambient_temp = ambient_temp - 30
97146
battery_percent = tp96_battery(battery_voltage)
98-
self.update_predefined_sensor(
99-
SensorLibrary.TEMPERATURE__CELSIUS,
100-
internal_temp,
101-
key=f"internal_temperature_probe_{probe_one_indexed}",
102-
name=f"Probe {probe_one_indexed} Internal Temperature",
103-
)
104-
self.update_predefined_sensor(
105-
SensorLibrary.TEMPERATURE__CELSIUS,
106-
ambient_temp,
107-
key=f"ambient_temperature_probe_{probe_one_indexed}",
108-
name=f"Probe {probe_one_indexed} Ambient Temperature",
109-
)
110-
self.set_precision(0)
111-
self.update_predefined_sensor(
112-
SensorLibrary.BATTERY__PERCENTAGE,
113-
battery_percent,
114-
key=f"battery_probe_{probe_one_indexed}",
115-
name=f"Probe {probe_one_indexed} Battery",
147+
self._update_sensors(
148+
probe_one_indexed, internal_temp, ambient_temp, battery_percent
116149
)
117150
return
118151

119152
# TP357S seems to be in 6, TP397 and TP393 in 4
120-
battery_byte = data[6] if len(data) == 7 else data[4]
121-
if battery_byte in BATTERY_VALUE_TO_LEVEL:
122-
self.update_predefined_sensor(
123-
SensorLibrary.BATTERY__PERCENTAGE,
124-
BATTERY_VALUE_TO_LEVEL[battery_byte],
125-
)
153+
if data_length >= 6 and name.startswith("TP3"):
154+
battery_byte = data[6] if data_length == 7 else data[4]
155+
if battery_byte in BATTERY_VALUE_TO_LEVEL:
156+
self.update_predefined_sensor(
157+
SensorLibrary.BATTERY__PERCENTAGE,
158+
BATTERY_VALUE_TO_LEVEL[battery_byte],
159+
)
160+
161+
(temp, humi) = UNPACK_TEMP_HUMID(data[1:4])
162+
self.update_predefined_sensor(SensorLibrary.TEMPERATURE__CELSIUS, temp / 10)
163+
self.update_predefined_sensor(SensorLibrary.HUMIDITY__PERCENTAGE, humi)
164+
return
126165

127-
(temp, humi) = UNPACK_TEMP_HUMID(data[1:4])
128-
self.update_predefined_sensor(SensorLibrary.TEMPERATURE__CELSIUS, temp / 10)
129-
self.update_predefined_sensor(SensorLibrary.HUMIDITY__PERCENTAGE, humi)
166+
_LOGGER.error("Error parsing data from probe: %s", data)

0 commit comments

Comments
 (0)