Add light platform to Advantage Air (#75425)

pull/77414/head^2
Brett Adams 2022-08-29 10:44:08 +10:00 committed by GitHub
parent a093057420
commit 2857739958
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 234 additions and 12 deletions

View File

@ -20,6 +20,7 @@ PLATFORMS = [
Platform.SELECT,
Platform.SENSOR,
Platform.SWITCH,
Platform.LIGHT,
]
_LOGGER = logging.getLogger(__name__)
@ -50,19 +51,25 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
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)
def error_handle_factory(func):
"""Return the provided API function wrapped in an error handler and coordinator refresh."""
async def error_handle(param):
try:
if await func(param):
await coordinator.async_refresh()
except ApiError as err:
_LOGGER.warning(err)
return error_handle
await coordinator.async_config_entry_first_refresh()
hass.data.setdefault(DOMAIN, {})
hass.data[DOMAIN][entry.entry_id] = {
"coordinator": coordinator,
"async_change": async_change,
"async_change": error_handle_factory(api.aircon.async_set),
"async_set_light": error_handle_factory(api.lights.async_set),
}
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

View File

@ -45,7 +45,7 @@ AC_HVAC_MODES = [
]
ADVANTAGE_AIR_FAN_MODES = {
"auto": FAN_AUTO,
"autoAA": FAN_AUTO,
"low": FAN_LOW,
"medium": FAN_MEDIUM,
"high": FAN_HIGH,

View File

@ -0,0 +1,90 @@
"""Light platform for Advantage Air integration."""
from typing import Any
from homeassistant.components.light import ATTR_BRIGHTNESS, ColorMode, LightEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import (
ADVANTAGE_AIR_STATE_OFF,
ADVANTAGE_AIR_STATE_ON,
DOMAIN as ADVANTAGE_AIR_DOMAIN,
)
from .entity import AdvantageAirEntity
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up AdvantageAir light platform."""
instance = hass.data[ADVANTAGE_AIR_DOMAIN][config_entry.entry_id]
entities = []
if "myLights" in instance["coordinator"].data:
for light in instance["coordinator"].data["myLights"]["lights"].values():
if light.get("relay"):
entities.append(AdvantageAirLight(instance, light))
else:
entities.append(AdvantageAirLightDimmable(instance, light))
async_add_entities(entities)
class AdvantageAirLight(AdvantageAirEntity, LightEntity):
"""Representation of Advantage Air Light."""
_attr_supported_color_modes = {ColorMode.ONOFF}
def __init__(self, instance, light):
"""Initialize an Advantage Air Light."""
super().__init__(instance)
self.async_set_light = instance["async_set_light"]
self._id = light["id"]
self._attr_unique_id += f"-{self._id}"
self._attr_device_info = DeviceInfo(
identifiers={(ADVANTAGE_AIR_DOMAIN, self._attr_unique_id)},
via_device=(ADVANTAGE_AIR_DOMAIN, self.coordinator.data["system"]["rid"]),
manufacturer="Advantage Air",
model=light.get("moduleType"),
name=light["name"],
)
@property
def _light(self):
"""Return the light object."""
return self.coordinator.data["myLights"]["lights"][self._id]
@property
def is_on(self) -> bool:
"""Return if the light is on."""
return self._light["state"] == ADVANTAGE_AIR_STATE_ON
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the light on."""
await self.async_set_light({"id": self._id, "state": ADVANTAGE_AIR_STATE_ON})
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the light off."""
await self.async_set_light({"id": self._id, "state": ADVANTAGE_AIR_STATE_OFF})
class AdvantageAirLightDimmable(AdvantageAirLight):
"""Representation of Advantage Air Dimmable Light."""
_attr_supported_color_modes = {ColorMode.ONOFF, ColorMode.BRIGHTNESS}
@property
def brightness(self) -> int:
"""Return the brightness of this light between 0..255."""
return round(self._light["value"] * 255 / 100)
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the light on and optionally set the brightness."""
data = {"id": self._id, "state": ADVANTAGE_AIR_STATE_ON}
if ATTR_BRIGHTNESS in kwargs:
data["value"] = round(kwargs[ATTR_BRIGHTNESS] * 100 / 255)
await self.async_set_light(data)

View File

@ -4,7 +4,7 @@
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/advantage_air",
"codeowners": ["@Bre77"],
"requirements": ["advantage_air==0.3.1"],
"requirements": ["advantage_air==0.4.1"],
"quality_scale": "platinum",
"iot_class": "local_polling",
"loggers": ["advantage_air"]

View File

@ -86,7 +86,7 @@ adext==0.4.2
adguardhome==0.5.1
# homeassistant.components.advantage_air
advantage_air==0.3.1
advantage_air==0.4.1
# homeassistant.components.frontier_silicon
afsapi==0.2.7

View File

@ -76,7 +76,7 @@ adext==0.4.2
adguardhome==0.5.1
# homeassistant.components.advantage_air
advantage_air==0.3.1
advantage_air==0.4.1
# homeassistant.components.agent_dvr
agent-py==0.0.23

View File

@ -17,6 +17,9 @@ 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"
TEST_SET_LIGHT_URL = (
f"http://{USER_INPUT[CONF_IP_ADDRESS]}:{USER_INPUT[CONF_PORT]}/setLight"
)
async def add_mock_config(hass):

View File

@ -143,9 +143,26 @@
}
}
},
"myLights": {
"lights": {
"100": {
"id": "100",
"moduleType": "RM2",
"name": "Light A",
"relay": true,
"state": "off"
},
"101": {
"id": "101",
"name": "Light B",
"value": 50,
"state": "on"
}
}
},
"system": {
"hasAircons": true,
"hasLights": false,
"hasLights": true,
"hasSensors": false,
"hasThings": false,
"hasThingsBOG": false,

View File

@ -0,0 +1,105 @@
"""Test the Advantage Air Switch Platform."""
from json import loads
from homeassistant.components.advantage_air.const import (
ADVANTAGE_AIR_STATE_OFF,
ADVANTAGE_AIR_STATE_ON,
)
from homeassistant.components.light import (
ATTR_BRIGHTNESS,
DOMAIN as LIGHT_DOMAIN,
SERVICE_TURN_OFF,
SERVICE_TURN_ON,
)
from homeassistant.const import ATTR_ENTITY_ID, STATE_OFF
from homeassistant.helpers import entity_registry as er
from tests.components.advantage_air import (
TEST_SET_LIGHT_URL,
TEST_SET_RESPONSE,
TEST_SYSTEM_DATA,
TEST_SYSTEM_URL,
add_mock_config,
)
async def test_light_async_setup_entry(hass, aioclient_mock):
"""Test light setup."""
aioclient_mock.get(
TEST_SYSTEM_URL,
text=TEST_SYSTEM_DATA,
)
aioclient_mock.get(
TEST_SET_LIGHT_URL,
text=TEST_SET_RESPONSE,
)
await add_mock_config(hass)
registry = er.async_get(hass)
assert len(aioclient_mock.mock_calls) == 1
# Test Light Entity
entity_id = "light.light_a"
state = hass.states.get(entity_id)
assert state
assert state.state == STATE_OFF
entry = registry.async_get(entity_id)
assert entry
assert entry.unique_id == "uniqueid-100"
await hass.services.async_call(
LIGHT_DOMAIN,
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: [entity_id]},
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 == "/setLight"
data = loads(aioclient_mock.mock_calls[-2][1].query["json"])
assert data["id"] == "100"
assert data["state"] == ADVANTAGE_AIR_STATE_ON
assert aioclient_mock.mock_calls[-1][0] == "GET"
assert aioclient_mock.mock_calls[-1][1].path == "/getSystemData"
await hass.services.async_call(
LIGHT_DOMAIN,
SERVICE_TURN_OFF,
{ATTR_ENTITY_ID: [entity_id]},
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 == "/setLight"
data = loads(aioclient_mock.mock_calls[-2][1].query["json"])
assert data["id"] == "100"
assert data["state"] == ADVANTAGE_AIR_STATE_OFF
assert aioclient_mock.mock_calls[-1][0] == "GET"
assert aioclient_mock.mock_calls[-1][1].path == "/getSystemData"
# Test Dimmable Light Entity
entity_id = "light.light_b"
entry = registry.async_get(entity_id)
assert entry
assert entry.unique_id == "uniqueid-101"
await hass.services.async_call(
LIGHT_DOMAIN,
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: [entity_id], ATTR_BRIGHTNESS: 128},
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 == "/setLight"
data = loads(aioclient_mock.mock_calls[-2][1].query["json"])
assert data["id"] == "101"
assert data["value"] == 50
assert data["state"] == ADVANTAGE_AIR_STATE_ON
assert aioclient_mock.mock_calls[-1][0] == "GET"
assert aioclient_mock.mock_calls[-1][1].path == "/getSystemData"