Add tests for Shelly binary_sensor platform (#82367)

* Add tests for Shelly binary_sensor platform

* Rename create to register for
This commit is contained in:
Shay Levy 2022-11-19 18:07:02 +02:00 committed by GitHub
parent e028516939
commit ea98f0e9e8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 238 additions and 44 deletions

View file

@ -1109,7 +1109,6 @@ omit =
homeassistant/components/sesame/lock.py homeassistant/components/sesame/lock.py
homeassistant/components/seven_segments/image_processing.py homeassistant/components/seven_segments/image_processing.py
homeassistant/components/seventeentrack/sensor.py homeassistant/components/seventeentrack/sensor.py
homeassistant/components/shelly/binary_sensor.py
homeassistant/components/shelly/climate.py homeassistant/components/shelly/climate.py
homeassistant/components/shelly/coordinator.py homeassistant/components/shelly/coordinator.py
homeassistant/components/shelly/entity.py homeassistant/components/shelly/entity.py

View file

@ -268,10 +268,8 @@ class RestBinarySensor(ShellyRestAttributeEntity, BinarySensorEntity):
entity_description: RestBinarySensorDescription entity_description: RestBinarySensorDescription
@property @property
def is_on(self) -> bool | None: def is_on(self) -> bool:
"""Return true if REST sensor state is on.""" """Return true if REST sensor state is on."""
if self.attribute_value is None:
return None
return bool(self.attribute_value) return bool(self.attribute_value)
@ -281,10 +279,8 @@ class RpcBinarySensor(ShellyRpcAttributeEntity, BinarySensorEntity):
entity_description: RpcBinarySensorDescription entity_description: RpcBinarySensorDescription
@property @property
def is_on(self) -> bool | None: def is_on(self) -> bool:
"""Return true if RPC sensor state is on.""" """Return true if RPC sensor state is on."""
if self.attribute_value is None:
return None
return bool(self.attribute_value) return bool(self.attribute_value)
@ -308,7 +304,7 @@ class RpcSleepingBinarySensor(ShellySleepingRpcAttributeEntity, BinarySensorEnti
entity_description: RpcBinarySensorDescription entity_description: RpcBinarySensorDescription
@property @property
def is_on(self) -> bool | None: def is_on(self) -> bool:
"""Return true if RPC sensor state is on.""" """Return true if RPC sensor state is on."""
if self.coordinator.device.initialized: if self.coordinator.device.initialized:
return bool(self.attribute_value) return bool(self.attribute_value)

View file

@ -2,16 +2,25 @@
from __future__ import annotations from __future__ import annotations
from copy import deepcopy from copy import deepcopy
from datetime import timedelta
from typing import Any from typing import Any
from unittest.mock import Mock from unittest.mock import Mock
import pytest import pytest
from homeassistant.components.shelly.const import CONF_SLEEP_PERIOD, DOMAIN from homeassistant.components.shelly.const import (
CONF_SLEEP_PERIOD,
DOMAIN,
REST_SENSORS_UPDATE_INTERVAL,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST from homeassistant.const import CONF_HOST
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry
from homeassistant.helpers.entity_registry import async_get
from homeassistant.util import dt
from tests.common import MockConfigEntry from tests.common import MockConfigEntry, async_fire_time_changed
MOCK_MAC = "123456789ABC" MOCK_MAC = "123456789ABC"
@ -22,6 +31,7 @@ async def init_integration(
model="SHSW-25", model="SHSW-25",
sleep_period=0, sleep_period=0,
options: dict[str, Any] | None = None, options: dict[str, Any] | None = None,
skip_setup: bool = False,
) -> MockConfigEntry: ) -> MockConfigEntry:
"""Set up the Shelly integration in Home Assistant.""" """Set up the Shelly integration in Home Assistant."""
data = { data = {
@ -36,8 +46,9 @@ async def init_integration(
) )
entry.add_to_hass(hass) entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id) if not skip_setup:
await hass.async_block_till_done() await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
return entry return entry
@ -63,3 +74,44 @@ def inject_rpc_device_event(
"""Inject event for rpc device.""" """Inject event for rpc device."""
monkeypatch.setattr(mock_rpc_device, "event", event) monkeypatch.setattr(mock_rpc_device, "event", event)
mock_rpc_device.mock_event() mock_rpc_device.mock_event()
async def mock_rest_update(hass: HomeAssistant):
"""Move time to create REST sensors update event."""
async_fire_time_changed(
hass, dt.utcnow() + timedelta(seconds=REST_SENSORS_UPDATE_INTERVAL)
)
await hass.async_block_till_done()
def register_entity(
hass: HomeAssistant,
domain: str,
object_id: str,
unique_id: str,
config_entry: ConfigEntry | None = None,
) -> str:
"""Register enabled entity, return entity_id."""
entity_registry = async_get(hass)
entity_registry.async_get_or_create(
domain,
DOMAIN,
f"{MOCK_MAC}-{unique_id}",
suggested_object_id=object_id,
disabled_by=None,
config_entry=config_entry,
)
return f"{domain}.{object_id}"
def register_device(device_reg, config_entry: ConfigEntry):
"""Register Shelly device."""
device_reg.async_get_or_create(
config_entry_id=config_entry.entry_id,
connections={
(
device_registry.CONNECTION_NETWORK_MAC,
device_registry.format_mac(MOCK_MAC),
)
},
)

View file

@ -63,9 +63,11 @@ def mock_light_set_state(
MOCK_BLOCKS = [ MOCK_BLOCKS = [
Mock( Mock(
sensor_ids={"inputEvent": "S", "inputEventCnt": 2}, sensor_ids={"inputEvent": "S", "inputEventCnt": 2, "overpower": 0},
channel="0", channel="0",
type="relay", type="relay",
overpower=0,
description="relay_0",
set_state=AsyncMock(side_effect=lambda turn: {"ison": turn == "on"}), set_state=AsyncMock(side_effect=lambda turn: {"ison": turn == "on"}),
), ),
Mock( Mock(
@ -88,6 +90,12 @@ MOCK_BLOCKS = [
type="light", type="light",
set_state=AsyncMock(side_effect=mock_light_set_state), set_state=AsyncMock(side_effect=mock_light_set_state),
), ),
Mock(
sensor_ids={"motion": 0},
motion=0,
description="sensor_0",
type="sensor",
),
] ]
MOCK_CONFIG = { MOCK_CONFIG = {
@ -135,7 +143,13 @@ MOCK_STATUS_COAP = {
MOCK_STATUS_RPC = { MOCK_STATUS_RPC = {
"switch:0": {"output": True}, "switch:0": {"output": True},
"cover:0": {"state": "stopped", "pos_control": True, "current_pos": 50}, "cloud": {"connected": False},
"cover:0": {
"state": "stopped",
"pos_control": True,
"current_pos": 50,
"apower": 85.3,
},
"sys": { "sys": {
"available_updates": { "available_updates": {
"beta": {"version": "some_beta_version"}, "beta": {"version": "some_beta_version"},

View file

@ -0,0 +1,155 @@
"""Tests for Shelly binary sensor platform."""
from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN
from homeassistant.const import STATE_OFF, STATE_ON
from homeassistant.core import State
from . import (
init_integration,
mock_rest_update,
mutate_rpc_device_status,
register_device,
register_entity,
)
from tests.common import mock_restore_cache
RELAY_BLOCK_ID = 0
SENSOR_BLOCK_ID = 3
async def test_block_binary_sensor(hass, mock_block_device, monkeypatch):
"""Test block binary sensor."""
entity_id = f"{BINARY_SENSOR_DOMAIN}.test_name_channel_1_overpowering"
await init_integration(hass, 1)
assert hass.states.get(entity_id).state == STATE_OFF
monkeypatch.setattr(mock_block_device.blocks[RELAY_BLOCK_ID], "overpower", 1)
mock_block_device.mock_update()
assert hass.states.get(entity_id).state == STATE_ON
async def test_block_rest_binary_sensor(hass, mock_block_device, monkeypatch):
"""Test block REST binary sensor."""
entity_id = register_entity(hass, BINARY_SENSOR_DOMAIN, "test_name_cloud", "cloud")
monkeypatch.setitem(mock_block_device.status, "cloud", {"connected": False})
await init_integration(hass, 1)
assert hass.states.get(entity_id).state == STATE_OFF
monkeypatch.setitem(mock_block_device.status["cloud"], "connected", True)
await mock_rest_update(hass)
assert hass.states.get(entity_id).state == STATE_ON
async def test_block_sleeping_binary_sensor(hass, mock_block_device, monkeypatch):
"""Test block sleeping binary sensor."""
entity_id = f"{BINARY_SENSOR_DOMAIN}.test_name_motion"
await init_integration(hass, 1, sleep_period=1000)
# Sensor should be created when device is online
assert hass.states.get(entity_id) is None
# Make device online
mock_block_device.mock_update()
await hass.async_block_till_done()
assert hass.states.get(entity_id).state == STATE_OFF
monkeypatch.setattr(mock_block_device.blocks[SENSOR_BLOCK_ID], "motion", 1)
mock_block_device.mock_update()
assert hass.states.get(entity_id).state == STATE_ON
async def test_block_restored_sleeping_binary_sensor(
hass, mock_block_device, device_reg, monkeypatch
):
"""Test block restored sleeping binary sensor."""
entry = await init_integration(hass, 1, sleep_period=1000, skip_setup=True)
register_device(device_reg, entry)
entity_id = register_entity(
hass, BINARY_SENSOR_DOMAIN, "test_name_motion", "sensor_0-motion", entry
)
mock_restore_cache(hass, [State(entity_id, STATE_ON)])
monkeypatch.setattr(mock_block_device, "initialized", False)
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
assert hass.states.get(entity_id).state == STATE_ON
# Make device online
monkeypatch.setattr(mock_block_device, "initialized", True)
mock_block_device.mock_update()
await hass.async_block_till_done()
assert hass.states.get(entity_id).state == STATE_OFF
async def test_rpc_binary_sensor(hass, mock_rpc_device, monkeypatch) -> None:
"""Test RPC binary sensor."""
entity_id = f"{BINARY_SENSOR_DOMAIN}.test_cover_0_overpowering"
await init_integration(hass, 2)
assert hass.states.get(entity_id).state == STATE_OFF
mutate_rpc_device_status(
monkeypatch, mock_rpc_device, "cover:0", "errors", "overpower"
)
mock_rpc_device.mock_update()
assert hass.states.get(entity_id).state == STATE_ON
async def test_rpc_sleeping_binary_sensor(
hass, mock_rpc_device, device_reg, monkeypatch
) -> None:
"""Test RPC online sleeping binary sensor."""
entity_id = f"{BINARY_SENSOR_DOMAIN}.test_name_cloud"
entry = await init_integration(hass, 2, sleep_period=1000)
# Sensor should be created when device is online
assert hass.states.get(entity_id) is None
register_entity(hass, BINARY_SENSOR_DOMAIN, "test_name_cloud", "cloud-cloud", entry)
# Make device online
mock_rpc_device.mock_update()
await hass.async_block_till_done()
assert hass.states.get(entity_id).state == STATE_OFF
mutate_rpc_device_status(monkeypatch, mock_rpc_device, "cloud", "connected", True)
mock_rpc_device.mock_update()
assert hass.states.get(entity_id).state == STATE_ON
async def test_rpc_restored_sleeping_binary_sensor(
hass, mock_rpc_device, device_reg, monkeypatch
):
"""Test RPC restored binary sensor."""
entry = await init_integration(hass, 2, sleep_period=1000, skip_setup=True)
register_device(device_reg, entry)
entity_id = register_entity(
hass, BINARY_SENSOR_DOMAIN, "test_name_cloud", "cloud-cloud", entry
)
mock_restore_cache(hass, [State(entity_id, STATE_ON)])
monkeypatch.setattr(mock_rpc_device, "initialized", False)
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
assert hass.states.get(entity_id).state == STATE_ON
# Make device online
monkeypatch.setattr(mock_rpc_device, "initialized", True)
mock_rpc_device.mock_update()
await hass.async_block_till_done()
assert hass.states.get(entity_id).state == STATE_OFF

View file

@ -1,5 +1,4 @@
"""Tests for Shelly update platform.""" """Tests for Shelly update platform."""
from datetime import timedelta
from unittest.mock import AsyncMock from unittest.mock import AsyncMock
from aioshelly.exceptions import DeviceConnectionError, InvalidAuthError, RpcCallError from aioshelly.exceptions import DeviceConnectionError, InvalidAuthError, RpcCallError
@ -7,7 +6,7 @@ import pytest
from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN
from homeassistant.components.button import DOMAIN as BUTTON_DOMAIN from homeassistant.components.button import DOMAIN as BUTTON_DOMAIN
from homeassistant.components.shelly.const import DOMAIN, REST_SENSORS_UPDATE_INTERVAL from homeassistant.components.shelly.const import DOMAIN
from homeassistant.components.update import ( from homeassistant.components.update import (
ATTR_IN_PROGRESS, ATTR_IN_PROGRESS,
ATTR_INSTALLED_VERSION, ATTR_INSTALLED_VERSION,
@ -20,11 +19,8 @@ from homeassistant.const import ATTR_ENTITY_ID, STATE_OFF, STATE_ON
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_registry import async_get from homeassistant.helpers.entity_registry import async_get
from homeassistant.util import dt
from . import MOCK_MAC, init_integration from . import MOCK_MAC, init_integration, mock_rest_update
from tests.common import async_fire_time_changed
@pytest.mark.parametrize( @pytest.mark.parametrize(
@ -100,10 +96,7 @@ async def test_block_update(hass: HomeAssistant, mock_block_device, monkeypatch)
assert state.attributes[ATTR_IN_PROGRESS] is True assert state.attributes[ATTR_IN_PROGRESS] is True
monkeypatch.setitem(mock_block_device.status["update"], "old_version", "2") monkeypatch.setitem(mock_block_device.status["update"], "old_version", "2")
async_fire_time_changed( await mock_rest_update(hass)
hass, dt.utcnow() + timedelta(seconds=REST_SENSORS_UPDATE_INTERVAL)
)
await hass.async_block_till_done()
state = hass.states.get("update.test_name_firmware_update") state = hass.states.get("update.test_name_firmware_update")
assert state.state == STATE_OFF assert state.state == STATE_OFF
@ -134,10 +127,7 @@ async def test_block_beta_update(hass: HomeAssistant, mock_block_device, monkeyp
assert state.attributes[ATTR_IN_PROGRESS] is False assert state.attributes[ATTR_IN_PROGRESS] is False
monkeypatch.setitem(mock_block_device.status["update"], "beta_version", "2b") monkeypatch.setitem(mock_block_device.status["update"], "beta_version", "2b")
async_fire_time_changed( await mock_rest_update(hass)
hass, dt.utcnow() + timedelta(seconds=REST_SENSORS_UPDATE_INTERVAL)
)
await hass.async_block_till_done()
state = hass.states.get("update.test_name_beta_firmware_update") state = hass.states.get("update.test_name_beta_firmware_update")
assert state.state == STATE_ON assert state.state == STATE_ON
@ -160,10 +150,7 @@ async def test_block_beta_update(hass: HomeAssistant, mock_block_device, monkeyp
assert state.attributes[ATTR_IN_PROGRESS] is True assert state.attributes[ATTR_IN_PROGRESS] is True
monkeypatch.setitem(mock_block_device.status["update"], "old_version", "2b") monkeypatch.setitem(mock_block_device.status["update"], "old_version", "2b")
async_fire_time_changed( await mock_rest_update(hass)
hass, dt.utcnow() + timedelta(seconds=REST_SENSORS_UPDATE_INTERVAL)
)
await hass.async_block_till_done()
state = hass.states.get("update.test_name_beta_firmware_update") state = hass.states.get("update.test_name_beta_firmware_update")
assert state.state == STATE_OFF assert state.state == STATE_OFF
@ -288,10 +275,7 @@ async def test_rpc_update(hass: HomeAssistant, mock_rpc_device, monkeypatch):
assert state.attributes[ATTR_IN_PROGRESS] is True assert state.attributes[ATTR_IN_PROGRESS] is True
monkeypatch.setitem(mock_rpc_device.shelly, "ver", "2") monkeypatch.setitem(mock_rpc_device.shelly, "ver", "2")
async_fire_time_changed( await mock_rest_update(hass)
hass, dt.utcnow() + timedelta(seconds=REST_SENSORS_UPDATE_INTERVAL)
)
await hass.async_block_till_done()
state = hass.states.get("update.test_name_firmware_update") state = hass.states.get("update.test_name_firmware_update")
assert state.state == STATE_OFF assert state.state == STATE_OFF
@ -335,10 +319,7 @@ async def test_rpc_beta_update(hass: HomeAssistant, mock_rpc_device, monkeypatch
"beta": {"version": "2b"}, "beta": {"version": "2b"},
}, },
) )
async_fire_time_changed( await mock_rest_update(hass)
hass, dt.utcnow() + timedelta(seconds=REST_SENSORS_UPDATE_INTERVAL)
)
await hass.async_block_till_done()
state = hass.states.get("update.test_name_beta_firmware_update") state = hass.states.get("update.test_name_beta_firmware_update")
assert state.state == STATE_ON assert state.state == STATE_ON
@ -361,10 +342,7 @@ async def test_rpc_beta_update(hass: HomeAssistant, mock_rpc_device, monkeypatch
assert state.attributes[ATTR_IN_PROGRESS] is True assert state.attributes[ATTR_IN_PROGRESS] is True
monkeypatch.setitem(mock_rpc_device.shelly, "ver", "2b") monkeypatch.setitem(mock_rpc_device.shelly, "ver", "2b")
async_fire_time_changed( await mock_rest_update(hass)
hass, dt.utcnow() + timedelta(seconds=REST_SENSORS_UPDATE_INTERVAL)
)
await hass.async_block_till_done()
state = hass.states.get("update.test_name_beta_firmware_update") state = hass.states.get("update.test_name_beta_firmware_update")
assert state.state == STATE_OFF assert state.state == STATE_OFF