Add MyPlace support to Advantage Air (#91108)

Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
Brett Adams 2023-04-17 08:27:37 +10:00 committed by GitHub
parent 4420201fe6
commit 5001a50876
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 390 additions and 51 deletions

View File

@ -75,6 +75,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"coordinator": coordinator, "coordinator": coordinator,
"aircon": error_handle_factory(api.aircon.async_set), "aircon": error_handle_factory(api.aircon.async_set),
"lights": error_handle_factory(api.lights.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) await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

View File

@ -16,7 +16,7 @@ from .const import (
ADVANTAGE_AIR_STATE_OPEN, ADVANTAGE_AIR_STATE_OPEN,
DOMAIN as ADVANTAGE_AIR_DOMAIN, DOMAIN as ADVANTAGE_AIR_DOMAIN,
) )
from .entity import AdvantageAirZoneEntity from .entity import AdvantageAirThingEntity, AdvantageAirZoneEntity
PARALLEL_UPDATES = 0 PARALLEL_UPDATES = 0
@ -37,6 +37,16 @@ async def async_setup_entry(
# Only add zone vent controls when zone in vent control mode. # Only add zone vent controls when zone in vent control mode.
if zone["type"] == 0: if zone["type"] == 0:
entities.append(AdvantageAirZoneVent(instance, ac_key, zone_key)) 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) async_add_entities(entities)
@ -113,3 +123,32 @@ class AdvantageAirZoneVent(AdvantageAirZoneEntity, CoverEntity):
} }
} }
) )
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()

View File

@ -1,5 +1,4 @@
"""Advantage Air parent entity class.""" """Advantage Air parent entity class."""
from typing import Any from typing import Any
from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity import DeviceInfo
@ -54,3 +53,40 @@ class AdvantageAirZoneEntity(AdvantageAirAcEntity):
@property @property
def _zone(self) -> dict[str, Any]: def _zone(self) -> dict[str, Any]:
return self.coordinator.data["aircons"][self.ac_key]["zones"][self.zone_key] 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}})

View File

@ -12,7 +12,7 @@ from .const import (
ADVANTAGE_AIR_STATE_ON, ADVANTAGE_AIR_STATE_ON,
DOMAIN as ADVANTAGE_AIR_DOMAIN, DOMAIN as ADVANTAGE_AIR_DOMAIN,
) )
from .entity import AdvantageAirEntity from .entity import AdvantageAirEntity, AdvantageAirThingEntity
async def async_setup_entry( async def async_setup_entry(
@ -31,6 +31,12 @@ async def async_setup_entry(
entities.append(AdvantageAirLight(instance, light)) entities.append(AdvantageAirLight(instance, light))
else: else:
entities.append(AdvantageAirLightDimmable(instance, light)) 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) async_add_entities(entities)
@ -42,7 +48,7 @@ class AdvantageAirLight(AdvantageAirEntity, LightEntity):
def __init__(self, instance: dict[str, Any], light: dict[str, Any]) -> None: def __init__(self, instance: dict[str, Any], light: dict[str, Any]) -> None:
"""Initialize an Advantage Air Light.""" """Initialize an Advantage Air Light."""
super().__init__(instance) super().__init__(instance)
self.lights = instance["lights"] self.set = instance["lights"]
self._id: str = light["id"] self._id: str = light["id"]
self._attr_unique_id += f"-{self._id}" self._attr_unique_id += f"-{self._id}"
self._attr_device_info = DeviceInfo( self._attr_device_info = DeviceInfo(
@ -54,24 +60,22 @@ class AdvantageAirLight(AdvantageAirEntity, LightEntity):
) )
@property @property
def _light(self) -> dict[str, Any]: def _data(self) -> dict[str, Any]:
"""Return the light object.""" """Return the light object."""
return self.coordinator.data["myLights"]["lights"][self._id] return self.coordinator.data["myLights"]["lights"][self._id]
@property @property
def is_on(self) -> bool: def is_on(self) -> bool:
"""Return if the light is on.""" """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: async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the light on.""" """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: async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the light off.""" """Turn the light off."""
await self.lights( await self.set({self._id: {"id": self._id, "state": ADVANTAGE_AIR_STATE_OFF}})
{self._id: {"id": self._id, "state": ADVANTAGE_AIR_STATE_OFF}}
)
class AdvantageAirLightDimmable(AdvantageAirLight): class AdvantageAirLightDimmable(AdvantageAirLight):
@ -82,7 +86,7 @@ class AdvantageAirLightDimmable(AdvantageAirLight):
@property @property
def brightness(self) -> int: def brightness(self) -> int:
"""Return the brightness of this light between 0..255.""" """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: async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the light on and optionally set the brightness.""" """Turn the light on and optionally set the brightness."""
@ -91,4 +95,32 @@ class AdvantageAirLightDimmable(AdvantageAirLight):
} }
if ATTR_BRIGHTNESS in kwargs: if ATTR_BRIGHTNESS in kwargs:
data[self._id]["value"] = round(kwargs[ATTR_BRIGHTNESS] * 100 / 255) 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),
}
}
)

