Add Advantage Air Integration (#40159)

Co-authored-by: Robert Svensson <Kane610@users.noreply.github.com>
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
Brett 2020-10-12 21:22:15 +10:00 committed by GitHub
parent cd21786536
commit 3e41c682f4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 930 additions and 0 deletions

View File

@ -17,6 +17,7 @@ homeassistant/components/abode/* @shred86
homeassistant/components/accuweather/* @bieniu
homeassistant/components/acmeda/* @atmurray
homeassistant/components/adguard/* @frenck
homeassistant/components/advantage_air/* @Bre77
homeassistant/components/agent_dvr/* @ispysoftware
homeassistant/components/airly/* @bieniu
homeassistant/components/airvisual/* @bachya

View File

@ -0,0 +1,74 @@
"""Advantage Air climate integration."""
from datetime import timedelta
import logging
from advantage_air import ApiError, advantage_air
from homeassistant.const import CONF_IP_ADDRESS, CONF_PORT
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import ADVANTAGE_AIR_RETRY, DOMAIN
ADVANTAGE_AIR_SYNC_INTERVAL = 15
ADVANTAGE_AIR_PLATFORMS = ["climate"]
_LOGGER = logging.getLogger(__name__)
async def async_setup(hass, config):
"""Set up AdvantageAir."""
hass.data[DOMAIN] = {}
return True
async def async_setup_entry(hass, config_entry):
"""Set up AdvantageAir Config."""
ip_address = config_entry.data[CONF_IP_ADDRESS]
port = config_entry.data[CONF_PORT]
api = advantage_air(
ip_address,
port=port,
session=async_get_clientsession(hass),
retry=ADVANTAGE_AIR_RETRY,
)
async def async_get():
try:
return await api.async_get()
except ApiError as err:
raise UpdateFailed(err) from err
coordinator = DataUpdateCoordinator(
hass,
_LOGGER,
name="Advantage Air",
update_method=async_get,
update_interval=timedelta(seconds=ADVANTAGE_AIR_SYNC_INTERVAL),
)
async def async_change(change):
try:
if await api.async_change(change):
await coordinator.async_refresh()
except ApiError as err:
_LOGGER.warning(err)
await coordinator.async_refresh()
if not coordinator.data:
raise ConfigEntryNotReady
hass.data[DOMAIN][config_entry.entry_id] = {
"coordinator": coordinator,
"async_change": async_change,
}
for platform in ADVANTAGE_AIR_PLATFORMS:
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(config_entry, platform)
)
return True

View File

@ -0,0 +1,297 @@
"""Climate platform for Advantage Air integration."""
import logging
from homeassistant.components.climate import ClimateEntity
from homeassistant.components.climate.const import (
FAN_AUTO,
FAN_HIGH,
FAN_LOW,
FAN_MEDIUM,
FAN_OFF,
HVAC_MODE_COOL,
HVAC_MODE_DRY,
HVAC_MODE_FAN_ONLY,
HVAC_MODE_HEAT,
HVAC_MODE_OFF,
SUPPORT_FAN_MODE,
SUPPORT_TARGET_TEMPERATURE,
)
from homeassistant.const import ATTR_TEMPERATURE, PRECISION_WHOLE, TEMP_CELSIUS
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import (
ADVANTAGE_AIR_STATE_CLOSE,
ADVANTAGE_AIR_STATE_OFF,
ADVANTAGE_AIR_STATE_ON,
ADVANTAGE_AIR_STATE_OPEN,
DOMAIN,
)
ADVANTAGE_AIR_HVAC_MODES = {
"heat": HVAC_MODE_HEAT,
"cool": HVAC_MODE_COOL,
"vent": HVAC_MODE_FAN_ONLY,
"dry": HVAC_MODE_DRY,
}
HASS_HVAC_MODES = {v: k for k, v in ADVANTAGE_AIR_HVAC_MODES.items()}
ADVANTAGE_AIR_FAN_MODES = {
"auto": FAN_AUTO,
"low": FAN_LOW,
"medium": FAN_MEDIUM,
"high": FAN_HIGH,
}
HASS_FAN_MODES = {v: k for k, v in ADVANTAGE_AIR_FAN_MODES.items()}
FAN_SPEEDS = {FAN_LOW: 30, FAN_MEDIUM: 60, FAN_HIGH: 100}
AC_HVAC_MODES = [
HVAC_MODE_OFF,
HVAC_MODE_COOL,
HVAC_MODE_HEAT,
HVAC_MODE_FAN_ONLY,
HVAC_MODE_DRY,
]
ZONE_HVAC_MODES = [HVAC_MODE_OFF, HVAC_MODE_FAN_ONLY]
_LOGGER = logging.getLogger(__name__)
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up AdvantageAir climate platform."""
instance = hass.data[DOMAIN][config_entry.entry_id]
entities = []
for ac_key in instance["coordinator"].data["aircons"]:
entities.append(AdvantageAirAC(instance, ac_key))
for zone_key in instance["coordinator"].data["aircons"][ac_key]["zones"]:
# Only add zone climate control when zone is in temperature control
if (
instance["coordinator"].data["aircons"][ac_key]["zones"][zone_key][
"type"
]
!= 0
):
entities.append(AdvantageAirZone(instance, ac_key, zone_key))
async_add_entities(entities)
class AdvantageAirClimateEntity(CoordinatorEntity, ClimateEntity):
"""AdvantageAir Climate class."""
def __init__(self, instance):
"""Initialize the base Advantage Air climate entity."""
super().__init__(instance["coordinator"])
self.async_change = instance["async_change"]
@property
def temperature_unit(self):
"""Return the temperature unit."""
return TEMP_CELSIUS
@property
def target_temperature_step(self):
"""Return the supported temperature step."""
return PRECISION_WHOLE
@property
def max_temp(self):
"""Return the maximum supported temperature."""
return 32
@property
def min_temp(self):
"""Return the minimum supported temperature."""
return 16
@property
def device_info(self):
"""Return parent device information."""
return {
"identifiers": {(DOMAIN, self.coordinator.data["system"]["rid"])},
"name": self.coordinator.data["system"]["name"],
"manufacturer": "Advantage Air",
"model": self.coordinator.data["system"]["sysType"],
"sw_version": self.coordinator.data["system"]["myAppRev"],
}
class AdvantageAirAC(AdvantageAirClimateEntity):
"""AdvantageAir AC unit."""
def __init__(self, instance, ac_key):
"""Initialize the Advantage Air AC climate entity."""
super().__init__(instance)
self.ac_key = ac_key
@property
def name(self):
"""Return the name."""
return self.coordinator.data["aircons"][self.ac_key]["info"]["name"]
@property
def unique_id(self):
"""Return a unique id."""
return f'{self.coordinator.data["system"]["rid"]}-{self.ac_key}'
@property
def target_temperature(self):
"""Return the current target temperature."""
return self.coordinator.data["aircons"][self.ac_key]["info"]["setTemp"]
@property
def hvac_mode(self):
"""Return the current HVAC modes."""
if (
self.coordinator.data["aircons"][self.ac_key]["info"]["state"]
== ADVANTAGE_AIR_STATE_ON
):
return ADVANTAGE_AIR_HVAC_MODES.get(
self.coordinator.data["aircons"][self.ac_key]["info"]["mode"]
)
return HVAC_MODE_OFF
@property
def hvac_modes(self):
"""Return the supported HVAC modes."""
return AC_HVAC_MODES
@property
def fan_mode(self):
"""Return the current fan modes."""
return ADVANTAGE_AIR_FAN_MODES.get(
self.coordinator.data["aircons"][self.ac_key]["info"]["fan"], FAN_OFF
)
@property
def fan_modes(self):
"""Return the supported fan modes."""
return [FAN_AUTO, FAN_LOW, FAN_MEDIUM, FAN_HIGH]
@property
def supported_features(self):
"""Return the supported features."""
return SUPPORT_TARGET_TEMPERATURE | SUPPORT_FAN_MODE
@property
def device_state_attributes(self):
"""Return additional attributes about AC unit."""
return self.coordinator.data["aircons"][self.ac_key]["info"]
async def async_set_hvac_mode(self, hvac_mode):
"""Set the HVAC Mode and State."""
if hvac_mode == HVAC_MODE_OFF:
await self.async_change(
{self.ac_key: {"info": {"state": ADVANTAGE_AIR_STATE_OFF}}}
)
else:
await self.async_change(
{
self.ac_key: {
"info": {
"state": ADVANTAGE_AIR_STATE_ON,
"mode": HASS_HVAC_MODES.get(hvac_mode),
}
}
}
)
async def async_set_fan_mode(self, fan_mode):
"""Set the Fan Mode."""
await self.async_change(
{self.ac_key: {"info": {"fan": HASS_FAN_MODES.get(fan_mode)}}}
)
async def async_set_temperature(self, **kwargs):
"""Set the Temperature."""
temp = kwargs.get(ATTR_TEMPERATURE)
await self.async_change({self.ac_key: {"info": {"setTemp": temp}}})
class AdvantageAirZone(AdvantageAirClimateEntity):
"""AdvantageAir Zone control."""
def __init__(self, instance, ac_key, zone_key):
"""Initialize the Advantage Air Zone climate entity."""
super().__init__(instance)
self.ac_key = ac_key
self.zone_key = zone_key
@property
def name(self):
"""Return the name."""
return self.coordinator.data["aircons"][self.ac_key]["zones"][self.zone_key][
"name"
]
@property
def unique_id(self):
"""Return a unique id."""
return f'{self.coordinator.data["system"]["rid"]}-{self.ac_key}-{self.zone_key}'
@property
def current_temperature(self):
"""Return the current temperature."""
return self.coordinator.data["aircons"][self.ac_key]["zones"][self.zone_key][
"measuredTemp"
]
@property
def target_temperature(self):
"""Return the target temperature."""
return self.coordinator.data["aircons"][self.ac_key]["zones"][self.zone_key][
"setTemp"
]
@property
def hvac_mode(self):
"""Return the current HVAC modes."""
if (
self.coordinator.data["aircons"][self.ac_key]["zones"][self.zone_key][
"state"
]
== ADVANTAGE_AIR_STATE_OPEN
):
return HVAC_MODE_FAN_ONLY
return HVAC_MODE_OFF
@property
def hvac_modes(self):
"""Return supported HVAC modes."""
return ZONE_HVAC_MODES
@property
def device_state_attributes(self):
"""Return additional attributes about Zone."""
return self.coordinator.data["aircons"][self.ac_key]["zones"][self.zone_key]
@property
def supported_features(self):
"""Return the supported features."""
return SUPPORT_TARGET_TEMPERATURE
async def async_set_hvac_mode(self, hvac_mode):
"""Set the HVAC Mode and State."""
if hvac_mode == HVAC_MODE_OFF:
await self.async_change(
{
self.ac_key: {
"zones": {self.zone_key: {"state": ADVANTAGE_AIR_STATE_CLOSE}}
}
}
)
else:
await self.async_change(
{
self.ac_key: {
"zones": {self.zone_key: {"state": ADVANTAGE_AIR_STATE_OPEN}}
}
}
)
async def async_set_temperature(self, **kwargs):
"""Set the Temperature."""
temp = kwargs.get(ATTR_TEMPERATURE)
await self.async_change(
{self.ac_key: {"zones": {self.zone_key: {"setTemp": temp}}}}
)

View File

@ -0,0 +1,61 @@
"""Config Flow for Advantage Air integration."""
import logging
from advantage_air import ApiError, advantage_air
import voluptuous as vol
from homeassistant import config_entries
from homeassistant.const import CONF_IP_ADDRESS, CONF_PORT
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .const import ADVANTAGE_AIR_RETRY, DOMAIN
ADVANTAGE_AIR_DEFAULT_PORT = 2025
ADVANTAGE_AIR_SCHEMA = vol.Schema(
{
vol.Required(CONF_IP_ADDRESS): str,
vol.Optional(CONF_PORT, default=ADVANTAGE_AIR_DEFAULT_PORT): int,
}
)
_LOGGER = logging.getLogger(__name__)
class AdvantageAirConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Config Advantage Air API connection."""
VERSION = 1
CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_POLL
DOMAIN = DOMAIN
async def async_step_user(self, user_input=None):
"""Get configuration from the user."""
errors = {}
if user_input:
ip_address = user_input[CONF_IP_ADDRESS]
port = user_input[CONF_PORT]
try:
data = await advantage_air(
ip_address,
port=port,
session=async_get_clientsession(self.hass),
retry=ADVANTAGE_AIR_RETRY,
).async_get(1)
except ApiError:
errors["base"] = "cannot_connect"
else:
await self.async_set_unique_id(data["system"]["rid"])
self._abort_if_unique_id_configured()
return self.async_create_entry(
title=data["system"]["name"],
data=user_input,
)
return self.async_show_form(
step_id="user",
data_schema=ADVANTAGE_AIR_SCHEMA,
errors=errors,
)

View File

@ -0,0 +1,7 @@
"""Constants used by Advantage Air integration."""
DOMAIN = "advantage_air"
ADVANTAGE_AIR_RETRY = 5
ADVANTAGE_AIR_STATE_OPEN = "open"
ADVANTAGE_AIR_STATE_CLOSE = "close"
ADVANTAGE_AIR_STATE_ON = "on"
ADVANTAGE_AIR_STATE_OFF = "off"

View File

@ -0,0 +1,12 @@
{
"domain": "advantage_air",
"name": "Advantage Air",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/advantage_air",
"codeowners": [
"@Bre77"
],
"requirements": [
"advantage_air==0.2.1"
]
}

View File

@ -0,0 +1,22 @@
{
"config": {
"flow_title": "Advantage Air Setup",
"error": {
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]"
},
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
},
"step": {
"user": {
"data": {
"ip_address": "[%key:common::config_flow::data::ip%]",
"port": "[%key:common::config_flow::data::port%]"
},
"description": "Connect to the API of your Advantage Air wall mounted tablet.",
"title": "Connect"
}
}
},
"title": "Advantage Air"
}

View File

@ -0,0 +1,22 @@
{
"config": {
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
},
"error": {
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]"
},
"flow_title": "Advantage Air Setup",
"step": {
"user": {
"data": {
"ip_address": "[%key:common::config_flow::data::ip%]",
"port": "[%key:common::config_flow::data::port%]"
},
"description": "Connect to the API of your Advantage Air wall mounted tablet.",
"title": "Connect"
}
}
},
"title": "Advantage Air"
}

View File

@ -10,6 +10,7 @@ FLOWS = [
"accuweather",
"acmeda",
"adguard",
"advantage_air",
"agent_dvr",
"airly",
"airvisual",

View File

@ -122,6 +122,9 @@ adext==0.3
# homeassistant.components.adguard
adguardhome==0.4.2
# homeassistant.components.advantage_air
advantage_air==0.2.1
# homeassistant.components.frontier_silicon
afsapi==0.0.4

View File

@ -56,6 +56,9 @@ adext==0.3
# homeassistant.components.adguard
adguardhome==0.4.2
# homeassistant.components.advantage_air
advantage_air==0.2.1
# homeassistant.components.agent_dvr
agent-py==0.0.23

View File

@ -0,0 +1,33 @@
"""Tests for the Advantage Air component."""
from homeassistant.components.advantage_air.const import DOMAIN
from homeassistant.const import CONF_IP_ADDRESS, CONF_PORT
from tests.common import MockConfigEntry, load_fixture
TEST_SYSTEM_DATA = load_fixture("advantage_air/getSystemData.json")
TEST_SET_RESPONSE = load_fixture("advantage_air/setAircon.json")
USER_INPUT = {
CONF_IP_ADDRESS: "1.2.3.4",
CONF_PORT: 2025,
}
TEST_SYSTEM_URL = (
f"http://{USER_INPUT[CONF_IP_ADDRESS]}:{USER_INPUT[CONF_PORT]}/getSystemData"
)
TEST_SET_URL = f"http://{USER_INPUT[CONF_IP_ADDRESS]}:{USER_INPUT[CONF_PORT]}/setAircon"
async def add_mock_config(hass):
"""Create a fake Advantage Air Config Entry."""
entry = MockConfigEntry(
domain=DOMAIN,
title="test entry",
unique_id="0123456",
data=USER_INPUT,
)
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
return entry

View File

@ -0,0 +1,176 @@
"""Test the Advantage Air Climate Platform."""
from homeassistant.components.climate.const import (
ATTR_FAN_MODE,
ATTR_HVAC_MODE,
DOMAIN as CLIMATE_DOMAIN,
FAN_OFF,
HVAC_MODE_FAN_ONLY,
HVAC_MODE_OFF,
SERVICE_SET_FAN_MODE,
SERVICE_SET_HVAC_MODE,
SERVICE_SET_TEMPERATURE,
)
from homeassistant.const import ATTR_ENTITY_ID, ATTR_TEMPERATURE
from tests.components.advantage_air import (
TEST_SET_RESPONSE,
TEST_SET_URL,
TEST_SYSTEM_DATA,
TEST_SYSTEM_URL,
add_mock_config,
)
async def test_climate_async_setup_entry(hass, aioclient_mock):
"""Test climate setup."""
aioclient_mock.get(
TEST_SYSTEM_URL,
text=TEST_SYSTEM_DATA,
)
aioclient_mock.get(
TEST_SET_URL,
text=TEST_SET_RESPONSE,
)
await add_mock_config(hass)
registry = await hass.helpers.entity_registry.async_get_registry()
assert len(aioclient_mock.mock_calls) == 1
# Test Main Climate Entity
entity_id = "climate.ac_one"
state = hass.states.get(entity_id)
assert state
assert state.state == HVAC_MODE_FAN_ONLY
assert state.attributes.get("min_temp") == 16
assert state.attributes.get("max_temp") == 32
assert state.attributes.get("current_temperature") is None
entry = registry.async_get(entity_id)
assert entry
assert entry.unique_id == "uniqueid-ac1"
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_HVAC_MODE,
{ATTR_ENTITY_ID: [entity_id], ATTR_HVAC_MODE: HVAC_MODE_FAN_ONLY},
blocking=True,
)
assert len(aioclient_mock.mock_calls) == 3
assert aioclient_mock.mock_calls[-2][0] == "GET"
assert aioclient_mock.mock_calls[-2][1].path == "/setAircon"
assert aioclient_mock.mock_calls[-1][0] == "GET"
assert aioclient_mock.mock_calls[-1][1].path == "/getSystemData"
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_HVAC_MODE,
{ATTR_ENTITY_ID: [entity_id], ATTR_HVAC_MODE: HVAC_MODE_OFF},
blocking=True,
)
assert len(aioclient_mock.mock_calls) == 5
assert aioclient_mock.mock_calls[-2][0] == "GET"
assert aioclient_mock.mock_calls[-2][1].path == "/setAircon"
assert aioclient_mock.mock_calls[-1][0] == "GET"
assert aioclient_mock.mock_calls[-1][1].path == "/getSystemData"
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_FAN_MODE,
{ATTR_ENTITY_ID: [entity_id], ATTR_FAN_MODE: FAN_OFF},
blocking=True,
)
assert len(aioclient_mock.mock_calls) == 7
assert aioclient_mock.mock_calls[-2][0] == "GET"
assert aioclient_mock.mock_calls[-2][1].path == "/setAircon"
assert aioclient_mock.mock_calls[-1][0] == "GET"
assert aioclient_mock.mock_calls[-1][1].path == "/getSystemData"
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_TEMPERATURE,
{ATTR_ENTITY_ID: [entity_id], ATTR_TEMPERATURE: 25},
blocking=True,
)
assert len(aioclient_mock.mock_calls) == 9
assert aioclient_mock.mock_calls[-2][0] == "GET"
assert aioclient_mock.mock_calls[-2][1].path == "/setAircon"
assert aioclient_mock.mock_calls[-1][0] == "GET"
assert aioclient_mock.mock_calls[-1][1].path == "/getSystemData"
# Test Climate Zone Entity
entity_id = "climate.zone_open_with_sensor"
state = hass.states.get(entity_id)
assert state
assert state.attributes.get("min_temp") == 16
assert state.attributes.get("max_temp") == 32
assert state.attributes.get("measuredTemp") == 25
assert state.attributes.get("setTemp") == 24
entry = registry.async_get(entity_id)
assert entry
assert entry.unique_id == "uniqueid-ac1-z01"
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_HVAC_MODE,
{ATTR_ENTITY_ID: [entity_id], ATTR_HVAC_MODE: HVAC_MODE_FAN_ONLY},
blocking=True,
)
assert len(aioclient_mock.mock_calls) == 11
assert aioclient_mock.mock_calls[-2][0] == "GET"
assert aioclient_mock.mock_calls[-2][1].path == "/setAircon"
assert aioclient_mock.mock_calls[-1][0] == "GET"
assert aioclient_mock.mock_calls[-1][1].path == "/getSystemData"
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_HVAC_MODE,
{ATTR_ENTITY_ID: [entity_id], ATTR_HVAC_MODE: HVAC_MODE_OFF},
blocking=True,
)
assert len(aioclient_mock.mock_calls) == 13
assert aioclient_mock.mock_calls[-2][0] == "GET"
assert aioclient_mock.mock_calls[-2][1].path == "/setAircon"
assert aioclient_mock.mock_calls[-1][0] == "GET"
assert aioclient_mock.mock_calls[-1][1].path == "/getSystemData"
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_TEMPERATURE,
{ATTR_ENTITY_ID: [entity_id], ATTR_TEMPERATURE: 25},
blocking=True,
)
assert len(aioclient_mock.mock_calls) == 15
assert aioclient_mock.mock_calls[-2][0] == "GET"
assert aioclient_mock.mock_calls[-2][1].path == "/setAircon"
assert aioclient_mock.mock_calls[-1][0] == "GET"
assert aioclient_mock.mock_calls[-1][1].path == "/getSystemData"
async def test_climate_async_failed_update(hass, aioclient_mock):
"""Test climate change failure."""
aioclient_mock.get(
TEST_SYSTEM_URL,
text=TEST_SYSTEM_DATA,
)
aioclient_mock.get(
TEST_SET_URL,
exc=SyntaxError,
)
await add_mock_config(hass)
assert len(aioclient_mock.mock_calls) == 1
await hass.services.async_call(
CLIMATE_DOMAIN,
SERVICE_SET_TEMPERATURE,
{ATTR_ENTITY_ID: ["climate.ac_one"], ATTR_TEMPERATURE: 25},
blocking=True,
)
assert len(aioclient_mock.mock_calls) == 2
assert aioclient_mock.mock_calls[-1][0] == "GET"
assert aioclient_mock.mock_calls[-1][1].path == "/setAircon"

View File

@ -0,0 +1,71 @@
"""Test the Advantage Air config flow."""
from homeassistant import config_entries, data_entry_flow
from homeassistant.components.advantage_air.const import DOMAIN
from tests.async_mock import patch
from tests.components.advantage_air import TEST_SYSTEM_DATA, TEST_SYSTEM_URL, USER_INPUT
async def test_form(hass, aioclient_mock):
"""Test that form shows up."""
aioclient_mock.get(
TEST_SYSTEM_URL,
text=TEST_SYSTEM_DATA,
)
result1 = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result1["type"] == data_entry_flow.RESULT_TYPE_FORM
assert result1["step_id"] == "user"
assert result1["errors"] == {}
with patch(
"homeassistant.components.advantage_air.async_setup_entry",
return_value=True,
) as mock_setup_entry:
result2 = await hass.config_entries.flow.async_configure(
result1["flow_id"],
USER_INPUT,
)
assert len(aioclient_mock.mock_calls) == 1
assert result2["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
assert result2["title"] == "testname"
assert result2["data"] == USER_INPUT
await hass.async_block_till_done()
assert len(mock_setup_entry.mock_calls) == 1
# Test Duplicate Config Flow
result3 = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
result4 = await hass.config_entries.flow.async_configure(
result3["flow_id"],
USER_INPUT,
)
assert result4["type"] == data_entry_flow.RESULT_TYPE_ABORT
async def test_form_cannot_connect(hass, aioclient_mock):
"""Test we handle cannot connect error."""
aioclient_mock.get(
TEST_SYSTEM_URL,
exc=SyntaxError,
)
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
USER_INPUT,
)
assert result2["type"] == data_entry_flow.RESULT_TYPE_FORM
assert result2["step_id"] == "user"
assert result2["errors"] == {"base": "cannot_connect"}
assert len(aioclient_mock.mock_calls) == 1

View File

@ -0,0 +1,33 @@
"""Test the Advantage Air Initialization."""
from homeassistant.config_entries import ENTRY_STATE_LOADED, ENTRY_STATE_SETUP_RETRY
from tests.components.advantage_air import (
TEST_SYSTEM_DATA,
TEST_SYSTEM_URL,
add_mock_config,
)
async def test_async_setup_entry(hass, aioclient_mock):
"""Test a successful setup entry."""
aioclient_mock.get(
TEST_SYSTEM_URL,
text=TEST_SYSTEM_DATA,
)
entry = await add_mock_config(hass)
assert entry.state == ENTRY_STATE_LOADED
async def test_async_setup_entry_failure(hass, aioclient_mock):
"""Test a unsuccessful setup entry."""
aioclient_mock.get(
TEST_SYSTEM_URL,
exc=SyntaxError,
)
entry = await add_mock_config(hass)
assert entry.state == ENTRY_STATE_SETUP_RETRY

View File

@ -0,0 +1,110 @@
{
"aircons": {
"ac1": {
"info": {
"climateControlModeIsRunning": false,
"countDownToOff": 0,
"countDownToOn": 0,
"fan": "high",
"filterCleanStatus": 0,
"freshAirStatus": "none",
"mode": "vent",
"myZone": 0,
"name": "AC One",
"setTemp": 24,
"state": "on"
},
"zones": {
"z01": {
"error": 0,
"maxDamper": 100,
"measuredTemp": 25,
"minDamper": 0,
"motion": 1,
"motionConfig": 1,
"name": "Zone open with Sensor",
"number": 1,
"rssi": -50,
"setTemp": 24,
"state": "open",
"type": 1,
"value": 100
},
"z02": {
"error": 0,
"maxDamper": 100,
"measuredTemp": 25,
"minDamper": 0,
"motion": 1,
"motionConfig": 1,
"name": "Zone closed with Sensor",
"number": 1,
"rssi": -50,
"setTemp": 24,
"state": "close",
"type": 1,
"value": 0
}
}
},
"ac2": {
"info": {
"climateControlModeIsRunning": false,
"countDownToOff": 0,
"countDownToOn": 0,
"fan": "low",
"filterCleanStatus": 0,
"freshAirStatus": "none",
"mode": "cool",
"myZone": 1,
"name": "AC Two",
"setTemp": 24,
"state": "off"
},
"zones": {
"z01": {
"error": 0,
"maxDamper": 100,
"measuredTemp": 0,
"minDamper": 0,
"motion": 0,
"motionConfig": 0,
"name": "Zone open without sensor",
"number": 1,
"rssi": 0,
"setTemp": 24,
"state": "open",
"type": 0,
"value": 100
},
"z02": {
"error": 0,
"maxDamper": 100,
"measuredTemp": 0,
"minDamper": 0,
"motion": 0,
"motionConfig": 0,
"name": "Zone closed without sensor",
"number": 1,
"rssi": 0,
"setTemp": 24,
"state": "close",
"type": 0,
"value": 0
}
}
}
},
"system": {
"hasAircons": true,
"hasLights": false,
"hasSensors": false,
"hasThings": false,
"hasThingsBOG": false,
"hasThingsLight": false,
"name": "testname",
"rid": "uniqueid",
"sysType": "e-zone",
"myAppRev": "testversion"
}
}

View File

@ -0,0 +1,4 @@
{
"ack": true,
"request": "setAircon"
}