Add xiaomi_ble voltage, consumable and formaldehyde sensors (#75807)

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
Jc2k 2022-07-27 11:28:58 +01:00 committed by GitHub
parent aaf5837759
commit f8b115dd9d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 165 additions and 3 deletions

View File

@ -8,7 +8,7 @@
"service_uuid": "0000fe95-0000-1000-8000-00805f9b34fb"
}
],
"requirements": ["xiaomi-ble==0.5.1"],
"requirements": ["xiaomi-ble==0.6.1"],
"dependencies": ["bluetooth"],
"codeowners": ["@Jc2k", "@Ernst79"],
"iot_class": "local_push"

View File

@ -30,7 +30,9 @@ from homeassistant.const import (
ATTR_MANUFACTURER,
ATTR_MODEL,
ATTR_NAME,
CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER,
CONDUCTIVITY,
ELECTRIC_POTENTIAL_VOLT,
LIGHT_LUX,
PERCENTAGE,
PRESSURE_MBAR,
@ -74,6 +76,12 @@ SENSOR_DESCRIPTIONS = {
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
),
(DeviceClass.VOLTAGE, Units.ELECTRIC_POTENTIAL_VOLT): SensorEntityDescription(
key=str(Units.ELECTRIC_POTENTIAL_VOLT),
device_class=SensorDeviceClass.VOLTAGE,
native_unit_of_measurement=ELECTRIC_POTENTIAL_VOLT,
state_class=SensorStateClass.MEASUREMENT,
),
(
DeviceClass.SIGNAL_STRENGTH,
Units.SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
@ -98,6 +106,12 @@ SENSOR_DESCRIPTIONS = {
native_unit_of_measurement=CONDUCTIVITY,
state_class=SensorStateClass.MEASUREMENT,
),
# Used for e.g. formaldehyde
(None, Units.CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER): SensorEntityDescription(
key=str(Units.CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER),
native_unit_of_measurement=CONCENTRATION_MILLIGRAMS_PER_CUBIC_METER,
state_class=SensorStateClass.MEASUREMENT,
),
}

View File

@ -2470,7 +2470,7 @@ xbox-webapi==2.0.11
xboxapi==2.0.1
# homeassistant.components.xiaomi_ble
xiaomi-ble==0.5.1
xiaomi-ble==0.6.1
# homeassistant.components.knx
xknx==0.22.0

View File

@ -1662,7 +1662,7 @@ wolf_smartset==0.1.11
xbox-webapi==2.0.11
# homeassistant.components.xiaomi_ble
xiaomi-ble==0.5.1
xiaomi-ble==0.6.1
# homeassistant.components.knx
xknx==0.22.0

View File

@ -50,6 +50,154 @@ async def test_sensors(hass):
await hass.async_block_till_done()
async def test_xiaomi_formaldeyhde(hass):
"""Make sure that formldehyde sensors are correctly mapped."""
entry = MockConfigEntry(
domain=DOMAIN,
unique_id="C4:7C:8D:6A:3E:7A",
)
entry.add_to_hass(hass)
saved_callback = None
def _async_register_callback(_hass, _callback, _matcher):
nonlocal saved_callback
saved_callback = _callback
return lambda: None
with patch(
"homeassistant.components.bluetooth.update_coordinator.async_register_callback",
_async_register_callback,
):
assert await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
assert len(hass.states.async_all()) == 0
# WARNING: This test data is synthetic, rather than captured from a real device
# obj type is 0x1010, payload len is 0x2 and payload is 0xf400
saved_callback(
make_advertisement(
"C4:7C:8D:6A:3E:7A", b"q \x98\x00iz>j\x8d|\xc4\r\x10\x10\x02\xf4\x00"
),
BluetoothChange.ADVERTISEMENT,
)
await hass.async_block_till_done()
assert len(hass.states.async_all()) == 1
sensor = hass.states.get("sensor.test_device_formaldehyde")
sensor_attr = sensor.attributes
assert sensor.state == "2.44"
assert sensor_attr[ATTR_FRIENDLY_NAME] == "Test Device Formaldehyde"
assert sensor_attr[ATTR_UNIT_OF_MEASUREMENT] == "mg/m³"
assert sensor_attr[ATTR_STATE_CLASS] == "measurement"
assert await hass.config_entries.async_unload(entry.entry_id)
await hass.async_block_till_done()
async def test_xiaomi_consumable(hass):
"""Make sure that consumable sensors are correctly mapped."""
entry = MockConfigEntry(
domain=DOMAIN,
unique_id="C4:7C:8D:6A:3E:7A",
)
entry.add_to_hass(hass)
saved_callback = None
def _async_register_callback(_hass, _callback, _matcher):
nonlocal saved_callback
saved_callback = _callback
return lambda: None
with patch(
"homeassistant.components.bluetooth.update_coordinator.async_register_callback",
_async_register_callback,
):
assert await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
assert len(hass.states.async_all()) == 0
# WARNING: This test data is synthetic, rather than captured from a real device
# obj type is 0x1310, payload len is 0x2 and payload is 0x6000
saved_callback(
make_advertisement(
"C4:7C:8D:6A:3E:7A", b"q \x98\x00iz>j\x8d|\xc4\r\x13\x10\x02\x60\x00"
),
BluetoothChange.ADVERTISEMENT,
)
await hass.async_block_till_done()
assert len(hass.states.async_all()) == 1
sensor = hass.states.get("sensor.test_device_consumable")
sensor_attr = sensor.attributes
assert sensor.state == "96"
assert sensor_attr[ATTR_FRIENDLY_NAME] == "Test Device Consumable"
assert sensor_attr[ATTR_UNIT_OF_MEASUREMENT] == "%"
assert sensor_attr[ATTR_STATE_CLASS] == "measurement"
assert await hass.config_entries.async_unload(entry.entry_id)
await hass.async_block_till_done()
async def test_xiaomi_battery_voltage(hass):
"""Make sure that battery voltage sensors are correctly mapped."""
entry = MockConfigEntry(
domain=DOMAIN,
unique_id="C4:7C:8D:6A:3E:7A",
)
entry.add_to_hass(hass)
saved_callback = None
def _async_register_callback(_hass, _callback, _matcher):
nonlocal saved_callback
saved_callback = _callback
return lambda: None
with patch(
"homeassistant.components.bluetooth.update_coordinator.async_register_callback",
_async_register_callback,
):
assert await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
assert len(hass.states.async_all()) == 0
# WARNING: This test data is synthetic, rather than captured from a real device
# obj type is 0x0a10, payload len is 0x2 and payload is 0x6400
saved_callback(
make_advertisement(
"C4:7C:8D:6A:3E:7A", b"q \x98\x00iz>j\x8d|\xc4\r\x0a\x10\x02\x64\x00"
),
BluetoothChange.ADVERTISEMENT,
)
await hass.async_block_till_done()
assert len(hass.states.async_all()) == 2
volt_sensor = hass.states.get("sensor.test_device_voltage")
volt_sensor_attr = volt_sensor.attributes
assert volt_sensor.state == "3.1"
assert volt_sensor_attr[ATTR_FRIENDLY_NAME] == "Test Device Voltage"
assert volt_sensor_attr[ATTR_UNIT_OF_MEASUREMENT] == "V"
assert volt_sensor_attr[ATTR_STATE_CLASS] == "measurement"
bat_sensor = hass.states.get("sensor.test_device_battery")
bat_sensor_attr = bat_sensor.attributes
assert bat_sensor.state == "100"
assert bat_sensor_attr[ATTR_FRIENDLY_NAME] == "Test Device Battery"
assert bat_sensor_attr[ATTR_UNIT_OF_MEASUREMENT] == "%"
assert bat_sensor_attr[ATTR_STATE_CLASS] == "measurement"
assert await hass.config_entries.async_unload(entry.entry_id)
await hass.async_block_till_done()
async def test_xiaomi_HHCCJCY01(hass):
"""This device has multiple advertisements before all sensors are visible. Test that this works."""
entry = MockConfigEntry(