View File

@ -42,7 +42,10 @@ class AdvantageAirMyZone(AdvantageAirAcEntity, SelectEntity):
self._number_to_name = {0: ADVANTAGE_AIR_INACTIVE} self._number_to_name = {0: ADVANTAGE_AIR_INACTIVE}
self._name_to_number = {ADVANTAGE_AIR_INACTIVE: 0} self._name_to_number = {ADVANTAGE_AIR_INACTIVE: 0}
for zone in instance["coordinator"].data["aircons"][ac_key]["zones"].values(): if "aircons" in instance["coordinator"].data:
for zone in (
instance["coordinator"].data["aircons"][ac_key]["zones"].values()
):
if zone["type"] > 0: if zone["type"] > 0:
self._name_to_number[zone["name"]] = zone["number"] self._name_to_number[zone["name"]] = zone["number"]
self._number_to_name[zone["number"]] = zone["name"] self._number_to_name[zone["number"]] = zone["name"]

View File

@ -1,7 +1,7 @@
"""Switch platform for Advantage Air integration.""" """Switch platform for Advantage Air integration."""
from typing import Any 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.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
@ -11,7 +11,7 @@ from .const import (
ADVANTAGE_AIR_STATE_ON, ADVANTAGE_AIR_STATE_ON,
DOMAIN as ADVANTAGE_AIR_DOMAIN, DOMAIN as ADVANTAGE_AIR_DOMAIN,
) )
from .entity import AdvantageAirAcEntity from .entity import AdvantageAirAcEntity, AdvantageAirThingEntity
async def async_setup_entry( async def async_setup_entry(
@ -28,6 +28,10 @@ async def async_setup_entry(
for ac_key, ac_device in aircons.items(): for ac_key, ac_device in aircons.items():
if ac_device["info"]["freshAirStatus"] != "none": if ac_device["info"]["freshAirStatus"] != "none":
entities.append(AdvantageAirFreshAir(instance, ac_key)) 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) async_add_entities(entities)
@ -36,6 +40,7 @@ class AdvantageAirFreshAir(AdvantageAirAcEntity, SwitchEntity):
_attr_icon = "mdi:air-filter" _attr_icon = "mdi:air-filter"
_attr_name = "Fresh air" _attr_name = "Fresh air"
_attr_device_class = SwitchDeviceClass.SWITCH
def __init__(self, instance: dict[str, Any], ac_key: str) -> None: def __init__(self, instance: dict[str, Any], ac_key: str) -> None:
"""Initialize an Advantage Air fresh air control.""" """Initialize an Advantage Air fresh air control."""
@ -58,3 +63,9 @@ class AdvantageAirFreshAir(AdvantageAirAcEntity, SwitchEntity):
await self.aircon( await self.aircon(
{self.ac_key: {"info": {"freshAirStatus": ADVANTAGE_AIR_STATE_OFF}}} {self.ac_key: {"info": {"freshAirStatus": ADVANTAGE_AIR_STATE_OFF}}}
) )
class AdvantageAirRelay(AdvantageAirThingEntity, SwitchEntity):
"""Representation of Advantage Air Thing."""
_attr_device_class = SwitchDeviceClass.SWITCH

View File

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

View File

@ -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": { "system": {
"hasAircons": true, "hasAircons": true,
"hasLights": true, "hasLights": true,
"hasSensors": false, "hasSensors": false,
"hasThings": false, "hasThings": true,
"hasThingsBOG": false, "hasThingsBOG": false,
"hasThingsLight": false, "hasThingsLight": false,
"needsUpdate": false, "needsUpdate": false,

View File

@ -19,6 +19,7 @@ from homeassistant.helpers import entity_registry as er
from . import ( from . import (
TEST_SET_RESPONSE, TEST_SET_RESPONSE,
TEST_SET_THING_URL,
TEST_SET_URL, TEST_SET_URL,
TEST_SYSTEM_DATA, TEST_SYSTEM_DATA,
TEST_SYSTEM_URL, TEST_SYSTEM_URL,
@ -28,7 +29,7 @@ from . import (
from tests.test_util.aiohttp import AiohttpClientMocker from tests.test_util.aiohttp import AiohttpClientMocker
async def test_cover_async_setup_entry( async def test_ac_cover(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
) -> None: ) -> None:
"""Test cover platform.""" """Test cover platform."""
@ -46,8 +47,6 @@ async def test_cover_async_setup_entry(
registry = er.async_get(hass) registry = er.async_get(hass)
assert len(aioclient_mock.mock_calls) == 1
# Test Cover Zone Entity # Test Cover Zone Entity
entity_id = "cover.myauto_zone_y" entity_id = "cover.myauto_zone_y"
state = hass.states.get(entity_id) state = hass.states.get(entity_id)
@ -66,7 +65,6 @@ async def test_cover_async_setup_entry(
{ATTR_ENTITY_ID: [entity_id]}, {ATTR_ENTITY_ID: [entity_id]},
blocking=True, blocking=True,
) )
assert len(aioclient_mock.mock_calls) == 3
assert aioclient_mock.mock_calls[-2][0] == "GET" assert aioclient_mock.mock_calls[-2][0] == "GET"
assert aioclient_mock.mock_calls[-2][1].path == "/setAircon" assert aioclient_mock.mock_calls[-2][1].path == "/setAircon"
data = loads(aioclient_mock.mock_calls[-2][1].query["json"]) data = loads(aioclient_mock.mock_calls[-2][1].query["json"])
@ -80,7 +78,6 @@ async def test_cover_async_setup_entry(
{ATTR_ENTITY_ID: [entity_id]}, {ATTR_ENTITY_ID: [entity_id]},
blocking=True, blocking=True,
) )
assert len(aioclient_mock.mock_calls) == 5
assert aioclient_mock.mock_calls[-2][0] == "GET" assert aioclient_mock.mock_calls[-2][0] == "GET"
assert aioclient_mock.mock_calls[-2][1].path == "/setAircon" assert aioclient_mock.mock_calls[-2][1].path == "/setAircon"
data = loads(aioclient_mock.mock_calls[-2][1].query["json"]) data = loads(aioclient_mock.mock_calls[-2][1].query["json"])
@ -95,7 +92,6 @@ async def test_cover_async_setup_entry(
{ATTR_ENTITY_ID: [entity_id], ATTR_POSITION: 50}, {ATTR_ENTITY_ID: [entity_id], ATTR_POSITION: 50},
blocking=True, blocking=True,
) )
assert len(aioclient_mock.mock_calls) == 7
assert aioclient_mock.mock_calls[-2][0] == "GET" assert aioclient_mock.mock_calls[-2][0] == "GET"
assert aioclient_mock.mock_calls[-2][1].path == "/setAircon" assert aioclient_mock.mock_calls[-2][1].path == "/setAircon"
data = loads(aioclient_mock.mock_calls[-2][1].query["json"]) data = loads(aioclient_mock.mock_calls[-2][1].query["json"])
@ -109,7 +105,6 @@ async def test_cover_async_setup_entry(
{ATTR_ENTITY_ID: [entity_id], ATTR_POSITION: 0}, {ATTR_ENTITY_ID: [entity_id], ATTR_POSITION: 0},
blocking=True, blocking=True,
) )
assert len(aioclient_mock.mock_calls) == 9
assert aioclient_mock.mock_calls[-2][0] == "GET" assert aioclient_mock.mock_calls[-2][0] == "GET"
assert aioclient_mock.mock_calls[-2][1].path == "/setAircon" assert aioclient_mock.mock_calls[-2][1].path == "/setAircon"
data = loads(aioclient_mock.mock_calls[-2][1].query["json"]) data = loads(aioclient_mock.mock_calls[-2][1].query["json"])
@ -129,7 +124,6 @@ async def test_cover_async_setup_entry(
}, },
blocking=True, blocking=True,
) )
assert len(aioclient_mock.mock_calls) == 11
data = loads(aioclient_mock.mock_calls[-2][1].query["json"]) data = loads(aioclient_mock.mock_calls[-2][1].query["json"])
assert data["ac3"]["zones"]["z01"]["state"] == ADVANTAGE_AIR_STATE_CLOSE assert data["ac3"]["zones"]["z01"]["state"] == ADVANTAGE_AIR_STATE_CLOSE
assert data["ac3"]["zones"]["z02"]["state"] == ADVANTAGE_AIR_STATE_CLOSE assert data["ac3"]["zones"]["z02"]["state"] == ADVANTAGE_AIR_STATE_CLOSE
@ -144,7 +138,65 @@ async def test_cover_async_setup_entry(
}, },
blocking=True, blocking=True,
) )
assert len(aioclient_mock.mock_calls) == 13
data = loads(aioclient_mock.mock_calls[-2][1].query["json"]) data = loads(aioclient_mock.mock_calls[-2][1].query["json"])
assert data["ac3"]["zones"]["z01"]["state"] == ADVANTAGE_AIR_STATE_OPEN assert data["ac3"]["zones"]["z01"]["state"] == ADVANTAGE_AIR_STATE_OPEN
assert data["ac3"]["zones"]["z02"]["state"] == ADVANTAGE_AIR_STATE_OPEN assert data["ac3"]["zones"]["z02"]["state"] == ADVANTAGE_AIR_STATE_OPEN
async def test_things_cover(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
) -> None:
"""Test cover platform."""
aioclient_mock.get(
TEST_SYSTEM_URL,
text=TEST_SYSTEM_DATA,
)
aioclient_mock.get(
TEST_SET_THING_URL,
text=TEST_SET_RESPONSE,
)
await add_mock_config(hass)
registry = er.async_get(hass)
# Test Blind 1 Entity
entity_id = "cover.blind_1"
thing_id = "200"
state = hass.states.get(entity_id)
assert state
assert state.state == STATE_OPEN
assert state.attributes.get("device_class") == CoverDeviceClass.BLIND
entry = registry.async_get(entity_id)
assert entry
assert entry.unique_id == "uniqueid-200"
await hass.services.async_call(
COVER_DOMAIN,
SERVICE_CLOSE_COVER,
{ATTR_ENTITY_ID: [entity_id]},
blocking=True,
)
assert aioclient_mock.mock_calls[-2][0] == "GET"
assert aioclient_mock.mock_calls[-2][1].path == "/setThings"
data = loads(aioclient_mock.mock_calls[-2][1].query["json"]).get(thing_id)
assert data["id"] == thing_id
assert data["value"] == 0
assert aioclient_mock.mock_calls[-1][0] == "GET"
assert aioclient_mock.mock_calls[-1][1].path == "/getSystemData"
await hass.services.async_call(
COVER_DOMAIN,
SERVICE_OPEN_COVER,
{ATTR_ENTITY_ID: [entity_id]},
blocking=True,
)
assert aioclient_mock.mock_calls[-2][0] == "GET"
assert aioclient_mock.mock_calls[-2][1].path == "/setThings"
data = loads(aioclient_mock.mock_calls[-2][1].query["json"]).get(thing_id)
assert data["id"] == thing_id
assert data["value"] == 100
assert aioclient_mock.mock_calls[-1][0] == "GET"
assert aioclient_mock.mock_calls[-1][1].path == "/getSystemData"

