Updated changes for aioshelly 1.0.0 (#56083)
This commit is contained in:
parent
ac1251c52b
commit
8c3c2ad8e3
14 changed files with 108 additions and 86 deletions
|
@ -7,6 +7,7 @@ import logging
|
||||||
from typing import Any, Final, cast
|
from typing import Any, Final, cast
|
||||||
|
|
||||||
import aioshelly
|
import aioshelly
|
||||||
|
from aioshelly.block_device import BlockDevice
|
||||||
import async_timeout
|
import async_timeout
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
|
@ -89,7 +90,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
|
|
||||||
temperature_unit = "C" if hass.config.units.is_metric else "F"
|
temperature_unit = "C" if hass.config.units.is_metric else "F"
|
||||||
|
|
||||||
options = aioshelly.ConnectionOptions(
|
options = aioshelly.common.ConnectionOptions(
|
||||||
entry.data[CONF_HOST],
|
entry.data[CONF_HOST],
|
||||||
entry.data.get(CONF_USERNAME),
|
entry.data.get(CONF_USERNAME),
|
||||||
entry.data.get(CONF_PASSWORD),
|
entry.data.get(CONF_PASSWORD),
|
||||||
|
@ -98,7 +99,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
|
|
||||||
coap_context = await get_coap_context(hass)
|
coap_context = await get_coap_context(hass)
|
||||||
|
|
||||||
device = await aioshelly.Device.create(
|
device = await BlockDevice.create(
|
||||||
aiohttp_client.async_get_clientsession(hass),
|
aiohttp_client.async_get_clientsession(hass),
|
||||||
coap_context,
|
coap_context,
|
||||||
options,
|
options,
|
||||||
|
@ -134,7 +135,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
_LOGGER.debug("Setting up online device %s", entry.title)
|
_LOGGER.debug("Setting up online device %s", entry.title)
|
||||||
try:
|
try:
|
||||||
async with async_timeout.timeout(AIOSHELLY_DEVICE_TIMEOUT_SEC):
|
async with async_timeout.timeout(AIOSHELLY_DEVICE_TIMEOUT_SEC):
|
||||||
await device.initialize(True)
|
await device.initialize()
|
||||||
except (asyncio.TimeoutError, OSError) as err:
|
except (asyncio.TimeoutError, OSError) as err:
|
||||||
raise ConfigEntryNotReady from err
|
raise ConfigEntryNotReady from err
|
||||||
|
|
||||||
|
@ -146,7 +147,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
"Setup for device %s will resume when device is online", entry.title
|
"Setup for device %s will resume when device is online", entry.title
|
||||||
)
|
)
|
||||||
device.subscribe_updates(_async_device_online)
|
device.subscribe_updates(_async_device_online)
|
||||||
await device.coap_request("s")
|
|
||||||
else:
|
else:
|
||||||
# Restore sensors for sleeping device
|
# Restore sensors for sleeping device
|
||||||
_LOGGER.debug("Setting up offline device %s", entry.title)
|
_LOGGER.debug("Setting up offline device %s", entry.title)
|
||||||
|
@ -156,7 +156,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
|
|
||||||
|
|
||||||
async def async_device_setup(
|
async def async_device_setup(
|
||||||
hass: HomeAssistant, entry: ConfigEntry, device: aioshelly.Device
|
hass: HomeAssistant, entry: ConfigEntry, device: BlockDevice
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up a device that is online."""
|
"""Set up a device that is online."""
|
||||||
device_wrapper = hass.data[DOMAIN][DATA_CONFIG_ENTRY][entry.entry_id][
|
device_wrapper = hass.data[DOMAIN][DATA_CONFIG_ENTRY][entry.entry_id][
|
||||||
|
@ -179,7 +179,7 @@ class ShellyDeviceWrapper(update_coordinator.DataUpdateCoordinator):
|
||||||
"""Wrapper for a Shelly device with Home Assistant specific functions."""
|
"""Wrapper for a Shelly device with Home Assistant specific functions."""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, hass: HomeAssistant, entry: ConfigEntry, device: aioshelly.Device
|
self, hass: HomeAssistant, entry: ConfigEntry, device: BlockDevice
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize the Shelly device wrapper."""
|
"""Initialize the Shelly device wrapper."""
|
||||||
self.device_id: str | None = None
|
self.device_id: str | None = None
|
||||||
|
@ -208,7 +208,9 @@ class ShellyDeviceWrapper(update_coordinator.DataUpdateCoordinator):
|
||||||
)
|
)
|
||||||
self._last_input_events_count: dict = {}
|
self._last_input_events_count: dict = {}
|
||||||
|
|
||||||
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, self._handle_ha_stop)
|
entry.async_on_unload(
|
||||||
|
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, self._handle_ha_stop)
|
||||||
|
)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _async_device_updates_handler(self) -> None:
|
def _async_device_updates_handler(self) -> None:
|
||||||
|
@ -216,6 +218,8 @@ class ShellyDeviceWrapper(update_coordinator.DataUpdateCoordinator):
|
||||||
if not self.device.initialized:
|
if not self.device.initialized:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
assert self.device.blocks
|
||||||
|
|
||||||
# For buttons which are battery powered - set initial value for last_event_count
|
# For buttons which are battery powered - set initial value for last_event_count
|
||||||
if self.model in SHBTN_MODELS and self._last_input_events_count.get(1) is None:
|
if self.model in SHBTN_MODELS and self._last_input_events_count.get(1) is None:
|
||||||
for block in self.device.blocks:
|
for block in self.device.blocks:
|
||||||
|
@ -298,7 +302,7 @@ class ShellyDeviceWrapper(update_coordinator.DataUpdateCoordinator):
|
||||||
# This is duplicate but otherwise via_device can't work
|
# This is duplicate but otherwise via_device can't work
|
||||||
identifiers={(DOMAIN, self.mac)},
|
identifiers={(DOMAIN, self.mac)},
|
||||||
manufacturer="Shelly",
|
manufacturer="Shelly",
|
||||||
model=aioshelly.MODEL_NAMES.get(self.model, self.model),
|
model=aioshelly.const.MODEL_NAMES.get(self.model, self.model),
|
||||||
sw_version=sw_version,
|
sw_version=sw_version,
|
||||||
)
|
)
|
||||||
self.device_id = entry.id
|
self.device_id = entry.id
|
||||||
|
@ -306,10 +310,8 @@ class ShellyDeviceWrapper(update_coordinator.DataUpdateCoordinator):
|
||||||
|
|
||||||
def shutdown(self) -> None:
|
def shutdown(self) -> None:
|
||||||
"""Shutdown the wrapper."""
|
"""Shutdown the wrapper."""
|
||||||
if self.device:
|
self.device.shutdown()
|
||||||
self.device.shutdown()
|
self._async_remove_device_updates_handler()
|
||||||
self._async_remove_device_updates_handler()
|
|
||||||
self.device = None
|
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _handle_ha_stop(self, _event: Event) -> None:
|
def _handle_ha_stop(self, _event: Event) -> None:
|
||||||
|
@ -321,7 +323,7 @@ class ShellyDeviceWrapper(update_coordinator.DataUpdateCoordinator):
|
||||||
class ShellyDeviceRestWrapper(update_coordinator.DataUpdateCoordinator):
|
class ShellyDeviceRestWrapper(update_coordinator.DataUpdateCoordinator):
|
||||||
"""Rest Wrapper for a Shelly device with Home Assistant specific functions."""
|
"""Rest Wrapper for a Shelly device with Home Assistant specific functions."""
|
||||||
|
|
||||||
def __init__(self, hass: HomeAssistant, device: aioshelly.Device) -> None:
|
def __init__(self, hass: HomeAssistant, device: BlockDevice) -> None:
|
||||||
"""Initialize the Shelly device wrapper."""
|
"""Initialize the Shelly device wrapper."""
|
||||||
if (
|
if (
|
||||||
device.settings["device"]["type"]
|
device.settings["device"]["type"]
|
||||||
|
|
|
@ -48,7 +48,7 @@ SENSORS: Final = {
|
||||||
("sensor", "dwIsOpened"): BlockAttributeDescription(
|
("sensor", "dwIsOpened"): BlockAttributeDescription(
|
||||||
name="Door",
|
name="Door",
|
||||||
device_class=DEVICE_CLASS_OPENING,
|
device_class=DEVICE_CLASS_OPENING,
|
||||||
available=lambda block: cast(bool, block.dwIsOpened != -1),
|
available=lambda block: cast(int, block.dwIsOpened) != -1,
|
||||||
),
|
),
|
||||||
("sensor", "flood"): BlockAttributeDescription(
|
("sensor", "flood"): BlockAttributeDescription(
|
||||||
name="Flood", device_class=DEVICE_CLASS_MOISTURE
|
name="Flood", device_class=DEVICE_CLASS_MOISTURE
|
||||||
|
|
|
@ -7,6 +7,7 @@ from typing import Any, Dict, Final, cast
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
import aioshelly
|
import aioshelly
|
||||||
|
from aioshelly.block_device import BlockDevice
|
||||||
import async_timeout
|
import async_timeout
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
|
@ -39,13 +40,13 @@ async def validate_input(
|
||||||
Data has the keys from DATA_SCHEMA with values provided by the user.
|
Data has the keys from DATA_SCHEMA with values provided by the user.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
options = aioshelly.ConnectionOptions(
|
options = aioshelly.common.ConnectionOptions(
|
||||||
host, data.get(CONF_USERNAME), data.get(CONF_PASSWORD)
|
host, data.get(CONF_USERNAME), data.get(CONF_PASSWORD)
|
||||||
)
|
)
|
||||||
coap_context = await get_coap_context(hass)
|
coap_context = await get_coap_context(hass)
|
||||||
|
|
||||||
async with async_timeout.timeout(AIOSHELLY_DEVICE_TIMEOUT_SEC):
|
async with async_timeout.timeout(AIOSHELLY_DEVICE_TIMEOUT_SEC):
|
||||||
device = await aioshelly.Device.create(
|
device = await BlockDevice.create(
|
||||||
aiohttp_client.async_get_clientsession(hass),
|
aiohttp_client.async_get_clientsession(hass),
|
||||||
coap_context,
|
coap_context,
|
||||||
options,
|
options,
|
||||||
|
@ -82,7 +83,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
info = await self._async_get_info(host)
|
info = await self._async_get_info(host)
|
||||||
except HTTP_CONNECT_ERRORS:
|
except HTTP_CONNECT_ERRORS:
|
||||||
errors["base"] = "cannot_connect"
|
errors["base"] = "cannot_connect"
|
||||||
except aioshelly.FirmwareUnsupported:
|
except aioshelly.exceptions.FirmwareUnsupported:
|
||||||
return self.async_abort(reason="unsupported_firmware")
|
return self.async_abort(reason="unsupported_firmware")
|
||||||
except Exception: # pylint: disable=broad-except
|
except Exception: # pylint: disable=broad-except
|
||||||
_LOGGER.exception("Unexpected exception")
|
_LOGGER.exception("Unexpected exception")
|
||||||
|
@ -165,7 +166,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
self.info = info = await self._async_get_info(discovery_info["host"])
|
self.info = info = await self._async_get_info(discovery_info["host"])
|
||||||
except HTTP_CONNECT_ERRORS:
|
except HTTP_CONNECT_ERRORS:
|
||||||
return self.async_abort(reason="cannot_connect")
|
return self.async_abort(reason="cannot_connect")
|
||||||
except aioshelly.FirmwareUnsupported:
|
except aioshelly.exceptions.FirmwareUnsupported:
|
||||||
return self.async_abort(reason="unsupported_firmware")
|
return self.async_abort(reason="unsupported_firmware")
|
||||||
|
|
||||||
await self.async_set_unique_id(info["mac"])
|
await self.async_set_unique_id(info["mac"])
|
||||||
|
@ -206,7 +207,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
step_id="confirm_discovery",
|
step_id="confirm_discovery",
|
||||||
description_placeholders={
|
description_placeholders={
|
||||||
"model": aioshelly.MODEL_NAMES.get(
|
"model": aioshelly.const.MODEL_NAMES.get(
|
||||||
self.info["type"], self.info["type"]
|
self.info["type"], self.info["type"]
|
||||||
),
|
),
|
||||||
"host": self.host,
|
"host": self.host,
|
||||||
|
@ -219,7 +220,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
async with async_timeout.timeout(AIOSHELLY_DEVICE_TIMEOUT_SEC):
|
async with async_timeout.timeout(AIOSHELLY_DEVICE_TIMEOUT_SEC):
|
||||||
return cast(
|
return cast(
|
||||||
Dict[str, Any],
|
Dict[str, Any],
|
||||||
await aioshelly.get_info(
|
await aioshelly.common.get_info(
|
||||||
aiohttp_client.async_get_clientsession(self.hass),
|
aiohttp_client.async_get_clientsession(self.hass),
|
||||||
host,
|
host,
|
||||||
),
|
),
|
||||||
|
|
|
@ -3,7 +3,7 @@ from __future__ import annotations
|
||||||
|
|
||||||
from typing import Any, cast
|
from typing import Any, cast
|
||||||
|
|
||||||
from aioshelly import Block
|
from aioshelly.block_device import Block
|
||||||
|
|
||||||
from homeassistant.components.cover import (
|
from homeassistant.components.cover import (
|
||||||
ATTR_POSITION,
|
ATTR_POSITION,
|
||||||
|
@ -57,7 +57,7 @@ class ShellyCover(ShellyBlockEntity, CoverEntity):
|
||||||
if self.control_result:
|
if self.control_result:
|
||||||
return cast(bool, self.control_result["current_pos"] == 0)
|
return cast(bool, self.control_result["current_pos"] == 0)
|
||||||
|
|
||||||
return cast(bool, self.block.rollerPos == 0)
|
return cast(int, self.block.rollerPos) == 0
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def current_cover_position(self) -> int:
|
def current_cover_position(self) -> int:
|
||||||
|
@ -73,7 +73,7 @@ class ShellyCover(ShellyBlockEntity, CoverEntity):
|
||||||
if self.control_result:
|
if self.control_result:
|
||||||
return cast(bool, self.control_result["state"] == "close")
|
return cast(bool, self.control_result["state"] == "close")
|
||||||
|
|
||||||
return cast(bool, self.block.roller == "close")
|
return self.block.roller == "close"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_opening(self) -> bool:
|
def is_opening(self) -> bool:
|
||||||
|
@ -81,7 +81,7 @@ class ShellyCover(ShellyBlockEntity, CoverEntity):
|
||||||
if self.control_result:
|
if self.control_result:
|
||||||
return cast(bool, self.control_result["state"] == "open")
|
return cast(bool, self.control_result["state"] == "open")
|
||||||
|
|
||||||
return cast(bool, self.block.roller == "open")
|
return self.block.roller == "open"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def supported_features(self) -> int:
|
def supported_features(self) -> int:
|
||||||
|
|
|
@ -60,6 +60,8 @@ async def async_validate_trigger_config(
|
||||||
|
|
||||||
trigger = (config[CONF_TYPE], config[CONF_SUBTYPE])
|
trigger = (config[CONF_TYPE], config[CONF_SUBTYPE])
|
||||||
|
|
||||||
|
assert wrapper.device.blocks
|
||||||
|
|
||||||
for block in wrapper.device.blocks:
|
for block in wrapper.device.blocks:
|
||||||
input_triggers = get_input_triggers(wrapper.device, block)
|
input_triggers = get_input_triggers(wrapper.device, block)
|
||||||
if trigger in input_triggers:
|
if trigger in input_triggers:
|
||||||
|
@ -93,6 +95,8 @@ async def async_get_triggers(
|
||||||
)
|
)
|
||||||
return triggers
|
return triggers
|
||||||
|
|
||||||
|
assert wrapper.device.blocks
|
||||||
|
|
||||||
for block in wrapper.device.blocks:
|
for block in wrapper.device.blocks:
|
||||||
input_triggers = get_input_triggers(wrapper.device, block)
|
input_triggers = get_input_triggers(wrapper.device, block)
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ from dataclasses import dataclass
|
||||||
import logging
|
import logging
|
||||||
from typing import Any, Callable, Final, cast
|
from typing import Any, Callable, Final, cast
|
||||||
|
|
||||||
import aioshelly
|
from aioshelly.block_device import Block
|
||||||
import async_timeout
|
import async_timeout
|
||||||
|
|
||||||
from homeassistant.components.sensor import ATTR_STATE_CLASS
|
from homeassistant.components.sensor import ATTR_STATE_CLASS
|
||||||
|
@ -62,6 +62,8 @@ async def async_setup_block_attribute_entities(
|
||||||
"""Set up entities for block attributes."""
|
"""Set up entities for block attributes."""
|
||||||
blocks = []
|
blocks = []
|
||||||
|
|
||||||
|
assert wrapper.device.blocks
|
||||||
|
|
||||||
for block in wrapper.device.blocks:
|
for block in wrapper.device.blocks:
|
||||||
for sensor_id in block.sensor_ids:
|
for sensor_id in block.sensor_ids:
|
||||||
description = sensors.get((block.type, sensor_id))
|
description = sensors.get((block.type, sensor_id))
|
||||||
|
@ -175,10 +177,10 @@ class BlockAttributeDescription:
|
||||||
device_class: str | None = None
|
device_class: str | None = None
|
||||||
state_class: str | None = None
|
state_class: str | None = None
|
||||||
default_enabled: bool = True
|
default_enabled: bool = True
|
||||||
available: Callable[[aioshelly.Block], bool] | None = None
|
available: Callable[[Block], bool] | None = None
|
||||||
# Callable (settings, block), return true if entity should be removed
|
# Callable (settings, block), return true if entity should be removed
|
||||||
removal_condition: Callable[[dict, aioshelly.Block], bool] | None = None
|
removal_condition: Callable[[dict, Block], bool] | None = None
|
||||||
extra_state_attributes: Callable[[aioshelly.Block], dict | None] | None = None
|
extra_state_attributes: Callable[[Block], dict | None] | None = None
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
@ -198,7 +200,7 @@ class RestAttributeDescription:
|
||||||
class ShellyBlockEntity(entity.Entity):
|
class ShellyBlockEntity(entity.Entity):
|
||||||
"""Helper class to represent a block."""
|
"""Helper class to represent a block."""
|
||||||
|
|
||||||
def __init__(self, wrapper: ShellyDeviceWrapper, block: aioshelly.Block) -> None:
|
def __init__(self, wrapper: ShellyDeviceWrapper, block: Block) -> None:
|
||||||
"""Initialize Shelly entity."""
|
"""Initialize Shelly entity."""
|
||||||
self.wrapper = wrapper
|
self.wrapper = wrapper
|
||||||
self.block = block
|
self.block = block
|
||||||
|
@ -267,7 +269,7 @@ class ShellyBlockAttributeEntity(ShellyBlockEntity, entity.Entity):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
wrapper: ShellyDeviceWrapper,
|
wrapper: ShellyDeviceWrapper,
|
||||||
block: aioshelly.Block,
|
block: Block,
|
||||||
attribute: str,
|
attribute: str,
|
||||||
description: BlockAttributeDescription,
|
description: BlockAttributeDescription,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
@ -418,7 +420,7 @@ class ShellySleepingBlockAttributeEntity(ShellyBlockAttributeEntity, RestoreEnti
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
wrapper: ShellyDeviceWrapper,
|
wrapper: ShellyDeviceWrapper,
|
||||||
block: aioshelly.Block,
|
block: Block | None,
|
||||||
attribute: str,
|
attribute: str,
|
||||||
description: BlockAttributeDescription,
|
description: BlockAttributeDescription,
|
||||||
entry: entity_registry.RegistryEntry | None = None,
|
entry: entity_registry.RegistryEntry | None = None,
|
||||||
|
@ -429,7 +431,7 @@ class ShellySleepingBlockAttributeEntity(ShellyBlockAttributeEntity, RestoreEnti
|
||||||
self.last_state: StateType = None
|
self.last_state: StateType = None
|
||||||
self.wrapper = wrapper
|
self.wrapper = wrapper
|
||||||
self.attribute = attribute
|
self.attribute = attribute
|
||||||
self.block = block
|
self.block: Block | None = block # type: ignore[assignment]
|
||||||
self.description = description
|
self.description = description
|
||||||
self._unit = self.description.unit
|
self._unit = self.description.unit
|
||||||
|
|
||||||
|
@ -468,6 +470,8 @@ class ShellySleepingBlockAttributeEntity(ShellyBlockAttributeEntity, RestoreEnti
|
||||||
|
|
||||||
_, entity_block, entity_sensor = self.unique_id.split("-")
|
_, entity_block, entity_sensor = self.unique_id.split("-")
|
||||||
|
|
||||||
|
assert self.wrapper.device.blocks
|
||||||
|
|
||||||
for block in self.wrapper.device.blocks:
|
for block in self.wrapper.device.blocks:
|
||||||
if block.description != entity_block:
|
if block.description != entity_block:
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -5,7 +5,7 @@ import asyncio
|
||||||
import logging
|
import logging
|
||||||
from typing import Any, Final, cast
|
from typing import Any, Final, cast
|
||||||
|
|
||||||
from aioshelly import Block
|
from aioshelly.block_device import Block
|
||||||
import async_timeout
|
import async_timeout
|
||||||
|
|
||||||
from homeassistant.components.light import (
|
from homeassistant.components.light import (
|
||||||
|
@ -117,7 +117,7 @@ class ShellyLight(ShellyBlockEntity, LightEntity):
|
||||||
self._supported_features |= SUPPORT_EFFECT
|
self._supported_features |= SUPPORT_EFFECT
|
||||||
|
|
||||||
if wrapper.model in MODELS_SUPPORTING_LIGHT_TRANSITION:
|
if wrapper.model in MODELS_SUPPORTING_LIGHT_TRANSITION:
|
||||||
match = FIRMWARE_PATTERN.search(wrapper.device.settings.get("fw"))
|
match = FIRMWARE_PATTERN.search(wrapper.device.settings.get("fw", ""))
|
||||||
if (
|
if (
|
||||||
match is not None
|
match is not None
|
||||||
and int(match[0]) >= LIGHT_TRANSITION_MIN_FIRMWARE_DATE
|
and int(match[0]) >= LIGHT_TRANSITION_MIN_FIRMWARE_DATE
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
"name": "Shelly",
|
"name": "Shelly",
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/shelly",
|
"documentation": "https://www.home-assistant.io/integrations/shelly",
|
||||||
"requirements": ["aioshelly==0.6.4"],
|
"requirements": ["aioshelly==1.0.0"],
|
||||||
"zeroconf": [
|
"zeroconf": [
|
||||||
{
|
{
|
||||||
"type": "_http._tcp.local.",
|
"type": "_http._tcp.local.",
|
||||||
|
|
|
@ -40,7 +40,7 @@ SENSORS: Final = {
|
||||||
device_class=sensor.DEVICE_CLASS_BATTERY,
|
device_class=sensor.DEVICE_CLASS_BATTERY,
|
||||||
state_class=sensor.STATE_CLASS_MEASUREMENT,
|
state_class=sensor.STATE_CLASS_MEASUREMENT,
|
||||||
removal_condition=lambda settings, _: settings.get("external_power") == 1,
|
removal_condition=lambda settings, _: settings.get("external_power") == 1,
|
||||||
available=lambda block: cast(bool, block.battery != -1),
|
available=lambda block: cast(int, block.battery) != -1,
|
||||||
),
|
),
|
||||||
("device", "deviceTemp"): BlockAttributeDescription(
|
("device", "deviceTemp"): BlockAttributeDescription(
|
||||||
name="Device Temperature",
|
name="Device Temperature",
|
||||||
|
@ -162,7 +162,7 @@ SENSORS: Final = {
|
||||||
value=lambda value: round(value, 1),
|
value=lambda value: round(value, 1),
|
||||||
device_class=sensor.DEVICE_CLASS_TEMPERATURE,
|
device_class=sensor.DEVICE_CLASS_TEMPERATURE,
|
||||||
state_class=sensor.STATE_CLASS_MEASUREMENT,
|
state_class=sensor.STATE_CLASS_MEASUREMENT,
|
||||||
available=lambda block: cast(bool, block.extTemp != 999),
|
available=lambda block: cast(int, block.extTemp) != 999,
|
||||||
),
|
),
|
||||||
("sensor", "humidity"): BlockAttributeDescription(
|
("sensor", "humidity"): BlockAttributeDescription(
|
||||||
name="Humidity",
|
name="Humidity",
|
||||||
|
@ -170,14 +170,14 @@ SENSORS: Final = {
|
||||||
value=lambda value: round(value, 1),
|
value=lambda value: round(value, 1),
|
||||||
device_class=sensor.DEVICE_CLASS_HUMIDITY,
|
device_class=sensor.DEVICE_CLASS_HUMIDITY,
|
||||||
state_class=sensor.STATE_CLASS_MEASUREMENT,
|
state_class=sensor.STATE_CLASS_MEASUREMENT,
|
||||||
available=lambda block: cast(bool, block.extTemp != 999),
|
available=lambda block: cast(int, block.extTemp) != 999,
|
||||||
),
|
),
|
||||||
("sensor", "luminosity"): BlockAttributeDescription(
|
("sensor", "luminosity"): BlockAttributeDescription(
|
||||||
name="Luminosity",
|
name="Luminosity",
|
||||||
unit=LIGHT_LUX,
|
unit=LIGHT_LUX,
|
||||||
device_class=sensor.DEVICE_CLASS_ILLUMINANCE,
|
device_class=sensor.DEVICE_CLASS_ILLUMINANCE,
|
||||||
state_class=sensor.STATE_CLASS_MEASUREMENT,
|
state_class=sensor.STATE_CLASS_MEASUREMENT,
|
||||||
available=lambda block: cast(bool, block.luminosity != -1),
|
available=lambda block: cast(int, block.luminosity) != -1,
|
||||||
),
|
),
|
||||||
("sensor", "tilt"): BlockAttributeDescription(
|
("sensor", "tilt"): BlockAttributeDescription(
|
||||||
name="Tilt",
|
name="Tilt",
|
||||||
|
@ -191,7 +191,7 @@ SENSORS: Final = {
|
||||||
icon="mdi:progress-wrench",
|
icon="mdi:progress-wrench",
|
||||||
value=lambda value: round(100 - (value / 3600 / SHAIR_MAX_WORK_HOURS), 1),
|
value=lambda value: round(100 - (value / 3600 / SHAIR_MAX_WORK_HOURS), 1),
|
||||||
extra_state_attributes=lambda block: {
|
extra_state_attributes=lambda block: {
|
||||||
"Operational hours": round(block.totalWorkTime / 3600, 1)
|
"Operational hours": round(cast(int, block.totalWorkTime) / 3600, 1)
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
("adc", "adc"): BlockAttributeDescription(
|
("adc", "adc"): BlockAttributeDescription(
|
||||||
|
|
|
@ -3,7 +3,7 @@ from __future__ import annotations
|
||||||
|
|
||||||
from typing import Any, cast
|
from typing import Any, cast
|
||||||
|
|
||||||
from aioshelly import Block
|
from aioshelly.block_device import Block
|
||||||
|
|
||||||
from homeassistant.components.switch import SwitchEntity
|
from homeassistant.components.switch import SwitchEntity
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
|
|
|
@ -5,7 +5,7 @@ from datetime import datetime, timedelta
|
||||||
import logging
|
import logging
|
||||||
from typing import Any, Final, cast
|
from typing import Any, Final, cast
|
||||||
|
|
||||||
import aioshelly
|
from aioshelly.block_device import BLOCK_VALUE_UNIT, COAP, Block, BlockDevice
|
||||||
|
|
||||||
from homeassistant.const import EVENT_HOMEASSISTANT_STOP, TEMP_CELSIUS, TEMP_FAHRENHEIT
|
from homeassistant.const import EVENT_HOMEASSISTANT_STOP, TEMP_CELSIUS, TEMP_FAHRENHEIT
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
|
@ -40,18 +40,20 @@ async def async_remove_shelly_entity(
|
||||||
|
|
||||||
def temperature_unit(block_info: dict[str, Any]) -> str:
|
def temperature_unit(block_info: dict[str, Any]) -> str:
|
||||||
"""Detect temperature unit."""
|
"""Detect temperature unit."""
|
||||||
if block_info[aioshelly.BLOCK_VALUE_UNIT] == "F":
|
if block_info[BLOCK_VALUE_UNIT] == "F":
|
||||||
return TEMP_FAHRENHEIT
|
return TEMP_FAHRENHEIT
|
||||||
return TEMP_CELSIUS
|
return TEMP_CELSIUS
|
||||||
|
|
||||||
|
|
||||||
def get_device_name(device: aioshelly.Device) -> str:
|
def get_device_name(device: BlockDevice) -> str:
|
||||||
"""Naming for device."""
|
"""Naming for device."""
|
||||||
return cast(str, device.settings["name"] or device.settings["device"]["hostname"])
|
return cast(str, device.settings["name"] or device.settings["device"]["hostname"])
|
||||||
|
|
||||||
|
|
||||||
def get_number_of_channels(device: aioshelly.Device, block: aioshelly.Block) -> int:
|
def get_number_of_channels(device: BlockDevice, block: Block) -> int:
|
||||||
"""Get number of channels for block type."""
|
"""Get number of channels for block type."""
|
||||||
|
assert isinstance(device.shelly, dict)
|
||||||
|
|
||||||
channels = None
|
channels = None
|
||||||
|
|
||||||
if block.type == "input":
|
if block.type == "input":
|
||||||
|
@ -71,8 +73,8 @@ def get_number_of_channels(device: aioshelly.Device, block: aioshelly.Block) ->
|
||||||
|
|
||||||
|
|
||||||
def get_entity_name(
|
def get_entity_name(
|
||||||
device: aioshelly.Device,
|
device: BlockDevice,
|
||||||
block: aioshelly.Block,
|
block: Block | None,
|
||||||
description: str | None = None,
|
description: str | None = None,
|
||||||
) -> str:
|
) -> str:
|
||||||
"""Naming for switch and sensors."""
|
"""Naming for switch and sensors."""
|
||||||
|
@ -84,10 +86,7 @@ def get_entity_name(
|
||||||
return channel_name
|
return channel_name
|
||||||
|
|
||||||
|
|
||||||
def get_device_channel_name(
|
def get_device_channel_name(device: BlockDevice, block: Block | None) -> str:
|
||||||
device: aioshelly.Device,
|
|
||||||
block: aioshelly.Block,
|
|
||||||
) -> str:
|
|
||||||
"""Get name based on device and channel name."""
|
"""Get name based on device and channel name."""
|
||||||
entity_name = get_device_name(device)
|
entity_name = get_device_name(device)
|
||||||
|
|
||||||
|
@ -98,8 +97,10 @@ def get_device_channel_name(
|
||||||
):
|
):
|
||||||
return entity_name
|
return entity_name
|
||||||
|
|
||||||
|
assert block.channel
|
||||||
|
|
||||||
channel_name: str | None = None
|
channel_name: str | None = None
|
||||||
mode = block.type + "s"
|
mode = cast(str, block.type) + "s"
|
||||||
if mode in device.settings:
|
if mode in device.settings:
|
||||||
channel_name = device.settings[mode][int(block.channel)].get("name")
|
channel_name = device.settings[mode][int(block.channel)].get("name")
|
||||||
|
|
||||||
|
@ -114,7 +115,7 @@ def get_device_channel_name(
|
||||||
return f"{entity_name} channel {chr(int(block.channel)+base)}"
|
return f"{entity_name} channel {chr(int(block.channel)+base)}"
|
||||||
|
|
||||||
|
|
||||||
def is_momentary_input(settings: dict[str, Any], block: aioshelly.Block) -> bool:
|
def is_momentary_input(settings: dict[str, Any], block: Block) -> bool:
|
||||||
"""Return true if input button settings is set to a momentary type."""
|
"""Return true if input button settings is set to a momentary type."""
|
||||||
# Shelly Button type is fixed to momentary and no btn_type
|
# Shelly Button type is fixed to momentary and no btn_type
|
||||||
if settings["device"]["type"] in SHBTN_MODELS:
|
if settings["device"]["type"] in SHBTN_MODELS:
|
||||||
|
@ -150,9 +151,7 @@ def get_device_uptime(status: dict[str, Any], last_uptime: str | None) -> str:
|
||||||
return last_uptime
|
return last_uptime
|
||||||
|
|
||||||
|
|
||||||
def get_input_triggers(
|
def get_input_triggers(device: BlockDevice, block: Block) -> list[tuple[str, str]]:
|
||||||
device: aioshelly.Device, block: aioshelly.Block
|
|
||||||
) -> list[tuple[str, str]]:
|
|
||||||
"""Return list of input triggers for block."""
|
"""Return list of input triggers for block."""
|
||||||
if "inputEvent" not in block.sensor_ids or "inputEventCnt" not in block.sensor_ids:
|
if "inputEvent" not in block.sensor_ids or "inputEventCnt" not in block.sensor_ids:
|
||||||
return []
|
return []
|
||||||
|
@ -165,6 +164,7 @@ def get_input_triggers(
|
||||||
if block.type == "device" or get_number_of_channels(device, block) == 1:
|
if block.type == "device" or get_number_of_channels(device, block) == 1:
|
||||||
subtype = "button"
|
subtype = "button"
|
||||||
else:
|
else:
|
||||||
|
assert block.channel
|
||||||
subtype = f"button{int(block.channel)+1}"
|
subtype = f"button{int(block.channel)+1}"
|
||||||
|
|
||||||
if device.settings["device"]["type"] in SHBTN_MODELS:
|
if device.settings["device"]["type"] in SHBTN_MODELS:
|
||||||
|
@ -181,9 +181,9 @@ def get_input_triggers(
|
||||||
|
|
||||||
|
|
||||||
@singleton.singleton("shelly_coap")
|
@singleton.singleton("shelly_coap")
|
||||||
async def get_coap_context(hass: HomeAssistant) -> aioshelly.COAP:
|
async def get_coap_context(hass: HomeAssistant) -> COAP:
|
||||||
"""Get CoAP context to be used in all Shelly devices."""
|
"""Get CoAP context to be used in all Shelly devices."""
|
||||||
context = aioshelly.COAP()
|
context = COAP()
|
||||||
if DOMAIN in hass.data:
|
if DOMAIN in hass.data:
|
||||||
port = hass.data[DOMAIN].get(CONF_COAP_PORT, DEFAULT_COAP_PORT)
|
port = hass.data[DOMAIN].get(CONF_COAP_PORT, DEFAULT_COAP_PORT)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -240,7 +240,7 @@ aiopylgtv==0.4.0
|
||||||
aiorecollect==1.0.8
|
aiorecollect==1.0.8
|
||||||
|
|
||||||
# homeassistant.components.shelly
|
# homeassistant.components.shelly
|
||||||
aioshelly==0.6.4
|
aioshelly==1.0.0
|
||||||
|
|
||||||
# homeassistant.components.switcher_kis
|
# homeassistant.components.switcher_kis
|
||||||
aioswitcher==2.0.5
|
aioswitcher==2.0.5
|
||||||
|
|
|
@ -161,7 +161,7 @@ aiopylgtv==0.4.0
|
||||||
aiorecollect==1.0.8
|
aiorecollect==1.0.8
|
||||||
|
|
||||||
# homeassistant.components.shelly
|
# homeassistant.components.shelly
|
||||||
aioshelly==0.6.4
|
aioshelly==1.0.0
|
||||||
|
|
||||||
# homeassistant.components.switcher_kis
|
# homeassistant.components.switcher_kis
|
||||||
aioswitcher==2.0.5
|
aioswitcher==2.0.5
|
||||||
|
|
|
@ -32,10 +32,10 @@ async def test_form(hass):
|
||||||
assert result["errors"] == {}
|
assert result["errors"] == {}
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"aioshelly.get_info",
|
"aioshelly.common.get_info",
|
||||||
return_value={"mac": "test-mac", "type": "SHSW-1", "auth": False},
|
return_value={"mac": "test-mac", "type": "SHSW-1", "auth": False},
|
||||||
), patch(
|
), patch(
|
||||||
"aioshelly.Device.create",
|
"aioshelly.block_device.BlockDevice.create",
|
||||||
new=AsyncMock(
|
new=AsyncMock(
|
||||||
return_value=Mock(
|
return_value=Mock(
|
||||||
settings=MOCK_SETTINGS,
|
settings=MOCK_SETTINGS,
|
||||||
|
@ -78,10 +78,10 @@ async def test_title_without_name(hass):
|
||||||
settings["device"] = settings["device"].copy()
|
settings["device"] = settings["device"].copy()
|
||||||
settings["device"]["hostname"] = "shelly1pm-12345"
|
settings["device"]["hostname"] = "shelly1pm-12345"
|
||||||
with patch(
|
with patch(
|
||||||
"aioshelly.get_info",
|
"aioshelly.common.get_info",
|
||||||
return_value={"mac": "test-mac", "type": "SHSW-1", "auth": False},
|
return_value={"mac": "test-mac", "type": "SHSW-1", "auth": False},
|
||||||
), patch(
|
), patch(
|
||||||
"aioshelly.Device.create",
|
"aioshelly.block_device.BlockDevice.create",
|
||||||
new=AsyncMock(
|
new=AsyncMock(
|
||||||
return_value=Mock(
|
return_value=Mock(
|
||||||
settings=settings,
|
settings=settings,
|
||||||
|
@ -119,7 +119,7 @@ async def test_form_auth(hass):
|
||||||
assert result["errors"] == {}
|
assert result["errors"] == {}
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"aioshelly.get_info",
|
"aioshelly.common.get_info",
|
||||||
return_value={"mac": "test-mac", "type": "SHSW-1", "auth": True},
|
return_value={"mac": "test-mac", "type": "SHSW-1", "auth": True},
|
||||||
):
|
):
|
||||||
result2 = await hass.config_entries.flow.async_configure(
|
result2 = await hass.config_entries.flow.async_configure(
|
||||||
|
@ -131,7 +131,7 @@ async def test_form_auth(hass):
|
||||||
assert result["errors"] == {}
|
assert result["errors"] == {}
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"aioshelly.Device.create",
|
"aioshelly.block_device.BlockDevice.create",
|
||||||
new=AsyncMock(
|
new=AsyncMock(
|
||||||
return_value=Mock(
|
return_value=Mock(
|
||||||
settings=MOCK_SETTINGS,
|
settings=MOCK_SETTINGS,
|
||||||
|
@ -172,7 +172,7 @@ async def test_form_errors_get_info(hass, error):
|
||||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||||
)
|
)
|
||||||
|
|
||||||
with patch("aioshelly.get_info", side_effect=exc):
|
with patch("aioshelly.common.get_info", side_effect=exc):
|
||||||
result2 = await hass.config_entries.flow.async_configure(
|
result2 = await hass.config_entries.flow.async_configure(
|
||||||
result["flow_id"],
|
result["flow_id"],
|
||||||
{"host": "1.1.1.1"},
|
{"host": "1.1.1.1"},
|
||||||
|
@ -193,8 +193,10 @@ async def test_form_errors_test_connection(hass, error):
|
||||||
)
|
)
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"aioshelly.get_info", return_value={"mac": "test-mac", "auth": False}
|
"aioshelly.common.get_info", return_value={"mac": "test-mac", "auth": False}
|
||||||
), patch("aioshelly.Device.create", new=AsyncMock(side_effect=exc)):
|
), patch(
|
||||||
|
"aioshelly.block_device.BlockDevice.create", new=AsyncMock(side_effect=exc)
|
||||||
|
):
|
||||||
result2 = await hass.config_entries.flow.async_configure(
|
result2 = await hass.config_entries.flow.async_configure(
|
||||||
result["flow_id"],
|
result["flow_id"],
|
||||||
{"host": "1.1.1.1"},
|
{"host": "1.1.1.1"},
|
||||||
|
@ -217,7 +219,7 @@ async def test_form_already_configured(hass):
|
||||||
)
|
)
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"aioshelly.get_info",
|
"aioshelly.common.get_info",
|
||||||
return_value={"mac": "test-mac", "type": "SHSW-1", "auth": False},
|
return_value={"mac": "test-mac", "type": "SHSW-1", "auth": False},
|
||||||
):
|
):
|
||||||
result2 = await hass.config_entries.flow.async_configure(
|
result2 = await hass.config_entries.flow.async_configure(
|
||||||
|
@ -252,10 +254,10 @@ async def test_user_setup_ignored_device(hass):
|
||||||
settings["fw"] = "20201124-092534/v1.9.0@57ac4ad8"
|
settings["fw"] = "20201124-092534/v1.9.0@57ac4ad8"
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"aioshelly.get_info",
|
"aioshelly.common.get_info",
|
||||||
return_value={"mac": "test-mac", "type": "SHSW-1", "auth": False},
|
return_value={"mac": "test-mac", "type": "SHSW-1", "auth": False},
|
||||||
), patch(
|
), patch(
|
||||||
"aioshelly.Device.create",
|
"aioshelly.block_device.BlockDevice.create",
|
||||||
new=AsyncMock(
|
new=AsyncMock(
|
||||||
return_value=Mock(
|
return_value=Mock(
|
||||||
settings=settings,
|
settings=settings,
|
||||||
|
@ -287,7 +289,10 @@ async def test_form_firmware_unsupported(hass):
|
||||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||||
)
|
)
|
||||||
|
|
||||||
with patch("aioshelly.get_info", side_effect=aioshelly.FirmwareUnsupported):
|
with patch(
|
||||||
|
"aioshelly.common.get_info",
|
||||||
|
side_effect=aioshelly.exceptions.FirmwareUnsupported,
|
||||||
|
):
|
||||||
result2 = await hass.config_entries.flow.async_configure(
|
result2 = await hass.config_entries.flow.async_configure(
|
||||||
result["flow_id"],
|
result["flow_id"],
|
||||||
{"host": "1.1.1.1"},
|
{"host": "1.1.1.1"},
|
||||||
|
@ -313,14 +318,17 @@ async def test_form_auth_errors_test_connection(hass, error):
|
||||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||||
)
|
)
|
||||||
|
|
||||||
with patch("aioshelly.get_info", return_value={"mac": "test-mac", "auth": True}):
|
with patch(
|
||||||
|
"aioshelly.common.get_info",
|
||||||
|
return_value={"mac": "test-mac", "auth": True},
|
||||||
|
):
|
||||||
result2 = await hass.config_entries.flow.async_configure(
|
result2 = await hass.config_entries.flow.async_configure(
|
||||||
result["flow_id"],
|
result["flow_id"],
|
||||||
{"host": "1.1.1.1"},
|
{"host": "1.1.1.1"},
|
||||||
)
|
)
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"aioshelly.Device.create",
|
"aioshelly.block_device.BlockDevice.create",
|
||||||
new=AsyncMock(side_effect=exc),
|
new=AsyncMock(side_effect=exc),
|
||||||
):
|
):
|
||||||
result3 = await hass.config_entries.flow.async_configure(
|
result3 = await hass.config_entries.flow.async_configure(
|
||||||
|
@ -336,10 +344,10 @@ async def test_zeroconf(hass):
|
||||||
await setup.async_setup_component(hass, "persistent_notification", {})
|
await setup.async_setup_component(hass, "persistent_notification", {})
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"aioshelly.get_info",
|
"aioshelly.common.get_info",
|
||||||
return_value={"mac": "test-mac", "type": "SHSW-1", "auth": False},
|
return_value={"mac": "test-mac", "type": "SHSW-1", "auth": False},
|
||||||
), patch(
|
), patch(
|
||||||
"aioshelly.Device.create",
|
"aioshelly.block_device.BlockDevice.create",
|
||||||
new=AsyncMock(
|
new=AsyncMock(
|
||||||
return_value=Mock(
|
return_value=Mock(
|
||||||
settings=MOCK_SETTINGS,
|
settings=MOCK_SETTINGS,
|
||||||
|
@ -388,7 +396,7 @@ async def test_zeroconf_sleeping_device(hass):
|
||||||
await setup.async_setup_component(hass, "persistent_notification", {})
|
await setup.async_setup_component(hass, "persistent_notification", {})
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"aioshelly.get_info",
|
"aioshelly.common.get_info",
|
||||||
return_value={
|
return_value={
|
||||||
"mac": "test-mac",
|
"mac": "test-mac",
|
||||||
"type": "SHSW-1",
|
"type": "SHSW-1",
|
||||||
|
@ -396,7 +404,7 @@ async def test_zeroconf_sleeping_device(hass):
|
||||||
"sleep_mode": True,
|
"sleep_mode": True,
|
||||||
},
|
},
|
||||||
), patch(
|
), patch(
|
||||||
"aioshelly.Device.create",
|
"aioshelly.block_device.BlockDevice.create",
|
||||||
new=AsyncMock(
|
new=AsyncMock(
|
||||||
return_value=Mock(
|
return_value=Mock(
|
||||||
settings={
|
settings={
|
||||||
|
@ -460,7 +468,7 @@ async def test_zeroconf_sleeping_device_error(hass, error):
|
||||||
await setup.async_setup_component(hass, "persistent_notification", {})
|
await setup.async_setup_component(hass, "persistent_notification", {})
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"aioshelly.get_info",
|
"aioshelly.common.get_info",
|
||||||
return_value={
|
return_value={
|
||||||
"mac": "test-mac",
|
"mac": "test-mac",
|
||||||
"type": "SHSW-1",
|
"type": "SHSW-1",
|
||||||
|
@ -468,7 +476,7 @@ async def test_zeroconf_sleeping_device_error(hass, error):
|
||||||
"sleep_mode": True,
|
"sleep_mode": True,
|
||||||
},
|
},
|
||||||
), patch(
|
), patch(
|
||||||
"aioshelly.Device.create",
|
"aioshelly.block_device.BlockDevice.create",
|
||||||
new=AsyncMock(side_effect=exc),
|
new=AsyncMock(side_effect=exc),
|
||||||
):
|
):
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
@ -489,7 +497,7 @@ async def test_zeroconf_already_configured(hass):
|
||||||
entry.add_to_hass(hass)
|
entry.add_to_hass(hass)
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"aioshelly.get_info",
|
"aioshelly.common.get_info",
|
||||||
return_value={"mac": "test-mac", "type": "SHSW-1", "auth": False},
|
return_value={"mac": "test-mac", "type": "SHSW-1", "auth": False},
|
||||||
):
|
):
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
@ -506,7 +514,10 @@ async def test_zeroconf_already_configured(hass):
|
||||||
|
|
||||||
async def test_zeroconf_firmware_unsupported(hass):
|
async def test_zeroconf_firmware_unsupported(hass):
|
||||||
"""Test we abort if device firmware is unsupported."""
|
"""Test we abort if device firmware is unsupported."""
|
||||||
with patch("aioshelly.get_info", side_effect=aioshelly.FirmwareUnsupported):
|
with patch(
|
||||||
|
"aioshelly.common.get_info",
|
||||||
|
side_effect=aioshelly.exceptions.FirmwareUnsupported,
|
||||||
|
):
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
data=DISCOVERY_INFO,
|
data=DISCOVERY_INFO,
|
||||||
|
@ -519,7 +530,7 @@ async def test_zeroconf_firmware_unsupported(hass):
|
||||||
|
|
||||||
async def test_zeroconf_cannot_connect(hass):
|
async def test_zeroconf_cannot_connect(hass):
|
||||||
"""Test we get the form."""
|
"""Test we get the form."""
|
||||||
with patch("aioshelly.get_info", side_effect=asyncio.TimeoutError):
|
with patch("aioshelly.common.get_info", side_effect=asyncio.TimeoutError):
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
data=DISCOVERY_INFO,
|
data=DISCOVERY_INFO,
|
||||||
|
@ -534,7 +545,7 @@ async def test_zeroconf_require_auth(hass):
|
||||||
await setup.async_setup_component(hass, "persistent_notification", {})
|
await setup.async_setup_component(hass, "persistent_notification", {})
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"aioshelly.get_info",
|
"aioshelly.common.get_info",
|
||||||
return_value={"mac": "test-mac", "type": "SHSW-1", "auth": True},
|
return_value={"mac": "test-mac", "type": "SHSW-1", "auth": True},
|
||||||
):
|
):
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
@ -546,7 +557,7 @@ async def test_zeroconf_require_auth(hass):
|
||||||
assert result["errors"] == {}
|
assert result["errors"] == {}
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"aioshelly.Device.create",
|
"aioshelly.block_device.BlockDevice.create",
|
||||||
new=AsyncMock(
|
new=AsyncMock(
|
||||||
return_value=Mock(
|
return_value=Mock(
|
||||||
settings=MOCK_SETTINGS,
|
settings=MOCK_SETTINGS,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue