mirror of
https://github.com/home-assistant/core.git
synced 2025-07-12 15:57:06 +00:00
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>
This commit is contained in:
parent
2443f5d108
commit
e71d851973
@ -17,7 +17,7 @@ from homeassistant.helpers.update_coordinator import (
|
|||||||
from .const import ADVANTAGE_AIR_RETRY, DOMAIN
|
from .const import ADVANTAGE_AIR_RETRY, DOMAIN
|
||||||
|
|
||||||
ADVANTAGE_AIR_SYNC_INTERVAL = 15
|
ADVANTAGE_AIR_SYNC_INTERVAL = 15
|
||||||
ADVANTAGE_AIR_PLATFORMS = ["binary_sensor", "climate", "cover"]
|
ADVANTAGE_AIR_PLATFORMS = ["climate", "cover", "binary_sensor", "sensor"]
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
151
homeassistant/components/advantage_air/sensor.py
Normal file
151
homeassistant/components/advantage_air/sensor.py
Normal 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"
|
9
homeassistant/components/advantage_air/services.yaml
Normal file
9
homeassistant/components/advantage_air/services.yaml
Normal 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"
|
126
tests/components/advantage_air/test_sensor.py
Normal file
126
tests/components/advantage_air/test_sensor.py
Normal 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"
|
53
tests/fixtures/advantage_air/getSystemData.json
vendored
53
tests/fixtures/advantage_air/getSystemData.json
vendored
@ -3,7 +3,7 @@
|
|||||||
"ac1": {
|
"ac1": {
|
||||||
"info": {
|
"info": {
|
||||||
"climateControlModeIsRunning": false,
|
"climateControlModeIsRunning": false,
|
||||||
"countDownToOff": 0,
|
"countDownToOff": 10,
|
||||||
"countDownToOn": 0,
|
"countDownToOn": 0,
|
||||||
"fan": "high",
|
"fan": "high",
|
||||||
"filterCleanStatus": 0,
|
"filterCleanStatus": 0,
|
||||||
@ -24,7 +24,7 @@
|
|||||||
"motionConfig": 2,
|
"motionConfig": 2,
|
||||||
"name": "Zone open with Sensor",
|
"name": "Zone open with Sensor",
|
||||||
"number": 1,
|
"number": 1,
|
||||||
"rssi": -50,
|
"rssi": 40,
|
||||||
"setTemp": 24,
|
"setTemp": 24,
|
||||||
"state": "open",
|
"state": "open",
|
||||||
"type": 1,
|
"type": 1,
|
||||||
@ -39,7 +39,52 @@
|
|||||||
"motionConfig": 2,
|
"motionConfig": 2,
|
||||||
"name": "Zone closed with Sensor",
|
"name": "Zone closed with Sensor",
|
||||||
"number": 1,
|
"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,
|
"setTemp": 24,
|
||||||
"state": "close",
|
"state": "close",
|
||||||
"type": 1,
|
"type": 1,
|
||||||
@ -51,7 +96,7 @@
|
|||||||
"info": {
|
"info": {
|
||||||
"climateControlModeIsRunning": false,
|
"climateControlModeIsRunning": false,
|
||||||
"countDownToOff": 0,
|
"countDownToOff": 0,
|
||||||
"countDownToOn": 0,
|
"countDownToOn": 20,
|
||||||
"fan": "low",
|
"fan": "low",
|
||||||
"filterCleanStatus": 1,
|
"filterCleanStatus": 1,
|
||||||
"freshAirStatus": "none",
|
"freshAirStatus": "none",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user