Skip to content

Commit bdcf2a0

Browse files
authored
feat: add support for raw data parsing (#30)
1 parent 8753e5c commit bdcf2a0

File tree

2 files changed

+139
-10
lines changed

2 files changed

+139
-10
lines changed

src/sensorpro_ble/parser.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@
1111
import logging
1212
from struct import unpack
1313

14-
from bluetooth_data_tools import short_address
14+
from bluetooth_data_tools import parse_advertisement_data_bytes, short_address
1515
from bluetooth_sensor_state_data import BluetoothData
16-
from home_assistant_bluetooth import BluetoothServiceInfo
16+
from habluetooth import BluetoothServiceInfoBleak
1717
from sensor_state_data import SensorLibrary
1818

1919
_LOGGER = logging.getLogger(__name__)
@@ -30,12 +30,19 @@
3030
class SensorProBluetoothDeviceData(BluetoothData):
3131
"""Date update for SensorPro Bluetooth devices."""
3232

33-
def _start_update(self, service_info: BluetoothServiceInfo) -> None:
33+
def _start_update(self, service_info: BluetoothServiceInfoBleak) -> None:
3434
"""Update from BLE advertisement data."""
3535
_LOGGER.debug("Parsing sensorpro BLE advertisement data: %s", service_info)
36-
if 43605 not in service_info.manufacturer_data:
36+
if service_info.raw:
37+
# If we have the raw data we don't need to work out
38+
# which one is the newest.
39+
_, _, _, changed_manufacturer_data, _ = parse_advertisement_data_bytes(
40+
service_info.raw
41+
)
42+
else:
43+
changed_manufacturer_data = self.changed_manufacturer_data(service_info)
44+
if 43605 not in changed_manufacturer_data:
3745
return
38-
changed_manufacturer_data = self.changed_manufacturer_data(service_info)
3946
if not changed_manufacturer_data or len(changed_manufacturer_data) > 1:
4047
# If len(changed_manufacturer_data) > 1 it means we switched
4148
# ble adapters so we do not know which data is the latest

tests/test_parser.py

Lines changed: 127 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
from bluetooth_sensor_state_data import BluetoothServiceInfo, SensorUpdate
1+
from uuid import UUID
2+
3+
from bleak.backends.device import BLEDevice
4+
from bluetooth_data_tools import monotonic_time_coarse
5+
from bluetooth_sensor_state_data import SensorUpdate
6+
from habluetooth import BluetoothServiceInfoBleak
27
from sensor_state_data import (
38
DeviceKey,
49
SensorDescription,
@@ -11,11 +16,44 @@
1116
from sensorpro_ble.parser import SensorProBluetoothDeviceData
1217

1318

19+
def make_bluetooth_service_info( # noqa: PLR0913
20+
name: str,
21+
manufacturer_data: dict[int, bytes],
22+
service_uuids: list[str],
23+
address: str,
24+
rssi: int,
25+
service_data: dict[UUID, bytes],
26+
source: str,
27+
tx_power: int = 0,
28+
raw: bytes | None = None,
29+
) -> BluetoothServiceInfoBleak:
30+
return BluetoothServiceInfoBleak(
31+
name=name,
32+
manufacturer_data=manufacturer_data,
33+
service_uuids=service_uuids,
34+
address=address,
35+
rssi=rssi,
36+
service_data=service_data,
37+
source=source,
38+
device=BLEDevice(
39+
name=name,
40+
address=address,
41+
details={},
42+
rssi=rssi,
43+
),
44+
time=monotonic_time_coarse(),
45+
advertisement=None,
46+
connectable=True,
47+
tx_power=tx_power,
48+
raw=raw,
49+
)
50+
51+
1452
def test_can_create():
1553
SensorProBluetoothDeviceData()
1654

1755

18-
MFR_T201 = BluetoothServiceInfo(
56+
MFR_T201 = make_bluetooth_service_info(
1957
name="T201",
2058
address="aa:bb:cc:dd:ee:ff",
2159
rssi=-60,
@@ -27,7 +65,7 @@ def test_can_create():
2765
source="local",
2866
)
2967

30-
MFR_T301 = BluetoothServiceInfo(
68+
MFR_T301 = make_bluetooth_service_info(
3169
name="T301",
3270
address="aa:bb:cc:dd:ee:ff",
3371
rssi=-60,
@@ -37,8 +75,19 @@ def test_can_create():
3775
source="local",
3876
)
3977

40-
MFR_T301_NO_NAME = BluetoothServiceInfo(
41-
name=None,
78+
MFR_T301_RAW = make_bluetooth_service_info(
79+
name="T301",
80+
address="aa:bb:cc:dd:ee:ff",
81+
rssi=-60,
82+
service_data={},
83+
manufacturer_data={44: b""},
84+
service_uuids=[],
85+
source="local",
86+
raw=b"\x14\xffU\xaa\x01\x05\xa4\xc18\x1aWv\x01\x07\tV\x17\x0ca\x00\x01",
87+
)
88+
89+
MFR_T301_NO_NAME = make_bluetooth_service_info(
90+
name="",
4291
address="aa:bb:cc:dd:ee:ff",
4392
rssi=-60,
4493
service_data={},
@@ -194,6 +243,79 @@ def test_t301():
194243
)
195244

196245

246+
def test_t301_raw():
247+
parser = SensorProBluetoothDeviceData()
248+
update = parser.update(MFR_T301_RAW)
249+
assert update == SensorUpdate(
250+
title="T301 EEFF",
251+
devices={
252+
None: SensorDeviceInfo(
253+
name="T301 EEFF",
254+
model=5,
255+
manufacturer="SensorPro",
256+
sw_version=None,
257+
hw_version=None,
258+
)
259+
},
260+
entity_descriptions={
261+
DeviceKey(key="voltage", device_id=None): SensorDescription(
262+
device_key=DeviceKey(key="voltage", device_id=None),
263+
device_class=SensorDeviceClass.VOLTAGE,
264+
native_unit_of_measurement=Units.ELECTRIC_POTENTIAL_VOLT,
265+
),
266+
DeviceKey(key="humidity", device_id=None): SensorDescription(
267+
device_key=DeviceKey(key="humidity", device_id=None),
268+
device_class=SensorDeviceClass.HUMIDITY,
269+
native_unit_of_measurement=Units.PERCENTAGE,
270+
),
271+
DeviceKey(key="temperature", device_id=None): SensorDescription(
272+
device_key=DeviceKey(key="temperature", device_id=None),
273+
device_class=SensorDeviceClass.TEMPERATURE,
274+
native_unit_of_measurement=Units.TEMP_CELSIUS,
275+
),
276+
DeviceKey(key="battery", device_id=None): SensorDescription(
277+
device_key=DeviceKey(key="battery", device_id=None),
278+
device_class=SensorDeviceClass.BATTERY,
279+
native_unit_of_measurement=Units.PERCENTAGE,
280+
),
281+
DeviceKey(key="signal_strength", device_id=None): SensorDescription(
282+
device_key=DeviceKey(key="signal_strength", device_id=None),
283+
device_class=SensorDeviceClass.SIGNAL_STRENGTH,
284+
native_unit_of_measurement=Units.SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
285+
),
286+
},
287+
entity_values={
288+
DeviceKey(key="voltage", device_id=None): SensorValue(
289+
device_key=DeviceKey(key="voltage", device_id=None),
290+
name="Voltage",
291+
native_value=2.63,
292+
),
293+
DeviceKey(key="humidity", device_id=None): SensorValue(
294+
device_key=DeviceKey(key="humidity", device_id=None),
295+
name="Humidity",
296+
native_value=59.0,
297+
),
298+
DeviceKey(key="temperature", device_id=None): SensorValue(
299+
device_key=DeviceKey(key="temperature", device_id=None),
300+
name="Temperature",
301+
native_value=23.9,
302+
),
303+
DeviceKey(key="battery", device_id=None): SensorValue(
304+
device_key=DeviceKey(key="battery", device_id=None),
305+
name="Battery",
306+
native_value=97,
307+
),
308+
DeviceKey(key="signal_strength", device_id=None): SensorValue(
309+
device_key=DeviceKey(key="signal_strength", device_id=None),
310+
name="Signal " "Strength",
311+
native_value=-60,
312+
),
313+
},
314+
binary_entity_descriptions={},
315+
binary_entity_values={},
316+
)
317+
318+
197319
def test_t301_passive():
198320
parser = SensorProBluetoothDeviceData()
199321
update = parser.update(MFR_T301_NO_NAME)

0 commit comments

Comments
 (0)