Add Sensor Platform to Advantage Air (#41870)

* WIP Add Sensor platform

* Code quality improvements

* Readability improvements

* Fix RSSI in fixture

* Sensor platform tests

* Created parent sensor class

* Fix DOMAIN namespace

* Code Coverage fix for impossible case

* Use parent class

* Add to fixture for code coverage

* Description Update

* Use consistent name for ADVANTAGE_AIR_DOMAIN

Co-authored-by: Robert Svensson <Kane610@users.noreply.github.com>

* Set ADVANTAGE_AIR_DOMAIN where required

Co-authored-by: Robert Svensson <Kane610@users.noreply.github.com>
pull/42010/head
Brett 2020-10-18 02:29:36 +10:00 committed by GitHub
parent 2443f5d108
commit e71d851973
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 336 additions and 5 deletions

View File

@ -17,7 +17,7 @@ from homeassistant.helpers.update_coordinator import (
from .const import ADVANTAGE_AIR_RETRY, DOMAIN
ADVANTAGE_AIR_SYNC_INTERVAL = 15
ADVANTAGE_AIR_PLATFORMS = ["binary_sensor", "climate", "cover"]
ADVANTAGE_AIR_PLATFORMS = ["climate", "cover", "binary_sensor", "sensor"]
_LOGGER = logging.getLogger(__name__)

View File

@ -0,0 +1,151 @@
"""Sensor platform for Advantage Air integration."""
import voluptuous as vol
from homeassistant.components.advantage_air import AdvantageAirEntity
from homeassistant.const import PERCENTAGE
from homeassistant.helpers import config_validation as cv, entity_platform
from .const import ADVANTAGE_AIR_STATE_OPEN, DOMAIN as ADVANTAGE_AIR_DOMAIN
ADVANTAGE_AIR_SET_COUNTDOWN_VALUE = "minutes"
ADVANTAGE_AIR_SET_COUNTDOWN_UNIT = "min"
ADVANTAGE_AIR_SERVICE_SET_TIME_TO = "set_time_to"
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up AdvantageAir sensor platform."""
instance = hass.data[ADVANTAGE_AIR_DOMAIN][config_entry.entry_id]
entities = []
for ac_key, ac_device in instance["coordinator"].data["aircons"].items():
entities.append(AdvantageAirTimeTo(instance, ac_key, "On"))
entities.append(AdvantageAirTimeTo(instance, ac_key, "Off"))
for zone_key, zone in ac_device["zones"].items():
# Only show damper sensors when zone is in temperature control
if zone["type"] != 0:
entities.append(AdvantageAirZoneVent(instance, ac_key, zone_key))
# Only show wireless signal strength sensors when using wireless sensors
if zone["rssi"] > 0:
entities.append(AdvantageAirZoneSignal(instance, ac_key, zone_key))
async_add_entities(entities)
platform = entity_platform.current_platform.get()
platform.async_register_entity_service(
ADVANTAGE_AIR_SERVICE_SET_TIME_TO,
{vol.Required("minutes"): cv.positive_int},
"set_time_to",
)
class AdvantageAirTimeTo(AdvantageAirEntity):
"""Representation of Advantage Air timer control."""
def __init__(self, instance, ac_key, time_period):
"""Initialize the Advantage Air timer control."""
super().__init__(instance, ac_key)
self.time_period = time_period
@property
def name(self):
"""Return the name."""
return f'{self._ac["name"]} Time To {self.time_period}'
@property
def unique_id(self):
"""Return a unique id."""
return f'{self.coordinator.data["system"]["rid"]}-{self.ac_key}-timeto{self.time_period}'
@property
def state(self):
"""Return the current value."""
return self._ac[f"countDownTo{self.time_period}"]
@property
def unit_of_measurement(self):
"""Return the unit of measurement."""
return ADVANTAGE_AIR_SET_COUNTDOWN_UNIT
@property
def icon(self):
"""Return a representative icon of the timer."""
if self._ac[f"countDownTo{self.time_period}"] > 0:
return "mdi:timer-outline"
return "mdi:timer-off-outline"
async def set_time_to(self, **kwargs):
"""Set the timer value."""
value = min(720, max(0, int(kwargs[ADVANTAGE_AIR_SET_COUNTDOWN_VALUE])))
await self.async_change(
{self.ac_key: {"info": {f"countDownTo{self.time_period}": value}}}
)
class AdvantageAirZoneVent(AdvantageAirEntity):
"""Representation of Advantage Air Zone Vent Sensor."""
@property
def name(self):
"""Return the name."""
return f'{self._zone["name"]} Vent'
@property
def unique_id(self):
"""Return a unique id."""
return f'{self.coordinator.data["system"]["rid"]}-{self.ac_key}-{self.zone_key}-vent'
@property
def state(self):
"""Return the current value of the air vent."""
if self._zone["state"] == ADVANTAGE_AIR_STATE_OPEN:
return self._zone["value"]
return 0
@property
def unit_of_measurement(self):
"""Return the percent sign."""
return PERCENTAGE
@property
def icon(self):
"""Return a representative icon."""
if self._zone["state"] == ADVANTAGE_AIR_STATE_OPEN:
return "mdi:fan"
return "mdi:fan-off"
class AdvantageAirZoneSignal(AdvantageAirEntity):
"""Representation of Advantage Air Zone wireless signal sensor."""
@property
def name(self):
"""Return the name."""
return f'{self._zone["name"]} Signal'
@property
def unique_id(self):
"""Return a unique id."""
return f'{self.coordinator.data["system"]["rid"]}-{self.ac_key}-{self.zone_key}-signal'
@property
def state(self):
"""Return the current value of the wireless signal."""
return self._zone["rssi"]
@property
def unit_of_measurement(self):
"""Return the percent sign."""
return PERCENTAGE
@property
def icon(self):
"""Return a representative icon."""
if self._zone["rssi"] >= 80:
return "mdi:wifi-strength-4"
if self._zone["rssi"] >= 60:
return "mdi:wifi-strength-3"
if self._zone["rssi"] >= 40:
return "mdi:wifi-strength-2"
if self._zone["rssi"] >= 20:
return "mdi:wifi-strength-1"
return "mdi:wifi-strength-outline"

View File

@ -0,0 +1,9 @@
set_time_to:
description: Control timers to turn the system on or off after a set number of minutes
fields:
entity_id:
description: Time To sensor entity
example: "sensor.ac_time_to_on"
minutes:
description: Minutes until action
example: "60"

View File

@ -0,0 +1,126 @@
"""Test the Advantage Air Sensor Platform."""
from json import loads
from homeassistant.components.advantage_air.const import DOMAIN as ADVANTAGE_AIR_DOMAIN
from homeassistant.components.advantage_air.sensor import (
ADVANTAGE_AIR_SERVICE_SET_TIME_TO,
ADVANTAGE_AIR_SET_COUNTDOWN_VALUE,
)
from homeassistant.const import ATTR_ENTITY_ID
from tests.components.advantage_air import (
TEST_SET_RESPONSE,
TEST_SET_URL,
TEST_SYSTEM_DATA,
TEST_SYSTEM_URL,
add_mock_config,
)
async def test_sensor_platform(hass, aioclient_mock):
"""Test sensor platform."""
aioclient_mock.get(
TEST_SYSTEM_URL,
text=TEST_SYSTEM_DATA,
)
aioclient_mock.get(
TEST_SET_URL,
text=TEST_SET_RESPONSE,
)
await add_mock_config(hass)
registry = await hass.helpers.entity_registry.async_get_registry()
assert len(aioclient_mock.mock_calls) == 1
# Test First TimeToOn Sensor
entity_id = "sensor.ac_one_time_to_on"
state = hass.states.get(entity_id)
assert state
assert int(state.state) == 0
entry = registry.async_get(entity_id)
assert entry
assert entry.unique_id == "uniqueid-ac1-timetoOn"
value = 20
await hass.services.async_call(
ADVANTAGE_AIR_DOMAIN,
ADVANTAGE_AIR_SERVICE_SET_TIME_TO,
{ATTR_ENTITY_ID: [entity_id], ADVANTAGE_AIR_SET_COUNTDOWN_VALUE: value},
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 == "/setAircon"
data = loads(aioclient_mock.mock_calls[-2][1].query["json"])
assert data["ac1"]["info"]["countDownToOn"] == value
assert aioclient_mock.mock_calls[-1][0] == "GET"
assert aioclient_mock.mock_calls[-1][1].path == "/getSystemData"
# Test First TimeToOff Sensor
entity_id = "sensor.ac_one_time_to_off"
state = hass.states.get(entity_id)
assert state
assert int(state.state) == 10
entry = registry.async_get(entity_id)
assert entry
assert entry.unique_id == "uniqueid-ac1-timetoOff"
value = 0
await hass.services.async_call(
ADVANTAGE_AIR_DOMAIN,
ADVANTAGE_AIR_SERVICE_SET_TIME_TO,
{ATTR_ENTITY_ID: [entity_id], ADVANTAGE_AIR_SET_COUNTDOWN_VALUE: value},
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 == "/setAircon"
data = loads(aioclient_mock.mock_calls[-2][1].query["json"])
assert data["ac1"]["info"]["countDownToOff"] == value
assert aioclient_mock.mock_calls[-1][0] == "GET"
assert aioclient_mock.mock_calls[-1][1].path == "/getSystemData"
# Test First Zone Vent Sensor
entity_id = "sensor.zone_open_with_sensor_vent"
state = hass.states.get(entity_id)
assert state
assert int(state.state) == 100
entry = registry.async_get(entity_id)
assert entry
assert entry.unique_id == "uniqueid-ac1-z01-vent"
# Test Second Zone Vent Sensor
entity_id = "sensor.zone_closed_with_sensor_vent"
state = hass.states.get(entity_id)
assert state
assert int(state.state) == 0
entry = registry.async_get(entity_id)
assert entry
assert entry.unique_id == "uniqueid-ac1-z02-vent"
# Test First Zone Signal Sensor
entity_id = "sensor.zone_open_with_sensor_signal"
state = hass.states.get(entity_id)
assert state
assert int(state.state) == 40
entry = registry.async_get(entity_id)
assert entry
assert entry.unique_id == "uniqueid-ac1-z01-signal"
# Test Second Zone Signal Sensor
entity_id = "sensor.zone_closed_with_sensor_signal"
state = hass.states.get(entity_id)
assert state
assert int(state.state) == 10
entry = registry.async_get(entity_id)
assert entry
assert entry.unique_id == "uniqueid-ac1-z02-signal"

View File

@ -3,7 +3,7 @@
"ac1": {
"info": {
"climateControlModeIsRunning": false,
"countDownToOff": 0,
"countDownToOff": 10,
"countDownToOn": 0,
"fan": "high",
"filterCleanStatus": 0,
@ -24,7 +24,7 @@
"motionConfig": 2,
"name": "Zone open with Sensor",
"number": 1,
"rssi": -50,
"rssi": 40,
"setTemp": 24,
"state": "open",
"type": 1,
@ -39,7 +39,52 @@
"motionConfig": 2,
"name": "Zone closed with Sensor",
"number": 1,
"rssi": -50,
"rssi": 10,
"setTemp": 24,
"state": "close",
"type": 1,
"value": 0
},
"z03": {
"error": 0,
"maxDamper": 100,
"measuredTemp": 25,
"minDamper": 0,
"motion": 1,
"motionConfig": 1,
"name": "Zone 3",
"number": 1,
"rssi": 25,
"setTemp": 24,
"state": "close",
"type": 1,
"value": 0
},
"z04": {
"error": 0,
"maxDamper": 100,
"measuredTemp": 25,
"minDamper": 0,
"motion": 1,
"motionConfig": 1,
"name": "Zone 4",
"number": 1,
"rssi": 75,
"setTemp": 24,
"state": "close",
"type": 1,
"value": 0
},
"z05": {
"error": 0,
"maxDamper": 100,
"measuredTemp": 25,
"minDamper": 0,
"motion": 1,
"motionConfig": 1,
"name": "Zone 5",
"number": 1,
"rssi": 100,
"setTemp": 24,
"state": "close",
"type": 1,
@ -51,7 +96,7 @@
"info": {
"climateControlModeIsRunning": false,
"countDownToOff": 0,
"countDownToOn": 0,
"countDownToOn": 20,
"fan": "low",
"filterCleanStatus": 1,
"freshAirStatus": "none",