Add Airzone DHCP discovery support (#82339)
This commit is contained in:
parent
f91e250e90
commit
f1ffb25d99
7 changed files with 332 additions and 12 deletions
|
@ -1,6 +1,7 @@
|
||||||
"""Config flow for Airzone."""
|
"""Config flow for Airzone."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from aioairzone.const import DEFAULT_PORT, DEFAULT_SYSTEM_ID
|
from aioairzone.const import DEFAULT_PORT, DEFAULT_SYSTEM_ID
|
||||||
|
@ -9,13 +10,16 @@ from aioairzone.localapi import AirzoneLocalApi, ConnectionOptions
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant import config_entries
|
from homeassistant import config_entries
|
||||||
|
from homeassistant.components import dhcp
|
||||||
from homeassistant.const import CONF_HOST, CONF_ID, CONF_PORT
|
from homeassistant.const import CONF_HOST, CONF_ID, CONF_PORT
|
||||||
from homeassistant.data_entry_flow import FlowResult
|
from homeassistant.data_entry_flow import AbortFlow, FlowResult
|
||||||
from homeassistant.helpers import aiohttp_client
|
from homeassistant.helpers import aiohttp_client
|
||||||
from homeassistant.helpers.device_registry import format_mac
|
from homeassistant.helpers.device_registry import format_mac
|
||||||
|
|
||||||
from .const import DOMAIN
|
from .const import DOMAIN
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
CONFIG_SCHEMA = vol.Schema(
|
CONFIG_SCHEMA = vol.Schema(
|
||||||
{
|
{
|
||||||
vol.Required(CONF_HOST): str,
|
vol.Required(CONF_HOST): str,
|
||||||
|
@ -29,9 +33,17 @@ SYSTEM_ID_SCHEMA = CONFIG_SCHEMA.extend(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def short_mac(addr: str) -> str:
|
||||||
|
"""Convert MAC address to short address."""
|
||||||
|
return addr.replace(":", "")[-4:].upper()
|
||||||
|
|
||||||
|
|
||||||
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
"""Handle config flow for an Airzone device."""
|
"""Handle config flow for an Airzone device."""
|
||||||
|
|
||||||
|
_discovered_ip: str | None = None
|
||||||
|
_discovered_mac: str | None = None
|
||||||
|
|
||||||
async def async_step_user(
|
async def async_step_user(
|
||||||
self, user_input: dict[str, Any] | None = None
|
self, user_input: dict[str, Any] | None = None
|
||||||
) -> FlowResult:
|
) -> FlowResult:
|
||||||
|
@ -60,7 +72,9 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
errors["base"] = "cannot_connect"
|
errors["base"] = "cannot_connect"
|
||||||
else:
|
else:
|
||||||
if mac:
|
if mac:
|
||||||
await self.async_set_unique_id(format_mac(mac))
|
await self.async_set_unique_id(
|
||||||
|
format_mac(mac), raise_on_progress=False
|
||||||
|
)
|
||||||
self._abort_if_unique_id_configured(
|
self._abort_if_unique_id_configured(
|
||||||
updates={
|
updates={
|
||||||
CONF_HOST: user_input[CONF_HOST],
|
CONF_HOST: user_input[CONF_HOST],
|
||||||
|
@ -76,3 +90,78 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
data_schema=data_schema,
|
data_schema=data_schema,
|
||||||
errors=errors,
|
errors=errors,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def async_step_dhcp(self, discovery_info: dhcp.DhcpServiceInfo) -> FlowResult:
|
||||||
|
"""Handle DHCP discovery."""
|
||||||
|
self._discovered_ip = discovery_info.ip
|
||||||
|
self._discovered_mac = discovery_info.macaddress
|
||||||
|
|
||||||
|
_LOGGER.debug(
|
||||||
|
"DHCP discovery detected Airzone WebServer: %s", self._discovered_mac
|
||||||
|
)
|
||||||
|
|
||||||
|
self._async_abort_entries_match({CONF_HOST: self._discovered_ip})
|
||||||
|
|
||||||
|
await self.async_set_unique_id(format_mac(self._discovered_mac))
|
||||||
|
self._abort_if_unique_id_configured()
|
||||||
|
|
||||||
|
options = ConnectionOptions(self._discovered_ip)
|
||||||
|
airzone = AirzoneLocalApi(
|
||||||
|
aiohttp_client.async_get_clientsession(self.hass), options
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
await airzone.get_version()
|
||||||
|
except AirzoneError as err:
|
||||||
|
raise AbortFlow("cannot_connect") from err
|
||||||
|
|
||||||
|
return await self.async_step_discovered_connection()
|
||||||
|
|
||||||
|
async def async_step_discovered_connection(
|
||||||
|
self, user_input: dict[str, Any] | None = None
|
||||||
|
) -> FlowResult:
|
||||||
|
"""Confirm discovery."""
|
||||||
|
assert self._discovered_ip is not None
|
||||||
|
assert self._discovered_mac is not None
|
||||||
|
|
||||||
|
errors = {}
|
||||||
|
base_schema = {vol.Required(CONF_PORT, default=DEFAULT_PORT): int}
|
||||||
|
|
||||||
|
if user_input is not None:
|
||||||
|
airzone = AirzoneLocalApi(
|
||||||
|
aiohttp_client.async_get_clientsession(self.hass),
|
||||||
|
ConnectionOptions(
|
||||||
|
self._discovered_ip,
|
||||||
|
user_input[CONF_PORT],
|
||||||
|
user_input.get(CONF_ID, DEFAULT_SYSTEM_ID),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
mac = await airzone.validate()
|
||||||
|
except InvalidSystem:
|
||||||
|
base_schema[vol.Required(CONF_ID, default=1)] = int
|
||||||
|
errors[CONF_ID] = "invalid_system_id"
|
||||||
|
except AirzoneError:
|
||||||
|
errors["base"] = "cannot_connect"
|
||||||
|
else:
|
||||||
|
user_input[CONF_HOST] = self._discovered_ip
|
||||||
|
|
||||||
|
if mac is None:
|
||||||
|
mac = self._discovered_mac
|
||||||
|
|
||||||
|
await self.async_set_unique_id(format_mac(mac))
|
||||||
|
self._abort_if_unique_id_configured(
|
||||||
|
updates={
|
||||||
|
CONF_HOST: user_input[CONF_HOST],
|
||||||
|
CONF_PORT: user_input[CONF_PORT],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
title = f"Airzone {short_mac(mac)}"
|
||||||
|
return self.async_create_entry(title=title, data=user_input)
|
||||||
|
|
||||||
|
return self.async_show_form(
|
||||||
|
step_id="discovered_connection",
|
||||||
|
data_schema=vol.Schema(base_schema),
|
||||||
|
errors=errors,
|
||||||
|
)
|
||||||
|
|
|
@ -6,5 +6,10 @@
|
||||||
"requirements": ["aioairzone==0.5.1"],
|
"requirements": ["aioairzone==0.5.1"],
|
||||||
"codeowners": ["@Noltari"],
|
"codeowners": ["@Noltari"],
|
||||||
"iot_class": "local_polling",
|
"iot_class": "local_polling",
|
||||||
"loggers": ["aioairzone"]
|
"loggers": ["aioairzone"],
|
||||||
|
"dhcp": [
|
||||||
|
{
|
||||||
|
"macaddress": "E84F25*"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,12 +8,19 @@
|
||||||
"invalid_system_id": "Invalid Airzone System ID"
|
"invalid_system_id": "Invalid Airzone System ID"
|
||||||
},
|
},
|
||||||
"step": {
|
"step": {
|
||||||
"user": {
|
"discovered_connection": {
|
||||||
"data": {
|
"data": {
|
||||||
|
"id": "[%key:component::airzone::config::step::user::data::id%]",
|
||||||
"host": "[%key:common::config_flow::data::host%]",
|
"host": "[%key:common::config_flow::data::host%]",
|
||||||
"port": "[%key:common::config_flow::data::port%]"
|
"port": "[%key:common::config_flow::data::port%]"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"description": "Set up Airzone integration."
|
"user": {
|
||||||
|
"data": {
|
||||||
|
"id": "System ID",
|
||||||
|
"host": "[%key:common::config_flow::data::host%]",
|
||||||
|
"port": "[%key:common::config_flow::data::port%]"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,12 +8,19 @@
|
||||||
"invalid_system_id": "Invalid Airzone System ID"
|
"invalid_system_id": "Invalid Airzone System ID"
|
||||||
},
|
},
|
||||||
"step": {
|
"step": {
|
||||||
"user": {
|
"discovered_connection": {
|
||||||
"data": {
|
"data": {
|
||||||
|
"id": "System ID",
|
||||||
"host": "Host",
|
"host": "Host",
|
||||||
"port": "Port"
|
"port": "Port"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"description": "Set up Airzone integration."
|
"user": {
|
||||||
|
"data": {
|
||||||
|
"id": "System ID",
|
||||||
|
"host": "Host",
|
||||||
|
"port": "Port"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,10 @@ To update, run python3 -m script.hassfest
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
DHCP: list[dict[str, str | bool]] = [
|
DHCP: list[dict[str, str | bool]] = [
|
||||||
|
{
|
||||||
|
"domain": "airzone",
|
||||||
|
"macaddress": "E84F25*",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"domain": "august",
|
"domain": "august",
|
||||||
"hostname": "connect",
|
"hostname": "connect",
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
from aioairzone.const import API_SYSTEMS
|
from aioairzone.const import API_MAC, API_SYSTEMS
|
||||||
from aioairzone.exceptions import (
|
from aioairzone.exceptions import (
|
||||||
AirzoneError,
|
AirzoneError,
|
||||||
InvalidMethod,
|
InvalidMethod,
|
||||||
|
@ -10,16 +10,28 @@ from aioairzone.exceptions import (
|
||||||
SystemOutOfRange,
|
SystemOutOfRange,
|
||||||
)
|
)
|
||||||
|
|
||||||
from homeassistant import data_entry_flow
|
from homeassistant import config_entries, data_entry_flow
|
||||||
|
from homeassistant.components import dhcp
|
||||||
|
from homeassistant.components.airzone.config_flow import short_mac
|
||||||
from homeassistant.components.airzone.const import DOMAIN
|
from homeassistant.components.airzone.const import DOMAIN
|
||||||
from homeassistant.config_entries import SOURCE_USER, ConfigEntryState
|
from homeassistant.config_entries import SOURCE_USER, ConfigEntryState
|
||||||
from homeassistant.const import CONF_HOST, CONF_ID, CONF_PORT
|
from homeassistant.const import CONF_HOST, CONF_ID, CONF_PORT
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
from .util import CONFIG, CONFIG_ID1, HVAC_MOCK, HVAC_WEBSERVER_MOCK
|
from .util import CONFIG, CONFIG_ID1, HVAC_MOCK, HVAC_VERSION_MOCK, HVAC_WEBSERVER_MOCK
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
|
DHCP_SERVICE_INFO = dhcp.DhcpServiceInfo(
|
||||||
|
hostname="airzone",
|
||||||
|
ip="192.168.1.100",
|
||||||
|
macaddress="E84F25000000",
|
||||||
|
)
|
||||||
|
|
||||||
|
TEST_ID = 1
|
||||||
|
TEST_IP = DHCP_SERVICE_INFO.ip
|
||||||
|
TEST_PORT = 3000
|
||||||
|
|
||||||
|
|
||||||
async def test_form(hass: HomeAssistant) -> None:
|
async def test_form(hass: HomeAssistant) -> None:
|
||||||
"""Test that the form is served with valid input."""
|
"""Test that the form is served with valid input."""
|
||||||
|
@ -145,3 +157,194 @@ async def test_connection_error(hass: HomeAssistant):
|
||||||
)
|
)
|
||||||
|
|
||||||
assert result["errors"] == {"base": "cannot_connect"}
|
assert result["errors"] == {"base": "cannot_connect"}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_dhcp_flow(hass: HomeAssistant) -> None:
|
||||||
|
"""Test that DHCP discovery works."""
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.airzone.AirzoneLocalApi.get_version",
|
||||||
|
return_value=HVAC_VERSION_MOCK,
|
||||||
|
):
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
data=DHCP_SERVICE_INFO,
|
||||||
|
context={"source": config_entries.SOURCE_DHCP},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||||
|
assert result["step_id"] == "discovered_connection"
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.airzone.async_setup_entry",
|
||||||
|
return_value=True,
|
||||||
|
) as mock_setup_entry, patch(
|
||||||
|
"homeassistant.components.airzone.AirzoneLocalApi.get_hvac",
|
||||||
|
return_value=HVAC_MOCK,
|
||||||
|
), patch(
|
||||||
|
"homeassistant.components.airzone.AirzoneLocalApi.get_hvac_systems",
|
||||||
|
side_effect=SystemOutOfRange,
|
||||||
|
), patch(
|
||||||
|
"homeassistant.components.airzone.AirzoneLocalApi.get_webserver",
|
||||||
|
return_value=HVAC_WEBSERVER_MOCK,
|
||||||
|
):
|
||||||
|
result2 = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
{
|
||||||
|
CONF_PORT: TEST_PORT,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result2["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||||
|
assert result2["data"] == {
|
||||||
|
CONF_HOST: TEST_IP,
|
||||||
|
CONF_PORT: TEST_PORT,
|
||||||
|
}
|
||||||
|
|
||||||
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
|
|
||||||
|
|
||||||
|
async def test_dhcp_flow_error(hass: HomeAssistant) -> None:
|
||||||
|
"""Test that DHCP discovery fails."""
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.airzone.AirzoneLocalApi.get_version",
|
||||||
|
side_effect=AirzoneError,
|
||||||
|
):
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
data=DHCP_SERVICE_INFO,
|
||||||
|
context={"source": config_entries.SOURCE_DHCP},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == data_entry_flow.FlowResultType.ABORT
|
||||||
|
assert result["reason"] == "cannot_connect"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_dhcp_connection_error(hass: HomeAssistant):
|
||||||
|
"""Test DHCP connection to host error."""
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.airzone.AirzoneLocalApi.get_version",
|
||||||
|
return_value=HVAC_VERSION_MOCK,
|
||||||
|
):
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
data=DHCP_SERVICE_INFO,
|
||||||
|
context={"source": config_entries.SOURCE_DHCP},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||||
|
assert result["step_id"] == "discovered_connection"
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.airzone.AirzoneLocalApi.validate",
|
||||||
|
side_effect=AirzoneError,
|
||||||
|
):
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
{
|
||||||
|
CONF_PORT: 3001,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["errors"] == {"base": "cannot_connect"}
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.airzone.async_setup_entry",
|
||||||
|
return_value=True,
|
||||||
|
) as mock_setup_entry, patch(
|
||||||
|
"homeassistant.components.airzone.AirzoneLocalApi.get_hvac",
|
||||||
|
return_value=HVAC_MOCK,
|
||||||
|
), patch(
|
||||||
|
"homeassistant.components.airzone.AirzoneLocalApi.get_hvac_systems",
|
||||||
|
side_effect=SystemOutOfRange,
|
||||||
|
), patch(
|
||||||
|
"homeassistant.components.airzone.AirzoneLocalApi.get_webserver",
|
||||||
|
return_value=HVAC_WEBSERVER_MOCK,
|
||||||
|
):
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
{
|
||||||
|
CONF_PORT: TEST_PORT,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
conf_entries = hass.config_entries.async_entries(DOMAIN)
|
||||||
|
entry = conf_entries[0]
|
||||||
|
assert entry.state is ConfigEntryState.LOADED
|
||||||
|
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||||
|
assert result["title"] == f"Airzone {short_mac(HVAC_WEBSERVER_MOCK[API_MAC])}"
|
||||||
|
assert result["data"][CONF_HOST] == TEST_IP
|
||||||
|
assert result["data"][CONF_PORT] == TEST_PORT
|
||||||
|
|
||||||
|
mock_setup_entry.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
|
async def test_dhcp_invalid_system_id(hass: HomeAssistant) -> None:
|
||||||
|
"""Test Invalid System ID 0."""
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.airzone.AirzoneLocalApi.get_version",
|
||||||
|
return_value=HVAC_VERSION_MOCK,
|
||||||
|
):
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
data=DHCP_SERVICE_INFO,
|
||||||
|
context={"source": config_entries.SOURCE_DHCP},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||||
|
assert result["step_id"] == "discovered_connection"
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.airzone.async_setup_entry",
|
||||||
|
return_value=True,
|
||||||
|
) as mock_setup_entry, patch(
|
||||||
|
"homeassistant.components.airzone.AirzoneLocalApi.get_hvac",
|
||||||
|
side_effect=InvalidSystem,
|
||||||
|
) as mock_hvac, patch(
|
||||||
|
"homeassistant.components.airzone.AirzoneLocalApi.get_hvac_systems",
|
||||||
|
side_effect=SystemOutOfRange,
|
||||||
|
), patch(
|
||||||
|
"homeassistant.components.airzone.AirzoneLocalApi.get_webserver",
|
||||||
|
side_effect=InvalidMethod,
|
||||||
|
):
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
{
|
||||||
|
CONF_PORT: TEST_PORT,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||||
|
assert result["step_id"] == "discovered_connection"
|
||||||
|
assert result["errors"] == {CONF_ID: "invalid_system_id"}
|
||||||
|
|
||||||
|
mock_hvac.return_value = HVAC_MOCK[API_SYSTEMS][0]
|
||||||
|
mock_hvac.side_effect = None
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
{
|
||||||
|
CONF_PORT: TEST_PORT,
|
||||||
|
CONF_ID: TEST_ID,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
conf_entries = hass.config_entries.async_entries(DOMAIN)
|
||||||
|
entry = conf_entries[0]
|
||||||
|
assert entry.state is ConfigEntryState.LOADED
|
||||||
|
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||||
|
assert result["title"] == f"Airzone {short_mac(DHCP_SERVICE_INFO.macaddress)}"
|
||||||
|
assert result["data"][CONF_HOST] == TEST_IP
|
||||||
|
assert result["data"][CONF_PORT] == TEST_PORT
|
||||||
|
assert result["data"][CONF_ID] == TEST_ID
|
||||||
|
|
||||||
|
mock_setup_entry.assert_called_once()
|
||||||
|
|
|
@ -30,6 +30,7 @@ from aioairzone.const import (
|
||||||
API_THERMOS_RADIO,
|
API_THERMOS_RADIO,
|
||||||
API_THERMOS_TYPE,
|
API_THERMOS_TYPE,
|
||||||
API_UNITS,
|
API_UNITS,
|
||||||
|
API_VERSION,
|
||||||
API_WIFI_CHANNEL,
|
API_WIFI_CHANNEL,
|
||||||
API_WIFI_RSSI,
|
API_WIFI_RSSI,
|
||||||
API_ZONE_ID,
|
API_ZONE_ID,
|
||||||
|
@ -191,6 +192,10 @@ HVAC_SYSTEMS_MOCK = {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HVAC_VERSION_MOCK = {
|
||||||
|
API_VERSION: "1.62",
|
||||||
|
}
|
||||||
|
|
||||||
HVAC_WEBSERVER_MOCK = {
|
HVAC_WEBSERVER_MOCK = {
|
||||||
API_MAC: "11:22:33:44:55:66",
|
API_MAC: "11:22:33:44:55:66",
|
||||||
API_WIFI_CHANNEL: 6,
|
API_WIFI_CHANNEL: 6,
|
||||||
|
|
Loading…
Add table
Reference in a new issue