mirror of
https://github.com/home-assistant/core.git
synced 2025-07-28 15:47:12 +00:00
Add light platform to Advantage Air (#75425)
This commit is contained in:
parent
a093057420
commit
2857739958
@ -20,6 +20,7 @@ PLATFORMS = [
|
|||||||
Platform.SELECT,
|
Platform.SELECT,
|
||||||
Platform.SENSOR,
|
Platform.SENSOR,
|
||||||
Platform.SWITCH,
|
Platform.SWITCH,
|
||||||
|
Platform.LIGHT,
|
||||||
]
|
]
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_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),
|
update_interval=timedelta(seconds=ADVANTAGE_AIR_SYNC_INTERVAL),
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_change(change):
|
def error_handle_factory(func):
|
||||||
try:
|
"""Return the provided API function wrapped in an error handler and coordinator refresh."""
|
||||||
if await api.async_change(change):
|
|
||||||
await coordinator.async_refresh()
|
async def error_handle(param):
|
||||||
except ApiError as err:
|
try:
|
||||||
_LOGGER.warning(err)
|
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()
|
await coordinator.async_config_entry_first_refresh()
|
||||||
|
|
||||||
hass.data.setdefault(DOMAIN, {})
|
hass.data.setdefault(DOMAIN, {})
|
||||||
hass.data[DOMAIN][entry.entry_id] = {
|
hass.data[DOMAIN][entry.entry_id] = {
|
||||||
"coordinator": coordinator,
|
"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)
|
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||||
|
@ -45,7 +45,7 @@ AC_HVAC_MODES = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
ADVANTAGE_AIR_FAN_MODES = {
|
ADVANTAGE_AIR_FAN_MODES = {
|
||||||
"auto": FAN_AUTO,
|
"autoAA": FAN_AUTO,
|
||||||
"low": FAN_LOW,
|
"low": FAN_LOW,
|
||||||
"medium": FAN_MEDIUM,
|
"medium": FAN_MEDIUM,
|
||||||
"high": FAN_HIGH,
|
"high": FAN_HIGH,
|
||||||
|
90
homeassistant/components/advantage_air/light.py
Normal file
90
homeassistant/components/advantage_air/light.py
Normal 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)
|
@ -4,7 +4,7 @@
|
|||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/advantage_air",
|
"documentation": "https://www.home-assistant.io/integrations/advantage_air",
|
||||||
"codeowners": ["@Bre77"],
|
"codeowners": ["@Bre77"],
|
||||||
"requirements": ["advantage_air==0.3.1"],
|
"requirements": ["advantage_air==0.4.1"],
|
||||||
"quality_scale": "platinum",
|
"quality_scale": "platinum",
|
||||||
"iot_class": "local_polling",
|
"iot_class": "local_polling",
|
||||||
"loggers": ["advantage_air"]
|
"loggers": ["advantage_air"]
|
||||||
|
@ -86,7 +86,7 @@ adext==0.4.2
|
|||||||
adguardhome==0.5.1
|
adguardhome==0.5.1
|
||||||
|
|
||||||
# homeassistant.components.advantage_air
|
# homeassistant.components.advantage_air
|
||||||
advantage_air==0.3.1
|
advantage_air==0.4.1
|
||||||
|
|
||||||
# homeassistant.components.frontier_silicon
|
# homeassistant.components.frontier_silicon
|
||||||
afsapi==0.2.7
|
afsapi==0.2.7
|
||||||
|
@ -76,7 +76,7 @@ adext==0.4.2
|
|||||||
adguardhome==0.5.1
|
adguardhome==0.5.1
|
||||||
|
|
||||||
# homeassistant.components.advantage_air
|
# homeassistant.components.advantage_air
|
||||||
advantage_air==0.3.1
|
advantage_air==0.4.1
|
||||||
|
|
||||||
# homeassistant.components.agent_dvr
|
# homeassistant.components.agent_dvr
|
||||||
agent-py==0.0.23
|
agent-py==0.0.23
|
||||||
|
@ -17,6 +17,9 @@ TEST_SYSTEM_URL = (
|
|||||||
f"http://{USER_INPUT[CONF_IP_ADDRESS]}:{USER_INPUT[CONF_PORT]}/getSystemData"
|
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_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):
|
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": {
|
"system": {
|
||||||
"hasAircons": true,
|
"hasAircons": true,
|
||||||
"hasLights": false,
|
"hasLights": true,
|
||||||
"hasSensors": false,
|
"hasSensors": false,
|
||||||
"hasThings": false,
|
"hasThings": false,
|
||||||
"hasThingsBOG": false,
|
"hasThingsBOG": false,
|
||||||
|
105
tests/components/advantage_air/test_light.py
Normal file
105
tests/components/advantage_air/test_light.py
Normal 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"
|
Loading…
x
Reference in New Issue
Block a user