mirror of
https://github.com/home-assistant/core.git
synced 2025-04-25 01:38:02 +00:00
Handle state unknown if last state is missing in Shelly (#86813)
Shelly - handle state unknown if last state is missing
This commit is contained in:
parent
50c2992f36
commit
803cd8d9a3
@ -296,12 +296,15 @@ class BlockSleepingBinarySensor(ShellySleepingBlockAttributeEntity, BinarySensor
|
||||
entity_description: BlockBinarySensorDescription
|
||||
|
||||
@property
|
||||
def is_on(self) -> bool:
|
||||
def is_on(self) -> bool | None:
|
||||
"""Return true if sensor state is on."""
|
||||
if self.block is not None:
|
||||
return bool(self.attribute_value)
|
||||
|
||||
return self.last_state == STATE_ON
|
||||
if self.last_state is None:
|
||||
return None
|
||||
|
||||
return self.last_state.state == STATE_ON
|
||||
|
||||
|
||||
class RpcSleepingBinarySensor(ShellySleepingRpcAttributeEntity, BinarySensorEntity):
|
||||
@ -310,9 +313,12 @@ class RpcSleepingBinarySensor(ShellySleepingRpcAttributeEntity, BinarySensorEnti
|
||||
entity_description: RpcBinarySensorDescription
|
||||
|
||||
@property
|
||||
def is_on(self) -> bool:
|
||||
def is_on(self) -> bool | None:
|
||||
"""Return true if RPC sensor state is on."""
|
||||
if self.coordinator.device.initialized:
|
||||
return bool(self.attribute_value)
|
||||
|
||||
return self.last_state == STATE_ON
|
||||
if self.last_state is None:
|
||||
return None
|
||||
|
||||
return self.last_state.state == STATE_ON
|
||||
|
@ -75,7 +75,6 @@ async def validate_input(
|
||||
options,
|
||||
)
|
||||
await rpc_device.shutdown()
|
||||
assert rpc_device.shelly
|
||||
|
||||
return {
|
||||
"title": rpc_device.name,
|
||||
|
@ -9,8 +9,7 @@ from aioshelly.block_device import Block
|
||||
from aioshelly.exceptions import DeviceConnectionError, InvalidAuthError, RpcCallError
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import ATTR_UNIT_OF_MEASUREMENT
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.core import HomeAssistant, State, callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC
|
||||
from homeassistant.helpers.entity import DeviceInfo, Entity, EntityDescription
|
||||
@ -566,8 +565,7 @@ class ShellySleepingBlockAttributeEntity(ShellyBlockAttributeEntity, RestoreEnti
|
||||
) -> None:
|
||||
"""Initialize the sleeping sensor."""
|
||||
self.sensors = sensors
|
||||
self.last_state: StateType = None
|
||||
self.last_unit: str | None = None
|
||||
self.last_state: State | None = None
|
||||
self.coordinator = coordinator
|
||||
self.attribute = attribute
|
||||
self.block: Block | None = block # type: ignore[assignment]
|
||||
@ -592,12 +590,7 @@ class ShellySleepingBlockAttributeEntity(ShellyBlockAttributeEntity, RestoreEnti
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Handle entity which will be added."""
|
||||
await super().async_added_to_hass()
|
||||
|
||||
last_state = await self.async_get_last_state()
|
||||
|
||||
if last_state is not None:
|
||||
self.last_state = last_state.state
|
||||
self.last_unit = last_state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||
self.last_state = await self.async_get_last_state()
|
||||
|
||||
@callback
|
||||
def _update_callback(self) -> None:
|
||||
@ -649,8 +642,7 @@ class ShellySleepingRpcAttributeEntity(ShellyRpcAttributeEntity, RestoreEntity):
|
||||
entry: RegistryEntry | None = None,
|
||||
) -> None:
|
||||
"""Initialize the sleeping sensor."""
|
||||
self.last_state: StateType = None
|
||||
self.last_unit: str | None = None
|
||||
self.last_state: State | None = None
|
||||
self.coordinator = coordinator
|
||||
self.key = key
|
||||
self.attribute = attribute
|
||||
@ -675,9 +667,4 @@ class ShellySleepingRpcAttributeEntity(ShellyRpcAttributeEntity, RestoreEntity):
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Handle entity which will be added."""
|
||||
await super().async_added_to_hass()
|
||||
|
||||
last_state = await self.async_get_last_state()
|
||||
|
||||
if last_state is not None:
|
||||
self.last_state = last_state.state
|
||||
self.last_unit = last_state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||
self.last_state = await self.async_get_last_state()
|
||||
|
@ -81,7 +81,6 @@ def async_setup_block_entry(
|
||||
continue
|
||||
|
||||
blocks.append(block)
|
||||
assert coordinator.device.shelly
|
||||
unique_id = f"{coordinator.mac}-{block.type}_{block.channel}"
|
||||
async_remove_shelly_entity(hass, "switch", unique_id)
|
||||
|
||||
|
@ -93,12 +93,15 @@ class BlockSleepingNumber(ShellySleepingBlockAttributeEntity, NumberEntity):
|
||||
entity_description: BlockNumberDescription
|
||||
|
||||
@property
|
||||
def native_value(self) -> float:
|
||||
def native_value(self) -> float | None:
|
||||
"""Return value of number."""
|
||||
if self.block is not None:
|
||||
return cast(float, self.attribute_value)
|
||||
|
||||
return cast(float, self.last_state)
|
||||
if self.last_state is None:
|
||||
return None
|
||||
|
||||
return cast(float, self.last_state.state)
|
||||
|
||||
async def async_set_native_value(self, value: float) -> None:
|
||||
"""Set value."""
|
||||
|
@ -15,6 +15,7 @@ from homeassistant.components.sensor import (
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
ATTR_UNIT_OF_MEASUREMENT,
|
||||
CONCENTRATION_PARTS_PER_MILLION,
|
||||
DEGREE,
|
||||
LIGHT_LUX,
|
||||
@ -669,7 +670,10 @@ class BlockSleepingSensor(ShellySleepingBlockAttributeEntity, SensorEntity):
|
||||
if self.block is not None:
|
||||
return self.attribute_value
|
||||
|
||||
return self.last_state
|
||||
if self.last_state is None:
|
||||
return None
|
||||
|
||||
return self.last_state.state
|
||||
|
||||
@property
|
||||
def native_unit_of_measurement(self) -> str | None:
|
||||
@ -677,7 +681,10 @@ class BlockSleepingSensor(ShellySleepingBlockAttributeEntity, SensorEntity):
|
||||
if self.block is not None:
|
||||
return self.entity_description.native_unit_of_measurement
|
||||
|
||||
return self.last_unit
|
||||
if self.last_state is None:
|
||||
return None
|
||||
|
||||
return self.last_state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||
|
||||
|
||||
class RpcSleepingSensor(ShellySleepingRpcAttributeEntity, SensorEntity):
|
||||
@ -691,12 +698,12 @@ class RpcSleepingSensor(ShellySleepingRpcAttributeEntity, SensorEntity):
|
||||
if self.coordinator.device.initialized:
|
||||
return self.attribute_value
|
||||
|
||||
return self.last_state
|
||||
if self.last_state is None:
|
||||
return None
|
||||
|
||||
return self.last_state.state
|
||||
|
||||
@property
|
||||
def native_unit_of_measurement(self) -> str | None:
|
||||
"""Return the unit of measurement of the sensor, if any."""
|
||||
if self.coordinator.device.initialized:
|
||||
return self.entity_description.native_unit_of_measurement
|
||||
|
||||
return self.last_unit
|
||||
return self.entity_description.native_unit_of_measurement
|
||||
|
@ -221,7 +221,6 @@ class RpcUpdateEntity(ShellyRpcAttributeEntity, UpdateEntity):
|
||||
@property
|
||||
def installed_version(self) -> str | None:
|
||||
"""Version currently in use."""
|
||||
assert self.coordinator.device.shelly
|
||||
return cast(str, self.coordinator.device.shelly["ver"])
|
||||
|
||||
@property
|
||||
|
@ -51,8 +51,6 @@ def async_remove_shelly_entity(
|
||||
|
||||
def get_number_of_channels(device: BlockDevice, block: Block) -> int:
|
||||
"""Get number of channels for block type."""
|
||||
assert isinstance(device.shelly, dict)
|
||||
|
||||
channels = None
|
||||
|
||||
if block.type == "input":
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN
|
||||
from homeassistant.components.shelly.const import SLEEP_PERIOD_MULTIPLIER
|
||||
from homeassistant.const import STATE_OFF, STATE_ON
|
||||
from homeassistant.const import STATE_OFF, STATE_ON, STATE_UNKNOWN
|
||||
from homeassistant.core import State
|
||||
from homeassistant.helpers.entity_registry import async_get
|
||||
|
||||
@ -134,6 +134,29 @@ async def test_block_restored_sleeping_binary_sensor(
|
||||
assert hass.states.get(entity_id).state == STATE_OFF
|
||||
|
||||
|
||||
async def test_block_restored_sleeping_binary_sensor_no_last_state(
|
||||
hass, mock_block_device, device_reg, monkeypatch
|
||||
):
|
||||
"""Test block restored sleeping binary sensor missing last state."""
|
||||
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
|
||||
)
|
||||
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_UNKNOWN
|
||||
|
||||
# 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"
|
||||
@ -212,3 +235,28 @@ async def test_rpc_restored_sleeping_binary_sensor(
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get(entity_id).state == STATE_OFF
|
||||
|
||||
|
||||
async def test_rpc_restored_sleeping_binary_sensor_no_last_state(
|
||||
hass, mock_rpc_device, device_reg, monkeypatch
|
||||
):
|
||||
"""Test RPC restored sleeping binary sensor missing last state."""
|
||||
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
|
||||
)
|
||||
|
||||
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_UNKNOWN
|
||||
|
||||
# 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
|
||||
|
@ -11,7 +11,7 @@ from homeassistant.components.number import (
|
||||
)
|
||||
from homeassistant.components.shelly.const import DOMAIN
|
||||
from homeassistant.config_entries import SOURCE_REAUTH, ConfigEntryState
|
||||
from homeassistant.const import ATTR_ENTITY_ID
|
||||
from homeassistant.const import ATTR_ENTITY_ID, STATE_UNKNOWN
|
||||
from homeassistant.core import State
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
|
||||
@ -74,6 +74,40 @@ async def test_block_restored_number(hass, mock_block_device, device_reg, monkey
|
||||
assert hass.states.get(entity_id).state == "50"
|
||||
|
||||
|
||||
async def test_block_restored_number_no_last_state(
|
||||
hass, mock_block_device, device_reg, monkeypatch
|
||||
):
|
||||
"""Test block restored number missing last state."""
|
||||
entry = await init_integration(hass, 1, sleep_period=1000, skip_setup=True)
|
||||
register_device(device_reg, entry)
|
||||
capabilities = {
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"step": 1,
|
||||
"mode": "slider",
|
||||
}
|
||||
entity_id = register_entity(
|
||||
hass,
|
||||
NUMBER_DOMAIN,
|
||||
"test_name_valve_position",
|
||||
"device_0-valvePos",
|
||||
entry,
|
||||
capabilities,
|
||||
)
|
||||
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_UNKNOWN
|
||||
|
||||
# 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 == "50"
|
||||
|
||||
|
||||
async def test_block_number_set_value(hass, mock_block_device):
|
||||
"""Test block device number set value."""
|
||||
await init_integration(hass, 1, sleep_period=1000)
|
||||
|
@ -95,6 +95,29 @@ async def test_block_restored_sleeping_sensor(
|
||||
assert hass.states.get(entity_id).state == "22.1"
|
||||
|
||||
|
||||
async def test_block_restored_sleeping_sensor_no_last_state(
|
||||
hass, mock_block_device, device_reg, monkeypatch
|
||||
):
|
||||
"""Test block restored sleeping sensor missing last state."""
|
||||
entry = await init_integration(hass, 1, sleep_period=1000, skip_setup=True)
|
||||
register_device(device_reg, entry)
|
||||
entity_id = register_entity(
|
||||
hass, SENSOR_DOMAIN, "test_name_temperature", "sensor_0-temp", entry
|
||||
)
|
||||
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_UNKNOWN
|
||||
|
||||
# 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 == "22.1"
|
||||
|
||||
|
||||
async def test_block_sensor_error(hass, mock_block_device, monkeypatch):
|
||||
"""Test block sensor unavailable on sensor error."""
|
||||
entity_id = f"{SENSOR_DOMAIN}.test_name_battery"
|
||||
@ -270,3 +293,32 @@ async def test_rpc_restored_sleeping_sensor(
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get(entity_id).state == "22.9"
|
||||
|
||||
|
||||
async def test_rpc_restored_sleeping_sensor_no_last_state(
|
||||
hass, mock_rpc_device, device_reg, monkeypatch
|
||||
):
|
||||
"""Test RPC restored sensor missing last state."""
|
||||
entry = await init_integration(hass, 2, sleep_period=1000, skip_setup=True)
|
||||
register_device(device_reg, entry)
|
||||
entity_id = register_entity(
|
||||
hass,
|
||||
SENSOR_DOMAIN,
|
||||
"test_name_temperature",
|
||||
"temperature:0-temperature_0",
|
||||
entry,
|
||||
)
|
||||
|
||||
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_UNKNOWN
|
||||
|
||||
# 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 == "22.9"
|
||||
|
Loading…
x
Reference in New Issue
Block a user