-
-
Notifications
You must be signed in to change notification settings - Fork 31.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: G Johansson <goran.johansson@shiftit.se> Co-authored-by: J. Nick Koston <nick@koston.org>
- Loading branch information
1 parent
4aa61b0
commit fe9f682
Showing
8 changed files
with
299 additions
and
4 deletions.
There are no files selected for viewing
Validating CODEOWNERS rules …
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
"""Base classes shared among Ecobee entities.""" | ||
from __future__ import annotations | ||
|
||
import logging | ||
from typing import Any | ||
|
||
from homeassistant.helpers.entity import DeviceInfo, Entity | ||
|
||
from . import EcobeeData | ||
from .const import DOMAIN, ECOBEE_MODEL_TO_NAME, MANUFACTURER | ||
|
||
_LOGGER = logging.getLogger(__name__) | ||
|
||
|
||
class EcobeeBaseEntity(Entity): | ||
"""Base methods for Ecobee entities.""" | ||
|
||
def __init__(self, data: EcobeeData, thermostat_index: int) -> None: | ||
"""Initiate base methods for Ecobee entities.""" | ||
self.data = data | ||
self.thermostat_index = thermostat_index | ||
thermostat = self.thermostat | ||
self.base_unique_id = thermostat["identifier"] | ||
self._attr_device_info = DeviceInfo( | ||
identifiers={(DOMAIN, thermostat["identifier"])}, | ||
manufacturer=MANUFACTURER, | ||
model=ECOBEE_MODEL_TO_NAME.get(thermostat["modelNumber"]), | ||
name=thermostat["name"], | ||
) | ||
|
||
@property | ||
def thermostat(self) -> dict[str, Any]: | ||
"""Return the thermostat data for the entity.""" | ||
return self.data.ecobee.get_thermostat(self.thermostat_index) | ||
|
||
@property | ||
def available(self) -> bool: | ||
"""Return if device is available.""" | ||
return self.thermostat["runtime"]["connected"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
"""Support for using number with ecobee thermostats.""" | ||
from __future__ import annotations | ||
|
||
from collections.abc import Awaitable, Callable | ||
from dataclasses import dataclass | ||
import logging | ||
|
||
from homeassistant.components.number import NumberEntity, NumberEntityDescription | ||
from homeassistant.config_entries import ConfigEntry | ||
from homeassistant.const import UnitOfTime | ||
from homeassistant.core import HomeAssistant | ||
from homeassistant.helpers.entity_platform import AddEntitiesCallback | ||
|
||
from . import EcobeeData | ||
from .const import DOMAIN | ||
from .entity import EcobeeBaseEntity | ||
|
||
_LOGGER = logging.getLogger(__name__) | ||
|
||
|
||
@dataclass | ||
class EcobeeNumberEntityDescriptionBase: | ||
"""Required values when describing Ecobee number entities.""" | ||
|
||
ecobee_setting_key: str | ||
set_fn: Callable[[EcobeeData, int, int], Awaitable] | ||
|
||
|
||
@dataclass | ||
class EcobeeNumberEntityDescription( | ||
NumberEntityDescription, EcobeeNumberEntityDescriptionBase | ||
): | ||
"""Class describing Ecobee number entities.""" | ||
|
||
|
||
VENTILATOR_NUMBERS = ( | ||
EcobeeNumberEntityDescription( | ||
key="home", | ||
name="home", | ||
ecobee_setting_key="ventilatorMinOnTimeHome", | ||
set_fn=lambda data, id, min_time: data.ecobee.set_ventilator_min_on_time_home( | ||
id, min_time | ||
), | ||
), | ||
EcobeeNumberEntityDescription( | ||
key="away", | ||
name="away", | ||
ecobee_setting_key="ventilatorMinOnTimeAway", | ||
set_fn=lambda data, id, min_time: data.ecobee.set_ventilator_min_on_time_away( | ||
id, min_time | ||
), | ||
), | ||
) | ||
|
||
|
||
async def async_setup_entry( | ||
hass: HomeAssistant, | ||
config_entry: ConfigEntry, | ||
async_add_entities: AddEntitiesCallback, | ||
) -> None: | ||
"""Set up the ecobee thermostat number entity.""" | ||
data: EcobeeData = hass.data[DOMAIN] | ||
entities = [] | ||
_LOGGER.debug("Adding min time ventilators numbers (if present)") | ||
for index, thermostat in enumerate(data.ecobee.thermostats): | ||
if thermostat["settings"]["ventilatorType"] == "none": | ||
continue | ||
_LOGGER.debug("Adding %s's ventilator min times number", thermostat["name"]) | ||
for numbers in VENTILATOR_NUMBERS: | ||
entities.append(EcobeeVentilatorMinTime(data, index, numbers)) | ||
|
||
async_add_entities(entities, True) | ||
|
||
|
||
class EcobeeVentilatorMinTime(EcobeeBaseEntity, NumberEntity): | ||
"""A number class, representing min time for an ecobee thermostat with ventilator attached.""" | ||
|
||
entity_description: EcobeeNumberEntityDescription | ||
|
||
_attr_native_min_value = 0 | ||
_attr_native_max_value = 60 | ||
_attr_native_step = 5 | ||
_attr_native_unit_of_measurement = UnitOfTime.MINUTES | ||
_attr_has_entity_name = True | ||
|
||
def __init__( | ||
self, | ||
data: EcobeeData, | ||
thermostat_index: int, | ||
description: EcobeeNumberEntityDescription, | ||
) -> None: | ||
"""Initialize ecobee ventilator platform.""" | ||
super().__init__(data, thermostat_index) | ||
self.entity_description = description | ||
self._attr_name = f"Ventilator min time {description.name}" | ||
self._attr_unique_id = f"{self.base_unique_id}_ventilator_{description.key}" | ||
|
||
async def async_update(self) -> None: | ||
"""Get the latest state from the thermostat.""" | ||
await self.data.update() | ||
self._attr_native_value = self.thermostat["settings"][ | ||
self.entity_description.ecobee_setting_key | ||
] | ||
|
||
def set_native_value(self, value: float) -> None: | ||
"""Set new ventilator Min On Time value.""" | ||
self.entity_description.set_fn(self.data, self.thermostat_index, int(value)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
"""The test for the ecobee thermostat number module.""" | ||
from unittest.mock import patch | ||
|
||
from homeassistant.components.number import ATTR_VALUE, DOMAIN, SERVICE_SET_VALUE | ||
from homeassistant.const import ATTR_ENTITY_ID, UnitOfTime | ||
from homeassistant.core import HomeAssistant | ||
|
||
from .common import setup_platform | ||
|
||
VENTILATOR_MIN_HOME_ID = "number.ecobee_ventilator_min_time_home" | ||
VENTILATOR_MIN_AWAY_ID = "number.ecobee_ventilator_min_time_away" | ||
THERMOSTAT_ID = 0 | ||
|
||
|
||
async def test_ventilator_min_on_home_attributes(hass): | ||
"""Test the ventilator number on home attributes are correct.""" | ||
await setup_platform(hass, DOMAIN) | ||
|
||
state = hass.states.get(VENTILATOR_MIN_HOME_ID) | ||
assert state.state == "20" | ||
assert state.attributes.get("min") == 0 | ||
assert state.attributes.get("max") == 60 | ||
assert state.attributes.get("step") == 5 | ||
assert state.attributes.get("friendly_name") == "ecobee Ventilator min time home" | ||
assert state.attributes.get("unit_of_measurement") == UnitOfTime.MINUTES | ||
|
||
|
||
async def test_ventilator_min_on_away_attributes(hass): | ||
"""Test the ventilator number on away attributes are correct.""" | ||
await setup_platform(hass, DOMAIN) | ||
|
||
state = hass.states.get(VENTILATOR_MIN_AWAY_ID) | ||
assert state.state == "10" | ||
assert state.attributes.get("min") == 0 | ||
assert state.attributes.get("max") == 60 | ||
assert state.attributes.get("step") == 5 | ||
assert state.attributes.get("friendly_name") == "ecobee Ventilator min time away" | ||
assert state.attributes.get("unit_of_measurement") == UnitOfTime.MINUTES | ||
|
||
|
||
async def test_set_min_time_home(hass: HomeAssistant): | ||
"""Test the number can set min time home.""" | ||
target_value = 40 | ||
with patch( | ||
"homeassistant.components.ecobee.Ecobee.set_ventilator_min_on_time_home" | ||
) as mock_set_min_home_time: | ||
await setup_platform(hass, DOMAIN) | ||
|
||
await hass.services.async_call( | ||
DOMAIN, | ||
SERVICE_SET_VALUE, | ||
{ATTR_ENTITY_ID: VENTILATOR_MIN_HOME_ID, ATTR_VALUE: target_value}, | ||
blocking=True, | ||
) | ||
await hass.async_block_till_done() | ||
mock_set_min_home_time.assert_called_once_with(THERMOSTAT_ID, target_value) | ||
|
||
|
||
async def test_set_min_time_away(hass: HomeAssistant) -> None: | ||
"""Test the number can set min time away.""" | ||
target_value = 0 | ||
with patch( | ||
"homeassistant.components.ecobee.Ecobee.set_ventilator_min_on_time_away" | ||
) as mock_set_min_away_time: | ||
await setup_platform(hass, DOMAIN) | ||
|
||
await hass.services.async_call( | ||
DOMAIN, | ||
SERVICE_SET_VALUE, | ||
{ATTR_ENTITY_ID: VENTILATOR_MIN_AWAY_ID, ATTR_VALUE: target_value}, | ||
blocking=True, | ||
) | ||
await hass.async_block_till_done() | ||
mock_set_min_away_time.assert_called_once_with(THERMOSTAT_ID, target_value) |