Skip to content

Commit

Permalink
Add MyPlace support to Advantage Air (home-assistant#91108)
Browse files Browse the repository at this point in the history
Co-authored-by: J. Nick Koston <[email protected]>
  • Loading branch information
Bre77 and bdraco authored Apr 16, 2023
1 parent 4420201 commit 5001a50
Show file tree
Hide file tree
Showing 11 changed files with 390 additions and 51 deletions.
1 change: 1 addition & 0 deletions homeassistant/components/advantage_air/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ async def error_handle(param):
"coordinator": coordinator,
"aircon": error_handle_factory(api.aircon.async_set),
"lights": error_handle_factory(api.lights.async_set),
"things": error_handle_factory(api.things.async_set),
}

await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
Expand Down
41 changes: 40 additions & 1 deletion homeassistant/components/advantage_air/cover.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
ADVANTAGE_AIR_STATE_OPEN,
DOMAIN as ADVANTAGE_AIR_DOMAIN,
)
from .entity import AdvantageAirZoneEntity
from .entity import AdvantageAirThingEntity, AdvantageAirZoneEntity

PARALLEL_UPDATES = 0

Expand All @@ -37,6 +37,16 @@ async def async_setup_entry(
# Only add zone vent controls when zone in vent control mode.
if zone["type"] == 0:
entities.append(AdvantageAirZoneVent(instance, ac_key, zone_key))
if things := instance["coordinator"].data.get("myThings"):
for thing in things["things"].values():
if thing["channelDipState"] in [1, 2]: # 1 = "Blind", 2 = "Blind 2"
entities.append(
AdvantageAirThingCover(instance, thing, CoverDeviceClass.BLIND)
)
elif thing["channelDipState"] == 3: # 3 = "Garage door"
entities.append(
AdvantageAirThingCover(instance, thing, CoverDeviceClass.GARAGE)
)
async_add_entities(entities)


Expand Down Expand Up @@ -113,3 +123,32 @@ async def async_set_cover_position(self, **kwargs: Any) -> None:
}
}
)


class AdvantageAirThingCover(AdvantageAirThingEntity, CoverEntity):
"""Representation of Advantage Air Cover controlled by MyPlace."""

_attr_supported_features = CoverEntityFeature.OPEN | CoverEntityFeature.CLOSE

def __init__(
self,
instance: dict[str, Any],
thing: dict[str, Any],
device_class: CoverDeviceClass,
) -> None:
"""Initialize an Advantage Air Things Cover."""
super().__init__(instance, thing)
self._attr_device_class = device_class

@property
def is_closed(self) -> bool:
"""Return if cover is fully closed."""
return self._data["value"] == 0

async def async_open_cover(self, **kwargs: Any) -> None:
"""Fully open zone vent."""
return await self.async_turn_on()

async def async_close_cover(self, **kwargs: Any) -> None:
"""Fully close zone vent."""
return await self.async_turn_off()
38 changes: 37 additions & 1 deletion homeassistant/components/advantage_air/entity.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
"""Advantage Air parent entity class."""

from typing import Any

from homeassistant.helpers.entity import DeviceInfo
Expand Down Expand Up @@ -54,3 +53,40 @@ def __init__(self, instance: dict[str, Any], ac_key: str, zone_key: str) -> None
@property
def _zone(self) -> dict[str, Any]:
return self.coordinator.data["aircons"][self.ac_key]["zones"][self.zone_key]


class AdvantageAirThingEntity(AdvantageAirEntity):
"""Parent class for Advantage Air Things Entities."""

def __init__(self, instance: dict[str, Any], thing: dict[str, Any]) -> None:
"""Initialize common aspects of an Advantage Air Things entity."""
super().__init__(instance)
self.set = instance["things"]
self._id = thing["id"]
self._attr_unique_id += f"-{self._id}"

self._attr_device_info = DeviceInfo(
via_device=(DOMAIN, self.coordinator.data["system"]["rid"]),
identifiers={(DOMAIN, self._attr_unique_id)},
manufacturer="Advantage Air",
model="MyPlace",
name=thing["name"],
)

@property
def _data(self) -> dict:
"""Return the thing data."""
return self.coordinator.data["myThings"]["things"][self._id]

@property
def is_on(self):
"""Return if the thing is considered on."""
return self._data["value"] > 0

