mirror of
https://github.com/home-assistant/core.git
synced 2025-07-26 22:57:17 +00:00
Provide ability to select nexia RoomIQ sensors (#144278)
Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
parent
6bc6733c40
commit
9729f1f38b
@ -65,6 +65,9 @@
|
||||
"hold": {
|
||||
"name": "Hold"
|
||||
},
|
||||
"room_iq_sensor": {
|
||||
"name": "Include {sensor_name}"
|
||||
},
|
||||
"emergency_heat": {
|
||||
"name": "Emergency heat"
|
||||
}
|
||||
|
@ -2,14 +2,19 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Iterable
|
||||
import functools as ft
|
||||
from typing import Any
|
||||
|
||||
from nexia.const import OPERATION_MODE_OFF
|
||||
from nexia.roomiq import NexiaRoomIQHarmonizer
|
||||
from nexia.sensor import NexiaSensor
|
||||
from nexia.thermostat import NexiaThermostat
|
||||
from nexia.zone import NexiaThermostatZone
|
||||
|
||||
from homeassistant.components.switch import SwitchEntity
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
|
||||
from homeassistant.core import Event, HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
|
||||
from .coordinator import NexiaDataUpdateCoordinator
|
||||
@ -17,6 +22,14 @@ from .entity import NexiaThermostatEntity, NexiaThermostatZoneEntity
|
||||
from .types import NexiaConfigEntry
|
||||
|
||||
|
||||
async def _stop_harmonizers(
|
||||
_: Event, harmonizers: Iterable[NexiaRoomIQHarmonizer]
|
||||
) -> None:
|
||||
"""Run the shutdown methods when preparing to stop."""
|
||||
for harmonizer in harmonizers:
|
||||
await harmonizer.async_shutdown() # Never suspends
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: NexiaConfigEntry,
|
||||
@ -25,7 +38,8 @@ async def async_setup_entry(
|
||||
"""Set up switches for a Nexia device."""
|
||||
coordinator = config_entry.runtime_data
|
||||
nexia_home = coordinator.nexia_home
|
||||
entities: list[NexiaHoldSwitch | NexiaEmergencyHeatSwitch] = []
|
||||
entities: list[SwitchEntity] = []
|
||||
room_iq_zones: dict[int, NexiaRoomIQHarmonizer] = {}
|
||||
for thermostat_id in nexia_home.get_thermostat_ids():
|
||||
thermostat: NexiaThermostat = nexia_home.get_thermostat_by_id(thermostat_id)
|
||||
if thermostat.has_emergency_heat():
|
||||
@ -33,8 +47,18 @@ async def async_setup_entry(
|
||||
for zone_id in thermostat.get_zone_ids():
|
||||
zone: NexiaThermostatZone = thermostat.get_zone_by_id(zone_id)
|
||||
entities.append(NexiaHoldSwitch(coordinator, zone))
|
||||
if len(zone_sensors := zone.get_sensors()) > 1:
|
||||
entities.extend(
|
||||
NexiaRoomIQSwitch(coordinator, zone, sensor, room_iq_zones)
|
||||
for sensor in zone_sensors
|
||||
)
|
||||
|
||||
async_add_entities(entities)
|
||||
if room_iq_zones:
|
||||
listener = ft.partial(_stop_harmonizers, harmonizers=room_iq_zones.values())
|
||||
config_entry.async_on_unload(
|
||||
hass.bus.async_listen(EVENT_HOMEASSISTANT_STOP, listener)
|
||||
)
|
||||
|
||||
|
||||
class NexiaHoldSwitch(NexiaThermostatZoneEntity, SwitchEntity):
|
||||
@ -68,6 +92,49 @@ class NexiaHoldSwitch(NexiaThermostatZoneEntity, SwitchEntity):
|
||||
self._signal_zone_update()
|
||||
|
||||
|
||||
class NexiaRoomIQSwitch(NexiaThermostatZoneEntity, SwitchEntity):
|
||||
"""Provides Nexia RoomIQ sensor switch support."""
|
||||
|
||||
_attr_translation_key = "room_iq_sensor"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: NexiaDataUpdateCoordinator,
|
||||
zone: NexiaThermostatZone,
|
||||
sensor: NexiaSensor,
|
||||
room_iq_zones: dict[int, NexiaRoomIQHarmonizer],
|
||||
) -> None:
|
||||
"""Initialize the RoomIQ sensor switch."""
|
||||
super().__init__(coordinator, zone, f"{sensor.id}_room_iq_sensor")
|
||||
self._attr_translation_placeholders = {"sensor_name": sensor.name}
|
||||
self._sensor_id = sensor.id
|
||||
if zone.zone_id in room_iq_zones:
|
||||
self._harmonizer = room_iq_zones[zone.zone_id]
|
||||
else:
|
||||
self._harmonizer = NexiaRoomIQHarmonizer(
|
||||
zone, coordinator.async_refresh, self._signal_zone_update
|
||||
)
|
||||
room_iq_zones[zone.zone_id] = self._harmonizer
|
||||
|
||||
@property
|
||||
def is_on(self) -> bool:
|
||||
"""Return if the sensor is part of the zone average temperature."""
|
||||
if self._harmonizer.request_pending():
|
||||
return self._sensor_id in self._harmonizer.selected_sensor_ids
|
||||
|
||||
return self._zone.get_sensor_by_id(self._sensor_id).weight > 0.0
|
||||
|
||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||
"""Include this sensor."""
|
||||
self._harmonizer.trigger_add_sensor(self._sensor_id)
|
||||
self._signal_zone_update()
|
||||
|
||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||
"""Remove this sensor."""
|
||||
self._harmonizer.trigger_remove_sensor(self._sensor_id)
|
||||
self._signal_zone_update()
|
||||
|
||||
|
||||
class NexiaEmergencyHeatSwitch(NexiaThermostatEntity, SwitchEntity):
|
||||
"""Provides Nexia emergency heat switch support."""
|
||||
|
||||
|
1096
tests/components/nexia/fixtures/sensors_xl1050_house.json
Normal file
1096
tests/components/nexia/fixtures/sensors_xl1050_house.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,12 +1,74 @@
|
||||
"""The switch tests for the nexia platform."""
|
||||
|
||||
from homeassistant.const import STATE_ON
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
|
||||
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
|
||||
from homeassistant.const import (
|
||||
ATTR_ENTITY_ID,
|
||||
EVENT_HOMEASSISTANT_STOP,
|
||||
SERVICE_TURN_OFF,
|
||||
SERVICE_TURN_ON,
|
||||
STATE_OFF,
|
||||
STATE_ON,
|
||||
Platform,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .util import async_init_integration
|
||||
|
||||
from tests.common import async_fire_time_changed
|
||||
|
||||
|
||||
async def test_hold_switch(hass: HomeAssistant) -> None:
|
||||
"""Test creation of the hold switch."""
|
||||
await async_init_integration(hass)
|
||||
assert hass.states.get("switch.nick_office_hold").state == STATE_ON
|
||||
|
||||
|
||||
async def test_nexia_sensor_switch(
|
||||
hass: HomeAssistant, freezer: FrozenDateTimeFactory
|
||||
) -> None:
|
||||
"""Test NexiaRoomIQSensorSwitch."""
|
||||
await async_init_integration(hass, house_fixture="nexia/sensors_xl1050_house.json")
|
||||
sw1_id = f"{Platform.SWITCH}.center_nativezone_include_center"
|
||||
sw1 = {ATTR_ENTITY_ID: sw1_id}
|
||||
sw2_id = f"{Platform.SWITCH}.center_nativezone_include_upstairs"
|
||||
sw2 = {ATTR_ENTITY_ID: sw2_id}
|
||||
|
||||
# Switch starts out on.
|
||||
assert (entity_state := hass.states.get(sw1_id)) is not None
|
||||
assert entity_state.state == STATE_ON
|
||||
|
||||
# Turn switch off.
|
||||
await hass.services.async_call(SWITCH_DOMAIN, SERVICE_TURN_OFF, sw1, blocking=True)
|
||||
assert hass.states.get(sw1_id).state == STATE_OFF
|
||||
|
||||
# Turn switch back on.
|
||||
await hass.services.async_call(SWITCH_DOMAIN, SERVICE_TURN_ON, sw1, blocking=True)
|
||||
assert hass.states.get(sw1_id).state == STATE_ON
|
||||
|
||||
# The other switch also starts out on.
|
||||
assert (entity_state := hass.states.get(sw2_id)) is not None
|
||||
assert entity_state.state == STATE_ON
|
||||
|
||||
# Turn both switches off, an invalid combination.
|
||||
await hass.services.async_call(SWITCH_DOMAIN, SERVICE_TURN_OFF, sw1, blocking=True)
|
||||
await hass.services.async_call(SWITCH_DOMAIN, SERVICE_TURN_OFF, sw2, blocking=True)
|
||||
assert hass.states.get(sw1_id).state == STATE_OFF
|
||||
assert hass.states.get(sw2_id).state == STATE_OFF
|
||||
|
||||
# Wait for switches to revert to device status.
|
||||
freezer.tick(6)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
assert hass.states.get(sw1_id).state == STATE_ON
|
||||
assert hass.states.get(sw2_id).state == STATE_ON
|
||||
|
||||
# Turn switch off.
|
||||
await hass.services.async_call(SWITCH_DOMAIN, SERVICE_TURN_OFF, sw2, blocking=True)
|
||||
assert hass.states.get(sw2_id).state == STATE_OFF
|
||||
|
||||
# Exercise shutdown path.
|
||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_STOP)
|
||||
await hass.async_block_till_done()
|
||||
assert hass.states.get(sw2_id).state == STATE_ON
|
||||
|
@ -17,10 +17,11 @@ async def async_init_integration(
|
||||
hass: HomeAssistant,
|
||||
skip_setup: bool = False,
|
||||
exception: Exception | None = None,
|
||||
*,
|
||||
house_fixture="nexia/mobile_houses_123456.json",
|
||||
) -> MockConfigEntry:
|
||||
"""Set up the nexia integration in Home Assistant."""
|
||||
|
||||
house_fixture = "nexia/mobile_houses_123456.json"
|
||||
session_fixture = "nexia/session_123456.json"
|
||||
sign_in_fixture = "nexia/sign_in.json"
|
||||
set_fan_speed_fixture = "nexia/set_fan_speed_2293892.json"
|
||||
|
Loading…
x
Reference in New Issue
Block a user