Add new sensors to BThome (#77561)

This commit is contained in:
Ernst Klamer 2022-08-30 23:03:41 +02:00 committed by GitHub
parent 7c5a5f86ee
commit f43f440739
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 134 additions and 21 deletions

View file

@ -13,7 +13,7 @@
"service_data_uuid": "0000181e-0000-1000-8000-00805f9b34fb"
}
],
"requirements": ["bthome-ble==0.4.0"],
"requirements": ["bthome-ble==0.5.2"],
"dependencies": ["bluetooth"],
"codeowners": ["@Ernst79"],
"iot_class": "local_push"

View file

@ -25,6 +25,7 @@ from homeassistant.const import (
ENERGY_KILO_WATT_HOUR,
LIGHT_LUX,
MASS_KILOGRAMS,
MASS_POUNDS,
PERCENTAGE,
POWER_WATT,
PRESSURE_MBAR,
@ -132,13 +133,41 @@ SENSOR_DESCRIPTIONS = {
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
# Used for e.g. weight sensor
# Used for mass sensor with kg unit
(None, Units.MASS_KILOGRAMS): SensorEntityDescription(
key=str(Units.MASS_KILOGRAMS),
key=f"{DeviceClass.MASS}_{Units.MASS_KILOGRAMS}",
device_class=None,
native_unit_of_measurement=MASS_KILOGRAMS,
state_class=SensorStateClass.MEASUREMENT,
),
# Used for mass sensor with lb unit
(None, Units.MASS_POUNDS): SensorEntityDescription(
key=f"{DeviceClass.MASS}_{Units.MASS_POUNDS}",
device_class=None,
native_unit_of_measurement=MASS_POUNDS,
state_class=SensorStateClass.MEASUREMENT,
),
# Used for moisture sensor
(None, Units.PERCENTAGE,): SensorEntityDescription(
key=f"{DeviceClass.MOISTURE}_{Units.PERCENTAGE}",
device_class=None,
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
),
# Used for dew point sensor
(None, Units.TEMP_CELSIUS): SensorEntityDescription(
key=f"{DeviceClass.DEW_POINT}_{Units.TEMP_CELSIUS}",
device_class=SensorDeviceClass.TEMPERATURE,
native_unit_of_measurement=TEMP_CELSIUS,
state_class=SensorStateClass.MEASUREMENT,
),
# Used for count sensor
(None, None): SensorEntityDescription(
key=f"{DeviceClass.COUNT}",
device_class=None,
native_unit_of_measurement=None,
state_class=SensorStateClass.MEASUREMENT,
),
}
@ -156,7 +185,6 @@ def sensor_update_to_bluetooth_data_update(
(description.device_class, description.native_unit_of_measurement)
]
for device_key, description in sensor_update.entity_descriptions.items()
if description.native_unit_of_measurement
},
entity_data={
device_key_to_bluetooth_entity_key(device_key): sensor_values.native_value

View file

@ -458,7 +458,7 @@ bsblan==0.5.0
bt_proximity==0.2.1
# homeassistant.components.bthome
bthome-ble==0.4.0
bthome-ble==0.5.2
# homeassistant.components.bt_home_hub_5
bthomehub5-devicelist==0.1.1

View file

@ -359,7 +359,7 @@ brunt==1.2.0
bsblan==0.5.0
# homeassistant.components.bthome
bthome-ble==0.4.0
bthome-ble==0.5.2
# homeassistant.components.buienradar
buienradar==1.0.5

View file

@ -37,18 +37,18 @@ TEMP_HUMI_ENCRYPTED_SERVICE_INFO = BluetoothServiceInfoBleak(
connectable=False,
)
PM_SERVICE_INFO = BluetoothServiceInfoBleak(
name="TEST DEVICE 8F80A5",
PRST_SERVICE_INFO = BluetoothServiceInfoBleak(
name="prst 8F80A5",
address="54:48:E6:8F:80:A5",
device=BLEDevice("54:48:E6:8F:80:A5", None),
rssi=-63,
manufacturer_data={},
service_data={
"0000181c-0000-1000-8000-00805f9b34fb": b"\x03\r\x12\x0c\x03\x0e\x02\x1c"
"0000181c-0000-1000-8000-00805f9b34fb": b'\x02\x14\x00\n"\x02\xdd\n\x02\x03{\x12\x02\x0c\n\x0b'
},
service_uuids=["0000181c-0000-1000-8000-00805f9b34fb"],
source="local",
advertisement=AdvertisementData(local_name="Not it"),
advertisement=AdvertisementData(local_name="prst"),
time=0,
connectable=False,
)

View file

@ -11,7 +11,7 @@ from homeassistant.data_entry_flow import FlowResultType
from . import (
NOT_BTHOME_SERVICE_INFO,
PM_SERVICE_INFO,
PRST_SERVICE_INFO,
TEMP_HUMI_ENCRYPTED_SERVICE_INFO,
TEMP_HUMI_SERVICE_INFO,
)
@ -185,7 +185,7 @@ async def test_async_step_user_with_found_devices(hass):
"""Test setup from service info cache with devices found."""
with patch(
"homeassistant.components.bthome.config_flow.async_discovered_service_info",
return_value=[PM_SERVICE_INFO],
return_value=[PRST_SERVICE_INFO],
):
result = await hass.config_entries.flow.async_init(
DOMAIN,
@ -199,7 +199,7 @@ async def test_async_step_user_with_found_devices(hass):
user_input={"address": "54:48:E6:8F:80:A5"},
)
assert result2["type"] == FlowResultType.CREATE_ENTRY
assert result2["title"] == "TEST DEVICE 80A5"
assert result2["title"] == "b-parasite 80A5"
assert result2["data"] == {}
assert result2["result"].unique_id == "54:48:E6:8F:80:A5"
@ -384,7 +384,7 @@ async def test_async_step_bluetooth_devices_already_setup(hass):
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_BLUETOOTH},
data=PM_SERVICE_INFO,
data=PRST_SERVICE_INFO,
)
assert result["type"] == FlowResultType.ABORT
assert result["reason"] == "already_configured"
@ -395,7 +395,7 @@ async def test_async_step_bluetooth_already_in_progress(hass):
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_BLUETOOTH},
data=PM_SERVICE_INFO,
data=PRST_SERVICE_INFO,
)
assert result["type"] == FlowResultType.FORM
assert result["step_id"] == "bluetooth_confirm"
@ -403,7 +403,7 @@ async def test_async_step_bluetooth_already_in_progress(hass):
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_BLUETOOTH},
data=PM_SERVICE_INFO,
data=PRST_SERVICE_INFO,
)
assert result["type"] == FlowResultType.ABORT
assert result["reason"] == "already_in_progress"
@ -414,14 +414,14 @@ async def test_async_step_user_takes_precedence_over_discovery(hass):
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_BLUETOOTH},
data=PM_SERVICE_INFO,
data=PRST_SERVICE_INFO,
)
assert result["type"] == FlowResultType.FORM
assert result["step_id"] == "bluetooth_confirm"
with patch(
"homeassistant.components.bthome.config_flow.async_discovered_service_info",
return_value=[PM_SERVICE_INFO],
return_value=[PRST_SERVICE_INFO],
):
result = await hass.config_entries.flow.async_init(
DOMAIN,
@ -435,7 +435,7 @@ async def test_async_step_user_takes_precedence_over_discovery(hass):
user_input={"address": "54:48:E6:8F:80:A5"},
)
assert result2["type"] == FlowResultType.CREATE_ENTRY
assert result2["title"] == "TEST DEVICE 80A5"
assert result2["title"] == "b-parasite 80A5"
assert result2["data"] == {}
assert result2["result"].unique_id == "54:48:E6:8F:80:A5"

