Introduce number platform for Shelly (#64207)
* Introduce number platform for Shelly * coverage * Rework based on review comment * Improve logic around channel * Remove unused value * rebase * Removed redundant properties * Update homeassistant/components/shelly/number.py Co-authored-by: epenet <6771947+epenet@users.noreply.github.com> * Remove channel workaround as currently not needed Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>
This commit is contained in:
parent
076faaa4a4
commit
17c41f4783
3 changed files with 134 additions and 0 deletions
|
@ -985,6 +985,7 @@ omit =
|
|||
homeassistant/components/shelly/climate.py
|
||||
homeassistant/components/shelly/entity.py
|
||||
homeassistant/components/shelly/light.py
|
||||
homeassistant/components/shelly/number.py
|
||||
homeassistant/components/shelly/sensor.py
|
||||
homeassistant/components/shelly/utils.py
|
||||
homeassistant/components/sht31/sensor.py
|
||||
|
|
|
@ -80,6 +80,7 @@ BLOCK_PLATFORMS: Final = [
|
|||
BLOCK_SLEEPING_PLATFORMS: Final = [
|
||||
Platform.BINARY_SENSOR,
|
||||
Platform.CLIMATE,
|
||||
Platform.NUMBER,
|
||||
Platform.SENSOR,
|
||||
]
|
||||
RPC_PLATFORMS: Final = [
|
||||
|
|
132
homeassistant/components/shelly/number.py
Normal file
132
homeassistant/components/shelly/number.py
Normal file
|
@ -0,0 +1,132 @@
|
|||
"""Number for Shelly."""
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from dataclasses import dataclass
|
||||
import logging
|
||||
from typing import Any, Final, cast
|
||||
|
||||
import async_timeout
|
||||
|
||||
from homeassistant.components.number import (
|
||||
NumberEntity,
|
||||
NumberEntityDescription,
|
||||
NumberMode,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import PERCENTAGE
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity import EntityCategory
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.entity_registry import RegistryEntry
|
||||
|
||||
from .const import AIOSHELLY_DEVICE_TIMEOUT_SEC, CONF_SLEEP_PERIOD
|
||||
from .entity import (
|
||||
BlockEntityDescription,
|
||||
ShellySleepingBlockAttributeEntity,
|
||||
async_setup_entry_attribute_entities,
|
||||
)
|
||||
from .utils import get_device_entry_gen
|
||||
|
||||
_LOGGER: Final = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@dataclass
|
||||
class BlockNumberDescription(BlockEntityDescription, NumberEntityDescription):
|
||||
"""Class to describe a BLOCK sensor."""
|
||||
|
||||
mode: NumberMode = NumberMode("slider")
|
||||
rest_path: str = ""
|
||||
rest_arg: str = ""
|
||||
|
||||
|
||||
NUMBERS: Final = {
|
||||
("device", "valvePos"): BlockNumberDescription(
|
||||
key="device|valvepos",
|
||||
icon="mdi:pipe-valve",
|
||||
name="Valve Position",
|
||||
unit_of_measurement=PERCENTAGE,
|
||||
available=lambda block: cast(int, block.valveError) != 1,
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
min_value=0,
|
||||
max_value=100,
|
||||
step=1,
|
||||
mode=NumberMode("slider"),
|
||||
rest_path="thermostat/0",
|
||||
rest_arg="pos",
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
def _build_block_description(entry: RegistryEntry) -> BlockNumberDescription:
|
||||
"""Build description when restoring block attribute entities."""
|
||||
assert entry.capabilities
|
||||
return BlockNumberDescription(
|
||||
key="",
|
||||
name="",
|
||||
icon=entry.original_icon,
|
||||
unit_of_measurement=entry.unit_of_measurement,
|
||||
device_class=entry.original_device_class,
|
||||
min_value=cast(float, entry.capabilities.get("min")),
|
||||
max_value=cast(float, entry.capabilities.get("max")),
|
||||
step=cast(float, entry.capabilities.get("step")),
|
||||
mode=cast(NumberMode, entry.capabilities.get("mode")),
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up numbers for device."""
|
||||
if get_device_entry_gen(config_entry) == 2:
|
||||
return
|
||||
|
||||
if config_entry.data[CONF_SLEEP_PERIOD]:
|
||||
await async_setup_entry_attribute_entities(
|
||||
hass,
|
||||
config_entry,
|
||||
async_add_entities,
|
||||
NUMBERS,
|
||||
BlockSleepingNumber,
|
||||
_build_block_description,
|
||||
)
|
||||
|
||||
|
||||
class BlockSleepingNumber(ShellySleepingBlockAttributeEntity, NumberEntity):
|
||||
"""Represent a block sleeping number."""
|
||||
|
||||
entity_description: BlockNumberDescription
|
||||
|
||||
@property
|
||||
def value(self) -> float:
|
||||
"""Return value of number."""
|
||||
if self.block is not None:
|
||||
return cast(float, self.attribute_value)
|
||||
|
||||
return cast(float, self.last_state)
|
||||
|
||||
async def async_set_value(self, value: float) -> None:
|
||||
"""Set value."""
|
||||
# Example for Shelly Valve: http://192.168.188.187/thermostat/0?pos=13.0
|
||||
await self._set_state_full_path(
|
||||
self.entity_description.rest_path,
|
||||
{self.entity_description.rest_arg: value},
|
||||
)
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def _set_state_full_path(self, path: str, params: Any) -> Any:
|
||||
"""Set block state (HTTP request)."""
|
||||
|
||||
_LOGGER.debug("Setting state for entity %s, state: %s", self.name, params)
|
||||
try:
|
||||
async with async_timeout.timeout(AIOSHELLY_DEVICE_TIMEOUT_SEC):
|
||||
return await self.wrapper.device.http_request("get", path, params)
|
||||
except (asyncio.TimeoutError, OSError) as err:
|
||||
_LOGGER.error(
|
||||
"Setting state for entity %s failed, state: %s, error: %s",
|
||||
self.name,
|
||||
params,
|
||||
repr(err),
|
||||
)
|
Loading…
Add table
Add a link
Reference in a new issue