mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 13:17:32 +00:00
Add Shelly support for REST sensors (#40429)
This commit is contained in:
parent
403514ccb3
commit
d8b067ebf9
@ -24,9 +24,12 @@ from homeassistant.helpers import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
|
COAP,
|
||||||
DATA_CONFIG_ENTRY,
|
DATA_CONFIG_ENTRY,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
POLLING_TIMEOUT_MULTIPLIER,
|
POLLING_TIMEOUT_MULTIPLIER,
|
||||||
|
REST,
|
||||||
|
REST_SENSORS_UPDATE_INTERVAL,
|
||||||
SETUP_ENTRY_TIMEOUT_SEC,
|
SETUP_ENTRY_TIMEOUT_SEC,
|
||||||
SLEEP_PERIOD_MULTIPLIER,
|
SLEEP_PERIOD_MULTIPLIER,
|
||||||
UPDATE_PERIOD_MULTIPLIER,
|
UPDATE_PERIOD_MULTIPLIER,
|
||||||
@ -82,10 +85,15 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
|
|||||||
except (asyncio.TimeoutError, OSError) as err:
|
except (asyncio.TimeoutError, OSError) as err:
|
||||||
raise ConfigEntryNotReady from err
|
raise ConfigEntryNotReady from err
|
||||||
|
|
||||||
wrapper = hass.data[DOMAIN][DATA_CONFIG_ENTRY][
|
hass.data[DOMAIN][DATA_CONFIG_ENTRY][entry.entry_id] = {}
|
||||||
entry.entry_id
|
coap_wrapper = hass.data[DOMAIN][DATA_CONFIG_ENTRY][entry.entry_id][
|
||||||
|
COAP
|
||||||
] = ShellyDeviceWrapper(hass, entry, device)
|
] = ShellyDeviceWrapper(hass, entry, device)
|
||||||
await wrapper.async_setup()
|
await coap_wrapper.async_setup()
|
||||||
|
|
||||||
|
hass.data[DOMAIN][DATA_CONFIG_ENTRY][entry.entry_id][
|
||||||
|
REST
|
||||||
|
] = ShellyDeviceRestWrapper(hass, device)
|
||||||
|
|
||||||
for component in PLATFORMS:
|
for component in PLATFORMS:
|
||||||
hass.async_create_task(
|
hass.async_create_task(
|
||||||
@ -169,6 +177,37 @@ class ShellyDeviceWrapper(update_coordinator.DataUpdateCoordinator):
|
|||||||
self.device.shutdown()
|
self.device.shutdown()
|
||||||
|
|
||||||
|
|
||||||
|
class ShellyDeviceRestWrapper(update_coordinator.DataUpdateCoordinator):
|
||||||
|
"""Rest Wrapper for a Shelly device with Home Assistant specific functions."""
|
||||||
|
|
||||||
|
def __init__(self, hass, device: aioshelly.Device):
|
||||||
|
"""Initialize the Shelly device wrapper."""
|
||||||
|
|
||||||
|
super().__init__(
|
||||||
|
hass,
|
||||||
|
_LOGGER,
|
||||||
|
name=device.settings["name"] or device.settings["device"]["hostname"],
|
||||||
|
update_interval=timedelta(seconds=REST_SENSORS_UPDATE_INTERVAL),
|
||||||
|
)
|
||||||
|
self.device = device
|
||||||
|
|
||||||
|
async def _async_update_data(self):
|
||||||
|
"""Fetch data."""
|
||||||
|
try:
|
||||||
|
async with async_timeout.timeout(5):
|
||||||
|
_LOGGER.debug(
|
||||||
|
"REST update for %s", self.device.settings["device"]["hostname"]
|
||||||
|
)
|
||||||
|
return await self.device.update_status()
|
||||||
|
except OSError as err:
|
||||||
|
raise update_coordinator.UpdateFailed("Error fetching data") from err
|
||||||
|
|
||||||
|
@property
|
||||||
|
def mac(self):
|
||||||
|
"""Mac address of the device."""
|
||||||
|
return self.device.settings["device"]["mac"]
|
||||||
|
|
||||||
|
|
||||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
|
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
|
||||||
"""Unload a config entry."""
|
"""Unload a config entry."""
|
||||||
unload_ok = all(
|
unload_ok = all(
|
||||||
@ -180,6 +219,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
if unload_ok:
|
if unload_ok:
|
||||||
hass.data[DOMAIN][DATA_CONFIG_ENTRY].pop(entry.entry_id).shutdown()
|
hass.data[DOMAIN][DATA_CONFIG_ENTRY][entry.entry_id][COAP].shutdown()
|
||||||
|
hass.data[DOMAIN][DATA_CONFIG_ENTRY].pop(entry.entry_id)
|
||||||
|
|
||||||
return unload_ok
|
return unload_ok
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
"""Binary sensor for Shelly."""
|
"""Binary sensor for Shelly."""
|
||||||
from homeassistant.components.binary_sensor import (
|
from homeassistant.components.binary_sensor import (
|
||||||
|
DEVICE_CLASS_CONNECTIVITY,
|
||||||
DEVICE_CLASS_GAS,
|
DEVICE_CLASS_GAS,
|
||||||
DEVICE_CLASS_MOISTURE,
|
DEVICE_CLASS_MOISTURE,
|
||||||
DEVICE_CLASS_OPENING,
|
DEVICE_CLASS_OPENING,
|
||||||
@ -11,8 +12,11 @@ from homeassistant.components.binary_sensor import (
|
|||||||
|
|
||||||
from .entity import (
|
from .entity import (
|
||||||
BlockAttributeDescription,
|
BlockAttributeDescription,
|
||||||
|
RestAttributeDescription,
|
||||||
ShellyBlockAttributeEntity,
|
ShellyBlockAttributeEntity,
|
||||||
|
ShellyRestAttributeEntity,
|
||||||
async_setup_entry_attribute_entities,
|
async_setup_entry_attribute_entities,
|
||||||
|
async_setup_entry_rest,
|
||||||
)
|
)
|
||||||
|
|
||||||
SENSORS = {
|
SENSORS = {
|
||||||
@ -48,6 +52,22 @@ SENSORS = {
|
|||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
REST_SENSORS = {
|
||||||
|
"cloud": RestAttributeDescription(
|
||||||
|
name="Cloud",
|
||||||
|
device_class=DEVICE_CLASS_CONNECTIVITY,
|
||||||
|
default_enabled=False,
|
||||||
|
path="cloud/connected",
|
||||||
|
),
|
||||||
|
"fwupdate": RestAttributeDescription(
|
||||||
|
name="Firmware update",
|
||||||
|
icon="mdi:update",
|
||||||
|
default_enabled=False,
|
||||||
|
path="update/has_update",
|
||||||
|
attributes={"description": "available version:", "path": "update/new_version"},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
"""Set up sensors for device."""
|
"""Set up sensors for device."""
|
||||||
@ -55,6 +75,10 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
|||||||
hass, config_entry, async_add_entities, SENSORS, ShellyBinarySensor
|
hass, config_entry, async_add_entities, SENSORS, ShellyBinarySensor
|
||||||
)
|
)
|
||||||
|
|
||||||
|
await async_setup_entry_rest(
|
||||||
|
hass, config_entry, async_add_entities, REST_SENSORS, ShellyRestBinarySensor
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ShellyBinarySensor(ShellyBlockAttributeEntity, BinarySensorEntity):
|
class ShellyBinarySensor(ShellyBlockAttributeEntity, BinarySensorEntity):
|
||||||
"""Shelly binary sensor entity."""
|
"""Shelly binary sensor entity."""
|
||||||
@ -63,3 +87,12 @@ class ShellyBinarySensor(ShellyBlockAttributeEntity, BinarySensorEntity):
|
|||||||
def is_on(self):
|
def is_on(self):
|
||||||
"""Return true if sensor state is on."""
|
"""Return true if sensor state is on."""
|
||||||
return bool(self.attribute_value)
|
return bool(self.attribute_value)
|
||||||
|
|
||||||
|
|
||||||
|
class ShellyRestBinarySensor(ShellyRestAttributeEntity, BinarySensorEntity):
|
||||||
|
"""Shelly REST binary sensor entity."""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_on(self):
|
||||||
|
"""Return true if REST sensor state is on."""
|
||||||
|
return bool(self.attribute_value)
|
||||||
|
@ -1,11 +1,16 @@
|
|||||||
"""Constants for the Shelly integration."""
|
"""Constants for the Shelly integration."""
|
||||||
|
|
||||||
|
COAP = "coap"
|
||||||
DATA_CONFIG_ENTRY = "config_entry"
|
DATA_CONFIG_ENTRY = "config_entry"
|
||||||
DOMAIN = "shelly"
|
DOMAIN = "shelly"
|
||||||
|
REST = "rest"
|
||||||
|
|
||||||
# Used to calculate the timeout in "_async_update_data" used for polling data from devices.
|
# Used to calculate the timeout in "_async_update_data" used for polling data from devices.
|
||||||
POLLING_TIMEOUT_MULTIPLIER = 1.2
|
POLLING_TIMEOUT_MULTIPLIER = 1.2
|
||||||
|
|
||||||
|
# Refresh interval for REST sensors
|
||||||
|
REST_SENSORS_UPDATE_INTERVAL = 60
|
||||||
|
|
||||||
# Timeout used for initial entry setup in "async_setup_entry".
|
# Timeout used for initial entry setup in "async_setup_entry".
|
||||||
SETUP_ENTRY_TIMEOUT_SEC = 10
|
SETUP_ENTRY_TIMEOUT_SEC = 10
|
||||||
|
|
||||||
|
@ -12,13 +12,13 @@ from homeassistant.components.cover import (
|
|||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
|
|
||||||
from . import ShellyDeviceWrapper
|
from . import ShellyDeviceWrapper
|
||||||
from .const import DATA_CONFIG_ENTRY, DOMAIN
|
from .const import COAP, DATA_CONFIG_ENTRY, DOMAIN
|
||||||
from .entity import ShellyBlockEntity
|
from .entity import ShellyBlockEntity
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
"""Set up cover for device."""
|
"""Set up cover for device."""
|
||||||
wrapper = hass.data[DOMAIN][DATA_CONFIG_ENTRY][config_entry.entry_id]
|
wrapper = hass.data[DOMAIN][DATA_CONFIG_ENTRY][config_entry.entry_id][COAP]
|
||||||
blocks = [block for block in wrapper.device.blocks if block.type == "roller"]
|
blocks = [block for block in wrapper.device.blocks if block.type == "roller"]
|
||||||
|
|
||||||
if not blocks:
|
if not blocks:
|
||||||
|
@ -4,12 +4,60 @@ from typing import Any, Callable, Optional, Union
|
|||||||
|
|
||||||
import aioshelly
|
import aioshelly
|
||||||
|
|
||||||
|
from homeassistant.const import TEMP_CELSIUS, TEMP_FAHRENHEIT
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
from homeassistant.helpers import device_registry, entity
|
from homeassistant.helpers import device_registry, entity, update_coordinator
|
||||||
|
|
||||||
from . import ShellyDeviceWrapper
|
from . import ShellyDeviceRestWrapper, ShellyDeviceWrapper
|
||||||
from .const import DATA_CONFIG_ENTRY, DOMAIN
|
from .const import COAP, DATA_CONFIG_ENTRY, DOMAIN, REST
|
||||||
from .utils import get_entity_name
|
from .utils import get_entity_name, get_rest_value_from_path
|
||||||
|
|
||||||
|
|
||||||
|
def temperature_unit(block_info: dict) -> str:
|
||||||
|
"""Detect temperature unit."""
|
||||||
|
if block_info[aioshelly.BLOCK_VALUE_UNIT] == "F":
|
||||||
|
return TEMP_FAHRENHEIT
|
||||||
|
return TEMP_CELSIUS
|
||||||
|
|
||||||
|
|
||||||
|
def shelly_naming(self, block, entity_type: str):
|
||||||
|
"""Naming for switch and sensors."""
|
||||||
|
|
||||||
|
entity_name = self.wrapper.name
|
||||||
|
if not block:
|
||||||
|
return f"{entity_name} {self.description.name}"
|
||||||
|
|
||||||
|
channels = 0
|
||||||
|
mode = block.type + "s"
|
||||||
|
if "num_outputs" in self.wrapper.device.shelly:
|
||||||
|
channels = self.wrapper.device.shelly["num_outputs"]
|
||||||
|
if (
|
||||||
|
self.wrapper.model in ["SHSW-21", "SHSW-25"]
|
||||||
|
and self.wrapper.device.settings["mode"] == "roller"
|
||||||
|
):
|
||||||
|
channels = 1
|
||||||
|
if block.type == "emeter" and "num_emeters" in self.wrapper.device.shelly:
|
||||||
|
channels = self.wrapper.device.shelly["num_emeters"]
|
||||||
|
if channels > 1 and block.type != "device":
|
||||||
|
# Shelly EM (SHEM) with firmware v1.8.1 doesn't have "name" key; will be fixed in next firmware release
|
||||||
|
if "name" in self.wrapper.device.settings[mode][int(block.channel)]:
|
||||||
|
entity_name = self.wrapper.device.settings[mode][int(block.channel)]["name"]
|
||||||
|
else:
|
||||||
|
entity_name = None
|
||||||
|
if not entity_name:
|
||||||
|
if self.wrapper.model == "SHEM-3":
|
||||||
|
base = ord("A")
|
||||||
|
else:
|
||||||
|
base = ord("1")
|
||||||
|
entity_name = f"{self.wrapper.name} channel {chr(int(block.channel)+base)}"
|
||||||
|
|
||||||
|
if entity_type == "switch":
|
||||||
|
return entity_name
|
||||||
|
|
||||||
|
if entity_type == "sensor":
|
||||||
|
return f"{entity_name} {self.description.name}"
|
||||||
|
|
||||||
|
raise ValueError
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry_attribute_entities(
|
async def async_setup_entry_attribute_entities(
|
||||||
@ -18,7 +66,7 @@ async def async_setup_entry_attribute_entities(
|
|||||||
"""Set up entities for block attributes."""
|
"""Set up entities for block attributes."""
|
||||||
wrapper: ShellyDeviceWrapper = hass.data[DOMAIN][DATA_CONFIG_ENTRY][
|
wrapper: ShellyDeviceWrapper = hass.data[DOMAIN][DATA_CONFIG_ENTRY][
|
||||||
config_entry.entry_id
|
config_entry.entry_id
|
||||||
]
|
][COAP]
|
||||||
blocks = []
|
blocks = []
|
||||||
|
|
||||||
for block in wrapper.device.blocks:
|
for block in wrapper.device.blocks:
|
||||||
@ -44,6 +92,27 @@ async def async_setup_entry_attribute_entities(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_entry_rest(
|
||||||
|
hass, config_entry, async_add_entities, sensors, sensor_class
|
||||||
|
):
|
||||||
|
"""Set up entities for REST sensors."""
|
||||||
|
wrapper: ShellyDeviceRestWrapper = hass.data[DOMAIN][DATA_CONFIG_ENTRY][
|
||||||
|
config_entry.entry_id
|
||||||
|
][REST]
|
||||||
|
|
||||||
|
entities = []
|
||||||
|
for sensor_id in sensors:
|
||||||
|
_desc = sensors.get(sensor_id)
|
||||||
|
|
||||||
|
if not wrapper.device.settings.get("sleep_mode"):
|
||||||
|
entities.append(_desc)
|
||||||
|
|
||||||
|
if not entities:
|
||||||
|
return
|
||||||
|
|
||||||
|
async_add_entities([sensor_class(wrapper, description) for description in entities])
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class BlockAttributeDescription:
|
class BlockAttributeDescription:
|
||||||
"""Class to describe a sensor."""
|
"""Class to describe a sensor."""
|
||||||
@ -60,6 +129,21 @@ class BlockAttributeDescription:
|
|||||||
] = None
|
] = None
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class RestAttributeDescription:
|
||||||
|
"""Class to describe a REST sensor."""
|
||||||
|
|
||||||
|
path: str
|
||||||
|
name: str
|
||||||
|
# Callable = lambda attr_info: unit
|
||||||
|
icon: Optional[str] = None
|
||||||
|
unit: Union[None, str, Callable[[dict], str]] = None
|
||||||
|
value: Callable[[Any], Any] = lambda val: val
|
||||||
|
device_class: Optional[str] = None
|
||||||
|
default_enabled: bool = True
|
||||||
|
attributes: Optional[dict] = None
|
||||||
|
|
||||||
|
|
||||||
class ShellyBlockEntity(entity.Entity):
|
class ShellyBlockEntity(entity.Entity):
|
||||||
"""Helper class to represent a block."""
|
"""Helper class to represent a block."""
|
||||||
|
|
||||||
@ -133,7 +217,7 @@ class ShellyBlockAttributeEntity(ShellyBlockEntity, entity.Entity):
|
|||||||
|
|
||||||
self._unit = unit
|
self._unit = unit
|
||||||
self._unique_id = f"{super().unique_id}-{self.attribute}"
|
self._unique_id = f"{super().unique_id}-{self.attribute}"
|
||||||
self._name = get_entity_name(wrapper, block, self.description.name)
|
self._name = shelly_naming(self, block, "sensor")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def unique_id(self):
|
def unique_id(self):
|
||||||
@ -187,3 +271,85 @@ class ShellyBlockAttributeEntity(ShellyBlockEntity, entity.Entity):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
return self.description.device_state_attributes(self.block)
|
return self.description.device_state_attributes(self.block)
|
||||||
|
|
||||||
|
|
||||||
|
class ShellyRestAttributeEntity(update_coordinator.CoordinatorEntity):
|
||||||
|
"""Class to load info from REST."""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self, wrapper: ShellyDeviceWrapper, description: RestAttributeDescription
|
||||||
|
) -> None:
|
||||||
|
"""Initialize sensor."""
|
||||||
|
super().__init__(wrapper)
|
||||||
|
self.wrapper = wrapper
|
||||||
|
self.description = description
|
||||||
|
|
||||||
|
self._unit = self.description.unit
|
||||||
|
self._name = shelly_naming(self, None, "sensor")
|
||||||
|
self.path = self.description.path
|
||||||
|
self._attributes = self.description.attributes
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
"""Name of sensor."""
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def device_info(self):
|
||||||
|
"""Device info."""
|
||||||
|
return {
|
||||||
|
"connections": {(device_registry.CONNECTION_NETWORK_MAC, self.wrapper.mac)}
|
||||||
|
}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def entity_registry_enabled_default(self) -> bool:
|
||||||
|
"""Return if it should be enabled by default."""
|
||||||
|
return self.description.default_enabled
|
||||||
|
|
||||||
|
@property
|
||||||
|
def available(self):
|
||||||
|
"""Available."""
|
||||||
|
return self.wrapper.last_update_success
|
||||||
|
|
||||||
|
@property
|
||||||
|
def attribute_value(self):
|
||||||
|
"""Attribute."""
|
||||||
|
return get_rest_value_from_path(
|
||||||
|
self.wrapper.device.status, self.description.device_class, self.path
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def unit_of_measurement(self):
|
||||||
|
"""Return unit of sensor."""
|
||||||
|
return self.description.unit
|
||||||
|
|
||||||
|
@property
|
||||||
|
def device_class(self):
|
||||||
|
"""Device class of sensor."""
|
||||||
|
return self.description.device_class
|
||||||
|
|
||||||
|
@property
|
||||||
|
def icon(self):
|
||||||
|
"""Icon of sensor."""
|
||||||
|
return self.description.icon
|
||||||
|
|
||||||
|
@property
|
||||||
|
def unique_id(self):
|
||||||
|
"""Return unique ID of entity."""
|
||||||
|
return f"{self.wrapper.mac}-{self.description.path}"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def device_state_attributes(self):
|
||||||
|
"""Return the state attributes."""
|
||||||
|
|
||||||
|
if self._attributes is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
_description = self._attributes.get("description")
|
||||||
|
_attribute_value = get_rest_value_from_path(
|
||||||
|
self.wrapper.device.status,
|
||||||
|
self.description.device_class,
|
||||||
|
self._attributes.get("path"),
|
||||||
|
)
|
||||||
|
|
||||||
|
return {_description: _attribute_value}
|
||||||
|
@ -17,14 +17,14 @@ from homeassistant.util.color import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
from . import ShellyDeviceWrapper
|
from . import ShellyDeviceWrapper
|
||||||
from .const import DATA_CONFIG_ENTRY, DOMAIN
|
from .const import COAP, DATA_CONFIG_ENTRY, DOMAIN
|
||||||
from .entity import ShellyBlockEntity
|
from .entity import ShellyBlockEntity
|
||||||
from .utils import async_remove_entity_by_domain
|
from .utils import async_remove_entity_by_domain
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
"""Set up lights for device."""
|
"""Set up lights for device."""
|
||||||
wrapper = hass.data[DOMAIN][DATA_CONFIG_ENTRY][config_entry.entry_id]
|
wrapper = hass.data[DOMAIN][DATA_CONFIG_ENTRY][config_entry.entry_id][COAP]
|
||||||
|
|
||||||
blocks = []
|
blocks = []
|
||||||
for block in wrapper.device.blocks:
|
for block in wrapper.device.blocks:
|
||||||
|
@ -8,13 +8,17 @@ from homeassistant.const import (
|
|||||||
LIGHT_LUX,
|
LIGHT_LUX,
|
||||||
PERCENTAGE,
|
PERCENTAGE,
|
||||||
POWER_WATT,
|
POWER_WATT,
|
||||||
|
SIGNAL_STRENGTH_DECIBELS,
|
||||||
VOLT,
|
VOLT,
|
||||||
)
|
)
|
||||||
|
|
||||||
from .entity import (
|
from .entity import (
|
||||||
BlockAttributeDescription,
|
BlockAttributeDescription,
|
||||||
|
RestAttributeDescription,
|
||||||
ShellyBlockAttributeEntity,
|
ShellyBlockAttributeEntity,
|
||||||
|
ShellyRestAttributeEntity,
|
||||||
async_setup_entry_attribute_entities,
|
async_setup_entry_attribute_entities,
|
||||||
|
async_setup_entry_rest,
|
||||||
)
|
)
|
||||||
from .utils import temperature_unit
|
from .utils import temperature_unit
|
||||||
|
|
||||||
@ -142,12 +146,30 @@ SENSORS = {
|
|||||||
("sensor", "tilt"): BlockAttributeDescription(name="tilt", unit=DEGREE),
|
("sensor", "tilt"): BlockAttributeDescription(name="tilt", unit=DEGREE),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
REST_SENSORS = {
|
||||||
|
"rssi": RestAttributeDescription(
|
||||||
|
name="RSSI",
|
||||||
|
unit=SIGNAL_STRENGTH_DECIBELS,
|
||||||
|
device_class=sensor.DEVICE_CLASS_SIGNAL_STRENGTH,
|
||||||
|
default_enabled=False,
|
||||||
|
path="wifi_sta/rssi",
|
||||||
|
),
|
||||||
|
"uptime": RestAttributeDescription(
|
||||||
|
name="Uptime",
|
||||||
|
device_class=sensor.DEVICE_CLASS_TIMESTAMP,
|
||||||
|
path="uptime",
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
"""Set up sensors for device."""
|
"""Set up sensors for device."""
|
||||||
await async_setup_entry_attribute_entities(
|
await async_setup_entry_attribute_entities(
|
||||||
hass, config_entry, async_add_entities, SENSORS, ShellySensor
|
hass, config_entry, async_add_entities, SENSORS, ShellySensor
|
||||||
)
|
)
|
||||||
|
await async_setup_entry_rest(
|
||||||
|
hass, config_entry, async_add_entities, REST_SENSORS, ShellyRestSensor
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ShellySensor(ShellyBlockAttributeEntity):
|
class ShellySensor(ShellyBlockAttributeEntity):
|
||||||
@ -157,3 +179,12 @@ class ShellySensor(ShellyBlockAttributeEntity):
|
|||||||
def state(self):
|
def state(self):
|
||||||
"""Return value of sensor."""
|
"""Return value of sensor."""
|
||||||
return self.attribute_value
|
return self.attribute_value
|
||||||
|
|
||||||
|
|
||||||
|
class ShellyRestSensor(ShellyRestAttributeEntity):
|
||||||
|
"""Represent a shelly REST sensor."""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def state(self):
|
||||||
|
"""Return value of sensor."""
|
||||||
|
return self.attribute_value
|
||||||
|
@ -5,14 +5,14 @@ from homeassistant.components.switch import SwitchEntity
|
|||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
|
|
||||||
from . import ShellyDeviceWrapper
|
from . import ShellyDeviceWrapper
|
||||||
from .const import DATA_CONFIG_ENTRY, DOMAIN
|
from .const import COAP, DATA_CONFIG_ENTRY, DOMAIN
|
||||||
from .entity import ShellyBlockEntity
|
from .entity import ShellyBlockEntity
|
||||||
from .utils import async_remove_entity_by_domain
|
from .utils import async_remove_entity_by_domain
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
"""Set up switches for device."""
|
"""Set up switches for device."""
|
||||||
wrapper = hass.data[DOMAIN][DATA_CONFIG_ENTRY][config_entry.entry_id]
|
wrapper = hass.data[DOMAIN][DATA_CONFIG_ENTRY][config_entry.entry_id][COAP]
|
||||||
|
|
||||||
# In roller mode the relay blocks exist but do not contain required info
|
# In roller mode the relay blocks exist but do not contain required info
|
||||||
if (
|
if (
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
"""Shelly helpers functions."""
|
"""Shelly helpers functions."""
|
||||||
|
|
||||||
|
from datetime import datetime, timedelta
|
||||||
import logging
|
import logging
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
import aioshelly
|
import aioshelly
|
||||||
|
|
||||||
|
from homeassistant.components.sensor import DEVICE_CLASS_TIMESTAMP
|
||||||
from homeassistant.const import TEMP_CELSIUS, TEMP_FAHRENHEIT
|
from homeassistant.const import TEMP_CELSIUS, TEMP_FAHRENHEIT
|
||||||
from homeassistant.helpers import entity_registry
|
from homeassistant.helpers import entity_registry
|
||||||
|
|
||||||
@ -73,3 +76,20 @@ def get_entity_name(
|
|||||||
entity_name = f"{entity_name} {description}"
|
entity_name = f"{entity_name} {description}"
|
||||||
|
|
||||||
return entity_name
|
return entity_name
|
||||||
|
|
||||||
|
|
||||||
|
def get_rest_value_from_path(status, device_class, path: str):
|
||||||
|
"""Parser for REST path from device status."""
|
||||||
|
|
||||||
|
if "/" not in path:
|
||||||
|
_attribute_value = status[path]
|
||||||
|
else:
|
||||||
|
_attribute_value = status[path.split("/")[0]][path.split("/")[1]]
|
||||||
|
if device_class == DEVICE_CLASS_TIMESTAMP:
|
||||||
|
last_boot = datetime.utcnow() - timedelta(seconds=_attribute_value)
|
||||||
|
_attribute_value = last_boot.replace(microsecond=0).isoformat()
|
||||||
|
|
||||||
|
if "new_version" in path:
|
||||||
|
_attribute_value = _attribute_value.split("/")[1].split("@")[0]
|
||||||
|
|
||||||
|
return _attribute_value
|
||||||
|
Loading…
x
Reference in New Issue
Block a user