async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the thing on."""
await self.set({self._id: {"id": self._id, "value": 100}})

async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the thing off."""
await self.set({self._id: {"id": self._id, "value": 0}})
52 changes: 42 additions & 10 deletions homeassistant/components/advantage_air/light.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
ADVANTAGE_AIR_STATE_ON,
DOMAIN as ADVANTAGE_AIR_DOMAIN,
)
from .entity import AdvantageAirEntity
from .entity import AdvantageAirEntity, AdvantageAirThingEntity


async def async_setup_entry(
Expand All @@ -31,6 +31,12 @@ async def async_setup_entry(
entities.append(AdvantageAirLight(instance, light))
else:
entities.append(AdvantageAirLightDimmable(instance, light))
if things := instance["coordinator"].data.get("myThings"):
for thing in things["things"].values():
if thing["channelDipState"] == 4: # 4 = "Light (on/off)""
entities.append(AdvantageAirThingLight(instance, thing))
elif thing["channelDipState"] == 5: # 5 = "Light (Dimmable)""
entities.append(AdvantageAirThingLightDimmable(instance, thing))
async_add_entities(entities)


Expand All @@ -42,7 +48,7 @@ class AdvantageAirLight(AdvantageAirEntity, LightEntity):
def __init__(self, instance: dict[str, Any], light: dict[str, Any]) -> None:
"""Initialize an Advantage Air Light."""
super().__init__(instance)
self.lights = instance["lights"]
self.set = instance["lights"]
self._id: str = light["id"]
self._attr_unique_id += f"-{self._id}"
self._attr_device_info = DeviceInfo(
Expand All @@ -54,24 +60,22 @@ def __init__(self, instance: dict[str, Any], light: dict[str, Any]) -> None:
)

@property
def _light(self) -> dict[str, Any]:
def _data(self) -> dict[str, Any]:
"""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
return self._data["state"] == ADVANTAGE_AIR_STATE_ON

async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the light on."""
await self.lights({self._id: {"id": self._id, "state": ADVANTAGE_AIR_STATE_ON}})
await self.set({self._id: {"id": self._id, "state": ADVANTAGE_AIR_STATE_ON}})

async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the light off."""
await self.lights(
{self._id: {"id": self._id, "state": ADVANTAGE_AIR_STATE_OFF}}
)
await self.set({self._id: {"id": self._id, "state": ADVANTAGE_AIR_STATE_OFF}})


class AdvantageAirLightDimmable(AdvantageAirLight):
Expand All @@ -82,7 +86,7 @@ class AdvantageAirLightDimmable(AdvantageAirLight):
@property
def brightness(self) -> int:
"""Return the brightness of this light between 0..255."""
return round(self._light["value"] * 255 / 100)
return round(self._data["value"] * 255 / 100)

async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the light on and optionally set the brightness."""
Expand All @@ -91,4 +95,32 @@ async def async_turn_on(self, **kwargs: Any) -> None:
}
if ATTR_BRIGHTNESS in kwargs:
data[self._id]["value"] = round(kwargs[ATTR_BRIGHTNESS] * 100 / 255)
await self.lights(data)
await self.set(data)


class AdvantageAirThingLight(AdvantageAirThingEntity, LightEntity):
"""Representation of Advantage Air Light controlled by myThings."""

_attr_supported_color_modes = {ColorMode.ONOFF}


class AdvantageAirThingLightDimmable(AdvantageAirThingEntity, LightEntity):
"""Representation of Advantage Air Dimmable Light controlled by myThings."""

_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._data["value"] * 255 / 100)

async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the light on by setting the brightness."""
await self.set(
{
self._id: {
"id": self._id,
"value": round(kwargs.get(ATTR_BRIGHTNESS, 255) * 100 / 255),
}
}
)
13 changes: 8 additions & 5 deletions homeassistant/components/advantage_air/select.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,14 @@ def __init__(self, instance: dict[str, Any], ac_key: str) -> None:
self._number_to_name = {0: ADVANTAGE_AIR_INACTIVE}
self._name_to_number = {ADVANTAGE_AIR_INACTIVE: 0}

for zone in instance["coordinator"].data["aircons"][ac_key]["zones"].values():
if zone["type"] > 0:
self._name_to_number[zone["name"]] = zone["number"]
self._number_to_name[zone["number"]] = zone["name"]
self._attr_options.append(zone["name"])
if "aircons" in instance["coordinator"].data:
for zone in (
instance["coordinator"].data["aircons"][ac_key]["zones"].values()
):
if zone["type"] > 0:
self._name_to_number[zone["name"]] = zone["number"]
self._number_to_name[zone["number"]] = zone["name"]
self._attr_options.append(zone["name"])

@property
def current_option(self) -> str:
Expand Down
15 changes: 13 additions & 2 deletions homeassistant/components/advantage_air/switch.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""Switch platform for Advantage Air integration."""
from typing import Any

