Add support for Shelly RPC devices custom TCP port (#110860)
* First coding * add port to config_entry + gen1 not supported msg * fix async_step_credentials * strings * fix reauth * fix visit device link * increased MINOR_VERSION * apply review comments * align to latest aioshelly * missing tests * introduce port parameter * update tests * remove leftover * remove "port" data_description key * missing key * apply review comments * apply more review comments * Add tests * apply review comment * apply review comment (part 2) * description update * fine tuning description * fix test patching --------- Co-authored-by: Shay Levy <levyshay1@gmail.com>
This commit is contained in:
parent
8141a246b0
commit
8728057b1b
7 changed files with 156 additions and 32 deletions
|
@ -56,6 +56,7 @@ from .utils import (
|
|||
get_block_device_sleep_period,
|
||||
get_coap_context,
|
||||
get_device_entry_gen,
|
||||
get_http_port,
|
||||
get_rpc_device_wakeup_period,
|
||||
get_ws_context,
|
||||
)
|
||||
|
@ -249,6 +250,7 @@ async def _async_setup_rpc_entry(hass: HomeAssistant, entry: ConfigEntry) -> boo
|
|||
entry.data.get(CONF_USERNAME),
|
||||
entry.data.get(CONF_PASSWORD),
|
||||
device_mac=entry.unique_id,
|
||||
port=get_http_port(entry.data),
|
||||
)
|
||||
|
||||
ws_context = await get_ws_context(hass)
|
||||
|
|
|
@ -7,8 +7,9 @@ from typing import Any, Final
|
|||
|
||||
from aioshelly.block_device import BlockDevice
|
||||
from aioshelly.common import ConnectionOptions, get_info
|
||||
from aioshelly.const import BLOCK_GENERATIONS, RPC_GENERATIONS
|
||||
from aioshelly.const import BLOCK_GENERATIONS, DEFAULT_HTTP_PORT, RPC_GENERATIONS
|
||||
from aioshelly.exceptions import (
|
||||
CustomPortNotSupported,
|
||||
DeviceConnectionError,
|
||||
FirmwareUnsupported,
|
||||
InvalidAuthError,
|
||||
|
@ -23,7 +24,7 @@ from homeassistant.config_entries import (
|
|||
ConfigFlowResult,
|
||||
OptionsFlow,
|
||||
)
|
||||
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
|
||||
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_PORT, CONF_USERNAME
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.helpers.selector import SelectSelector, SelectSelectorConfig
|
||||
|
@ -42,6 +43,7 @@ from .utils import (
|
|||
get_block_device_sleep_period,
|
||||
get_coap_context,
|
||||
get_device_entry_gen,
|
||||
get_http_port,
|
||||
get_info_auth,
|
||||
get_info_gen,
|
||||
get_model_name,
|
||||
|
@ -50,7 +52,12 @@ from .utils import (
|
|||
mac_address_from_name,
|
||||
)
|
||||
|
||||
HOST_SCHEMA: Final = vol.Schema({vol.Required(CONF_HOST): str})
|
||||
CONFIG_SCHEMA: Final = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_HOST): str,
|
||||
vol.Required(CONF_PORT, default=DEFAULT_HTTP_PORT): vol.Coerce(int),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
BLE_SCANNER_OPTIONS = [
|
||||
|
@ -65,14 +72,20 @@ INTERNAL_WIFI_AP_IP = "192.168.33.1"
|
|||
async def validate_input(
|
||||
hass: HomeAssistant,
|
||||
host: str,
|
||||
port: int,
|
||||
info: dict[str, Any],
|
||||
data: dict[str, Any],
|
||||
) -> dict[str, Any]:
|
||||
"""Validate the user input allows us to connect.
|
||||
|
||||
Data has the keys from HOST_SCHEMA with values provided by the user.
|
||||
Data has the keys from CONFIG_SCHEMA with values provided by the user.
|
||||
"""
|
||||
options = ConnectionOptions(host, data.get(CONF_USERNAME), data.get(CONF_PASSWORD))
|
||||
options = ConnectionOptions(
|
||||
ip_address=host,
|
||||
username=data.get(CONF_USERNAME),
|
||||
password=data.get(CONF_PASSWORD),
|
||||
port=port,
|
||||
)
|
||||
|
||||
gen = get_info_gen(info)
|
||||
|
||||
|
@ -114,8 +127,10 @@ class ShellyConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||
"""Handle a config flow for Shelly."""
|
||||
|
||||
VERSION = 1
|
||||
MINOR_VERSION = 2
|
||||
|
||||
host: str = ""
|
||||
port: int = DEFAULT_HTTP_PORT
|
||||
info: dict[str, Any] = {}
|
||||
device_info: dict[str, Any] = {}
|
||||
entry: ConfigEntry | None = None
|
||||
|
@ -126,9 +141,10 @@ class ShellyConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||
"""Handle the initial step."""
|
||||
errors: dict[str, str] = {}
|
||||
if user_input is not None:
|
||||
host: str = user_input[CONF_HOST]
|
||||
host = user_input[CONF_HOST]
|
||||
port = user_input[CONF_PORT]
|
||||
try:
|
||||
self.info = await self._async_get_info(host)
|
||||
self.info = await self._async_get_info(host, port)
|
||||
except DeviceConnectionError:
|
||||
errors["base"] = "cannot_connect"
|
||||
except FirmwareUnsupported:
|
||||
|
@ -140,15 +156,18 @@ class ShellyConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||
await self.async_set_unique_id(self.info["mac"])
|
||||
self._abort_if_unique_id_configured({CONF_HOST: host})
|
||||
self.host = host
|
||||
self.port = port
|
||||
if get_info_auth(self.info):
|
||||
return await self.async_step_credentials()
|
||||
|
||||
try:
|
||||
device_info = await validate_input(
|
||||
self.hass, self.host, self.info, {}
|
||||
self.hass, host, port, self.info, {}
|
||||
)
|
||||
except DeviceConnectionError:
|
||||
errors["base"] = "cannot_connect"
|
||||
except CustomPortNotSupported:
|
||||
errors["base"] = "custom_port_not_supported"
|
||||
except Exception: # pylint: disable=broad-except
|
||||
LOGGER.exception("Unexpected exception")
|
||||
errors["base"] = "unknown"
|
||||
|
@ -157,7 +176,8 @@ class ShellyConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||
return self.async_create_entry(
|
||||
title=device_info["title"],
|
||||
data={
|
||||
**user_input,
|
||||
CONF_HOST: user_input[CONF_HOST],
|
||||
CONF_PORT: user_input[CONF_PORT],
|
||||
CONF_SLEEP_PERIOD: device_info[CONF_SLEEP_PERIOD],
|
||||
"model": device_info["model"],
|
||||
CONF_GEN: device_info[CONF_GEN],
|
||||
|
@ -166,7 +186,7 @@ class ShellyConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||
errors["base"] = "firmware_not_fully_provisioned"
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="user", data_schema=HOST_SCHEMA, errors=errors
|
||||
step_id="user", data_schema=CONFIG_SCHEMA, errors=errors
|
||||
)
|
||||
|
||||
async def async_step_credentials(
|
||||
|
@ -179,7 +199,7 @@ class ShellyConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||
user_input[CONF_USERNAME] = "admin"
|
||||
try:
|
||||
device_info = await validate_input(
|
||||
self.hass, self.host, self.info, user_input
|
||||
self.hass, self.host, self.port, self.info, user_input
|
||||
)
|
||||
except InvalidAuthError:
|
||||
errors["base"] = "invalid_auth"
|
||||
|
@ -195,6 +215,7 @@ class ShellyConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||
data={
|
||||
**user_input,
|
||||
CONF_HOST: self.host,
|
||||
CONF_PORT: self.port,
|
||||
CONF_SLEEP_PERIOD: device_info[CONF_SLEEP_PERIOD],
|
||||
"model": device_info["model"],
|
||||
CONF_GEN: device_info[CONF_GEN],
|
||||
|
@ -254,7 +275,9 @@ class ShellyConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||
await self._async_discovered_mac(mac, host)
|
||||
|
||||
try:
|
||||
self.info = await self._async_get_info(host)
|
||||
# Devices behind range extender doesn't generate zeroconf packets
|
||||
# so port is always the default one
|
||||
self.info = await self._async_get_info(host, DEFAULT_HTTP_PORT)
|
||||
except DeviceConnectionError:
|
||||
return self.async_abort(reason="cannot_connect")
|
||||
except FirmwareUnsupported:
|
||||
|
@ -277,7 +300,9 @@ class ShellyConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||
return await self.async_step_credentials()
|
||||
|
||||
try:
|
||||
self.device_info = await validate_input(self.hass, self.host, self.info, {})
|
||||
self.device_info = await validate_input(
|
||||
self.hass, self.host, self.port, self.info, {}
|
||||
)
|
||||
except DeviceConnectionError:
|
||||
return self.async_abort(reason="cannot_connect")
|
||||
|
||||
|
@ -329,17 +354,18 @@ class ShellyConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||
errors: dict[str, str] = {}
|
||||
assert self.entry is not None
|
||||
host = self.entry.data[CONF_HOST]
|
||||
port = get_http_port(self.entry.data)
|
||||
|
||||
if user_input is not None:
|
||||
try:
|
||||
info = await self._async_get_info(host)
|
||||
info = await self._async_get_info(host, port)
|
||||
except (DeviceConnectionError, InvalidAuthError, FirmwareUnsupported):
|
||||
return self.async_abort(reason="reauth_unsuccessful")
|
||||
|
||||
if get_device_entry_gen(self.entry) != 1:
|
||||
user_input[CONF_USERNAME] = "admin"
|
||||
try:
|
||||
await validate_input(self.hass, host, info, user_input)
|
||||
await validate_input(self.hass, host, port, info, user_input)
|
||||
except (DeviceConnectionError, InvalidAuthError, FirmwareUnsupported):
|
||||
return self.async_abort(reason="reauth_unsuccessful")
|
||||
|
||||
|
@ -361,9 +387,9 @@ class ShellyConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||
errors=errors,
|
||||
)
|
||||
|
||||
async def _async_get_info(self, host: str) -> dict[str, Any]:
|
||||
async def _async_get_info(self, host: str, port: int) -> dict[str, Any]:
|
||||
"""Get info from shelly device."""
|
||||
return await get_info(async_get_clientsession(self.hass), host)
|
||||
return await get_info(async_get_clientsession(self.hass), host, port=port)
|
||||
|
||||
@staticmethod
|
||||
@callback
|
||||
|
|
|
@ -59,6 +59,7 @@ from .const import (
|
|||
)
|
||||
from .utils import (
|
||||
get_device_entry_gen,
|
||||
get_http_port,
|
||||
get_rpc_device_wakeup_period,
|
||||
update_device_fw_info,
|
||||
)
|
||||
|
@ -140,7 +141,7 @@ class ShellyCoordinatorBase(DataUpdateCoordinator[None], Generic[_DeviceT]):
|
|||
model=MODEL_NAMES.get(self.model, self.model),
|
||||
sw_version=self.sw_version,
|
||||
hw_version=f"gen{get_device_entry_gen(self.entry)} ({self.model})",
|
||||
configuration_url=f"http://{self.entry.data[CONF_HOST]}",
|
||||
configuration_url=f"http://{self.entry.data[CONF_HOST]}:{get_http_port(self.entry.data)}",
|
||||
)
|
||||
self.device_id = device_entry.id
|
||||
|
||||
|
|
|
@ -5,10 +5,12 @@
|
|||
"user": {
|
||||
"description": "Before setup, battery-powered devices must be woken up, you can now wake the device up using a button on it.",
|
||||
"data": {
|
||||
"host": "[%key:common::config_flow::data::host%]"
|
||||
"host": "[%key:common::config_flow::data::host%]",
|
||||
"port": "[%key:common::config_flow::data::port%]"
|
||||
},
|
||||
"data_description": {
|
||||
"host": "The hostname or IP address of the Shelly device to connect to."
|
||||
"host": "The hostname or IP address of the Shelly device to connect to.",
|
||||
"port": "The TCP port of the Shelly device to connect to (Gen2+)."
|
||||
}
|
||||
},
|
||||
"credentials": {
|
||||
|
@ -31,7 +33,8 @@
|
|||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
|
||||
"unknown": "[%key:common::config_flow::error::unknown%]",
|
||||
"firmware_not_fully_provisioned": "Device not fully provisioned. Please contact Shelly support"
|
||||
"firmware_not_fully_provisioned": "Device not fully provisioned. Please contact Shelly support",
|
||||
"custom_port_not_supported": "Gen1 device does not support custom port."
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
|
||||
|
|
|
@ -4,6 +4,7 @@ from __future__ import annotations
|
|||
|
||||
from datetime import datetime, timedelta
|
||||
from ipaddress import IPv4Address
|
||||
from types import MappingProxyType
|
||||
from typing import Any, cast
|
||||
|
||||
from aiohttp.web import Request, WebSocketResponse
|
||||
|
@ -11,6 +12,7 @@ from aioshelly.block_device import COAP, Block, BlockDevice
|
|||
from aioshelly.const import (
|
||||
BLOCK_GENERATIONS,
|
||||
DEFAULT_COAP_PORT,
|
||||
DEFAULT_HTTP_PORT,
|
||||
MODEL_1L,
|
||||
MODEL_DIMMER,
|
||||
MODEL_DIMMER_2,
|
||||
|
@ -24,7 +26,7 @@ from aioshelly.rpc_device import RpcDevice, WsServer
|
|||
from homeassistant.components import network
|
||||
from homeassistant.components.http import HomeAssistantView
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
|
||||
from homeassistant.const import CONF_PORT, EVENT_HOMEASSISTANT_STOP
|
||||
from homeassistant.core import Event, HomeAssistant, callback
|
||||
from homeassistant.helpers import issue_registry as ir, singleton
|
||||
from homeassistant.helpers.device_registry import (
|
||||
|
@ -473,3 +475,8 @@ def is_rpc_wifi_stations_disabled(
|
|||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def get_http_port(data: MappingProxyType[str, Any]) -> int:
|
||||
"""Get port from config entry data."""
|
||||
return cast(int, data.get(CONF_PORT, DEFAULT_HTTP_PORT))
|
||||
|
|
|
@ -6,8 +6,9 @@ from ipaddress import ip_address
|
|||
from typing import Any
|
||||
from unittest.mock import AsyncMock, Mock, patch
|
||||
|
||||
from aioshelly.const import MODEL_1, MODEL_PLUS_2PM
|
||||
from aioshelly.const import DEFAULT_HTTP_PORT, MODEL_1, MODEL_PLUS_2PM
|
||||
from aioshelly.exceptions import (
|
||||
CustomPortNotSupported,
|
||||
DeviceConnectionError,
|
||||
FirmwareUnsupported,
|
||||
InvalidAuthError,
|
||||
|
@ -54,17 +55,18 @@ DISCOVERY_INFO_WITH_MAC = zeroconf.ZeroconfServiceInfo(
|
|||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("gen", "model"),
|
||||
("gen", "model", "port"),
|
||||
[
|
||||
(1, MODEL_1),
|
||||
(2, MODEL_PLUS_2PM),
|
||||
(3, MODEL_PLUS_2PM),
|
||||
(1, MODEL_1, DEFAULT_HTTP_PORT),
|
||||
(2, MODEL_PLUS_2PM, DEFAULT_HTTP_PORT),
|
||||
(3, MODEL_PLUS_2PM, 11200),
|
||||
],
|
||||
)
|
||||
async def test_form(
|
||||
hass: HomeAssistant,
|
||||
gen: int,
|
||||
model: str,
|
||||
port: int,
|
||||
mock_block_device: Mock,
|
||||
mock_rpc_device: Mock,
|
||||
) -> None:
|
||||
|
@ -72,12 +74,18 @@ async def test_form(
|
|||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
assert result["type"] == "form"
|
||||
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
||||
assert result["errors"] == {}
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.shelly.config_flow.get_info",
|
||||
return_value={"mac": "test-mac", "type": MODEL_1, "auth": False, "gen": gen},
|
||||
return_value={
|
||||
"mac": "test-mac",
|
||||
"type": MODEL_1,
|
||||
"auth": False,
|
||||
"gen": gen,
|
||||
"port": port,
|
||||
},
|
||||
), patch(
|
||||
"homeassistant.components.shelly.async_setup", return_value=True
|
||||
) as mock_setup, patch(
|
||||
|
@ -86,7 +94,7 @@ async def test_form(
|
|||
) as mock_setup_entry:
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{"host": "1.1.1.1"},
|
||||
{"host": "1.1.1.1", "port": port},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
@ -94,6 +102,7 @@ async def test_form(
|
|||
assert result2["title"] == "Test name"
|
||||
assert result2["data"] == {
|
||||
"host": "1.1.1.1",
|
||||
"port": port,
|
||||
"model": model,
|
||||
"sleep_period": 0,
|
||||
"gen": gen,
|
||||
|
@ -102,6 +111,33 @@ async def test_form(
|
|||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
async def test_form_gen1_custom_port(
|
||||
hass: HomeAssistant,
|
||||
mock_block_device: Mock,
|
||||
) -> None:
|
||||
"""Test we get the form."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
||||
assert result["errors"] == {}
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.shelly.config_flow.get_info",
|
||||
return_value={"mac": "test-mac", "type": MODEL_1, "gen": 1},
|
||||
), patch(
|
||||
"aioshelly.block_device.BlockDevice.create",
|
||||
side_effect=CustomPortNotSupported,
|
||||
):
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{"host": "1.1.1.1", "port": "1100"},
|
||||
)
|
||||
|
||||
assert result2["type"] == data_entry_flow.FlowResultType.FORM
|
||||
assert result2["errors"]["base"] == "custom_port_not_supported"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("gen", "model", "user_input", "username"),
|
||||
[
|
||||
|
@ -168,6 +204,7 @@ async def test_form_auth(
|
|||
assert result3["title"] == "Test name"
|
||||
assert result3["data"] == {
|
||||
"host": "1.1.1.1",
|
||||
"port": DEFAULT_HTTP_PORT,
|
||||
"model": model,
|
||||
"sleep_period": 0,
|
||||
"gen": gen,
|
||||
|
@ -757,6 +794,7 @@ async def test_zeroconf_require_auth(
|
|||
assert result2["title"] == "Test name"
|
||||
assert result2["data"] == {
|
||||
"host": "1.1.1.1",
|
||||
"port": DEFAULT_HTTP_PORT,
|
||||
"model": MODEL_1,
|
||||
"sleep_period": 0,
|
||||
"gen": 1,
|
||||
|
@ -1126,7 +1164,7 @@ async def test_sleeping_device_gen2_with_new_firmware(
|
|||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
assert result["type"] == "form"
|
||||
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
||||
assert result["errors"] == {}
|
||||
|
||||
with patch(
|
||||
|
@ -1144,6 +1182,7 @@ async def test_sleeping_device_gen2_with_new_firmware(
|
|||
|
||||
assert result["data"] == {
|
||||
"host": "1.1.1.1",
|
||||
"port": DEFAULT_HTTP_PORT,
|
||||
"model": MODEL_PLUS_2PM,
|
||||
"sleep_period": 666,
|
||||
"gen": 2,
|
||||
|
|
|
@ -4,6 +4,8 @@ from ipaddress import IPv4Address
|
|||
from unittest.mock import AsyncMock, Mock, call, patch
|
||||
|
||||
from aioshelly.block_device import COAP
|
||||
from aioshelly.common import ConnectionOptions
|
||||
from aioshelly.const import MODEL_PLUS_2PM
|
||||
from aioshelly.exceptions import (
|
||||
DeviceConnectionError,
|
||||
FirmwareUnsupported,
|
||||
|
@ -16,13 +18,14 @@ from homeassistant.components.shelly.const import (
|
|||
BLOCK_EXPECTED_SLEEP_PERIOD,
|
||||
BLOCK_WRONG_SLEEP_PERIOD,
|
||||
CONF_BLE_SCANNER_MODE,
|
||||
CONF_GEN,
|
||||
CONF_SLEEP_PERIOD,
|
||||
DOMAIN,
|
||||
MODELS_WITH_WRONG_SLEEP_PERIOD,
|
||||
BLEScannerMode,
|
||||
)
|
||||
from homeassistant.config_entries import SOURCE_REAUTH, ConfigEntryState
|
||||
from homeassistant.const import STATE_ON, STATE_UNAVAILABLE
|
||||
from homeassistant.const import CONF_HOST, CONF_PORT, STATE_ON, STATE_UNAVAILABLE
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.device_registry import (
|
||||
CONNECTION_NETWORK_MAC,
|
||||
|
@ -392,6 +395,49 @@ async def test_entry_missing_gen(hass: HomeAssistant, mock_block_device: Mock) -
|
|||
assert hass.states.get("switch.test_name_channel_1").state is STATE_ON
|
||||
|
||||
|
||||
async def test_entry_missing_port(hass: HomeAssistant) -> None:
|
||||
"""Test successful Gen2 device init when port is missing in entry data."""
|
||||
data = {
|
||||
CONF_HOST: "192.168.1.37",
|
||||
CONF_SLEEP_PERIOD: 0,
|
||||
"model": MODEL_PLUS_2PM,
|
||||
CONF_GEN: 2,
|
||||
}
|
||||
entry = MockConfigEntry(domain=DOMAIN, data=data, unique_id=MOCK_MAC)
|
||||
entry.add_to_hass(hass)
|
||||
with patch(
|
||||
"homeassistant.components.shelly.RpcDevice.create", return_value=Mock()
|
||||
) as rpc_device_mock:
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert rpc_device_mock.call_args[0][2] == ConnectionOptions(
|
||||
ip_address="192.168.1.37", device_mac="123456789ABC", port=80
|
||||
)
|
||||
|
||||
|
||||
async def test_rpc_entry_custom_port(hass: HomeAssistant) -> None:
|
||||
"""Test successful Gen2 device init using custom port."""
|
||||
data = {
|
||||
CONF_HOST: "192.168.1.37",
|
||||
CONF_SLEEP_PERIOD: 0,
|
||||
"model": MODEL_PLUS_2PM,
|
||||
CONF_GEN: 2,
|
||||
CONF_PORT: 8001,
|
||||
}
|
||||
entry = MockConfigEntry(domain=DOMAIN, data=data, unique_id=MOCK_MAC)
|
||||
entry.add_to_hass(hass)
|
||||
with patch(
|
||||
"homeassistant.components.shelly.RpcDevice.create", return_value=Mock()
|
||||
) as rpc_device_mock:
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert rpc_device_mock.call_args[0][2] == ConnectionOptions(
|
||||
ip_address="192.168.1.37", device_mac="123456789ABC", port=8001
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(("model"), MODELS_WITH_WRONG_SLEEP_PERIOD)
|
||||
async def test_sleeping_block_device_wrong_sleep_period(
|
||||
hass: HomeAssistant, mock_block_device: Mock, model: str
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue