Add door, opening and motion sensors to Xiaomi-ble (#84990)
This commit is contained in:
parent
b5664f9eaf
commit
171e114ec1
5 changed files with 219 additions and 20 deletions
|
@ -5,6 +5,7 @@ from typing import Optional
|
||||||
|
|
||||||
from xiaomi_ble.parser import (
|
from xiaomi_ble.parser import (
|
||||||
BinarySensorDeviceClass as XiaomiBinarySensorDeviceClass,
|
BinarySensorDeviceClass as XiaomiBinarySensorDeviceClass,
|
||||||
|
ExtendedBinarySensorDeviceClass,
|
||||||
SensorUpdate,
|
SensorUpdate,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -28,20 +29,48 @@ from .const import DOMAIN
|
||||||
from .device import device_key_to_bluetooth_entity_key
|
from .device import device_key_to_bluetooth_entity_key
|
||||||
|
|
||||||
BINARY_SENSOR_DESCRIPTIONS = {
|
BINARY_SENSOR_DESCRIPTIONS = {
|
||||||
XiaomiBinarySensorDeviceClass.MOTION: BinarySensorEntityDescription(
|
XiaomiBinarySensorDeviceClass.DOOR: BinarySensorEntityDescription(
|
||||||
key=XiaomiBinarySensorDeviceClass.MOTION,
|
key=XiaomiBinarySensorDeviceClass.DOOR,
|
||||||
device_class=BinarySensorDeviceClass.MOTION,
|
device_class=BinarySensorDeviceClass.DOOR,
|
||||||
),
|
),
|
||||||
XiaomiBinarySensorDeviceClass.LIGHT: BinarySensorEntityDescription(
|
XiaomiBinarySensorDeviceClass.LIGHT: BinarySensorEntityDescription(
|
||||||
key=XiaomiBinarySensorDeviceClass.LIGHT,
|
key=XiaomiBinarySensorDeviceClass.LIGHT,
|
||||||
device_class=BinarySensorDeviceClass.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(
|
XiaomiBinarySensorDeviceClass.SMOKE: BinarySensorEntityDescription(
|
||||||
key=XiaomiBinarySensorDeviceClass.SMOKE,
|
key=XiaomiBinarySensorDeviceClass.SMOKE,
|
||||||
device_class=BinarySensorDeviceClass.SMOKE,
|
device_class=BinarySensorDeviceClass.SMOKE,
|
||||||
),
|
),
|
||||||
XiaomiBinarySensorDeviceClass.MOISTURE: BinarySensorEntityDescription(
|
ExtendedBinarySensorDeviceClass.DEVICE_FORCIBLY_REMOVED: BinarySensorEntityDescription(
|
||||||
key=XiaomiBinarySensorDeviceClass.MOISTURE,
|
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,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
"service_data_uuid": "0000fe95-0000-1000-8000-00805f9b34fb"
|
"service_data_uuid": "0000fe95-0000-1000-8000-00805f9b34fb"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"requirements": ["xiaomi-ble==0.12.2"],
|
"requirements": ["xiaomi-ble==0.14.3"],
|
||||||
"dependencies": ["bluetooth"],
|
"dependencies": ["bluetooth"],
|
||||||
"codeowners": ["@Jc2k", "@Ernst79"],
|
"codeowners": ["@Jc2k", "@Ernst79"],
|
||||||
"iot_class": "local_push"
|
"iot_class": "local_push"
|
||||||
|
|
|
@ -2597,7 +2597,7 @@ xbox-webapi==2.0.11
|
||||||
xboxapi==2.0.1
|
xboxapi==2.0.1
|
||||||
|
|
||||||
# homeassistant.components.xiaomi_ble
|
# homeassistant.components.xiaomi_ble
|
||||||
xiaomi-ble==0.12.2
|
xiaomi-ble==0.14.3
|
||||||
|
|
||||||
# homeassistant.components.knx
|
# homeassistant.components.knx
|
||||||
xknx==2.2.0
|
xknx==2.2.0
|
||||||
|
|
|
@ -1816,7 +1816,7 @@ wolf_smartset==0.1.11
|
||||||
xbox-webapi==2.0.11
|
xbox-webapi==2.0.11
|
||||||
|
|
||||||
# homeassistant.components.xiaomi_ble
|
# homeassistant.components.xiaomi_ble
|
||||||
xiaomi-ble==0.12.2
|
xiaomi-ble==0.14.3
|
||||||
|
|
||||||
# homeassistant.components.knx
|
# homeassistant.components.knx
|
||||||
xknx==2.2.0
|
xknx==2.2.0
|
||||||
|
|
|
@ -9,12 +9,12 @@ from tests.common import MockConfigEntry
|
||||||
from tests.components.bluetooth import inject_bluetooth_service_info_bleak
|
from tests.components.bluetooth import inject_bluetooth_service_info_bleak
|
||||||
|
|
||||||
|
|
||||||
async def test_smoke_sensor(hass):
|
async def test_door_problem_sensors(hass):
|
||||||
"""Test setting up a smoke sensor."""
|
"""Test setting up a door binary sensor with additional problem sensors."""
|
||||||
entry = MockConfigEntry(
|
entry = MockConfigEntry(
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
unique_id="54:EF:44:E3:9C:BC",
|
unique_id="EE:89:73:44:BE:98",
|
||||||
data={"bindkey": "5b51a7c91cde6707c9ef18dfda143a58"},
|
data={"bindkey": "2c3795afa33019a8afdc17ba99e6f217"},
|
||||||
)
|
)
|
||||||
entry.add_to_hass(hass)
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
@ -25,24 +25,72 @@ async def test_smoke_sensor(hass):
|
||||||
inject_bluetooth_service_info_bleak(
|
inject_bluetooth_service_info_bleak(
|
||||||
hass,
|
hass,
|
||||||
make_advertisement(
|
make_advertisement(
|
||||||
"54:EF:44:E3:9C:BC",
|
"EE:89:73:44:BE:98",
|
||||||
b"XY\x97\tf\xbc\x9c\xe3D\xefT\x01" b"\x08\x12\x05\x00\x00\x00q^\xbe\x90",
|
b"HU9\x0e3\x9cq\xc0$\x1f\xff\xee\x80S\x00\x00\x02\xb4\xc59",
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
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")
|
door_sensor = hass.states.get("binary_sensor.door_lock_be98_door")
|
||||||
smoke_sensor_attribtes = smoke_sensor.attributes
|
door_sensor_attribtes = door_sensor.attributes
|
||||||
assert smoke_sensor.state == "on"
|
assert door_sensor.state == "off"
|
||||||
assert smoke_sensor_attribtes[ATTR_FRIENDLY_NAME] == "Thermometer 9CBC Smoke"
|
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)
|
assert await hass.config_entries.async_unload(entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
|
||||||
async def test_moisture(hass):
|
async def test_moisture(hass):
|
||||||
"""Make sure that formldehyde sensors are correctly mapped."""
|
"""Test setting up a moisture binary sensor."""
|
||||||
entry = MockConfigEntry(
|
entry = MockConfigEntry(
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
unique_id="C4:7C:8D:6A:3E:7A",
|
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)
|
assert await hass.config_entries.async_unload(entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
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()
|
||||||
|
|
Loading…
Add table
Reference in a new issue