Add set_myzone service to Advantage Air (#46934)

* Add set_myzone service requested on forums

* Add MyZone binary sensor for climate zones

* Fixed Black on binary_sensor.py

* Add the new entity

* Fix spelling

* Test myZone value

* MyZone Binary Sensor test

* Fixed new binary sensor tests

* Fix removed dependancy

* Correct fixture

* Update homeassistant/components/advantage_air/binary_sensor.py

Co-authored-by: Philip Allgaier <philip.allgaier@gmx.de>

* Updated services.yaml to use target

Co-authored-by: Philip Allgaier <philip.allgaier@gmx.de>
pull/49371/head
Brett 2021-04-18 18:36:34 +10:00 committed by GitHub
parent b2c33c1373
commit afd79a675c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 125 additions and 9 deletions

View File

@ -24,6 +24,9 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
# Only add motion sensor when motion is enabled
if zone["motionConfig"] >= 2:
entities.append(AdvantageAirZoneMotion(instance, ac_key, zone_key))
# Only add MyZone if it is available
if zone["type"] != 0:
entities.append(AdvantageAirZoneMyZone(instance, ac_key, zone_key))
async_add_entities(entities)
@ -73,3 +76,27 @@ class AdvantageAirZoneMotion(AdvantageAirEntity, BinarySensorEntity):
def is_on(self):
"""Return if motion is detect."""
return self._zone["motion"]
class AdvantageAirZoneMyZone(AdvantageAirEntity, BinarySensorEntity):
"""Advantage Air Zone MyZone."""
@property
def name(self):
"""Return the name."""
return f'{self._zone["name"]} MyZone'
@property
def unique_id(self):
"""Return a unique id."""
return f'{self.coordinator.data["system"]["rid"]}-{self.ac_key}-{self.zone_key}-myzone'
@property
def is_on(self):
"""Return if this zone is the myZone."""
return self._zone["number"] == self._ac["myZone"]
@property
def entity_registry_enabled_default(self):
"""Return false to disable this entity by default."""
return False

View File

@ -15,6 +15,7 @@ from homeassistant.components.climate.const import (
SUPPORT_TARGET_TEMPERATURE,
)
from homeassistant.const import ATTR_TEMPERATURE, PRECISION_WHOLE, TEMP_CELSIUS
from homeassistant.helpers import entity_platform
from .const import (
ADVANTAGE_AIR_STATE_CLOSE,
@ -49,6 +50,7 @@ AC_HVAC_MODES = [
HVAC_MODE_FAN_ONLY,
HVAC_MODE_DRY,
]
ADVANTAGE_AIR_SERVICE_SET_MYZONE = "set_myzone"
ZONE_HVAC_MODES = [HVAC_MODE_OFF, HVAC_MODE_FAN_ONLY]
PARALLEL_UPDATES = 0
@ -68,6 +70,13 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
entities.append(AdvantageAirZone(instance, ac_key, zone_key))
async_add_entities(entities)
platform = entity_platform.current_platform.get()
platform.async_register_entity_service(
ADVANTAGE_AIR_SERVICE_SET_MYZONE,
{},
"set_myzone",
)
class AdvantageAirClimateEntity(AdvantageAirEntity, ClimateEntity):
"""AdvantageAir Climate class."""
@ -233,3 +242,9 @@ class AdvantageAirZone(AdvantageAirClimateEntity):
await self.async_change(
{self.ac_key: {"zones": {self.zone_key: {"setTemp": temp}}}}
)
async def set_myzone(self, **kwargs):
"""Set this zone as the 'MyZone'."""
await self.async_change(
{self.ac_key: {"info": {"myZone": self._zone["number"]}}}
)

View File

@ -1,9 +1,18 @@
set_time_to:
name: Set Time To
description: Control timers to turn the system on or off after a set number of minutes
target:
entity:
integration: advantage_air
domain: sensor
fields:
entity_id:
description: Time To sensor entity
example: "sensor.ac_time_to_on"
minutes:
description: Minutes until action
example: "60"
set_myzone:
name: Set MyZone
description: Change which zone is set as the reference for temperature control
target:
entity:
integration: advantage_air
domain: climate

View File

@ -1,8 +1,12 @@
"""Test the Advantage Air Binary Sensor Platform."""
from datetime import timedelta
from homeassistant.config_entries import RELOAD_AFTER_UPDATE_DELAY
from homeassistant.const import STATE_OFF, STATE_ON
from homeassistant.helpers import entity_registry as er
from homeassistant.util import dt
from tests.common import async_fire_time_changed
from tests.components.advantage_air import (
TEST_SET_RESPONSE,
TEST_SET_URL,
@ -68,3 +72,47 @@ async def test_binary_sensor_async_setup_entry(hass, aioclient_mock):
entry = registry.async_get(entity_id)
assert entry
assert entry.unique_id == "uniqueid-ac1-z02-motion"
# Test First MyZone Sensor (disabled by default)
entity_id = "binary_sensor.zone_open_with_sensor_myzone"
assert not hass.states.get(entity_id)
registry.async_update_entity(entity_id=entity_id, disabled_by=None)
await hass.async_block_till_done()
async_fire_time_changed(
hass,
dt.utcnow() + timedelta(seconds=RELOAD_AFTER_UPDATE_DELAY + 1),
)
await hass.async_block_till_done()
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-ac1-z01-myzone"
# Test Second Motion Sensor (disabled by default)
entity_id = "binary_sensor.zone_closed_with_sensor_myzone"
assert not hass.states.get(entity_id)
registry.async_update_entity(entity_id=entity_id, disabled_by=None)
await hass.async_block_till_done()
async_fire_time_changed(
hass,
dt.utcnow() + timedelta(seconds=RELOAD_AFTER_UPDATE_DELAY + 1),
)
await hass.async_block_till_done()
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-ac1-z02-myzone"

View File

@ -3,12 +3,14 @@
from json import loads
from homeassistant.components.advantage_air.climate import (
ADVANTAGE_AIR_SERVICE_SET_MYZONE,
HASS_FAN_MODES,
HASS_HVAC_MODES,
)
from homeassistant.components.advantage_air.const import (
ADVANTAGE_AIR_STATE_OFF,
ADVANTAGE_AIR_STATE_ON,
DOMAIN as ADVANTAGE_AIR_DOMAIN,
)
from homeassistant.components.climate.const import (
ATTR_FAN_MODE,
@ -170,6 +172,21 @@ async def test_climate_async_setup_entry(hass, aioclient_mock):
assert aioclient_mock.mock_calls[-1][0] == "GET"
assert aioclient_mock.mock_calls[-1][1].path == "/getSystemData"
# Test set_myair service
await hass.services.async_call(
ADVANTAGE_AIR_DOMAIN,
ADVANTAGE_AIR_SERVICE_SET_MYZONE,
{ATTR_ENTITY_ID: [entity_id]},
blocking=True,
)
assert len(aioclient_mock.mock_calls) == 17
assert aioclient_mock.mock_calls[-2][0] == "GET"
assert aioclient_mock.mock_calls[-2][1].path == "/setAircon"
data = loads(aioclient_mock.mock_calls[-2][1].query["json"])
assert data["ac1"]["info"]["myZone"] == 1
assert aioclient_mock.mock_calls[-1][0] == "GET"
assert aioclient_mock.mock_calls[-1][1].path == "/getSystemData"
async def test_climate_async_failed_update(hass, aioclient_mock):
"""Test climate change failure."""

View File

@ -9,7 +9,7 @@
"filterCleanStatus": 0,
"freshAirStatus": "off",
"mode": "vent",
"myZone": 0,
"myZone": 1,
"name": "AC One",
"setTemp": 24,
"state": "on"
@ -38,7 +38,7 @@
"motion": 0,
"motionConfig": 2,
"name": "Zone closed with Sensor",
"number": 1,
"number": 2,
"rssi": 10,
"setTemp": 24,
"state": "close",
@ -53,7 +53,7 @@
"motion": 1,
"motionConfig": 1,
"name": "Zone 3",
"number": 1,
"number": 3,
"rssi": 25,
"setTemp": 24,
"state": "close",
@ -68,7 +68,7 @@
"motion": 1,
"motionConfig": 1,
"name": "Zone 4",
"number": 1,
"number": 4,
"rssi": 75,
"setTemp": 24,
"state": "close",
@ -80,7 +80,7 @@
"maxDamper": 100,
"measuredTemp": 25,
"minDamper": 0,
"motion": 1,
"motion": 5,
"motionConfig": 1,
"name": "Zone 5",
"number": 1,
@ -130,7 +130,7 @@
"motion": 0,
"motionConfig": 0,
"name": "Zone closed without sensor",
"number": 1,
"number": 2,
"rssi": 0,
"setTemp": 24,
"state": "close",