mirror of
https://github.com/home-assistant/core.git
synced 2026-07-04 23:51:32 +02:00
Mark entity unavailable if data can't be fetched (#156928)
This commit is contained in:
@@ -73,14 +73,17 @@ class LcnBinarySensor(LcnEntity, BinarySensorEntity):
|
||||
|
||||
async def async_update(self) -> None:
|
||||
"""Update the state of the entity."""
|
||||
await self.device_connection.request_status_binary_sensors(
|
||||
SCAN_INTERVAL.seconds
|
||||
self._attr_available = (
|
||||
await self.device_connection.request_status_binary_sensors(
|
||||
SCAN_INTERVAL.seconds
|
||||
)
|
||||
is not None
|
||||
)
|
||||
|
||||
def input_received(self, input_obj: InputType) -> None:
|
||||
"""Set sensor value when LCN input object (command) is received."""
|
||||
if not isinstance(input_obj, pypck.inputs.ModStatusBinSensors):
|
||||
return
|
||||
|
||||
self._attr_available = True
|
||||
self._attr_is_on = input_obj.get_state(self.bin_sensor_port.value)
|
||||
self.async_write_ha_state()
|
||||
|
||||
@@ -171,20 +171,22 @@ class LcnClimate(LcnEntity, ClimateEntity):
|
||||
|
||||
async def async_update(self) -> None:
|
||||
"""Update the state of the entity."""
|
||||
await asyncio.gather(
|
||||
self.device_connection.request_status_variable(
|
||||
self.variable, SCAN_INTERVAL.seconds
|
||||
),
|
||||
self.device_connection.request_status_variable(
|
||||
self.setpoint, SCAN_INTERVAL.seconds
|
||||
),
|
||||
self._attr_available = any(
|
||||
await asyncio.gather(
|
||||
self.device_connection.request_status_variable(
|
||||
self.variable, SCAN_INTERVAL.seconds
|
||||
),
|
||||
self.device_connection.request_status_variable(
|
||||
self.setpoint, SCAN_INTERVAL.seconds
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
def input_received(self, input_obj: InputType) -> None:
|
||||
"""Set temperature value when LCN input object is received."""
|
||||
if not isinstance(input_obj, pypck.inputs.ModStatusVar):
|
||||
return
|
||||
|
||||
self._attr_available = True
|
||||
if input_obj.get_var() == self.variable:
|
||||
self._attr_current_temperature = float(
|
||||
input_obj.get_value().to_var_unit(self.unit)
|
||||
|
||||
@@ -133,13 +133,15 @@ class LcnOutputsCover(LcnEntity, CoverEntity):
|
||||
async def async_update(self) -> None:
|
||||
"""Update the state of the entity."""
|
||||
if not self.device_connection.is_group:
|
||||
await asyncio.gather(
|
||||
self.device_connection.request_status_output(
|
||||
pypck.lcn_defs.OutputPort["OUTPUTUP"], SCAN_INTERVAL.seconds
|
||||
),
|
||||
self.device_connection.request_status_output(
|
||||
pypck.lcn_defs.OutputPort["OUTPUTDOWN"], SCAN_INTERVAL.seconds
|
||||
),
|
||||
self._attr_available = any(
|
||||
await asyncio.gather(
|
||||
self.device_connection.request_status_output(
|
||||
pypck.lcn_defs.OutputPort["OUTPUTUP"], SCAN_INTERVAL.seconds
|
||||
),
|
||||
self.device_connection.request_status_output(
|
||||
pypck.lcn_defs.OutputPort["OUTPUTDOWN"], SCAN_INTERVAL.seconds
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
def input_received(self, input_obj: InputType) -> None:
|
||||
@@ -149,7 +151,7 @@ class LcnOutputsCover(LcnEntity, CoverEntity):
|
||||
or input_obj.get_output_id() not in self.output_ids
|
||||
):
|
||||
return
|
||||
|
||||
self._attr_available = True
|
||||
if input_obj.get_percent() > 0: # motor is on
|
||||
if input_obj.get_output_id() == self.output_ids[0]:
|
||||
self._attr_is_opening = True
|
||||
@@ -272,11 +274,12 @@ class LcnRelayCover(LcnEntity, CoverEntity):
|
||||
self.motor, self.positioning_mode, SCAN_INTERVAL.seconds
|
||||
)
|
||||
)
|
||||
await asyncio.gather(*coros)
|
||||
self._attr_available = any(await asyncio.gather(*coros))
|
||||
|
||||
def input_received(self, input_obj: InputType) -> None:
|
||||
"""Set cover states when LCN input object (command) is received."""
|
||||
if isinstance(input_obj, pypck.inputs.ModStatusRelays):
|
||||
self._attr_available = True
|
||||
self._attr_is_opening = input_obj.is_opening(self.motor.value)
|
||||
self._attr_is_closing = input_obj.is_closing(self.motor.value)
|
||||
|
||||
@@ -293,6 +296,7 @@ class LcnRelayCover(LcnEntity, CoverEntity):
|
||||
)
|
||||
and input_obj.motor == self.motor.value
|
||||
):
|
||||
self._attr_available = True
|
||||
self._attr_current_cover_position = int(input_obj.position)
|
||||
if self._attr_current_cover_position in [0, 100]:
|
||||
self._attr_is_opening = False
|
||||
|
||||
@@ -149,8 +149,11 @@ class LcnOutputLight(LcnEntity, LightEntity):
|
||||
|
||||
async def async_update(self) -> None:
|
||||
"""Update the state of the entity."""
|
||||
await self.device_connection.request_status_output(
|
||||
self.output, SCAN_INTERVAL.seconds
|
||||
self._attr_available = (
|
||||
await self.device_connection.request_status_output(
|
||||
self.output, SCAN_INTERVAL.seconds
|
||||
)
|
||||
is not None
|
||||
)
|
||||
|
||||
def input_received(self, input_obj: InputType) -> None:
|
||||
@@ -200,12 +203,15 @@ class LcnRelayLight(LcnEntity, LightEntity):
|
||||
|
||||
async def async_update(self) -> None:
|
||||
"""Update the state of the entity."""
|
||||
await self.device_connection.request_status_relays(SCAN_INTERVAL.seconds)
|
||||
self._attr_available = (
|
||||
await self.device_connection.request_status_relays(SCAN_INTERVAL.seconds)
|
||||
is not None
|
||||
)
|
||||
|
||||
def input_received(self, input_obj: InputType) -> None:
|
||||
"""Set light state when LCN input object (command) is received."""
|
||||
if not isinstance(input_obj, pypck.inputs.ModStatusRelays):
|
||||
return
|
||||
|
||||
self._attr_available = True
|
||||
self._attr_is_on = input_obj.get_state(self.output.value)
|
||||
self.async_write_ha_state()
|
||||
|
||||
@@ -25,7 +25,7 @@ rules:
|
||||
status: exempt
|
||||
comment: Integration has no configuration parameters
|
||||
docs-installation-parameters: done
|
||||
entity-unavailable: todo
|
||||
entity-unavailable: done
|
||||
integration-owner: done
|
||||
log-when-unavailable: done
|
||||
parallel-updates: done
|
||||
|
||||
@@ -133,8 +133,11 @@ class LcnVariableSensor(LcnEntity, SensorEntity):
|
||||
|
||||
async def async_update(self) -> None:
|
||||
"""Update the state of the entity."""
|
||||
await self.device_connection.request_status_variable(
|
||||
self.variable, SCAN_INTERVAL.seconds
|
||||
self._attr_available = (
|
||||
await self.device_connection.request_status_variable(
|
||||
self.variable, SCAN_INTERVAL.seconds
|
||||
)
|
||||
is not None
|
||||
)
|
||||
|
||||
def input_received(self, input_obj: InputType) -> None:
|
||||
@@ -144,7 +147,7 @@ class LcnVariableSensor(LcnEntity, SensorEntity):
|
||||
or input_obj.get_var() != self.variable
|
||||
):
|
||||
return
|
||||
|
||||
self._attr_available = True
|
||||
is_regulator = self.variable.name in SETPOINTS
|
||||
self._attr_native_value = input_obj.get_value().to_var_unit(
|
||||
self.unit, is_regulator
|
||||
@@ -171,15 +174,18 @@ class LcnLedLogicSensor(LcnEntity, SensorEntity):
|
||||
|
||||
async def async_update(self) -> None:
|
||||
"""Update the state of the entity."""
|
||||
await self.device_connection.request_status_led_and_logic_ops(
|
||||
SCAN_INTERVAL.seconds
|
||||
self._attr_available = (
|
||||
await self.device_connection.request_status_led_and_logic_ops(
|
||||
SCAN_INTERVAL.seconds
|
||||
)
|
||||
is not None
|
||||
)
|
||||
|
||||
def input_received(self, input_obj: InputType) -> None:
|
||||
"""Set sensor value when LCN input object (command) is received."""
|
||||
if not isinstance(input_obj, pypck.inputs.ModStatusLedsAndLogicOps):
|
||||
return
|
||||
|
||||
self._attr_available = True
|
||||
if self.source in pypck.lcn_defs.LedPort:
|
||||
self._attr_native_value = input_obj.get_led_state(
|
||||
self.source.value
|
||||
|
||||
@@ -95,8 +95,11 @@ class LcnOutputSwitch(LcnEntity, SwitchEntity):
|
||||
|
||||
async def async_update(self) -> None:
|
||||
"""Update the state of the entity."""
|
||||
await self.device_connection.request_status_output(
|
||||
self.output, SCAN_INTERVAL.seconds
|
||||
self._attr_available = (
|
||||
await self.device_connection.request_status_output(
|
||||
self.output, SCAN_INTERVAL.seconds
|
||||
)
|
||||
is not None
|
||||
)
|
||||
|
||||
def input_received(self, input_obj: InputType) -> None:
|
||||
@@ -106,7 +109,7 @@ class LcnOutputSwitch(LcnEntity, SwitchEntity):
|
||||
or input_obj.get_output_id() != self.output.value
|
||||
):
|
||||
return
|
||||
|
||||
self._attr_available = True
|
||||
self._attr_is_on = input_obj.get_percent() > 0
|
||||
self.async_write_ha_state()
|
||||
|
||||
@@ -142,13 +145,16 @@ class LcnRelaySwitch(LcnEntity, SwitchEntity):
|
||||
|
||||
async def async_update(self) -> None:
|
||||
"""Update the state of the entity."""
|
||||
await self.device_connection.request_status_relays(SCAN_INTERVAL.seconds)
|
||||
self._attr_available = (
|
||||
await self.device_connection.request_status_relays(SCAN_INTERVAL.seconds)
|
||||
is not None
|
||||
)
|
||||
|
||||
def input_received(self, input_obj: InputType) -> None:
|
||||
"""Set switch state when LCN input object (command) is received."""
|
||||
if not isinstance(input_obj, pypck.inputs.ModStatusRelays):
|
||||
return
|
||||
|
||||
self._attr_available = True
|
||||
self._attr_is_on = input_obj.get_state(self.output.value)
|
||||
self.async_write_ha_state()
|
||||
|
||||
@@ -183,8 +189,11 @@ class LcnRegulatorLockSwitch(LcnEntity, SwitchEntity):
|
||||
|
||||
async def async_update(self) -> None:
|
||||
"""Update the state of the entity."""
|
||||
await self.device_connection.request_status_variable(
|
||||
self.setpoint_variable, SCAN_INTERVAL.seconds
|
||||
self._attr_available = (
|
||||
await self.device_connection.request_status_variable(
|
||||
self.setpoint_variable, SCAN_INTERVAL.seconds
|
||||
)
|
||||
is not None
|
||||
)
|
||||
|
||||
def input_received(self, input_obj: InputType) -> None:
|
||||
@@ -194,7 +203,7 @@ class LcnRegulatorLockSwitch(LcnEntity, SwitchEntity):
|
||||
or input_obj.get_var() != self.setpoint_variable
|
||||
):
|
||||
return
|
||||
|
||||
self._attr_available = True
|
||||
self._attr_is_on = input_obj.get_value().is_locked_regulator()
|
||||
self.async_write_ha_state()
|
||||
|
||||
@@ -236,7 +245,12 @@ class LcnKeyLockSwitch(LcnEntity, SwitchEntity):
|
||||
|
||||
async def async_update(self) -> None:
|
||||
"""Update the state of the entity."""
|
||||
await self.device_connection.request_status_locked_keys(SCAN_INTERVAL.seconds)
|
||||
self._attr_available = (
|
||||
await self.device_connection.request_status_locked_keys(
|
||||
SCAN_INTERVAL.seconds
|
||||
)
|
||||
is not None
|
||||
)
|
||||
|
||||
def input_received(self, input_obj: InputType) -> None:
|
||||
"""Set switch state when LCN input object (command) is received."""
|
||||
@@ -245,6 +259,6 @@ class LcnKeyLockSwitch(LcnEntity, SwitchEntity):
|
||||
or self.key not in pypck.lcn_defs.Key
|
||||
):
|
||||
return
|
||||
|
||||
self._attr_available = True
|
||||
self._attr_is_on = input_obj.get_state(self.table_id, self.key_id)
|
||||
self.async_write_ha_state()
|
||||
|
||||
@@ -2,18 +2,20 @@
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
from pypck.inputs import ModStatusBinSensors
|
||||
from pypck.lcn_addr import LcnAddr
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.components.lcn.binary_sensor import SCAN_INTERVAL
|
||||
from homeassistant.components.lcn.helpers import get_device_connection
|
||||
from homeassistant.const import STATE_OFF, STATE_ON, STATE_UNAVAILABLE, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from .conftest import MockConfigEntry, init_integration
|
||||
from .conftest import MockConfigEntry, MockDeviceConnection, init_integration
|
||||
|
||||
from tests.common import snapshot_platform
|
||||
from tests.common import async_fire_time_changed, snapshot_platform
|
||||
|
||||
BINARY_SENSOR_SENSOR1 = "binary_sensor.testmodule_binary_sensor1"
|
||||
|
||||
@@ -61,6 +63,43 @@ async def test_pushed_binsensor_status_change(
|
||||
assert state.state == STATE_ON
|
||||
|
||||
|
||||
async def test_availability(
|
||||
hass: HomeAssistant, freezer: FrozenDateTimeFactory, entry: MockConfigEntry
|
||||
) -> None:
|
||||
"""Test the availability of binary_sensor entity."""
|
||||
await init_integration(hass, entry)
|
||||
|
||||
state = hass.states.get(BINARY_SENSOR_SENSOR1)
|
||||
assert state is not None
|
||||
assert state.state != STATE_UNAVAILABLE
|
||||
|
||||
# no response from device -> unavailable
|
||||
with patch.object(
|
||||
MockDeviceConnection, "request_status_binary_sensors", return_value=None
|
||||
):
|
||||
freezer.tick(SCAN_INTERVAL)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done(wait_background_tasks=True)
|
||||
|
||||
state = hass.states.get(BINARY_SENSOR_SENSOR1)
|
||||
assert state is not None
|
||||
assert state.state == STATE_UNAVAILABLE
|
||||
|
||||
# response from device -> available
|
||||
with patch.object(
|
||||
MockDeviceConnection,
|
||||
"request_status_binary_sensors",
|
||||
return_value=ModStatusBinSensors(LcnAddr(0, 7, False), [False] * 8),
|
||||
):
|
||||
freezer.tick(SCAN_INTERVAL)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done(wait_background_tasks=True)
|
||||
|
||||
state = hass.states.get(BINARY_SENSOR_SENSOR1)
|
||||
assert state is not None
|
||||
assert state.state != STATE_UNAVAILABLE
|
||||
|
||||
|
||||
async def test_unload_config_entry(hass: HomeAssistant, entry: MockConfigEntry) -> None:
|
||||
"""Test the binary sensor is removed when the config entry is unloaded."""
|
||||
await init_integration(hass, entry)
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
from pypck.inputs import ModStatusVar, Unknown
|
||||
from pypck.lcn_addr import LcnAddr
|
||||
from pypck.lcn_defs import Var, VarUnit, VarValue
|
||||
@@ -18,6 +19,7 @@ from homeassistant.components.climate import (
|
||||
SERVICE_SET_TEMPERATURE,
|
||||
HVACMode,
|
||||
)
|
||||
from homeassistant.components.lcn.climate import SCAN_INTERVAL
|
||||
from homeassistant.components.lcn.helpers import get_device_connection
|
||||
from homeassistant.const import (
|
||||
ATTR_ENTITY_ID,
|
||||
@@ -31,7 +33,9 @@ from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from .conftest import MockConfigEntry, MockDeviceConnection, init_integration
|
||||
|
||||
from tests.common import snapshot_platform
|
||||
from tests.common import async_fire_time_changed, snapshot_platform
|
||||
|
||||
CLIMATE_CLIMATE1 = "climate.testmodule_climate1"
|
||||
|
||||
|
||||
async def test_setup_lcn_climate(
|
||||
@@ -56,7 +60,7 @@ async def test_set_hvac_mode_heat(hass: HomeAssistant, entry: MockConfigEntry) -
|
||||
DOMAIN_CLIMATE,
|
||||
SERVICE_SET_HVAC_MODE,
|
||||
{
|
||||
ATTR_ENTITY_ID: "climate.testmodule_climate1",
|
||||
ATTR_ENTITY_ID: CLIMATE_CLIMATE1,
|
||||
ATTR_HVAC_MODE: HVACMode.OFF,
|
||||
},
|
||||
blocking=True,
|
||||
@@ -69,7 +73,7 @@ async def test_set_hvac_mode_heat(hass: HomeAssistant, entry: MockConfigEntry) -
|
||||
DOMAIN_CLIMATE,
|
||||
SERVICE_SET_HVAC_MODE,
|
||||
{
|
||||
ATTR_ENTITY_ID: "climate.testmodule_climate1",
|
||||
ATTR_ENTITY_ID: CLIMATE_CLIMATE1,
|
||||
ATTR_HVAC_MODE: HVACMode.HEAT,
|
||||
},
|
||||
blocking=True,
|
||||
@@ -77,7 +81,7 @@ async def test_set_hvac_mode_heat(hass: HomeAssistant, entry: MockConfigEntry) -
|
||||
|
||||
lock_regulator.assert_awaited_with(0, False)
|
||||
|
||||
state = hass.states.get("climate.testmodule_climate1")
|
||||
state = hass.states.get(CLIMATE_CLIMATE1)
|
||||
assert state is not None
|
||||
assert state.state != HVACMode.HEAT
|
||||
|
||||
@@ -89,7 +93,7 @@ async def test_set_hvac_mode_heat(hass: HomeAssistant, entry: MockConfigEntry) -
|
||||
DOMAIN_CLIMATE,
|
||||
SERVICE_SET_HVAC_MODE,
|
||||
{
|
||||
ATTR_ENTITY_ID: "climate.testmodule_climate1",
|
||||
ATTR_ENTITY_ID: CLIMATE_CLIMATE1,
|
||||
ATTR_HVAC_MODE: HVACMode.HEAT,
|
||||
},
|
||||
blocking=True,
|
||||
@@ -97,7 +101,7 @@ async def test_set_hvac_mode_heat(hass: HomeAssistant, entry: MockConfigEntry) -
|
||||
|
||||
lock_regulator.assert_awaited_with(0, False)
|
||||
|
||||
state = hass.states.get("climate.testmodule_climate1")
|
||||
state = hass.states.get(CLIMATE_CLIMATE1)
|
||||
assert state is not None
|
||||
assert state.state == HVACMode.HEAT
|
||||
|
||||
@@ -107,7 +111,7 @@ async def test_set_hvac_mode_off(hass: HomeAssistant, entry: MockConfigEntry) ->
|
||||
await init_integration(hass, entry)
|
||||
|
||||
with patch.object(MockDeviceConnection, "lock_regulator") as lock_regulator:
|
||||
state = hass.states.get("climate.testmodule_climate1")
|
||||
state = hass.states.get(CLIMATE_CLIMATE1)
|
||||
state.state = HVACMode.HEAT
|
||||
|
||||
# command failed
|
||||
@@ -117,7 +121,7 @@ async def test_set_hvac_mode_off(hass: HomeAssistant, entry: MockConfigEntry) ->
|
||||
DOMAIN_CLIMATE,
|
||||
SERVICE_SET_HVAC_MODE,
|
||||
{
|
||||
ATTR_ENTITY_ID: "climate.testmodule_climate1",
|
||||
ATTR_ENTITY_ID: CLIMATE_CLIMATE1,
|
||||
ATTR_HVAC_MODE: HVACMode.OFF,
|
||||
},
|
||||
blocking=True,
|
||||
@@ -125,7 +129,7 @@ async def test_set_hvac_mode_off(hass: HomeAssistant, entry: MockConfigEntry) ->
|
||||
|
||||
lock_regulator.assert_awaited_with(0, True, -1)
|
||||
|
||||
state = hass.states.get("climate.testmodule_climate1")
|
||||
state = hass.states.get(CLIMATE_CLIMATE1)
|
||||
assert state is not None
|
||||
assert state.state != HVACMode.OFF
|
||||
|
||||
@@ -137,7 +141,7 @@ async def test_set_hvac_mode_off(hass: HomeAssistant, entry: MockConfigEntry) ->
|
||||
DOMAIN_CLIMATE,
|
||||
SERVICE_SET_HVAC_MODE,
|
||||
{
|
||||
ATTR_ENTITY_ID: "climate.testmodule_climate1",
|
||||
ATTR_ENTITY_ID: CLIMATE_CLIMATE1,
|
||||
ATTR_HVAC_MODE: HVACMode.OFF,
|
||||
},
|
||||
blocking=True,
|
||||
@@ -145,7 +149,7 @@ async def test_set_hvac_mode_off(hass: HomeAssistant, entry: MockConfigEntry) ->
|
||||
|
||||
lock_regulator.assert_awaited_with(0, True, -1)
|
||||
|
||||
state = hass.states.get("climate.testmodule_climate1")
|
||||
state = hass.states.get(CLIMATE_CLIMATE1)
|
||||
assert state is not None
|
||||
assert state.state == HVACMode.OFF
|
||||
|
||||
@@ -155,7 +159,7 @@ async def test_set_temperature(hass: HomeAssistant, entry: MockConfigEntry) -> N
|
||||
await init_integration(hass, entry)
|
||||
|
||||
with patch.object(MockDeviceConnection, "var_abs") as var_abs:
|
||||
state = hass.states.get("climate.testmodule_climate1")
|
||||
state = hass.states.get(CLIMATE_CLIMATE1)
|
||||
state.state = HVACMode.HEAT
|
||||
|
||||
# wrong temperature set via service call with high/low attributes
|
||||
@@ -166,7 +170,7 @@ async def test_set_temperature(hass: HomeAssistant, entry: MockConfigEntry) -> N
|
||||
DOMAIN_CLIMATE,
|
||||
SERVICE_SET_TEMPERATURE,
|
||||
{
|
||||
ATTR_ENTITY_ID: "climate.testmodule_climate1",
|
||||
ATTR_ENTITY_ID: CLIMATE_CLIMATE1,
|
||||
ATTR_TARGET_TEMP_LOW: 24.5,
|
||||
ATTR_TARGET_TEMP_HIGH: 25.5,
|
||||
},
|
||||
@@ -182,13 +186,13 @@ async def test_set_temperature(hass: HomeAssistant, entry: MockConfigEntry) -> N
|
||||
await hass.services.async_call(
|
||||
DOMAIN_CLIMATE,
|
||||
SERVICE_SET_TEMPERATURE,
|
||||
{ATTR_ENTITY_ID: "climate.testmodule_climate1", ATTR_TEMPERATURE: 25.5},
|
||||
{ATTR_ENTITY_ID: CLIMATE_CLIMATE1, ATTR_TEMPERATURE: 25.5},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
var_abs.assert_awaited_with(Var.R1VARSETPOINT, 25.5, VarUnit.CELSIUS)
|
||||
|
||||
state = hass.states.get("climate.testmodule_climate1")
|
||||
state = hass.states.get(CLIMATE_CLIMATE1)
|
||||
assert state is not None
|
||||
assert state.attributes[ATTR_TEMPERATURE] != 25.5
|
||||
|
||||
@@ -199,13 +203,13 @@ async def test_set_temperature(hass: HomeAssistant, entry: MockConfigEntry) -> N
|
||||
await hass.services.async_call(
|
||||
DOMAIN_CLIMATE,
|
||||
SERVICE_SET_TEMPERATURE,
|
||||
{ATTR_ENTITY_ID: "climate.testmodule_climate1", ATTR_TEMPERATURE: 25.5},
|
||||
{ATTR_ENTITY_ID: CLIMATE_CLIMATE1, ATTR_TEMPERATURE: 25.5},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
var_abs.assert_awaited_with(Var.R1VARSETPOINT, 25.5, VarUnit.CELSIUS)
|
||||
|
||||
state = hass.states.get("climate.testmodule_climate1")
|
||||
state = hass.states.get(CLIMATE_CLIMATE1)
|
||||
assert state is not None
|
||||
assert state.attributes[ATTR_TEMPERATURE] == 25.5
|
||||
|
||||
@@ -226,7 +230,7 @@ async def test_pushed_current_temperature_status_change(
|
||||
await device_connection.async_process_input(inp)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("climate.testmodule_climate1")
|
||||
state = hass.states.get(CLIMATE_CLIMATE1)
|
||||
assert state is not None
|
||||
assert state.state == HVACMode.HEAT
|
||||
assert state.attributes[ATTR_CURRENT_TEMPERATURE] == 25.5
|
||||
@@ -249,7 +253,7 @@ async def test_pushed_setpoint_status_change(
|
||||
await device_connection.async_process_input(inp)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("climate.testmodule_climate1")
|
||||
state = hass.states.get(CLIMATE_CLIMATE1)
|
||||
assert state is not None
|
||||
assert state.state == HVACMode.HEAT
|
||||
assert state.attributes[ATTR_CURRENT_TEMPERATURE] is None
|
||||
@@ -272,7 +276,7 @@ async def test_pushed_lock_status_change(
|
||||
await device_connection.async_process_input(inp)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("climate.testmodule_climate1")
|
||||
state = hass.states.get(CLIMATE_CLIMATE1)
|
||||
assert state is not None
|
||||
assert state.state == HVACMode.OFF
|
||||
assert state.attributes[ATTR_CURRENT_TEMPERATURE] is None
|
||||
@@ -291,11 +295,50 @@ async def test_pushed_wrong_input(
|
||||
await device_connection.async_process_input(Unknown("input"))
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("climate.testmodule_climate1")
|
||||
state = hass.states.get(CLIMATE_CLIMATE1)
|
||||
assert state.attributes[ATTR_CURRENT_TEMPERATURE] is None
|
||||
assert state.attributes[ATTR_TEMPERATURE] is None
|
||||
|
||||
|
||||
async def test_availability(
|
||||
hass: HomeAssistant, freezer: FrozenDateTimeFactory, entry: MockConfigEntry
|
||||
) -> None:
|
||||
"""Test the availability of climate entity."""
|
||||
await init_integration(hass, entry)
|
||||
|
||||
state = hass.states.get(CLIMATE_CLIMATE1)
|
||||
assert state is not None
|
||||
assert state.state != STATE_UNAVAILABLE
|
||||
|
||||
# no response from device -> unavailable
|
||||
with patch.object(
|
||||
MockDeviceConnection, "request_status_variable", return_value=None
|
||||
):
|
||||
freezer.tick(SCAN_INTERVAL)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done(wait_background_tasks=True)
|
||||
|
||||
state = hass.states.get(CLIMATE_CLIMATE1)
|
||||
assert state is not None
|
||||
assert state.state == STATE_UNAVAILABLE
|
||||
|
||||
# response from device -> available
|
||||
with patch.object(
|
||||
MockDeviceConnection,
|
||||
"request_status_variable",
|
||||
return_value=ModStatusVar(
|
||||
LcnAddr(0, 7, False), Var.R1VARSETPOINT, VarValue.from_celsius(25.5)
|
||||
),
|
||||
):
|
||||
freezer.tick(SCAN_INTERVAL)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done(wait_background_tasks=True)
|
||||
|
||||
state = hass.states.get(CLIMATE_CLIMATE1)
|
||||
assert state is not None
|
||||
assert state.state != STATE_UNAVAILABLE
|
||||
|
||||
|
||||
async def test_unload_config_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: MockConfigEntry,
|
||||
@@ -304,5 +347,5 @@ async def test_unload_config_entry(
|
||||
await init_integration(hass, entry)
|
||||
|
||||
await hass.config_entries.async_unload(entry.entry_id)
|
||||
state = hass.states.get("climate.testmodule_climate1")
|
||||
state = hass.states.get(CLIMATE_CLIMATE1)
|
||||
assert state.state == STATE_UNAVAILABLE
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
"""Test for the LCN cover platform."""
|
||||
|
||||
from unittest.mock import patch
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
from pypck.inputs import (
|
||||
ModStatusMotorPositionBS4,
|
||||
ModStatusMotorPositionModule,
|
||||
@@ -19,6 +20,7 @@ from homeassistant.components.cover import (
|
||||
DOMAIN as DOMAIN_COVER,
|
||||
CoverState,
|
||||
)
|
||||
from homeassistant.components.lcn.cover import SCAN_INTERVAL
|
||||
from homeassistant.components.lcn.helpers import get_device_connection
|
||||
from homeassistant.const import (
|
||||
ATTR_ENTITY_ID,
|
||||
@@ -34,7 +36,7 @@ from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from .conftest import MockConfigEntry, MockDeviceConnection, init_integration
|
||||
|
||||
from tests.common import snapshot_platform
|
||||
from tests.common import async_fire_time_changed, snapshot_platform
|
||||
|
||||
COVER_OUTPUTS = "cover.testmodule_cover_outputs"
|
||||
COVER_RELAYS = "cover.testmodule_cover_relays"
|
||||
@@ -522,6 +524,78 @@ async def test_pushed_relays_status_change(
|
||||
assert state.attributes[ATTR_CURRENT_POSITION] == 75
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("entity_id", "request_methods", "return_values"),
|
||||
[
|
||||
(
|
||||
COVER_OUTPUTS,
|
||||
("request_status_output",),
|
||||
(ModStatusOutput(LcnAddr(0, 7, False), 0, 100),),
|
||||
),
|
||||
(
|
||||
COVER_RELAYS,
|
||||
("request_status_relays",),
|
||||
(ModStatusRelays(LcnAddr(0, 7, False), [False] * 8),),
|
||||
),
|
||||
(
|
||||
COVER_RELAYS_BS4,
|
||||
("request_status_relays", "request_status_motor_position"),
|
||||
(
|
||||
ModStatusRelays(LcnAddr(0, 7, False), [False] * 8),
|
||||
ModStatusMotorPositionBS4(LcnAddr(0, 7, False), 1, 50),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_availability(
|
||||
hass: HomeAssistant,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
entry: MockConfigEntry,
|
||||
entity_id: str,
|
||||
request_methods: list[str],
|
||||
return_values: list[ModStatusOutput | ModStatusRelays],
|
||||
) -> None:
|
||||
"""Test the availability of cover entity."""
|
||||
await init_integration(hass, entry)
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state is not None
|
||||
assert state.state != STATE_UNAVAILABLE
|
||||
|
||||
# no response from device -> unavailable
|
||||
with patch.multiple(
|
||||
MockDeviceConnection,
|
||||
**{
|
||||
request_method: AsyncMock(return_value=None)
|
||||
for request_method in request_methods
|
||||
},
|
||||
):
|
||||
freezer.tick(SCAN_INTERVAL)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done(wait_background_tasks=True)
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state is not None
|
||||
assert state.state == STATE_UNAVAILABLE
|
||||
|
||||
with patch.multiple(
|
||||
MockDeviceConnection,
|
||||
**{
|
||||
request_method: AsyncMock(return_value=return_value)
|
||||
for request_method, return_value in zip(
|
||||
request_methods, return_values, strict=True
|
||||
)
|
||||
},
|
||||
):
|
||||
freezer.tick(SCAN_INTERVAL)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done(wait_background_tasks=True)
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state is not None
|
||||
assert state.state != STATE_UNAVAILABLE
|
||||
|
||||
|
||||
async def test_unload_config_entry(hass: HomeAssistant, entry: MockConfigEntry) -> None:
|
||||
"""Test the cover is removed when the config entry is unloaded."""
|
||||
await init_integration(hass, entry)
|
||||
|
||||
@@ -2,12 +2,15 @@
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
from pypck.inputs import ModStatusOutput, ModStatusRelays
|
||||
from pypck.lcn_addr import LcnAddr
|
||||
from pypck.lcn_defs import RelayStateModifier
|
||||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.components.lcn.helpers import get_device_connection
|
||||
from homeassistant.components.lcn.light import SCAN_INTERVAL
|
||||
from homeassistant.components.light import (
|
||||
ATTR_BRIGHTNESS,
|
||||
ATTR_TRANSITION,
|
||||
@@ -27,7 +30,7 @@ from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from .conftest import MockConfigEntry, MockDeviceConnection, init_integration
|
||||
|
||||
from tests.common import snapshot_platform
|
||||
from tests.common import async_fire_time_changed, snapshot_platform
|
||||
|
||||
LIGHT_OUTPUT1 = "light.testmodule_light_output1"
|
||||
LIGHT_OUTPUT2 = "light.testmodule_light_output2"
|
||||
@@ -310,6 +313,61 @@ async def test_pushed_relay_status_change(
|
||||
assert state.state == STATE_OFF
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("entity_id", "request_method", "return_value"),
|
||||
[
|
||||
(
|
||||
LIGHT_OUTPUT1,
|
||||
"request_status_output",
|
||||
ModStatusOutput(LcnAddr(0, 7, False), 0, 0),
|
||||
),
|
||||
(
|
||||
LIGHT_RELAY1,
|
||||
"request_status_relays",
|
||||
ModStatusRelays(LcnAddr(0, 7, False), [False] * 8),
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_availability(
|
||||
hass: HomeAssistant,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
entry: MockConfigEntry,
|
||||
entity_id: str,
|
||||
request_method: str,
|
||||
return_value: ModStatusOutput | ModStatusRelays,
|
||||
) -> None:
|
||||
"""Test the availability of light entity."""
|
||||
await init_integration(hass, entry)
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state is not None
|
||||
assert state.state != STATE_UNAVAILABLE
|
||||
|
||||
# no response from device -> unavailable
|
||||
with patch.object(MockDeviceConnection, request_method, return_value=None):
|
||||
freezer.tick(SCAN_INTERVAL)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done(wait_background_tasks=True)
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state is not None
|
||||
assert state.state == STATE_UNAVAILABLE
|
||||
|
||||
# response from device -> available
|
||||
with patch.object(
|
||||
MockDeviceConnection,
|
||||
request_method,
|
||||
return_value=return_value,
|
||||
):
|
||||
freezer.tick(SCAN_INTERVAL)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done(wait_background_tasks=True)
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state is not None
|
||||
assert state.state != STATE_UNAVAILABLE
|
||||
|
||||
|
||||
async def test_unload_config_entry(hass: HomeAssistant, entry: MockConfigEntry) -> None:
|
||||
"""Test the light is removed when the config entry is unloaded."""
|
||||
await init_integration(hass, entry)
|
||||
|
||||
@@ -2,19 +2,22 @@
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
from pypck.inputs import ModStatusLedsAndLogicOps, ModStatusVar
|
||||
from pypck.lcn_addr import LcnAddr
|
||||
from pypck.lcn_defs import LedStatus, LogicOpStatus, Var, VarValue
|
||||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.components.lcn.helpers import get_device_connection
|
||||
from homeassistant.components.lcn.sensor import SCAN_INTERVAL
|
||||
from homeassistant.const import STATE_UNAVAILABLE, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from .conftest import MockConfigEntry, init_integration
|
||||
from .conftest import MockConfigEntry, MockDeviceConnection, init_integration
|
||||
|
||||
from tests.common import snapshot_platform
|
||||
from tests.common import async_fire_time_changed, snapshot_platform
|
||||
|
||||
SENSOR_VAR1 = "sensor.testmodule_sensor_var1"
|
||||
SENSOR_SETPOINT1 = "sensor.testmodule_sensor_setpoint1"
|
||||
@@ -92,6 +95,63 @@ async def test_pushed_ledlogicop_status_change(
|
||||
assert state.state == "all"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("entity_id", "request_method", "return_value"),
|
||||
[
|
||||
(
|
||||
SENSOR_VAR1,
|
||||
"request_status_variable",
|
||||
ModStatusVar(LcnAddr(0, 7, False), Var.VAR1, VarValue.from_celsius(20)),
|
||||
),
|
||||
(
|
||||
SENSOR_LED6,
|
||||
"request_status_led_and_logic_ops",
|
||||
ModStatusLedsAndLogicOps(
|
||||
LcnAddr(0, 7, False), [LedStatus.OFF] * 12, [LogicOpStatus.NONE] * 4
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_availability(
|
||||
hass: HomeAssistant,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
entry: MockConfigEntry,
|
||||
entity_id: str,
|
||||
request_method: str,
|
||||
return_value: ModStatusVar | ModStatusLedsAndLogicOps,
|
||||
) -> None:
|
||||
"""Test the availability of sensor entity."""
|
||||
await init_integration(hass, entry)
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state is not None
|
||||
assert state.state != STATE_UNAVAILABLE
|
||||
|
||||
# no response from device -> unavailable
|
||||
with patch.object(MockDeviceConnection, request_method, return_value=None):
|
||||
freezer.tick(SCAN_INTERVAL)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done(wait_background_tasks=True)
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state is not None
|
||||
assert state.state == STATE_UNAVAILABLE
|
||||
|
||||
# response from device -> available
|
||||
with patch.object(
|
||||
MockDeviceConnection,
|
||||
request_method,
|
||||
return_value=return_value,
|
||||
):
|
||||
freezer.tick(SCAN_INTERVAL)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done(wait_background_tasks=True)
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state is not None
|
||||
assert state.state != STATE_UNAVAILABLE
|
||||
|
||||
|
||||
async def test_unload_config_entry(hass: HomeAssistant, entry: MockConfigEntry) -> None:
|
||||
"""Test the sensor is removed when the config entry is unloaded."""
|
||||
await init_integration(hass, entry)
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
from pypck.inputs import (
|
||||
ModStatusKeyLocks,
|
||||
ModStatusOutput,
|
||||
@@ -10,9 +11,11 @@ from pypck.inputs import (
|
||||
)
|
||||
from pypck.lcn_addr import LcnAddr
|
||||
from pypck.lcn_defs import KeyLockStateModifier, RelayStateModifier, Var, VarValue
|
||||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.components.lcn.helpers import get_device_connection
|
||||
from homeassistant.components.lcn.switch import SCAN_INTERVAL
|
||||
from homeassistant.components.switch import DOMAIN as DOMAIN_SWITCH
|
||||
from homeassistant.const import (
|
||||
ATTR_ENTITY_ID,
|
||||
@@ -28,7 +31,7 @@ from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from .conftest import MockConfigEntry, MockDeviceConnection, init_integration
|
||||
|
||||
from tests.common import snapshot_platform
|
||||
from tests.common import async_fire_time_changed, snapshot_platform
|
||||
|
||||
SWITCH_OUTPUT1 = "switch.testmodule_switch_output1"
|
||||
SWITCH_OUTPUT2 = "switch.testmodule_switch_output2"
|
||||
@@ -504,6 +507,71 @@ async def test_pushed_keylock_status_change(
|
||||
assert state.state == STATE_OFF
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("entity_id", "request_method", "return_value"),
|
||||
[
|
||||
(
|
||||
SWITCH_OUTPUT1,
|
||||
"request_status_output",
|
||||
ModStatusOutput(LcnAddr(0, 7, False), 0, 0),
|
||||
),
|
||||
(
|
||||
SWITCH_RELAY1,
|
||||
"request_status_relays",
|
||||
ModStatusRelays(LcnAddr(0, 7, False), [False] * 8),
|
||||
),
|
||||
(
|
||||
SWITCH_REGULATOR1,
|
||||
"request_status_variable",
|
||||
ModStatusVar(LcnAddr(0, 7, False), Var.R1VARSETPOINT, VarValue(0x8000)),
|
||||
),
|
||||
(
|
||||
SWITCH_KEYLOCKK1,
|
||||
"request_status_locked_keys",
|
||||
ModStatusKeyLocks(LcnAddr(0, 7, False), [[False] * 8 for i in range(4)]),
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_availability(
|
||||
hass: HomeAssistant,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
entry: MockConfigEntry,
|
||||
entity_id: str,
|
||||
request_method: str,
|
||||
return_value: ModStatusOutput | ModStatusRelays,
|
||||
) -> None:
|
||||
"""Test the availability of switch entity."""
|
||||
await init_integration(hass, entry)
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state is not None
|
||||
assert state.state != STATE_UNAVAILABLE
|
||||
|
||||
# no response from device -> unavailable
|
||||
with patch.object(MockDeviceConnection, request_method, return_value=None):
|
||||
freezer.tick(SCAN_INTERVAL)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done(wait_background_tasks=True)
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state is not None
|
||||
assert state.state == STATE_UNAVAILABLE
|
||||
|
||||
# response from device -> available
|
||||
with patch.object(
|
||||
MockDeviceConnection,
|
||||
request_method,
|
||||
return_value=return_value,
|
||||
):
|
||||
freezer.tick(SCAN_INTERVAL)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done(wait_background_tasks=True)
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state is not None
|
||||
assert state.state != STATE_UNAVAILABLE
|
||||
|
||||
|
||||
async def test_unload_config_entry(hass: HomeAssistant, entry: MockConfigEntry) -> None:
|
||||
"""Test the switch is removed when the config entry is unloaded."""
|
||||
await init_integration(hass, entry)
|
||||
|
||||
Reference in New Issue
Block a user