from homeassistant.components.switch import SwitchEntity
from homeassistant.components.switch import SwitchDeviceClass, SwitchEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
Expand All @@ -11,7 +11,7 @@
ADVANTAGE_AIR_STATE_ON,
DOMAIN as ADVANTAGE_AIR_DOMAIN,
)
from .entity import AdvantageAirAcEntity
from .entity import AdvantageAirAcEntity, AdvantageAirThingEntity


async def async_setup_entry(
Expand All @@ -28,6 +28,10 @@ async def async_setup_entry(
for ac_key, ac_device in aircons.items():
if ac_device["info"]["freshAirStatus"] != "none":
entities.append(AdvantageAirFreshAir(instance, ac_key))
if things := instance["coordinator"].data.get("myThings"):
for thing in things["things"].values():
if thing["channelDipState"] == 8: # 8 = Other relay
entities.append(AdvantageAirRelay(instance, thing))
async_add_entities(entities)


Expand All @@ -36,6 +40,7 @@ class AdvantageAirFreshAir(AdvantageAirAcEntity, SwitchEntity):

_attr_icon = "mdi:air-filter"
_attr_name = "Fresh air"
_attr_device_class = SwitchDeviceClass.SWITCH

def __init__(self, instance: dict[str, Any], ac_key: str) -> None:
"""Initialize an Advantage Air fresh air control."""
Expand All @@ -58,3 +63,9 @@ async def async_turn_off(self, **kwargs: Any) -> None:
await self.aircon(
{self.ac_key: {"info": {"freshAirStatus": ADVANTAGE_AIR_STATE_OFF}}}
)


class AdvantageAirRelay(AdvantageAirThingEntity, SwitchEntity):
"""Representation of Advantage Air Thing."""

_attr_device_class = SwitchDeviceClass.SWITCH
3 changes: 3 additions & 0 deletions tests/components/advantage_air/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
TEST_SET_LIGHT_URL = (
f"http://{USER_INPUT[CONF_IP_ADDRESS]}:{USER_INPUT[CONF_PORT]}/setLights"
)
TEST_SET_THING_URL = (
f"http://{USER_INPUT[CONF_IP_ADDRESS]}:{USER_INPUT[CONF_PORT]}/setThings"
)


async def add_mock_config(hass):
Expand Down
55 changes: 54 additions & 1 deletion tests/components/advantage_air/fixtures/getSystemData.json
Original file line number Diff line number Diff line change
Expand Up @@ -220,11 +220,64 @@
}
}
},
"myThings": {
"things": {
"200": {
"buttonType": "upDown",
"channelDipState": 1,
"id": "200",
"name": "Blind 1",
"value": 100
},
"201": {
"buttonType": "upDown",
"channelDipState": 2,
"id": "201",
"name": "Blind 2",
"value": 0
},
"202": {
"buttonType": "openClose",
"channelDipState": 3,
"id": "202",
"name": "Garage",
"value": 100
},
"203": {
"buttonType": "onOff",
"channelDipState": 4,
"id": "203",
"name": "Thing Light",
"value": 100
},
"204": {
"buttonType": "upDown",
"channelDipState": 5,
"id": "204",
"name": "Thing Light Dimmable",
"value": 100
},
"205": {
"buttonType": "onOff",
"channelDipState": 8,
"id": "205",
"name": "Relay",
"value": 100
},
"206": {
"buttonType": "onOff",
"channelDipState": 9,
"id": "206",
"name": "Fan",
"value": 100
}
}
},
"system": {
"hasAircons": true,
"hasLights": true,
"hasSensors": false,
"hasThings": false,
"hasThings": true,
"hasThingsBOG": false,
"hasThingsLight": false,
"needsUpdate": false,
Expand Down
Loading

0 comments on commit 5001a50

Please sign in to comment.