Add light platform to Advantage Air (#75425)
parent
a093057420
commit
2857739958
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
|
@ -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"]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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"
|
Loading…
Reference in New Issue