View file

@ -106,6 +106,73 @@ from tests.common import MockConfigEntry
},
],
),
(
"A4:C1:38:8D:18:B2",
make_advertisement(
"A4:C1:38:8D:18:B2",
b"\x03\x06\x5e\x1f",
),
None,
[
{
"sensor_entity": "sensor.test_device_18b2_mass",
"friendly_name": "Test Device 18B2 Mass",
"unit_of_measurement": "kg",
"state_class": "measurement",
"expected_state": "80.3",
},
],
),
(
"A4:C1:38:8D:18:B2",
make_advertisement(
"A4:C1:38:8D:18:B2",
b"\x03\x07\x3e\x1d",
),
None,
[
{
"sensor_entity": "sensor.test_device_18b2_mass",
"friendly_name": "Test Device 18B2 Mass",
"unit_of_measurement": "lb",
"state_class": "measurement",
"expected_state": "74.86",
},
],
),
(
"A4:C1:38:8D:18:B2",
make_advertisement(
"A4:C1:38:8D:18:B2",
b"\x23\x08\xCA\x06",
),
None,
[
{
"sensor_entity": "sensor.test_device_18b2_dew_point",
"friendly_name": "Test Device 18B2 Dew Point",
"unit_of_measurement": "°C",
"state_class": "measurement",
"expected_state": "17.38",
},
],
),
(
"A4:C1:38:8D:18:B2",
make_advertisement(
"A4:C1:38:8D:18:B2",
b"\x02\x09\x60",
),
None,
[
{
"sensor_entity": "sensor.test_device_18b2_count",
"friendly_name": "Test Device 18B2 Count",
"state_class": "measurement",
"expected_state": "96",
},
],
),
(
"A4:C1:38:8D:18:B2",
make_advertisement(
@ -215,6 +282,23 @@ from tests.common import MockConfigEntry
},
],
),
(
"A4:C1:38:8D:18:B2",
make_advertisement(
"A4:C1:38:8D:18:B2",
b"\x03\x14\x02\x0c",
),
None,
[
{
"sensor_entity": "sensor.test_device_18b2_moisture",
"friendly_name": "Test Device 18B2 Moisture",
"unit_of_measurement": "%",
"state_class": "measurement",
"expected_state": "30.74",
},
],
),
(
"54:48:E6:8F:80:A5",
make_encrypted_advertisement(
@ -283,8 +367,9 @@ async def test_sensors(
sensor = hass.states.get(meas["sensor_entity"])
sensor_attr = sensor.attributes
assert sensor.state == meas["expected_state"]
assert sensor_attr[ATTR_FRIENDLY_NAME] == meas["friendly_name"]
if ATTR_UNIT_OF_MEASUREMENT in sensor_attr:
# Count sensor does not have a unit of measurement
assert sensor_attr[ATTR_UNIT_OF_MEASUREMENT] == meas["unit_of_measurement"]
assert sensor_attr[ATTR_STATE_CLASS] == meas["state_class"]
assert await hass.config_entries.async_unload(entry.entry_id)