|
|
|
@@ -1,12 +1,29 @@
|
|
|
|
|
"""Test Xiaomi binary sensors."""
|
|
|
|
|
|
|
|
|
|
from datetime import timedelta
|
|
|
|
|
import time
|
|
|
|
|
from unittest.mock import patch
|
|
|
|
|
|
|
|
|
|
from homeassistant.components.bluetooth import (
|
|
|
|
|
FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS,
|
|
|
|
|
)
|
|
|
|
|
from homeassistant.components.xiaomi_ble.const import DOMAIN
|
|
|
|
|
from homeassistant.const import ATTR_FRIENDLY_NAME
|
|
|
|
|
from homeassistant.const import (
|
|
|
|
|
ATTR_FRIENDLY_NAME,
|
|
|
|
|
STATE_OFF,
|
|
|
|
|
STATE_ON,
|
|
|
|
|
STATE_UNAVAILABLE,
|
|
|
|
|
)
|
|
|
|
|
from homeassistant.core import HomeAssistant
|
|
|
|
|
from homeassistant.util import dt as dt_util
|
|
|
|
|
|
|
|
|
|
from . import make_advertisement
|
|
|
|
|
|
|
|
|
|
from tests.common import MockConfigEntry
|
|
|
|
|
from tests.components.bluetooth import inject_bluetooth_service_info_bleak
|
|
|
|
|
from tests.common import MockConfigEntry, async_fire_time_changed
|
|
|
|
|
from tests.components.bluetooth import (
|
|
|
|
|
inject_bluetooth_service_info_bleak,
|
|
|
|
|
patch_all_discovered_devices,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def test_door_problem_sensors(hass: HomeAssistant) -> None:
|
|
|
|
@@ -34,19 +51,19 @@ async def test_door_problem_sensors(hass: HomeAssistant) -> None:
|
|
|
|
|
|
|
|
|
|
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.state == 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.state == 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.state == 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)
|
|
|
|
@@ -77,12 +94,12 @@ async def test_light_motion(hass: HomeAssistant) -> None:
|
|
|
|
|
|
|
|
|
|
motion_sensor = hass.states.get("binary_sensor.nightlight_9321_motion")
|
|
|
|
|
motion_sensor_attribtes = motion_sensor.attributes
|
|
|
|
|
assert motion_sensor.state == "on"
|
|
|
|
|
assert motion_sensor.state == 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.state == STATE_OFF
|
|
|
|
|
assert light_sensor_attribtes[ATTR_FRIENDLY_NAME] == "Nightlight 9321 Light"
|
|
|
|
|
|
|
|
|
|
assert await hass.config_entries.async_unload(entry.entry_id)
|
|
|
|
@@ -116,7 +133,7 @@ async def test_moisture(hass: HomeAssistant) -> None:
|
|
|
|
|
|
|
|
|
|
sensor = hass.states.get("binary_sensor.smart_flower_pot_3e7a_moisture")
|
|
|
|
|
sensor_attr = sensor.attributes
|
|
|
|
|
assert sensor.state == "on"
|
|
|
|
|
assert sensor.state == STATE_ON
|
|
|
|
|
assert sensor_attr[ATTR_FRIENDLY_NAME] == "Smart Flower Pot 3E7A Moisture"
|
|
|
|
|
|
|
|
|
|
assert await hass.config_entries.async_unload(entry.entry_id)
|
|
|
|
@@ -148,12 +165,12 @@ async def test_opening(hass: HomeAssistant) -> None:
|
|
|
|
|
|
|
|
|
|
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.state == 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()
|
|
|
|
|
|
|
|
|
@@ -183,7 +200,7 @@ async def test_opening_problem_sensors(hass: HomeAssistant) -> None:
|
|
|
|
|
|
|
|
|
|
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.state == STATE_OFF
|
|
|
|
|
assert (
|
|
|
|
|
opening_sensor_attribtes[ATTR_FRIENDLY_NAME]
|
|
|
|
|
== "Door/Window Sensor E567 Opening"
|
|
|
|
@@ -193,7 +210,7 @@ async def test_opening_problem_sensors(hass: HomeAssistant) -> None:
|
|
|
|
|
"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.state == STATE_OFF
|
|
|
|
|
assert (
|
|
|
|
|
door_left_open_attribtes[ATTR_FRIENDLY_NAME]
|
|
|
|
|
== "Door/Window Sensor E567 Door left open"
|
|
|
|
@@ -203,7 +220,7 @@ async def test_opening_problem_sensors(hass: HomeAssistant) -> None:
|
|
|
|
|
"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.state == STATE_OFF
|
|
|
|
|
assert (
|
|
|
|
|
device_forcibly_removed_attribtes[ATTR_FRIENDLY_NAME]
|
|
|
|
|
== "Door/Window Sensor E567 Device forcibly removed"
|
|
|
|
@@ -238,8 +255,111 @@ async def test_smoke(hass: HomeAssistant) -> None:
|
|
|
|
|
|
|
|
|
|
smoke_sensor = hass.states.get("binary_sensor.thermometer_9cbc_smoke")
|
|
|
|
|
smoke_sensor_attribtes = smoke_sensor.attributes
|
|
|
|
|
assert smoke_sensor.state == "on"
|
|
|
|
|
assert smoke_sensor.state == 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()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def test_unavailable(hass):
|
|
|
|
|
"""Test normal device goes to unavailable after 60 minutes."""
|
|
|
|
|
start_monotonic = time.monotonic()
|
|
|
|
|
|
|
|
|
|
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")
|
|
|
|
|
|
|
|
|
|
assert opening_sensor.state == STATE_ON
|
|
|
|
|
|
|
|
|
|
# Fastforward time without BLE advertisements
|
|
|
|
|
monotonic_now = start_monotonic + FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS + 1
|
|
|
|
|
|
|
|
|
|
with patch(
|
|
|
|
|
"homeassistant.components.bluetooth.manager.MONOTONIC_TIME",
|
|
|
|
|
return_value=monotonic_now,
|
|
|
|
|
), patch_all_discovered_devices([]):
|
|
|
|
|
async_fire_time_changed(
|
|
|
|
|
hass,
|
|
|
|
|
dt_util.utcnow()
|
|
|
|
|
+ timedelta(seconds=FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS + 1),
|
|
|
|
|
)
|
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
|
|
opening_sensor = hass.states.get("binary_sensor.door_window_sensor_e567_opening")
|
|
|
|
|
|
|
|
|
|
# Normal devices should go to unavailable
|
|
|
|
|
assert opening_sensor.state == STATE_UNAVAILABLE
|
|
|
|
|
|
|
|
|
|
assert await hass.config_entries.async_unload(entry.entry_id)
|
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def test_sleepy_device(hass):
|
|
|
|
|
"""Test sleepy device does not go to unavailable after 60 minutes."""
|
|
|
|
|
start_monotonic = time.monotonic()
|
|
|
|
|
|
|
|
|
|
entry = MockConfigEntry(
|
|
|
|
|
domain=DOMAIN,
|
|
|
|
|
unique_id="A4:C1:38:66:E5:67",
|
|
|
|
|
)
|
|
|
|
|
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"@0\xd6\x03$\x19\x10\x01\x00",
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
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")
|
|
|
|
|
|
|
|
|
|
assert opening_sensor.state == STATE_ON
|
|
|
|
|
|
|
|
|
|
# Fastforward time without BLE advertisements
|
|
|
|
|
monotonic_now = start_monotonic + FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS + 1
|
|
|
|
|
|
|
|
|
|
with patch(
|
|
|
|
|
"homeassistant.components.bluetooth.manager.MONOTONIC_TIME",
|
|
|
|
|
return_value=monotonic_now,
|
|
|
|
|
), patch_all_discovered_devices([]):
|
|
|
|
|
async_fire_time_changed(
|
|
|
|
|
hass,
|
|
|
|
|
dt_util.utcnow()
|
|
|
|
|
+ timedelta(seconds=FALLBACK_MAXIMUM_STALE_ADVERTISEMENT_SECONDS + 1),
|
|
|
|
|
)
|
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
|
|
|
|
|
opening_sensor = hass.states.get("binary_sensor.door_window_sensor_e567_opening")
|
|
|
|
|
|
|
|
|
|
# Sleepy devices should keep their state over time
|
|
|
|
|
assert opening_sensor.state == STATE_ON
|
|
|
|
|
|
|
|
|
|
assert await hass.config_entries.async_unload(entry.entry_id)
|
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
|