Add basic light and sensor support to Shelly (#39288)
* Add basic light platform * Add sensor support * Bump aioshelly to 0.2.1 * Lint * Use UNIT_PERCENTAGE Co-authored-by: Maciej Bieniek <bieniu@users.noreply.github.com> * Format sensor.py Co-authored-by: Maciej Bieniek <bieniu@users.noreply.github.com>
This commit is contained in:
parent
d9f3bdea53
commit
4b8217777e
9 changed files with 181 additions and 16 deletions
|
@ -754,6 +754,8 @@ omit =
|
|||
homeassistant/components/shiftr/*
|
||||
homeassistant/components/shodan/sensor.py
|
||||
homeassistant/components/shelly/__init__.py
|
||||
homeassistant/components/shelly/light.py
|
||||
homeassistant/components/shelly/sensor.py
|
||||
homeassistant/components/shelly/switch.py
|
||||
homeassistant/components/sht31/sensor.py
|
||||
homeassistant/components/sigfox/sensor.py
|
||||
|
|
|
@ -20,7 +20,7 @@ from homeassistant.helpers import (
|
|||
|
||||
from .const import DOMAIN
|
||||
|
||||
PLATFORMS = ["switch"]
|
||||
PLATFORMS = ["switch", "light", "sensor"]
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
@ -129,11 +129,12 @@ class ShellyBlockEntity(entity.Entity):
|
|||
"""Initialize Shelly entity."""
|
||||
self.wrapper = wrapper
|
||||
self.block = block
|
||||
self._name = f"{self.wrapper.name} - {self.block.description.replace('_', ' ')}"
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Name of entity."""
|
||||
return f"{self.wrapper.name} - {self.block.description}"
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def should_poll(self):
|
||||
|
@ -155,7 +156,7 @@ class ShellyBlockEntity(entity.Entity):
|
|||
@property
|
||||
def unique_id(self):
|
||||
"""Return unique ID of entity."""
|
||||
return f"{self.wrapper.mac}-{self.block.index}"
|
||||
return f"{self.wrapper.mac}-{self.block.description}"
|
||||
|
||||
async def async_added_to_hass(self):
|
||||
"""When entity is added to HASS."""
|
||||
|
|
|
@ -61,7 +61,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
|
||||
try:
|
||||
device_info = await validate_input(self.hass, user_input)
|
||||
except asyncio.TimeoutError:
|
||||
except HTTP_CONNECT_ERRORS:
|
||||
errors["base"] = "cannot_connect"
|
||||
except Exception: # pylint: disable=broad-except
|
||||
_LOGGER.exception("Unexpected exception")
|
||||
|
@ -103,7 +103,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
if user_input is not None:
|
||||
try:
|
||||
device_info = await validate_input(self.hass, {"host": self.host})
|
||||
except asyncio.TimeoutError:
|
||||
except HTTP_CONNECT_ERRORS:
|
||||
errors["base"] = "cannot_connect"
|
||||
except Exception: # pylint: disable=broad-except
|
||||
_LOGGER.exception("Unexpected exception")
|
||||
|
|
74
homeassistant/components/shelly/light.py
Normal file
74
homeassistant/components/shelly/light.py
Normal file
|
@ -0,0 +1,74 @@
|
|||
"""Light for Shelly."""
|
||||
from aioshelly import Block
|
||||
|
||||
from homeassistant.components.light import SUPPORT_BRIGHTNESS, LightEntity
|
||||
from homeassistant.core import callback
|
||||
|
||||
from . import ShellyBlockEntity, ShellyDeviceWrapper
|
||||
from .const import DOMAIN
|
||||
|
||||
|
||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
"""Set up lights for device."""
|
||||
wrapper = hass.data[DOMAIN][config_entry.entry_id]
|
||||
blocks = [block for block in wrapper.device.blocks if block.type == "light"]
|
||||
|
||||
if not blocks:
|
||||
return
|
||||
|
||||
async_add_entities(ShellyLight(wrapper, block) for block in blocks)
|
||||
|
||||
|
||||
class ShellyLight(ShellyBlockEntity, LightEntity):
|
||||
"""Switch that controls a relay block on Shelly devices."""
|
||||
|
||||
def __init__(self, wrapper: ShellyDeviceWrapper, block: Block) -> None:
|
||||
"""Initialize light."""
|
||||
super().__init__(wrapper, block)
|
||||
self.control_result = None
|
||||
self._supported_features = 0
|
||||
if hasattr(block, "brightness"):
|
||||
self._supported_features |= SUPPORT_BRIGHTNESS
|
||||
|
||||
@property
|
||||
def is_on(self) -> bool:
|
||||
"""If light is on."""
|
||||
if self.control_result:
|
||||
return self.control_result["ison"]
|
||||
|
||||
return self.block.output
|
||||
|
||||
@property
|
||||
def brightness(self):
|
||||
"""Brightness of light."""
|
||||
if self.control_result:
|
||||
brightness = self.control_result["brightness"]
|
||||
else:
|
||||
brightness = self.block.brightness
|
||||
return int(brightness / 100 * 255)
|
||||
|
||||
@property
|
||||
def supported_features(self):
|
||||
"""Supported features."""
|
||||
return self._supported_features
|
||||
|
||||
async def async_turn_on(
|
||||
self, brightness=None, **kwargs
|
||||
): # pylint: disable=arguments-differ
|
||||
"""Turn on light."""
|
||||
params = {"turn": "on"}
|
||||
if brightness is not None:
|
||||
params["brightness"] = int(brightness / 255 * 100)
|
||||
self.control_result = await self.block.set_state(**params)
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def async_turn_off(self, **kwargs):
|
||||
"""Turn off light."""
|
||||
self.control_result = await self.block.set_state(turn="off")
|
||||
self.async_write_ha_state()
|
||||
|
||||
@callback
|
||||
def _update_callback(self):
|
||||
"""When device updates, clear control result that overrides state."""
|
||||
self.control_result = None
|
||||
super()._update_callback()
|
|
@ -3,7 +3,7 @@
|
|||
"name": "Shelly",
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/shelly2",
|
||||
"requirements": ["aioshelly==0.1.2"],
|
||||
"requirements": ["aioshelly==0.2.1"],
|
||||
"zeroconf": ["_http._tcp.local."],
|
||||
"codeowners": ["@balloob"]
|
||||
}
|
||||
|
|
83
homeassistant/components/shelly/sensor.py
Normal file
83
homeassistant/components/shelly/sensor.py
Normal file
|
@ -0,0 +1,83 @@
|
|||
"""Sensor for Shelly."""
|
||||
import aioshelly
|
||||
|
||||
from homeassistant.components import sensor
|
||||
from homeassistant.const import TEMP_CELSIUS, TEMP_FAHRENHEIT, UNIT_PERCENTAGE
|
||||
from homeassistant.helpers.entity import Entity
|
||||
|
||||
from . import ShellyBlockEntity, ShellyDeviceWrapper
|
||||
from .const import DOMAIN
|
||||
|
||||
SENSORS = {
|
||||
"extTemp": [None, sensor.DEVICE_CLASS_TEMPERATURE],
|
||||
"humidity": [UNIT_PERCENTAGE, sensor.DEVICE_CLASS_HUMIDITY],
|
||||
}
|
||||
|
||||
|
||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
"""Set up sensors for device."""
|
||||
wrapper = hass.data[DOMAIN][config_entry.entry_id]
|
||||
sensors = []
|
||||
|
||||
for block in wrapper.device.blocks:
|
||||
if block.type != "sensor":
|
||||
continue
|
||||
|
||||
for attr in SENSORS:
|
||||
if not hasattr(block, attr):
|
||||
continue
|
||||
|
||||
sensors.append(ShellySensor(wrapper, block, attr))
|
||||
|
||||
if sensors:
|
||||
async_add_entities(sensors)
|
||||
|
||||
|
||||
class ShellySensor(ShellyBlockEntity, Entity):
|
||||
"""Switch that controls a relay block on Shelly devices."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
wrapper: ShellyDeviceWrapper,
|
||||
block: aioshelly.Block,
|
||||
attribute: str,
|
||||
) -> None:
|
||||
"""Initialize sensor."""
|
||||
super().__init__(wrapper, block)
|
||||
self.attribute = attribute
|
||||
unit, device_class = SENSORS[attribute]
|
||||
info = block.info(attribute)
|
||||
|
||||
if info[aioshelly.BLOCK_VALUE_TYPE] == aioshelly.BLOCK_VALUE_TYPE_TEMPERATURE:
|
||||
if info[aioshelly.BLOCK_VALUE_UNIT] == "C":
|
||||
unit = TEMP_CELSIUS
|
||||
else:
|
||||
unit = TEMP_FAHRENHEIT
|
||||
|
||||
self._unit = unit
|
||||
self._device_class = device_class
|
||||
|
||||
@property
|
||||
def unique_id(self):
|
||||
"""Return unique ID of entity."""
|
||||
return f"{super().unique_id}-{self.attribute}"
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Name of sensor."""
|
||||
return f"{self.wrapper.name} - {self.attribute}"
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
"""Value of sensor."""
|
||||
return getattr(self.block, self.attribute)
|
||||
|
||||
@property
|
||||
def unit_of_measurement(self):
|
||||
"""Return unit of sensor."""
|
||||
return self._unit
|
||||
|
||||
@property
|
||||
def device_class(self):
|
||||
"""Device class of sensor."""
|
||||
return self._device_class
|
|
@ -1,22 +1,25 @@
|
|||
"""Switch for Shelly."""
|
||||
from homeassistant.components.shelly import ShellyBlockEntity
|
||||
from aioshelly import RelayBlock
|
||||
|
||||
from homeassistant.components.switch import SwitchEntity
|
||||
from homeassistant.core import callback
|
||||
|
||||
from . import ShellyBlockEntity, ShellyDeviceWrapper
|
||||
from .const import DOMAIN
|
||||
|
||||
|
||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
"""Set up switches for device."""
|
||||
wrapper = hass.data[DOMAIN][config_entry.entry_id]
|
||||
|
||||
if wrapper.model == "SHSW-25" and wrapper.device.settings["mode"] != "relay":
|
||||
return
|
||||
|
||||
relay_blocks = [block for block in wrapper.device.blocks if block.type == "relay"]
|
||||
|
||||
if not relay_blocks:
|
||||
return
|
||||
|
||||
if wrapper.model == "SHSW-25" and wrapper.device.settings["mode"] != "relay":
|
||||
return
|
||||
|
||||
multiple_blocks = len(relay_blocks) > 1
|
||||
async_add_entities(
|
||||
RelaySwitch(wrapper, block, multiple_blocks=multiple_blocks)
|
||||
|
@ -27,9 +30,11 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
|||
class RelaySwitch(ShellyBlockEntity, SwitchEntity):
|
||||
"""Switch that controls a relay block on Shelly devices."""
|
||||
|
||||
def __init__(self, *args, multiple_blocks) -> None:
|
||||
def __init__(
|
||||
self, wrapper: ShellyDeviceWrapper, block: RelayBlock, multiple_blocks
|
||||
) -> None:
|
||||
"""Initialize relay switch."""
|
||||
super().__init__(*args)
|
||||
super().__init__(wrapper, block)
|
||||
self.multiple_blocks = multiple_blocks
|
||||
self.control_result = None
|
||||
|
||||
|
@ -56,12 +61,12 @@ class RelaySwitch(ShellyBlockEntity, SwitchEntity):
|
|||
|
||||
async def async_turn_on(self, **kwargs):
|
||||
"""Turn on relay."""
|
||||
self.control_result = await self.block.turn_on()
|
||||
self.control_result = await self.block.set_state(turn="on")
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def async_turn_off(self, **kwargs):
|
||||
"""Turn off relay."""
|
||||
self.control_result = await self.block.turn_off()
|
||||
self.control_result = await self.block.set_state(turn="off")
|
||||
self.async_write_ha_state()
|
||||
|
||||
@callback
|
||||
|
|
|
@ -221,7 +221,7 @@ aiopvpc==2.0.2
|
|||
aiopylgtv==0.3.3
|
||||
|
||||
# homeassistant.components.shelly
|
||||
aioshelly==0.1.2
|
||||
aioshelly==0.2.1
|
||||
|
||||
# homeassistant.components.switcher_kis
|
||||
aioswitcher==1.2.0
|
||||
|
|
|
@ -131,7 +131,7 @@ aiopvpc==2.0.2
|
|||
aiopylgtv==0.3.3
|
||||
|
||||
# homeassistant.components.shelly
|
||||
aioshelly==0.1.2
|
||||
aioshelly==0.2.1
|
||||
|
||||
# homeassistant.components.switcher_kis
|
||||
aioswitcher==1.2.0
|
||||
|
|
Loading…
Add table
Reference in a new issue