Add config flow support for RPC device (#56118)
This commit is contained in:
parent
8c3c2ad8e3
commit
f1a88f0563
8 changed files with 145 additions and 54 deletions
|
@ -46,7 +46,11 @@ from .const import (
|
||||||
SLEEP_PERIOD_MULTIPLIER,
|
SLEEP_PERIOD_MULTIPLIER,
|
||||||
UPDATE_PERIOD_MULTIPLIER,
|
UPDATE_PERIOD_MULTIPLIER,
|
||||||
)
|
)
|
||||||
from .utils import get_coap_context, get_device_name, get_device_sleep_period
|
from .utils import (
|
||||||
|
get_block_device_name,
|
||||||
|
get_block_device_sleep_period,
|
||||||
|
get_coap_context,
|
||||||
|
)
|
||||||
|
|
||||||
PLATFORMS: Final = ["binary_sensor", "cover", "light", "sensor", "switch"]
|
PLATFORMS: Final = ["binary_sensor", "cover", "light", "sensor", "switch"]
|
||||||
SLEEPING_PLATFORMS: Final = ["binary_sensor", "sensor"]
|
SLEEPING_PLATFORMS: Final = ["binary_sensor", "sensor"]
|
||||||
|
@ -85,6 +89,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
)
|
)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
if entry.data.get("gen") == 2:
|
||||||
|
return True
|
||||||
|
|
||||||
hass.data[DOMAIN][DATA_CONFIG_ENTRY][entry.entry_id] = {}
|
hass.data[DOMAIN][DATA_CONFIG_ENTRY][entry.entry_id] = {}
|
||||||
hass.data[DOMAIN][DATA_CONFIG_ENTRY][entry.entry_id][DEVICE] = None
|
hass.data[DOMAIN][DATA_CONFIG_ENTRY][entry.entry_id][DEVICE] = None
|
||||||
|
|
||||||
|
@ -124,7 +131,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
|
|
||||||
if sleep_period is None:
|
if sleep_period is None:
|
||||||
data = {**entry.data}
|
data = {**entry.data}
|
||||||
data["sleep_period"] = get_device_sleep_period(device.settings)
|
data["sleep_period"] = get_block_device_sleep_period(device.settings)
|
||||||
data["model"] = device.settings["device"]["type"]
|
data["model"] = device.settings["device"]["type"]
|
||||||
hass.config_entries.async_update_entry(entry, data=data)
|
hass.config_entries.async_update_entry(entry, data=data)
|
||||||
|
|
||||||
|
@ -192,7 +199,9 @@ class ShellyDeviceWrapper(update_coordinator.DataUpdateCoordinator):
|
||||||
UPDATE_PERIOD_MULTIPLIER * device.settings["coiot"]["update_period"]
|
UPDATE_PERIOD_MULTIPLIER * device.settings["coiot"]["update_period"]
|
||||||
)
|
)
|
||||||
|
|
||||||
device_name = get_device_name(device) if device.initialized else entry.title
|
device_name = (
|
||||||
|
get_block_device_name(device) if device.initialized else entry.title
|
||||||
|
)
|
||||||
super().__init__(
|
super().__init__(
|
||||||
hass,
|
hass,
|
||||||
_LOGGER,
|
_LOGGER,
|
||||||
|
@ -338,7 +347,7 @@ class ShellyDeviceRestWrapper(update_coordinator.DataUpdateCoordinator):
|
||||||
super().__init__(
|
super().__init__(
|
||||||
hass,
|
hass,
|
||||||
_LOGGER,
|
_LOGGER,
|
||||||
name=get_device_name(device),
|
name=get_block_device_name(device),
|
||||||
update_interval=timedelta(seconds=update_interval),
|
update_interval=timedelta(seconds=update_interval),
|
||||||
)
|
)
|
||||||
self.device = device
|
self.device = device
|
||||||
|
@ -360,6 +369,9 @@ class ShellyDeviceRestWrapper(update_coordinator.DataUpdateCoordinator):
|
||||||
|
|
||||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
"""Unload a config entry."""
|
"""Unload a config entry."""
|
||||||
|
if entry.data.get("gen") == 2:
|
||||||
|
return True
|
||||||
|
|
||||||
device = hass.data[DOMAIN][DATA_CONFIG_ENTRY][entry.entry_id].get(DEVICE)
|
device = hass.data[DOMAIN][DATA_CONFIG_ENTRY][entry.entry_id].get(DEVICE)
|
||||||
if device is not None:
|
if device is not None:
|
||||||
# If device is present, device wrapper is not setup yet
|
# If device is present, device wrapper is not setup yet
|
||||||
|
|
|
@ -3,27 +3,37 @@ from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
from typing import Any, Dict, Final, cast
|
from typing import Any, Final
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
import aioshelly
|
import aioshelly
|
||||||
from aioshelly.block_device import BlockDevice
|
from aioshelly.block_device import BlockDevice
|
||||||
|
from aioshelly.rpc_device import RpcDevice
|
||||||
import async_timeout
|
import async_timeout
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant import config_entries, core
|
from homeassistant import config_entries
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
CONF_HOST,
|
CONF_HOST,
|
||||||
CONF_PASSWORD,
|
CONF_PASSWORD,
|
||||||
CONF_USERNAME,
|
CONF_USERNAME,
|
||||||
HTTP_UNAUTHORIZED,
|
HTTP_UNAUTHORIZED,
|
||||||
)
|
)
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.data_entry_flow import FlowResult
|
from homeassistant.data_entry_flow import FlowResult
|
||||||
from homeassistant.helpers import aiohttp_client
|
from homeassistant.helpers import aiohttp_client
|
||||||
from homeassistant.helpers.typing import DiscoveryInfoType
|
from homeassistant.helpers.typing import DiscoveryInfoType
|
||||||
|
|
||||||
from .const import AIOSHELLY_DEVICE_TIMEOUT_SEC, DOMAIN
|
from .const import AIOSHELLY_DEVICE_TIMEOUT_SEC, DOMAIN
|
||||||
from .utils import get_coap_context, get_device_sleep_period
|
from .utils import (
|
||||||
|
get_block_device_name,
|
||||||
|
get_block_device_sleep_period,
|
||||||
|
get_coap_context,
|
||||||
|
get_info_auth,
|
||||||
|
get_info_gen,
|
||||||
|
get_model_name,
|
||||||
|
get_rpc_device_name,
|
||||||
|
)
|
||||||
|
|
||||||
_LOGGER: Final = logging.getLogger(__name__)
|
_LOGGER: Final = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -33,34 +43,49 @@ HTTP_CONNECT_ERRORS: Final = (asyncio.TimeoutError, aiohttp.ClientError)
|
||||||
|
|
||||||
|
|
||||||
async def validate_input(
|
async def validate_input(
|
||||||
hass: core.HomeAssistant, host: str, data: dict[str, Any]
|
hass: HomeAssistant,
|
||||||
|
host: str,
|
||||||
|
info: dict[str, Any],
|
||||||
|
data: dict[str, Any],
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
"""Validate the user input allows us to connect.
|
"""Validate the user input allows us to connect.
|
||||||
|
|
||||||
Data has the keys from DATA_SCHEMA with values provided by the user.
|
Data has the keys from HOST_SCHEMA with values provided by the user.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
options = aioshelly.common.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)
|
|
||||||
|
|
||||||
async with async_timeout.timeout(AIOSHELLY_DEVICE_TIMEOUT_SEC):
|
async with async_timeout.timeout(AIOSHELLY_DEVICE_TIMEOUT_SEC):
|
||||||
device = await BlockDevice.create(
|
if get_info_gen(info) == 2:
|
||||||
|
rpc_device = await RpcDevice.create(
|
||||||
|
aiohttp_client.async_get_clientsession(hass),
|
||||||
|
options,
|
||||||
|
)
|
||||||
|
await rpc_device.shutdown()
|
||||||
|
return {
|
||||||
|
"title": get_rpc_device_name(rpc_device),
|
||||||
|
"sleep_period": 0,
|
||||||
|
"model": rpc_device.model,
|
||||||
|
"gen": 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
# Gen1
|
||||||
|
coap_context = await get_coap_context(hass)
|
||||||
|
block_device = await BlockDevice.create(
|
||||||
aiohttp_client.async_get_clientsession(hass),
|
aiohttp_client.async_get_clientsession(hass),
|
||||||
coap_context,
|
coap_context,
|
||||||
options,
|
options,
|
||||||
)
|
)
|
||||||
|
block_device.shutdown()
|
||||||
device.shutdown()
|
return {
|
||||||
|
"title": get_block_device_name(block_device),
|
||||||
# Return info that you want to store in the config entry.
|
"sleep_period": get_block_device_sleep_period(block_device.settings),
|
||||||
return {
|
"model": block_device.model,
|
||||||
"title": device.settings["name"],
|
"gen": 1,
|
||||||
"hostname": device.settings["device"]["hostname"],
|
}
|
||||||
"sleep_period": get_device_sleep_period(device.settings),
|
|
||||||
"model": device.settings["device"]["type"],
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
|
@ -80,7 +105,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
host: str = user_input[CONF_HOST]
|
host: str = user_input[CONF_HOST]
|
||||||
try:
|
try:
|
||||||
info = await self._async_get_info(host)
|
self.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.exceptions.FirmwareUnsupported:
|
except aioshelly.exceptions.FirmwareUnsupported:
|
||||||
|
@ -89,14 +114,16 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
_LOGGER.exception("Unexpected exception")
|
_LOGGER.exception("Unexpected exception")
|
||||||
errors["base"] = "unknown"
|
errors["base"] = "unknown"
|
||||||
else:
|
else:
|
||||||
await self.async_set_unique_id(info["mac"])
|
await self.async_set_unique_id(self.info["mac"])
|
||||||
self._abort_if_unique_id_configured({CONF_HOST: host})
|
self._abort_if_unique_id_configured({CONF_HOST: host})
|
||||||
self.host = host
|
self.host = host
|
||||||
if info["auth"]:
|
if get_info_auth(self.info):
|
||||||
return await self.async_step_credentials()
|
return await self.async_step_credentials()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
device_info = await validate_input(self.hass, self.host, {})
|
device_info = await validate_input(
|
||||||
|
self.hass, self.host, self.info, {}
|
||||||
|
)
|
||||||
except HTTP_CONNECT_ERRORS:
|
except HTTP_CONNECT_ERRORS:
|
||||||
errors["base"] = "cannot_connect"
|
errors["base"] = "cannot_connect"
|
||||||
except Exception: # pylint: disable=broad-except
|
except Exception: # pylint: disable=broad-except
|
||||||
|
@ -104,11 +131,12 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
errors["base"] = "unknown"
|
errors["base"] = "unknown"
|
||||||
else:
|
else:
|
||||||
return self.async_create_entry(
|
return self.async_create_entry(
|
||||||
title=device_info["title"] or device_info["hostname"],
|
title=device_info["title"],
|
||||||
data={
|
data={
|
||||||
**user_input,
|
**user_input,
|
||||||
"sleep_period": device_info["sleep_period"],
|
"sleep_period": device_info["sleep_period"],
|
||||||
"model": device_info["model"],
|
"model": device_info["model"],
|
||||||
|
"gen": device_info["gen"],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -123,7 +151,9 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
errors: dict[str, str] = {}
|
errors: dict[str, str] = {}
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
try:
|
try:
|
||||||
device_info = await validate_input(self.hass, self.host, user_input)
|
device_info = await validate_input(
|
||||||
|
self.hass, self.host, self.info, user_input
|
||||||
|
)
|
||||||
except aiohttp.ClientResponseError as error:
|
except aiohttp.ClientResponseError as error:
|
||||||
if error.status == HTTP_UNAUTHORIZED:
|
if error.status == HTTP_UNAUTHORIZED:
|
||||||
errors["base"] = "invalid_auth"
|
errors["base"] = "invalid_auth"
|
||||||
|
@ -136,12 +166,13 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
errors["base"] = "unknown"
|
errors["base"] = "unknown"
|
||||||
else:
|
else:
|
||||||
return self.async_create_entry(
|
return self.async_create_entry(
|
||||||
title=device_info["title"] or device_info["hostname"],
|
title=device_info["title"],
|
||||||
data={
|
data={
|
||||||
**user_input,
|
**user_input,
|
||||||
CONF_HOST: self.host,
|
CONF_HOST: self.host,
|
||||||
"sleep_period": device_info["sleep_period"],
|
"sleep_period": device_info["sleep_period"],
|
||||||
"model": device_info["model"],
|
"model": device_info["model"],
|
||||||
|
"gen": device_info["gen"],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
@ -163,13 +194,13 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
) -> FlowResult:
|
) -> FlowResult:
|
||||||
"""Handle zeroconf discovery."""
|
"""Handle zeroconf discovery."""
|
||||||
try:
|
try:
|
||||||
self.info = info = await self._async_get_info(discovery_info["host"])
|
self.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.exceptions.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(self.info["mac"])
|
||||||
self._abort_if_unique_id_configured({CONF_HOST: discovery_info["host"]})
|
self._abort_if_unique_id_configured({CONF_HOST: discovery_info["host"]})
|
||||||
self.host = discovery_info["host"]
|
self.host = discovery_info["host"]
|
||||||
|
|
||||||
|
@ -177,11 +208,11 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
"name": discovery_info.get("name", "").split(".")[0]
|
"name": discovery_info.get("name", "").split(".")[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
if info["auth"]:
|
if get_info_auth(self.info):
|
||||||
return await self.async_step_credentials()
|
return await self.async_step_credentials()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.device_info = await validate_input(self.hass, self.host, {})
|
self.device_info = await validate_input(self.hass, self.host, self.info, {})
|
||||||
except HTTP_CONNECT_ERRORS:
|
except HTTP_CONNECT_ERRORS:
|
||||||
return self.async_abort(reason="cannot_connect")
|
return self.async_abort(reason="cannot_connect")
|
||||||
|
|
||||||
|
@ -194,11 +225,12 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
errors: dict[str, str] = {}
|
errors: dict[str, str] = {}
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
return self.async_create_entry(
|
return self.async_create_entry(
|
||||||
title=self.device_info["title"] or self.device_info["hostname"],
|
title=self.device_info["title"],
|
||||||
data={
|
data={
|
||||||
"host": self.host,
|
"host": self.host,
|
||||||
"sleep_period": self.device_info["sleep_period"],
|
"sleep_period": self.device_info["sleep_period"],
|
||||||
"model": self.device_info["model"],
|
"model": self.device_info["model"],
|
||||||
|
"gen": self.device_info["gen"],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -207,9 +239,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.const.MODEL_NAMES.get(
|
"model": get_model_name(self.info),
|
||||||
self.info["type"], self.info["type"]
|
|
||||||
),
|
|
||||||
"host": self.host,
|
"host": self.host,
|
||||||
},
|
},
|
||||||
errors=errors,
|
errors=errors,
|
||||||
|
@ -218,10 +248,6 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
async def _async_get_info(self, host: str) -> dict[str, Any]:
|
async def _async_get_info(self, host: str) -> dict[str, Any]:
|
||||||
"""Get info from shelly device."""
|
"""Get info from shelly device."""
|
||||||
async with async_timeout.timeout(AIOSHELLY_DEVICE_TIMEOUT_SEC):
|
async with async_timeout.timeout(AIOSHELLY_DEVICE_TIMEOUT_SEC):
|
||||||
return cast(
|
return await aioshelly.common.get_info(
|
||||||
Dict[str, Any],
|
aiohttp_client.async_get_clientsession(self.hass), host
|
||||||
await aioshelly.common.get_info(
|
|
||||||
aiohttp_client.async_get_clientsession(self.hass),
|
|
||||||
host,
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -15,7 +15,7 @@ from .const import (
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
EVENT_SHELLY_CLICK,
|
EVENT_SHELLY_CLICK,
|
||||||
)
|
)
|
||||||
from .utils import get_device_name
|
from .utils import get_block_device_name
|
||||||
|
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
|
@ -30,7 +30,7 @@ def async_describe_events(
|
||||||
"""Describe shelly.click logbook event."""
|
"""Describe shelly.click logbook event."""
|
||||||
wrapper = get_device_wrapper(hass, event.data[ATTR_DEVICE_ID])
|
wrapper = get_device_wrapper(hass, event.data[ATTR_DEVICE_ID])
|
||||||
if wrapper and wrapper.device.initialized:
|
if wrapper and wrapper.device.initialized:
|
||||||
device_name = get_device_name(wrapper.device)
|
device_name = get_block_device_name(wrapper.device)
|
||||||
else:
|
else:
|
||||||
device_name = event.data[ATTR_DEVICE]
|
device_name = event.data[ATTR_DEVICE]
|
||||||
|
|
||||||
|
|
|
@ -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==1.0.0"],
|
"requirements": ["aioshelly==1.0.1"],
|
||||||
"zeroconf": [
|
"zeroconf": [
|
||||||
{
|
{
|
||||||
"type": "_http._tcp.local.",
|
"type": "_http._tcp.local.",
|
||||||
|
|
|
@ -6,6 +6,8 @@ import logging
|
||||||
from typing import Any, Final, cast
|
from typing import Any, Final, cast
|
||||||
|
|
||||||
from aioshelly.block_device import BLOCK_VALUE_UNIT, COAP, Block, BlockDevice
|
from aioshelly.block_device import BLOCK_VALUE_UNIT, COAP, Block, BlockDevice
|
||||||
|
from aioshelly.const import MODEL_NAMES
|
||||||
|
from aioshelly.rpc_device import RpcDevice
|
||||||
|
|
||||||
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
|
||||||
|
@ -45,11 +47,18 @@ def temperature_unit(block_info: dict[str, Any]) -> str:
|
||||||
return TEMP_CELSIUS
|
return TEMP_CELSIUS
|
||||||
|
|
||||||
|
|
||||||
def get_device_name(device: BlockDevice) -> str:
|
def get_block_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_rpc_device_name(device: RpcDevice) -> str:
|
||||||
|
"""Naming for device."""
|
||||||
|
# Gen2 does not support setting device name
|
||||||
|
# AP SSID name is used as a nicely formatted device name
|
||||||
|
return cast(str, device.config["wifi"]["ap"]["ssid"] or device.hostname)
|
||||||
|
|
||||||
|
|
||||||
def get_number_of_channels(device: BlockDevice, block: 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)
|
assert isinstance(device.shelly, dict)
|
||||||
|
@ -88,7 +97,7 @@ def get_entity_name(
|
||||||
|
|
||||||
def get_device_channel_name(device: BlockDevice, block: Block | None) -> str:
|
def get_device_channel_name(device: BlockDevice, block: Block | None) -> 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_block_device_name(device)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
not block
|
not block
|
||||||
|
@ -200,7 +209,7 @@ async def get_coap_context(hass: HomeAssistant) -> COAP:
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
def get_device_sleep_period(settings: dict[str, Any]) -> int:
|
def get_block_device_sleep_period(settings: dict[str, Any]) -> int:
|
||||||
"""Return the device sleep period in seconds or 0 for non sleeping devices."""
|
"""Return the device sleep period in seconds or 0 for non sleeping devices."""
|
||||||
sleep_period = 0
|
sleep_period = 0
|
||||||
|
|
||||||
|
@ -210,3 +219,21 @@ def get_device_sleep_period(settings: dict[str, Any]) -> int:
|
||||||
sleep_period *= 60 # hours to minutes
|
sleep_period *= 60 # hours to minutes
|
||||||
|
|
||||||
return sleep_period * 60 # minutes to seconds
|
return sleep_period * 60 # minutes to seconds
|
||||||
|
|
||||||
|
|
||||||
|
def get_info_auth(info: dict[str, Any]) -> bool:
|
||||||
|
"""Return true if device has authorization enabled."""
|
||||||
|
return cast(bool, info.get("auth") or info.get("auth_en"))
|
||||||
|
|
||||||
|
|
||||||
|
def get_info_gen(info: dict[str, Any]) -> int:
|
||||||
|
"""Return the device generation from shelly info."""
|
||||||
|
return int(info.get("gen", 1))
|
||||||
|
|
||||||
|
|
||||||
|
def get_model_name(info: dict[str, Any]) -> str:
|
||||||
|
"""Return the device model name."""
|
||||||
|
if get_info_gen(info) == 2:
|
||||||
|
return cast(str, MODEL_NAMES.get(info["model"], info["model"]))
|
||||||
|
|
||||||
|
return cast(str, MODEL_NAMES.get(info["type"], info["type"]))
|
||||||
|
|
|
@ -240,7 +240,7 @@ aiopylgtv==0.4.0
|
||||||
aiorecollect==1.0.8
|
aiorecollect==1.0.8
|
||||||
|
|
||||||
# homeassistant.components.shelly
|
# homeassistant.components.shelly
|
||||||
aioshelly==1.0.0
|
aioshelly==1.0.1
|
||||||
|
|
||||||
# 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==1.0.0
|
aioshelly==1.0.1
|
||||||
|
|
||||||
# homeassistant.components.switcher_kis
|
# homeassistant.components.switcher_kis
|
||||||
aioswitcher==2.0.5
|
aioswitcher==2.0.5
|
||||||
|
|
|
@ -20,9 +20,13 @@ DISCOVERY_INFO = {
|
||||||
"name": "shelly1pm-12345",
|
"name": "shelly1pm-12345",
|
||||||
"properties": {"id": "shelly1pm-12345"},
|
"properties": {"id": "shelly1pm-12345"},
|
||||||
}
|
}
|
||||||
|
MOCK_CONFIG = {
|
||||||
|
"wifi": {"ap": {"ssid": "Test name"}},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
async def test_form(hass):
|
@pytest.mark.parametrize("gen", [1, 2])
|
||||||
|
async def test_form(hass, gen):
|
||||||
"""Test we get the form."""
|
"""Test we get the form."""
|
||||||
await setup.async_setup_component(hass, "persistent_notification", {})
|
await setup.async_setup_component(hass, "persistent_notification", {})
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
@ -33,14 +37,24 @@ async def test_form(hass):
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"aioshelly.common.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, "gen": gen},
|
||||||
), patch(
|
), patch(
|
||||||
"aioshelly.block_device.BlockDevice.create",
|
"aioshelly.block_device.BlockDevice.create",
|
||||||
new=AsyncMock(
|
new=AsyncMock(
|
||||||
return_value=Mock(
|
return_value=Mock(
|
||||||
|
model="SHSW-1",
|
||||||
settings=MOCK_SETTINGS,
|
settings=MOCK_SETTINGS,
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
), patch(
|
||||||
|
"aioshelly.rpc_device.RpcDevice.create",
|
||||||
|
new=AsyncMock(
|
||||||
|
return_value=Mock(
|
||||||
|
model="SHSW-1",
|
||||||
|
config=MOCK_CONFIG,
|
||||||
|
shutdown=AsyncMock(),
|
||||||
|
)
|
||||||
|
),
|
||||||
), patch(
|
), patch(
|
||||||
"homeassistant.components.shelly.async_setup", return_value=True
|
"homeassistant.components.shelly.async_setup", return_value=True
|
||||||
) as mock_setup, patch(
|
) as mock_setup, patch(
|
||||||
|
@ -59,6 +73,7 @@ async def test_form(hass):
|
||||||
"host": "1.1.1.1",
|
"host": "1.1.1.1",
|
||||||
"model": "SHSW-1",
|
"model": "SHSW-1",
|
||||||
"sleep_period": 0,
|
"sleep_period": 0,
|
||||||
|
"gen": gen,
|
||||||
}
|
}
|
||||||
assert len(mock_setup.mock_calls) == 1
|
assert len(mock_setup.mock_calls) == 1
|
||||||
assert len(mock_setup_entry.mock_calls) == 1
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
|
@ -84,6 +99,7 @@ async def test_title_without_name(hass):
|
||||||
"aioshelly.block_device.BlockDevice.create",
|
"aioshelly.block_device.BlockDevice.create",
|
||||||
new=AsyncMock(
|
new=AsyncMock(
|
||||||
return_value=Mock(
|
return_value=Mock(
|
||||||
|
model="SHSW-1",
|
||||||
settings=settings,
|
settings=settings,
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
@ -105,6 +121,7 @@ async def test_title_without_name(hass):
|
||||||
"host": "1.1.1.1",
|
"host": "1.1.1.1",
|
||||||
"model": "SHSW-1",
|
"model": "SHSW-1",
|
||||||
"sleep_period": 0,
|
"sleep_period": 0,
|
||||||
|
"gen": 1,
|
||||||
}
|
}
|
||||||
assert len(mock_setup.mock_calls) == 1
|
assert len(mock_setup.mock_calls) == 1
|
||||||
assert len(mock_setup_entry.mock_calls) == 1
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
|
@ -134,6 +151,7 @@ async def test_form_auth(hass):
|
||||||
"aioshelly.block_device.BlockDevice.create",
|
"aioshelly.block_device.BlockDevice.create",
|
||||||
new=AsyncMock(
|
new=AsyncMock(
|
||||||
return_value=Mock(
|
return_value=Mock(
|
||||||
|
model="SHSW-1",
|
||||||
settings=MOCK_SETTINGS,
|
settings=MOCK_SETTINGS,
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
@ -155,6 +173,7 @@ async def test_form_auth(hass):
|
||||||
"host": "1.1.1.1",
|
"host": "1.1.1.1",
|
||||||
"model": "SHSW-1",
|
"model": "SHSW-1",
|
||||||
"sleep_period": 0,
|
"sleep_period": 0,
|
||||||
|
"gen": 1,
|
||||||
"username": "test username",
|
"username": "test username",
|
||||||
"password": "test password",
|
"password": "test password",
|
||||||
}
|
}
|
||||||
|
@ -260,6 +279,7 @@ async def test_user_setup_ignored_device(hass):
|
||||||
"aioshelly.block_device.BlockDevice.create",
|
"aioshelly.block_device.BlockDevice.create",
|
||||||
new=AsyncMock(
|
new=AsyncMock(
|
||||||
return_value=Mock(
|
return_value=Mock(
|
||||||
|
model="SHSW-1",
|
||||||
settings=settings,
|
settings=settings,
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
@ -350,6 +370,7 @@ async def test_zeroconf(hass):
|
||||||
"aioshelly.block_device.BlockDevice.create",
|
"aioshelly.block_device.BlockDevice.create",
|
||||||
new=AsyncMock(
|
new=AsyncMock(
|
||||||
return_value=Mock(
|
return_value=Mock(
|
||||||
|
model="SHSW-1",
|
||||||
settings=MOCK_SETTINGS,
|
settings=MOCK_SETTINGS,
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
@ -386,6 +407,7 @@ async def test_zeroconf(hass):
|
||||||
"host": "1.1.1.1",
|
"host": "1.1.1.1",
|
||||||
"model": "SHSW-1",
|
"model": "SHSW-1",
|
||||||
"sleep_period": 0,
|
"sleep_period": 0,
|
||||||
|
"gen": 1,
|
||||||
}
|
}
|
||||||
assert len(mock_setup.mock_calls) == 1
|
assert len(mock_setup.mock_calls) == 1
|
||||||
assert len(mock_setup_entry.mock_calls) == 1
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
|
@ -407,6 +429,7 @@ async def test_zeroconf_sleeping_device(hass):
|
||||||
"aioshelly.block_device.BlockDevice.create",
|
"aioshelly.block_device.BlockDevice.create",
|
||||||
new=AsyncMock(
|
new=AsyncMock(
|
||||||
return_value=Mock(
|
return_value=Mock(
|
||||||
|
model="SHSW-1",
|
||||||
settings={
|
settings={
|
||||||
"name": "Test name",
|
"name": "Test name",
|
||||||
"device": {
|
"device": {
|
||||||
|
@ -450,6 +473,7 @@ async def test_zeroconf_sleeping_device(hass):
|
||||||
"host": "1.1.1.1",
|
"host": "1.1.1.1",
|
||||||
"model": "SHSW-1",
|
"model": "SHSW-1",
|
||||||
"sleep_period": 600,
|
"sleep_period": 600,
|
||||||
|
"gen": 1,
|
||||||
}
|
}
|
||||||
assert len(mock_setup.mock_calls) == 1
|
assert len(mock_setup.mock_calls) == 1
|
||||||
assert len(mock_setup_entry.mock_calls) == 1
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
|
@ -560,6 +584,7 @@ async def test_zeroconf_require_auth(hass):
|
||||||
"aioshelly.block_device.BlockDevice.create",
|
"aioshelly.block_device.BlockDevice.create",
|
||||||
new=AsyncMock(
|
new=AsyncMock(
|
||||||
return_value=Mock(
|
return_value=Mock(
|
||||||
|
model="SHSW-1",
|
||||||
settings=MOCK_SETTINGS,
|
settings=MOCK_SETTINGS,
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
@ -581,6 +606,7 @@ async def test_zeroconf_require_auth(hass):
|
||||||
"host": "1.1.1.1",
|
"host": "1.1.1.1",
|
||||||
"model": "SHSW-1",
|
"model": "SHSW-1",
|
||||||
"sleep_period": 0,
|
"sleep_period": 0,
|
||||||
|
"gen": 1,
|
||||||
"username": "test username",
|
"username": "test username",
|
||||||
"password": "test password",
|
"password": "test password",
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue