mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 11:17:21 +00:00
Add Shelly Gen2 polling for sesnors missing push updates (#64171)
This commit is contained in:
parent
b66fd820ff
commit
601f3f9c6d
@ -53,7 +53,9 @@ from .const import (
|
|||||||
REST_SENSORS_UPDATE_INTERVAL,
|
REST_SENSORS_UPDATE_INTERVAL,
|
||||||
RPC,
|
RPC,
|
||||||
RPC_INPUTS_EVENTS_TYPES,
|
RPC_INPUTS_EVENTS_TYPES,
|
||||||
|
RPC_POLL,
|
||||||
RPC_RECONNECT_INTERVAL,
|
RPC_RECONNECT_INTERVAL,
|
||||||
|
RPC_SENSORS_POLLING_INTERVAL,
|
||||||
SHBTN_MODELS,
|
SHBTN_MODELS,
|
||||||
SLEEP_PERIOD_MULTIPLIER,
|
SLEEP_PERIOD_MULTIPLIER,
|
||||||
UPDATE_PERIOD_MULTIPLIER,
|
UPDATE_PERIOD_MULTIPLIER,
|
||||||
@ -253,6 +255,10 @@ async def async_setup_rpc_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool
|
|||||||
] = RpcDeviceWrapper(hass, entry, device)
|
] = RpcDeviceWrapper(hass, entry, device)
|
||||||
device_wrapper.async_setup()
|
device_wrapper.async_setup()
|
||||||
|
|
||||||
|
hass.data[DOMAIN][DATA_CONFIG_ENTRY][entry.entry_id][RPC_POLL] = RpcPollingWrapper(
|
||||||
|
hass, entry, device
|
||||||
|
)
|
||||||
|
|
||||||
hass.config_entries.async_setup_platforms(entry, RPC_PLATFORMS)
|
hass.config_entries.async_setup_platforms(entry, RPC_PLATFORMS)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
@ -768,3 +774,45 @@ class RpcDeviceWrapper(update_coordinator.DataUpdateCoordinator):
|
|||||||
"""Handle Home Assistant stopping."""
|
"""Handle Home Assistant stopping."""
|
||||||
_LOGGER.debug("Stopping RpcDeviceWrapper for %s", self.name)
|
_LOGGER.debug("Stopping RpcDeviceWrapper for %s", self.name)
|
||||||
await self.shutdown()
|
await self.shutdown()
|
||||||
|
|
||||||
|
|
||||||
|
class RpcPollingWrapper(update_coordinator.DataUpdateCoordinator):
|
||||||
|
"""Polling Wrapper for a Shelly RPC based device."""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self, hass: HomeAssistant, entry: ConfigEntry, device: RpcDevice
|
||||||
|
) -> None:
|
||||||
|
"""Initialize the RPC polling coordinator."""
|
||||||
|
self.device_id: str | None = None
|
||||||
|
|
||||||
|
device_name = get_rpc_device_name(device) if device.initialized else entry.title
|
||||||
|
super().__init__(
|
||||||
|
hass,
|
||||||
|
_LOGGER,
|
||||||
|
name=device_name,
|
||||||
|
update_interval=timedelta(seconds=RPC_SENSORS_POLLING_INTERVAL),
|
||||||
|
)
|
||||||
|
self.entry = entry
|
||||||
|
self.device = device
|
||||||
|
|
||||||
|
async def _async_update_data(self) -> None:
|
||||||
|
"""Fetch data."""
|
||||||
|
if not self.device.connected:
|
||||||
|
raise update_coordinator.UpdateFailed("Device disconnected")
|
||||||
|
|
||||||
|
try:
|
||||||
|
_LOGGER.debug("Polling Shelly RPC Device - %s", self.name)
|
||||||
|
async with async_timeout.timeout(AIOSHELLY_DEVICE_TIMEOUT_SEC):
|
||||||
|
await self.device.update_status()
|
||||||
|
except OSError as err:
|
||||||
|
raise update_coordinator.UpdateFailed("Device disconnected") from err
|
||||||
|
|
||||||
|
@property
|
||||||
|
def model(self) -> str:
|
||||||
|
"""Model of the device."""
|
||||||
|
return cast(str, self.entry.data["model"])
|
||||||
|
|
||||||
|
@property
|
||||||
|
def mac(self) -> str:
|
||||||
|
"""Mac address of the device."""
|
||||||
|
return cast(str, self.entry.unique_id)
|
||||||
|
@ -10,6 +10,7 @@ DEVICE: Final = "device"
|
|||||||
DOMAIN: Final = "shelly"
|
DOMAIN: Final = "shelly"
|
||||||
REST: Final = "rest"
|
REST: Final = "rest"
|
||||||
RPC: Final = "rpc"
|
RPC: Final = "rpc"
|
||||||
|
RPC_POLL: Final = "rpc_poll"
|
||||||
|
|
||||||
CONF_COAP_PORT: Final = "coap_port"
|
CONF_COAP_PORT: Final = "coap_port"
|
||||||
DEFAULT_COAP_PORT: Final = 5683
|
DEFAULT_COAP_PORT: Final = 5683
|
||||||
@ -53,6 +54,9 @@ POLLING_TIMEOUT_SEC: Final = 18
|
|||||||
# Refresh interval for REST sensors
|
# Refresh interval for REST sensors
|
||||||
REST_SENSORS_UPDATE_INTERVAL: Final = 60
|
REST_SENSORS_UPDATE_INTERVAL: Final = 60
|
||||||
|
|
||||||
|
# Refresh interval for RPC polling sensors
|
||||||
|
RPC_SENSORS_POLLING_INTERVAL: Final = 60
|
||||||
|
|
||||||
# Timeout used for aioshelly calls
|
# Timeout used for aioshelly calls
|
||||||
AIOSHELLY_DEVICE_TIMEOUT_SEC: Final = 10
|
AIOSHELLY_DEVICE_TIMEOUT_SEC: Final = 10
|
||||||
|
|
||||||
|
@ -24,7 +24,12 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|||||||
from homeassistant.helpers.restore_state import RestoreEntity
|
from homeassistant.helpers.restore_state import RestoreEntity
|
||||||
from homeassistant.helpers.typing import StateType
|
from homeassistant.helpers.typing import StateType
|
||||||
|
|
||||||
from . import BlockDeviceWrapper, RpcDeviceWrapper, ShellyDeviceRestWrapper
|
from . import (
|
||||||
|
BlockDeviceWrapper,
|
||||||
|
RpcDeviceWrapper,
|
||||||
|
RpcPollingWrapper,
|
||||||
|
ShellyDeviceRestWrapper,
|
||||||
|
)
|
||||||
from .const import (
|
from .const import (
|
||||||
AIOSHELLY_DEVICE_TIMEOUT_SEC,
|
AIOSHELLY_DEVICE_TIMEOUT_SEC,
|
||||||
BLOCK,
|
BLOCK,
|
||||||
@ -32,6 +37,7 @@ from .const import (
|
|||||||
DOMAIN,
|
DOMAIN,
|
||||||
REST,
|
REST,
|
||||||
RPC,
|
RPC,
|
||||||
|
RPC_POLL,
|
||||||
)
|
)
|
||||||
from .utils import (
|
from .utils import (
|
||||||
async_remove_shelly_entity,
|
async_remove_shelly_entity,
|
||||||
@ -160,6 +166,10 @@ async def async_setup_entry_rpc(
|
|||||||
config_entry.entry_id
|
config_entry.entry_id
|
||||||
][RPC]
|
][RPC]
|
||||||
|
|
||||||
|
polling_wrapper: RpcPollingWrapper = hass.data[DOMAIN][DATA_CONFIG_ENTRY][
|
||||||
|
config_entry.entry_id
|
||||||
|
][RPC_POLL]
|
||||||
|
|
||||||
entities = []
|
entities = []
|
||||||
for sensor_id in sensors:
|
for sensor_id in sensors:
|
||||||
description = sensors[sensor_id]
|
description = sensors[sensor_id]
|
||||||
@ -178,17 +188,17 @@ async def async_setup_entry_rpc(
|
|||||||
unique_id = f"{wrapper.mac}-{key}-{sensor_id}"
|
unique_id = f"{wrapper.mac}-{key}-{sensor_id}"
|
||||||
await async_remove_shelly_entity(hass, domain, unique_id)
|
await async_remove_shelly_entity(hass, domain, unique_id)
|
||||||
else:
|
else:
|
||||||
entities.append((key, sensor_id, description))
|
if description.should_poll:
|
||||||
|
entities.append(
|
||||||
|
sensor_class(polling_wrapper, key, sensor_id, description)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
entities.append(sensor_class(wrapper, key, sensor_id, description))
|
||||||
|
|
||||||
if not entities:
|
if not entities:
|
||||||
return
|
return
|
||||||
|
|
||||||
async_add_entities(
|
async_add_entities(entities)
|
||||||
[
|
|
||||||
sensor_class(wrapper, key, sensor_id, description)
|
|
||||||
for key, sensor_id, description in entities
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry_rest(
|
async def async_setup_entry_rest(
|
||||||
@ -257,6 +267,7 @@ class RpcAttributeDescription:
|
|||||||
removal_condition: Callable[[dict, str], bool] | None = None
|
removal_condition: Callable[[dict, str], bool] | None = None
|
||||||
extra_state_attributes: Callable[[dict, dict], dict | None] | None = None
|
extra_state_attributes: Callable[[dict, dict], dict | None] | None = None
|
||||||
entity_category: EntityCategory | None = None
|
entity_category: EntityCategory | None = None
|
||||||
|
should_poll: bool = False
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@ -343,7 +354,11 @@ class ShellyBlockEntity(entity.Entity):
|
|||||||
class ShellyRpcEntity(entity.Entity):
|
class ShellyRpcEntity(entity.Entity):
|
||||||
"""Helper class to represent a rpc entity."""
|
"""Helper class to represent a rpc entity."""
|
||||||
|
|
||||||
def __init__(self, wrapper: RpcDeviceWrapper, key: str) -> None:
|
def __init__(
|
||||||
|
self,
|
||||||
|
wrapper: RpcDeviceWrapper | RpcPollingWrapper,
|
||||||
|
key: str,
|
||||||
|
) -> None:
|
||||||
"""Initialize Shelly entity."""
|
"""Initialize Shelly entity."""
|
||||||
self.wrapper = wrapper
|
self.wrapper = wrapper
|
||||||
self.key = key
|
self.key = key
|
||||||
|
@ -287,6 +287,7 @@ RPC_SENSORS: Final = {
|
|||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
default_enabled=False,
|
default_enabled=False,
|
||||||
entity_category=EntityCategory.DIAGNOSTIC,
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
|
should_poll=True,
|
||||||
),
|
),
|
||||||
"rssi": RpcAttributeDescription(
|
"rssi": RpcAttributeDescription(
|
||||||
key="wifi",
|
key="wifi",
|
||||||
@ -297,6 +298,7 @@ RPC_SENSORS: Final = {
|
|||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
default_enabled=False,
|
default_enabled=False,
|
||||||
entity_category=EntityCategory.DIAGNOSTIC,
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
|
should_poll=True,
|
||||||
),
|
),
|
||||||
"uptime": RpcAttributeDescription(
|
"uptime": RpcAttributeDescription(
|
||||||
key="sys",
|
key="sys",
|
||||||
@ -306,6 +308,7 @@ RPC_SENSORS: Final = {
|
|||||||
device_class=SensorDeviceClass.TIMESTAMP,
|
device_class=SensorDeviceClass.TIMESTAMP,
|
||||||
default_enabled=False,
|
default_enabled=False,
|
||||||
entity_category=EntityCategory.DIAGNOSTIC,
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
|
should_poll=True,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user