Add door, opening and motion sensors to Xiaomi-ble (#84990)

This commit is contained in:
Ernst Klamer 2023-01-03 19:21:28 +01:00 committed by GitHub
parent b5664f9eaf
commit 171e114ec1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 219 additions and 20 deletions

View file

@ -5,6 +5,7 @@ from typing import Optional
from xiaomi_ble.parser import (
BinarySensorDeviceClass as XiaomiBinarySensorDeviceClass,
ExtendedBinarySensorDeviceClass,
SensorUpdate,
)
@ -28,20 +29,48 @@ from .const import DOMAIN
from .device import device_key_to_bluetooth_entity_key
BINARY_SENSOR_DESCRIPTIONS = {
XiaomiBinarySensorDeviceClass.MOTION: BinarySensorEntityDescription(
key=XiaomiBinarySensorDeviceClass.MOTION,
device_class=BinarySensorDeviceClass.MOTION,
XiaomiBinarySensorDeviceClass.DOOR: BinarySensorEntityDescription(
key=XiaomiBinarySensorDeviceClass.DOOR,
device_class=BinarySensorDeviceClass.DOOR,
),
XiaomiBinarySensorDeviceClass.LIGHT: BinarySensorEntityDescription(
key=XiaomiBinarySensorDeviceClass.LIGHT,
device_class=BinarySensorDeviceClass.LIGHT,
),
XiaomiBinarySensorDeviceClass.MOISTURE: BinarySensorEntityDescription(
key=XiaomiBinarySensorDeviceClass.MOISTURE,
device_class=BinarySensorDeviceClass.MOISTURE,
),
XiaomiBinarySensorDeviceClass.MOTION: BinarySensorEntityDescription(
key=XiaomiBinarySensorDeviceClass.MOTION,
device_class=BinarySensorDeviceClass.MOTION,
),
XiaomiBinarySensorDeviceClass.OPENING: BinarySensorEntityDescription(
key=XiaomiBinarySensorDeviceClass.OPENING,
device_class=BinarySensorDeviceClass.OPENING,
),
XiaomiBinarySensorDeviceClass.SMOKE: BinarySensorEntityDescription(
key=XiaomiBinarySensorDeviceClass.SMOKE,
device_class=BinarySensorDeviceClass.SMOKE,
),
XiaomiBinarySensorDeviceClass.MOISTURE: BinarySensorEntityDescription(
key=XiaomiBinarySensorDeviceClass.MOISTURE,
ExtendedBinarySensorDeviceClass.DEVICE_FORCIBLY_REMOVED: BinarySensorEntityDescription(
key=ExtendedBinarySensorDeviceClass.DEVICE_FORCIBLY_REMOVED,
device_class=BinarySensorDeviceClass.PROBLEM,
),
ExtendedBinarySensorDeviceClass.DOOR_LEFT_OPEN: BinarySensorEntityDescription(
key=ExtendedBinarySensorDeviceClass.DOOR_LEFT_OPEN,
device_class=BinarySensorDeviceClass.PROBLEM,
),
ExtendedBinarySensorDeviceClass.DOOR_STUCK: BinarySensorEntityDescription(
key=ExtendedBinarySensorDeviceClass.DOOR_STUCK,
device_class=BinarySensorDeviceClass.PROBLEM,
),
ExtendedBinarySensorDeviceClass.KNOCK_ON_THE_DOOR: BinarySensorEntityDescription(
key=ExtendedBinarySensorDeviceClass.KNOCK_ON_THE_DOOR,
),
ExtendedBinarySensorDeviceClass.PRY_THE_DOOR: BinarySensorEntityDescription(
key=ExtendedBinarySensorDeviceClass.PRY_THE_DOOR,
device_class=BinarySensorDeviceClass.TAMPER,
),
}

View file

@ -13,7 +13,7 @@
"service_data_uuid": "0000fe95-0000-1000-8000-00805f9b34fb"
}
],
"requirements": ["xiaomi-ble==0.12.2"],
"requirements": ["xiaomi-ble==0.14.3"],
"dependencies": ["bluetooth"],
"codeowners": ["@Jc2k", "@Ernst79"],
"iot_class": "local_push"

View file

@ -2597,7 +2597,7 @@ xbox-webapi==2.0.11
xboxapi==2.0.1
# homeassistant.components.xiaomi_ble
xiaomi-ble==0.12.2
xiaomi-ble==0.14.3
# homeassistant.components.knx
xknx==2.2.0

View file

@ -1816,7 +1816,7 @@ wolf_smartset==0.1.11
xbox-webapi==2.0.11
# homeassistant.components.xiaomi_ble
xiaomi-ble==0.12.2
xiaomi-ble==0.14.3
# homeassistant.components.knx
xknx==2.2.0

View file

@ -9,12 +9,12 @@ from tests.common import MockConfigEntry
from tests.components.bluetooth import inject_bluetooth_service_info_bleak
async def test_smoke_sensor(hass):
"""Test setting up a smoke sensor."""
async def test_door_problem_sensors(hass):
"""Test setting up a door binary sensor with additional problem sensors."""
entry = MockConfigEntry(
domain=DOMAIN,
unique_id="54:EF:44:E3:9C:BC",
data={"bindkey": "5b51a7c91cde6707c9ef18dfda143a58"},
unique_id="EE:89:73:44:BE:98",
data={"bindkey": "2c3795afa33019a8afdc17ba99e6f217"},
)
entry.add_to_hass(hass)
@ -25,24 +25,72 @@ async def test_smoke_sensor(hass):
inject_bluetooth_service_info_bleak(
hass,
make_advertisement(
"54:EF:44:E3:9C:BC",
b"XY\x97\tf\xbc\x9c\xe3D\xefT\x01" b"\x08\x12\x05\x00\x00\x00q^\xbe\x90",
"EE:89:73:44:BE:98",
b"HU9\x0e3\x9cq\xc0$\x1f\xff\xee\x80S\x00\x00\x02\xb4\xc59",
),
)
await hass.async_block_till_done()
assert len(hass.states.async_all()) == 1
assert len(hass.states.async_all()) == 3
smoke_sensor = hass.states.get("binary_sensor.thermometer_9cbc_smoke")
smoke_sensor_attribtes = smoke_sensor.attributes
assert smoke_sensor.state == "on"
assert smoke_sensor_attribtes[ATTR_FRIENDLY_NAME] == "Thermometer 9CBC Smoke"
door_sensor = hass.states.get("binary_sensor.door_lock_be98_door")
door_sensor_attribtes = door_sensor.attributes
assert door_sensor.state == "off"
assert door_sensor_attribtes[ATTR_FRIENDLY_NAME] == "Door Lock BE98 Door"
door_left_open = hass.states.get("binary_sensor.door_lock_be98_door_left_open")
door_left_open_attribtes = door_left_open.attributes
assert door_left_open.state == "off"
assert (
door_left_open_attribtes[ATTR_FRIENDLY_NAME] == "Door Lock BE98 Door left open"
)
pry_the_door = hass.states.get("binary_sensor.door_lock_be98_pry_the_door")
pry_the_door_attribtes = pry_the_door.attributes
assert pry_the_door.state == "off"
assert pry_the_door_attribtes[ATTR_FRIENDLY_NAME] == "Door Lock BE98 Pry the door"
assert await hass.config_entries.async_unload(entry.entry_id)
await hass.async_block_till_done()
async def test_light_motion(hass):
"""Test setting up a light and motion binary sensor."""
entry = MockConfigEntry(
domain=DOMAIN,
unique_id="58:2D:34:35:93:21",
)
entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
assert len(hass.states.async_all()) == 0
inject_bluetooth_service_info_bleak(
hass,
make_advertisement(
"58:2D:34:35:93:21",
b"P \xf6\x07\xda!\x9354-X\x0f\x00\x03\x01\x00\x00",
),
)
await hass.async_block_till_done()
assert len(hass.states.async_all()) == 2
motion_sensor = hass.states.get("binary_sensor.nightlight_9321_motion")
motion_sensor_attribtes = motion_sensor.attributes
assert motion_sensor.state == "on"
assert motion_sensor_attribtes[ATTR_FRIENDLY_NAME] == "Nightlight 9321 Motion"
light_sensor = hass.states.get("binary_sensor.nightlight_9321_light")
light_sensor_attribtes = light_sensor.attributes
assert light_sensor.state == "off"
assert light_sensor_attribtes[ATTR_FRIENDLY_NAME] == "Nightlight 9321 Light"
assert await hass.config_entries.async_unload(entry.entry_id)
await hass.async_block_till_done()
async def test_moisture(hass):
"""Make sure that formldehyde sensors are correctly mapped."""
"""Test setting up a moisture binary sensor."""
entry = MockConfigEntry(
domain=DOMAIN,
unique_id="C4:7C:8D:6A:3E:7A",
@ -73,3 +121,125 @@ async def test_moisture(hass):
assert await hass.config_entries.async_unload(entry.entry_id)
await hass.async_block_till_done()
async def test_opening(hass):
"""Test setting up a opening binary sensor."""
entry = MockConfigEntry(
domain=DOMAIN,
unique_id="A4:C1:38:66:E5:67",
data={"bindkey": "0fdcc30fe9289254876b5ef7c11ef1f0"},
)
entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
assert len(hass.states.async_all()) == 0
inject_bluetooth_service_info_bleak(
hass,
make_advertisement(
"A4:C1:38:66:E5:67",
b"XY\x89\x18\x9ag\xe5f8\xc1\xa4\x9d\xd9z\xf3&\x00\x00\xc8\xa6\x0b\xd5",
),
)
await hass.async_block_till_done()
assert len(hass.states.async_all()) == 1
opening_sensor = hass.states.get("binary_sensor.door_window_sensor_e567_opening")
opening_sensor_attribtes = opening_sensor.attributes
assert opening_sensor.state == "on"
assert (
opening_sensor_attribtes[ATTR_FRIENDLY_NAME]
== "Door/Window Sensor E567 Opening"
)
assert await hass.config_entries.async_unload(entry.entry_id)
await hass.async_block_till_done()
async def test_opening_problem_sensors(hass):
"""Test setting up a opening binary sensor with additional problem sensors."""
entry = MockConfigEntry(
domain=DOMAIN,
unique_id="A4:C1:38:66:E5:67",
data={"bindkey": "0fdcc30fe9289254876b5ef7c11ef1f0"},
)
entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
assert len(hass.states.async_all()) == 0
inject_bluetooth_service_info_bleak(
hass,
make_advertisement(
"A4:C1:38:66:E5:67",
b"XY\x89\x18ug\xe5f8\xc1\xa4i\xdd\xf3\xa1&\x00\x00\xa2J\x1bE",
),
)
await hass.async_block_till_done()
assert len(hass.states.async_all()) == 3
opening_sensor = hass.states.get("binary_sensor.door_window_sensor_e567_opening")
opening_sensor_attribtes = opening_sensor.attributes
assert opening_sensor.state == "off"
assert (
opening_sensor_attribtes[ATTR_FRIENDLY_NAME]
== "Door/Window Sensor E567 Opening"
)
door_left_open = hass.states.get(
"binary_sensor.door_window_sensor_e567_door_left_open"
)
door_left_open_attribtes = door_left_open.attributes
assert door_left_open.state == "off"
assert (
door_left_open_attribtes[ATTR_FRIENDLY_NAME]
== "Door/Window Sensor E567 Door left open"
)
device_forcibly_removed = hass.states.get(
"binary_sensor.door_window_sensor_e567_device_forcibly_removed"
)
device_forcibly_removed_attribtes = device_forcibly_removed.attributes
assert device_forcibly_removed.state == "off"
assert (
device_forcibly_removed_attribtes[ATTR_FRIENDLY_NAME]
== "Door/Window Sensor E567 Device forcibly removed"
)
assert await hass.config_entries.async_unload(entry.entry_id)
await hass.async_block_till_done()
async def test_smoke(hass):
"""Test setting up a smoke binary sensor."""
entry = MockConfigEntry(
domain=DOMAIN,
unique_id="54:EF:44:E3:9C:BC",
data={"bindkey": "5b51a7c91cde6707c9ef18dfda143a58"},
)
entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
assert len(hass.states.async_all()) == 0
inject_bluetooth_service_info_bleak(
hass,
make_advertisement(
"54:EF:44:E3:9C:BC",
b"XY\x97\tf\xbc\x9c\xe3D\xefT\x01" b"\x08\x12\x05\x00\x00\x00q^\xbe\x90",
),
)
await hass.async_block_till_done()
assert len(hass.states.async_all()) == 1
smoke_sensor = hass.states.get("binary_sensor.thermometer_9cbc_smoke")
smoke_sensor_attribtes = smoke_sensor.attributes
assert smoke_sensor.state == "on"
assert smoke_sensor_attribtes[ATTR_FRIENDLY_NAME] == "Thermometer 9CBC Smoke"
assert await hass.config_entries.async_unload(entry.entry_id)
await hass.async_block_till_done()