View File

@ -11,13 +11,14 @@ from homeassistant.components.light import (
SERVICE_TURN_OFF, SERVICE_TURN_OFF,
SERVICE_TURN_ON, SERVICE_TURN_ON,
) )
from homeassistant.const import ATTR_ENTITY_ID, STATE_OFF from homeassistant.const import ATTR_ENTITY_ID, STATE_OFF, STATE_ON
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er from homeassistant.helpers import entity_registry as er
from . import ( from . import (
TEST_SET_LIGHT_URL, TEST_SET_LIGHT_URL,
TEST_SET_RESPONSE, TEST_SET_RESPONSE,
TEST_SET_THING_URL,
TEST_SYSTEM_DATA, TEST_SYSTEM_DATA,
TEST_SYSTEM_URL, TEST_SYSTEM_URL,
add_mock_config, add_mock_config,
@ -26,9 +27,7 @@ from . import (
from tests.test_util.aiohttp import AiohttpClientMocker from tests.test_util.aiohttp import AiohttpClientMocker
async def test_light_async_setup_entry( async def test_light(hass: HomeAssistant, aioclient_mock: AiohttpClientMocker) -> None:
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
) -> None:
"""Test light setup.""" """Test light setup."""
aioclient_mock.get( aioclient_mock.get(
@ -44,17 +43,16 @@ async def test_light_async_setup_entry(
registry = er.async_get(hass) registry = er.async_get(hass)
assert len(aioclient_mock.mock_calls) == 1
# Test Light Entity # Test Light Entity
entity_id = "light.light_a" entity_id = "light.light_a"
light_id = "100"
state = hass.states.get(entity_id) state = hass.states.get(entity_id)
assert state assert state
assert state.state == STATE_OFF assert state.state == STATE_OFF
entry = registry.async_get(entity_id) entry = registry.async_get(entity_id)
assert entry assert entry
assert entry.unique_id == "uniqueid-100" assert entry.unique_id == f"uniqueid-{light_id}"
await hass.services.async_call( await hass.services.async_call(
LIGHT_DOMAIN, LIGHT_DOMAIN,
@ -62,11 +60,10 @@ async def test_light_async_setup_entry(
{ATTR_ENTITY_ID: [entity_id]}, {ATTR_ENTITY_ID: [entity_id]},
blocking=True, blocking=True,
) )
assert len(aioclient_mock.mock_calls) == 3
assert aioclient_mock.mock_calls[-2][0] == "GET" assert aioclient_mock.mock_calls[-2][0] == "GET"
assert aioclient_mock.mock_calls[-2][1].path == "/setLights" assert aioclient_mock.mock_calls[-2][1].path == "/setLights"
data = loads(aioclient_mock.mock_calls[-2][1].query["json"]).get("100") data = loads(aioclient_mock.mock_calls[-2][1].query["json"]).get(light_id)
assert data["id"] == "100" assert data["id"] == light_id
assert data["state"] == ADVANTAGE_AIR_STATE_ON assert data["state"] == ADVANTAGE_AIR_STATE_ON
assert aioclient_mock.mock_calls[-1][0] == "GET" assert aioclient_mock.mock_calls[-1][0] == "GET"
assert aioclient_mock.mock_calls[-1][1].path == "/getSystemData" assert aioclient_mock.mock_calls[-1][1].path == "/getSystemData"
@ -77,21 +74,21 @@ async def test_light_async_setup_entry(
{ATTR_ENTITY_ID: [entity_id]}, {ATTR_ENTITY_ID: [entity_id]},
blocking=True, blocking=True,
) )
assert len(aioclient_mock.mock_calls) == 5
assert aioclient_mock.mock_calls[-2][0] == "GET" assert aioclient_mock.mock_calls[-2][0] == "GET"
assert aioclient_mock.mock_calls[-2][1].path == "/setLights" assert aioclient_mock.mock_calls[-2][1].path == "/setLights"
data = loads(aioclient_mock.mock_calls[-2][1].query["json"]).get("100") data = loads(aioclient_mock.mock_calls[-2][1].query["json"]).get(light_id)
assert data["id"] == "100" assert data["id"] == light_id
assert data["state"] == ADVANTAGE_AIR_STATE_OFF assert data["state"] == ADVANTAGE_AIR_STATE_OFF
assert aioclient_mock.mock_calls[-1][0] == "GET" assert aioclient_mock.mock_calls[-1][0] == "GET"
assert aioclient_mock.mock_calls[-1][1].path == "/getSystemData" assert aioclient_mock.mock_calls[-1][1].path == "/getSystemData"
# Test Dimmable Light Entity # Test Dimmable Light Entity
entity_id = "light.light_b" entity_id = "light.light_b"
light_id = "101"
entry = registry.async_get(entity_id) entry = registry.async_get(entity_id)
assert entry assert entry
assert entry.unique_id == "uniqueid-101" assert entry.unique_id == f"uniqueid-{light_id}"
await hass.services.async_call( await hass.services.async_call(
LIGHT_DOMAIN, LIGHT_DOMAIN,
@ -99,12 +96,69 @@ async def test_light_async_setup_entry(
{ATTR_ENTITY_ID: [entity_id], ATTR_BRIGHTNESS: 128}, {ATTR_ENTITY_ID: [entity_id], ATTR_BRIGHTNESS: 128},
blocking=True, blocking=True,
) )
assert len(aioclient_mock.mock_calls) == 7
assert aioclient_mock.mock_calls[-2][0] == "GET" assert aioclient_mock.mock_calls[-2][0] == "GET"
assert aioclient_mock.mock_calls[-2][1].path == "/setLights" assert aioclient_mock.mock_calls[-2][1].path == "/setLights"
data = loads(aioclient_mock.mock_calls[-2][1].query["json"]).get("101") data = loads(aioclient_mock.mock_calls[-2][1].query["json"]).get(light_id)
assert data["id"] == "101" assert data["id"] == light_id
assert data["value"] == 50 assert data["value"] == 50
assert data["state"] == ADVANTAGE_AIR_STATE_ON assert data["state"] == ADVANTAGE_AIR_STATE_ON
assert aioclient_mock.mock_calls[-1][0] == "GET" assert aioclient_mock.mock_calls[-1][0] == "GET"
assert aioclient_mock.mock_calls[-1][1].path == "/getSystemData" assert aioclient_mock.mock_calls[-1][1].path == "/getSystemData"
async def test_things_light(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
) -> None:
"""Test things lights."""
aioclient_mock.get(
TEST_SYSTEM_URL,
text=TEST_SYSTEM_DATA,
)
aioclient_mock.get(
TEST_SET_THING_URL,
text=TEST_SET_RESPONSE,
)
await add_mock_config(hass)
registry = er.async_get(hass)
# Test Switch Entity
entity_id = "light.thing_light_dimmable"
light_id = "204"
state = hass.states.get(entity_id)
assert state
assert state.state == STATE_ON
entry = registry.async_get(entity_id)
assert entry
assert entry.unique_id == "uniqueid-204"
await hass.services.async_call(
LIGHT_DOMAIN,
SERVICE_TURN_OFF,
{ATTR_ENTITY_ID: [entity_id]},
blocking=True,
)
assert aioclient_mock.mock_calls[-2][0] == "GET"
assert aioclient_mock.mock_calls[-2][1].path == "/setThings"
data = loads(aioclient_mock.mock_calls[-2][1].query["json"]).get(light_id)
assert data["id"] == light_id
assert data["value"] == 0
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_ON,
{ATTR_ENTITY_ID: [entity_id], ATTR_BRIGHTNESS: 128},
blocking=True,
)
assert aioclient_mock.mock_calls[-2][0] == "GET"
assert aioclient_mock.mock_calls[-2][1].path == "/setThings"
data = loads(aioclient_mock.mock_calls[-2][1].query["json"]).get(light_id)
assert data["id"] == light_id
assert data["value"] == 50
assert aioclient_mock.mock_calls[-1][0] == "GET"
assert aioclient_mock.mock_calls[-1][1].path == "/getSystemData"

View File

@ -10,12 +10,13 @@ from homeassistant.components.switch import (
SERVICE_TURN_OFF, SERVICE_TURN_OFF,
SERVICE_TURN_ON, SERVICE_TURN_ON,
) )
from homeassistant.const import ATTR_ENTITY_ID, STATE_OFF from homeassistant.const import ATTR_ENTITY_ID, STATE_OFF, STATE_ON
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er from homeassistant.helpers import entity_registry as er
from . import ( from . import (
TEST_SET_RESPONSE, TEST_SET_RESPONSE,
TEST_SET_THING_URL,
TEST_SET_URL, TEST_SET_URL,
TEST_SYSTEM_DATA, TEST_SYSTEM_DATA,
TEST_SYSTEM_URL, TEST_SYSTEM_URL,
@ -43,8 +44,6 @@ async def test_cover_async_setup_entry(
registry = er.async_get(hass) registry = er.async_get(hass)
assert len(aioclient_mock.mock_calls) == 1
# Test Switch Entity # Test Switch Entity
entity_id = "switch.myzone_fresh_air" entity_id = "switch.myzone_fresh_air"
state = hass.states.get(entity_id) state = hass.states.get(entity_id)
@ -61,7 +60,6 @@ async def test_cover_async_setup_entry(
{ATTR_ENTITY_ID: [entity_id]}, {ATTR_ENTITY_ID: [entity_id]},
blocking=True, blocking=True,
) )
assert len(aioclient_mock.mock_calls) == 3
assert aioclient_mock.mock_calls[-2][0] == "GET" assert aioclient_mock.mock_calls[-2][0] == "GET"
assert aioclient_mock.mock_calls[-2][1].path == "/setAircon" assert aioclient_mock.mock_calls[-2][1].path == "/setAircon"
data = loads(aioclient_mock.mock_calls[-2][1].query["json"]) data = loads(aioclient_mock.mock_calls[-2][1].query["json"])
@ -75,10 +73,67 @@ async def test_cover_async_setup_entry(
{ATTR_ENTITY_ID: [entity_id]}, {ATTR_ENTITY_ID: [entity_id]},
blocking=True, blocking=True,
) )
assert len(aioclient_mock.mock_calls) == 5
assert aioclient_mock.mock_calls[-2][0] == "GET" assert aioclient_mock.mock_calls[-2][0] == "GET"
assert aioclient_mock.mock_calls[-2][1].path == "/setAircon" assert aioclient_mock.mock_calls[-2][1].path == "/setAircon"
data = loads(aioclient_mock.mock_calls[-2][1].query["json"]) data = loads(aioclient_mock.mock_calls[-2][1].query["json"])
assert data["ac1"]["info"]["freshAirStatus"] == ADVANTAGE_AIR_STATE_OFF assert data["ac1"]["info"]["freshAirStatus"] == ADVANTAGE_AIR_STATE_OFF
assert aioclient_mock.mock_calls[-1][0] == "GET" assert aioclient_mock.mock_calls[-1][0] == "GET"
assert aioclient_mock.mock_calls[-1][1].path == "/getSystemData" assert aioclient_mock.mock_calls[-1][1].path == "/getSystemData"
async def test_things_switch(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
) -> None:
"""Test things switches."""
aioclient_mock.get(
TEST_SYSTEM_URL,
text=TEST_SYSTEM_DATA,
)
aioclient_mock.get(
TEST_SET_THING_URL,
text=TEST_SET_RESPONSE,
)
await add_mock_config(hass)
registry = er.async_get(hass)
# Test Switch Entity
entity_id = "switch.relay"
thing_id = "205"
state = hass.states.get(entity_id)
assert state
assert state.state == STATE_ON
entry = registry.async_get(entity_id)
assert entry
assert entry.unique_id == "uniqueid-205"
await hass.services.async_call(
SWITCH_DOMAIN,
SERVICE_TURN_OFF,
{ATTR_ENTITY_ID: [entity_id]},
blocking=True,
)
assert aioclient_mock.mock_calls[-2][0] == "GET"
assert aioclient_mock.mock_calls[-2][1].path == "/setThings"
data = loads(aioclient_mock.mock_calls[-2][1].query["json"]).get(thing_id)
assert data["id"] == thing_id
assert data["value"] == 0
assert aioclient_mock.mock_calls[-1][0] == "GET"
assert aioclient_mock.mock_calls[-1][1].path == "/getSystemData"
await hass.services.async_call(
SWITCH_DOMAIN,
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: [entity_id]},
blocking=True,
)
assert aioclient_mock.mock_calls[-2][0] == "GET"
assert aioclient_mock.mock_calls[-2][1].path == "/setThings"
data = loads(aioclient_mock.mock_calls[-2][1].query["json"]).get(thing_id)
assert data["id"] == thing_id
assert data["value"] == 100
assert aioclient_mock.mock_calls[-1][0] == "GET"
assert aioclient_mock.mock_calls[-1][1].path == "/getSystemData"