mirror of
https://github.com/home-assistant/core.git
synced 2025-07-18 18:57:06 +00:00
Add water_heater to alexa (#106011)
* Add water_heater support to alexa * Improve test coverage * Follow up comments
This commit is contained in:
parent
5a3db078d5
commit
93c800c4e8
@ -19,6 +19,7 @@ from homeassistant.components import (
|
||||
number,
|
||||
timer,
|
||||
vacuum,
|
||||
water_heater,
|
||||
)
|
||||
from homeassistant.components.alarm_control_panel import (
|
||||
AlarmControlPanelEntityFeature,
|
||||
@ -435,7 +436,8 @@ class AlexaPowerController(AlexaCapability):
|
||||
is_on = self.entity.state == vacuum.STATE_CLEANING
|
||||
elif self.entity.domain == timer.DOMAIN:
|
||||
is_on = self.entity.state != STATE_IDLE
|
||||
|
||||
elif self.entity.domain == water_heater.DOMAIN:
|
||||
is_on = self.entity.state not in (STATE_OFF, STATE_UNKNOWN)
|
||||
else:
|
||||
is_on = self.entity.state != STATE_OFF
|
||||
|
||||
@ -938,6 +940,9 @@ class AlexaTemperatureSensor(AlexaCapability):
|
||||
if self.entity.domain == climate.DOMAIN:
|
||||
unit = self.hass.config.units.temperature_unit
|
||||
temp = self.entity.attributes.get(climate.ATTR_CURRENT_TEMPERATURE)
|
||||
elif self.entity.domain == water_heater.DOMAIN:
|
||||
unit = self.hass.config.units.temperature_unit
|
||||
temp = self.entity.attributes.get(water_heater.ATTR_CURRENT_TEMPERATURE)
|
||||
|
||||
if temp is None or temp in (STATE_UNAVAILABLE, STATE_UNKNOWN):
|
||||
return None
|
||||
@ -1108,6 +1113,8 @@ class AlexaThermostatController(AlexaCapability):
|
||||
supported = self.entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
|
||||
if supported & climate.ClimateEntityFeature.TARGET_TEMPERATURE:
|
||||
properties.append({"name": "targetSetpoint"})
|
||||
if supported & water_heater.WaterHeaterEntityFeature.TARGET_TEMPERATURE:
|
||||
properties.append({"name": "targetSetpoint"})
|
||||
if supported & climate.ClimateEntityFeature.TARGET_TEMPERATURE_RANGE:
|
||||
properties.append({"name": "lowerSetpoint"})
|
||||
properties.append({"name": "upperSetpoint"})
|
||||
@ -1127,6 +1134,8 @@ class AlexaThermostatController(AlexaCapability):
|
||||
return None
|
||||
|
||||
if name == "thermostatMode":
|
||||
if self.entity.domain == water_heater.DOMAIN:
|
||||
return None
|
||||
preset = self.entity.attributes.get(climate.ATTR_PRESET_MODE)
|
||||
|
||||
mode: dict[str, str] | str | None
|
||||
@ -1176,9 +1185,13 @@ class AlexaThermostatController(AlexaCapability):
|
||||
ThermostatMode Values.
|
||||
|
||||
ThermostatMode Value must be AUTO, COOL, HEAT, ECO, OFF, or CUSTOM.
|
||||
Water heater devices do not return thermostat modes.
|
||||
"""
|
||||
if self.entity.domain == water_heater.DOMAIN:
|
||||
return None
|
||||
|
||||
supported_modes: list[str] = []
|
||||
hvac_modes = self.entity.attributes[climate.ATTR_HVAC_MODES]
|
||||
hvac_modes = self.entity.attributes.get(climate.ATTR_HVAC_MODES, [])
|
||||
for mode in hvac_modes:
|
||||
if thermostat_mode := API_THERMOSTAT_MODES.get(mode):
|
||||
supported_modes.append(thermostat_mode)
|
||||
@ -1408,6 +1421,16 @@ class AlexaModeController(AlexaCapability):
|
||||
if mode in self.entity.attributes.get(humidifier.ATTR_AVAILABLE_MODES, []):
|
||||
return f"{humidifier.ATTR_MODE}.{mode}"
|
||||
|
||||
# Water heater operation mode
|
||||
if self.instance == f"{water_heater.DOMAIN}.{water_heater.ATTR_OPERATION_MODE}":
|
||||
operation_mode = self.entity.attributes.get(
|
||||
water_heater.ATTR_OPERATION_MODE, None
|
||||
)
|
||||
if operation_mode in self.entity.attributes.get(
|
||||
water_heater.ATTR_OPERATION_LIST, []
|
||||
):
|
||||
return f"{water_heater.ATTR_OPERATION_MODE}.{operation_mode}"
|
||||
|
||||
# Cover Position
|
||||
if self.instance == f"{cover.DOMAIN}.{cover.ATTR_POSITION}":
|
||||
# Return state instead of position when using ModeController.
|
||||
@ -1478,6 +1501,26 @@ class AlexaModeController(AlexaCapability):
|
||||
)
|
||||
return self._resource.serialize_capability_resources()
|
||||
|
||||
# Water heater operation modes
|
||||
if self.instance == f"{water_heater.DOMAIN}.{water_heater.ATTR_OPERATION_MODE}":
|
||||
self._resource = AlexaModeResource([AlexaGlobalCatalog.SETTING_MODE], False)
|
||||
operation_modes = self.entity.attributes.get(
|
||||
water_heater.ATTR_OPERATION_LIST, []
|
||||
)
|
||||
for operation_mode in operation_modes:
|
||||
self._resource.add_mode(
|
||||
f"{water_heater.ATTR_OPERATION_MODE}.{operation_mode}",
|
||||
[operation_mode],
|
||||
)
|
||||
# Devices with a single mode completely break Alexa discovery,
|
||||
# add a fake preset (see issue #53832).
|
||||
if len(operation_modes) == 1:
|
||||
self._resource.add_mode(
|
||||
f"{water_heater.ATTR_OPERATION_MODE}.{PRESET_MODE_NA}",
|
||||
[PRESET_MODE_NA],
|
||||
)
|
||||
return self._resource.serialize_capability_resources()
|
||||
|
||||
# Cover Position Resources
|
||||
if self.instance == f"{cover.DOMAIN}.{cover.ATTR_POSITION}":
|
||||
self._resource = AlexaModeResource(
|
||||
|
@ -32,6 +32,7 @@ from homeassistant.components import (
|
||||
switch,
|
||||
timer,
|
||||
vacuum,
|
||||
water_heater,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
ATTR_DEVICE_CLASS,
|
||||
@ -248,6 +249,9 @@ class DisplayCategory:
|
||||
# Indicates a vacuum cleaner.
|
||||
VACUUM_CLEANER = "VACUUM_CLEANER"
|
||||
|
||||
# Indicates a water heater.
|
||||
WATER_HEATER = "WATER_HEATER"
|
||||
|
||||
# Indicates a network-connected wearable device, such as an Apple Watch,
|
||||
# Fitbit, or Samsung Gear.
|
||||
WEARABLE = "WEARABLE"
|
||||
@ -456,23 +460,46 @@ class ButtonCapabilities(AlexaEntity):
|
||||
|
||||
|
||||
@ENTITY_ADAPTERS.register(climate.DOMAIN)
|
||||
@ENTITY_ADAPTERS.register(water_heater.DOMAIN)
|
||||
class ClimateCapabilities(AlexaEntity):
|
||||
"""Class to represent Climate capabilities."""
|
||||
|
||||
def default_display_categories(self) -> list[str]:
|
||||
"""Return the display categories for this entity."""
|
||||
if self.entity.domain == water_heater.DOMAIN:
|
||||
return [DisplayCategory.WATER_HEATER]
|
||||
return [DisplayCategory.THERMOSTAT]
|
||||
|
||||
def interfaces(self) -> Generator[AlexaCapability, None, None]:
|
||||
"""Yield the supported interfaces."""
|
||||
# If we support two modes, one being off, we allow turning on too.
|
||||
if climate.HVACMode.OFF in self.entity.attributes.get(
|
||||
climate.ATTR_HVAC_MODES, []
|
||||
supported_features = self.entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
|
||||
if (
|
||||
self.entity.domain == climate.DOMAIN
|
||||
and climate.HVACMode.OFF
|
||||
in self.entity.attributes.get(climate.ATTR_HVAC_MODES, [])
|
||||
or self.entity.domain == water_heater.DOMAIN
|
||||
and (supported_features & water_heater.WaterHeaterEntityFeature.ON_OFF)
|
||||
):
|
||||
yield AlexaPowerController(self.entity)
|
||||
|
||||
yield AlexaThermostatController(self.hass, self.entity)
|
||||
yield AlexaTemperatureSensor(self.hass, self.entity)
|
||||
if (
|
||||
self.entity.domain == climate.DOMAIN
|
||||
or self.entity.domain == water_heater.DOMAIN
|
||||
and (
|
||||
supported_features
|
||||
& water_heater.WaterHeaterEntityFeature.OPERATION_MODE
|
||||
)
|
||||
):
|
||||
yield AlexaThermostatController(self.hass, self.entity)
|
||||
yield AlexaTemperatureSensor(self.hass, self.entity)
|
||||
if self.entity.domain == water_heater.DOMAIN and (
|
||||
supported_features & water_heater.WaterHeaterEntityFeature.OPERATION_MODE
|
||||
):
|
||||
yield AlexaModeController(
|
||||
self.entity,
|
||||
instance=f"{water_heater.DOMAIN}.{water_heater.ATTR_OPERATION_MODE}",
|
||||
)
|
||||
yield AlexaEndpointHealth(self.hass, self.entity)
|
||||
yield Alexa(self.entity)
|
||||
|
||||
|
@ -22,6 +22,7 @@ from homeassistant.components import (
|
||||
number,
|
||||
timer,
|
||||
vacuum,
|
||||
water_heater,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
ATTR_ENTITY_ID,
|
||||
@ -80,6 +81,23 @@ from .state_report import AlexaDirective, AlexaResponse, async_enable_proactive_
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
DIRECTIVE_NOT_SUPPORTED = "Entity does not support directive"
|
||||
|
||||
MIN_MAX_TEMP = {
|
||||
climate.DOMAIN: {
|
||||
"min_temp": climate.ATTR_MIN_TEMP,
|
||||
"max_temp": climate.ATTR_MAX_TEMP,
|
||||
},
|
||||
water_heater.DOMAIN: {
|
||||
"min_temp": water_heater.ATTR_MIN_TEMP,
|
||||
"max_temp": water_heater.ATTR_MAX_TEMP,
|
||||
},
|
||||
}
|
||||
|
||||
SERVICE_SET_TEMPERATURE = {
|
||||
climate.DOMAIN: climate.SERVICE_SET_TEMPERATURE,
|
||||
water_heater.DOMAIN: water_heater.SERVICE_SET_TEMPERATURE,
|
||||
}
|
||||
|
||||
HANDLERS: Registry[
|
||||
tuple[str, str],
|
||||
Callable[
|
||||
@ -804,8 +822,10 @@ async def async_api_set_target_temp(
|
||||
) -> AlexaResponse:
|
||||
"""Process a set target temperature request."""
|
||||
entity = directive.entity
|
||||
min_temp = entity.attributes[climate.ATTR_MIN_TEMP]
|
||||
max_temp = entity.attributes[climate.ATTR_MAX_TEMP]
|
||||
domain = entity.domain
|
||||
|
||||
min_temp = entity.attributes[MIN_MAX_TEMP[domain]["min_temp"]]
|
||||
max_temp = entity.attributes["max_temp"]
|
||||
unit = hass.config.units.temperature_unit
|
||||
|
||||
data: dict[str, Any] = {ATTR_ENTITY_ID: entity.entity_id}
|
||||
@ -849,9 +869,11 @@ async def async_api_set_target_temp(
|
||||
}
|
||||
)
|
||||
|
||||
service = SERVICE_SET_TEMPERATURE[domain]
|
||||
|
||||
await hass.services.async_call(
|
||||
entity.domain,
|
||||
climate.SERVICE_SET_TEMPERATURE,
|
||||
service,
|
||||
data,
|
||||
blocking=False,
|
||||
context=context,
|
||||
@ -867,11 +889,12 @@ async def async_api_adjust_target_temp(
|
||||
directive: AlexaDirective,
|
||||
context: ha.Context,
|
||||
) -> AlexaResponse:
|
||||
"""Process an adjust target temperature request."""
|
||||
"""Process an adjust target temperature request for climates and water heaters."""
|
||||
data: dict[str, Any]
|
||||
entity = directive.entity
|
||||
min_temp = entity.attributes[climate.ATTR_MIN_TEMP]
|
||||
max_temp = entity.attributes[climate.ATTR_MAX_TEMP]
|
||||
domain = entity.domain
|
||||
min_temp = entity.attributes[MIN_MAX_TEMP[domain]["min_temp"]]
|
||||
max_temp = entity.attributes[MIN_MAX_TEMP[domain]["max_temp"]]
|
||||
unit = hass.config.units.temperature_unit
|
||||
|
||||
temp_delta = temperature_from_object(
|
||||
@ -932,9 +955,11 @@ async def async_api_adjust_target_temp(
|
||||
}
|
||||
)
|
||||
|
||||
service = SERVICE_SET_TEMPERATURE[domain]
|
||||
|
||||
await hass.services.async_call(
|
||||
entity.domain,
|
||||
climate.SERVICE_SET_TEMPERATURE,
|
||||
service,
|
||||
data,
|
||||
blocking=False,
|
||||
context=context,
|
||||
@ -1163,6 +1188,23 @@ async def async_api_set_mode(
|
||||
msg = f"Entity '{entity.entity_id}' does not support Mode '{mode}'"
|
||||
raise AlexaInvalidValueError(msg)
|
||||
|
||||
# Water heater operation mode
|
||||
elif instance == f"{water_heater.DOMAIN}.{water_heater.ATTR_OPERATION_MODE}":
|
||||
operation_mode = mode.split(".")[1]
|
||||
operation_modes: list[str] | None = entity.attributes.get(
|
||||
water_heater.ATTR_OPERATION_LIST
|
||||
)
|
||||
if (
|
||||
operation_mode != PRESET_MODE_NA
|
||||
and operation_modes
|
||||
and operation_mode in operation_modes
|
||||
):
|
||||
service = water_heater.SERVICE_SET_OPERATION_MODE
|
||||
data[water_heater.ATTR_OPERATION_MODE] = operation_mode
|
||||
else:
|
||||
msg = f"Entity '{entity.entity_id}' does not support Operation mode '{operation_mode}'"
|
||||
raise AlexaInvalidValueError(msg)
|
||||
|
||||
# Cover Position
|
||||
elif instance == f"{cover.DOMAIN}.{cover.ATTR_POSITION}":
|
||||
position = mode.split(".")[1]
|
||||
|
@ -8,6 +8,13 @@ from homeassistant.components.alexa import smart_home
|
||||
from homeassistant.components.climate import ATTR_CURRENT_TEMPERATURE, HVACMode
|
||||
from homeassistant.components.lock import STATE_JAMMED, STATE_LOCKING, STATE_UNLOCKING
|
||||
from homeassistant.components.media_player import MediaPlayerEntityFeature
|
||||
from homeassistant.components.water_heater import (
|
||||
ATTR_OPERATION_LIST,
|
||||
ATTR_OPERATION_MODE,
|
||||
STATE_ECO,
|
||||
STATE_GAS,
|
||||
STATE_HEAT_PUMP,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
ATTR_UNIT_OF_MEASUREMENT,
|
||||
STATE_ALARM_ARMED_AWAY,
|
||||
@ -16,6 +23,7 @@ from homeassistant.const import (
|
||||
STATE_ALARM_ARMED_NIGHT,
|
||||
STATE_ALARM_DISARMED,
|
||||
STATE_LOCKED,
|
||||
STATE_OFF,
|
||||
STATE_UNAVAILABLE,
|
||||
STATE_UNKNOWN,
|
||||
STATE_UNLOCKED,
|
||||
@ -777,6 +785,96 @@ async def test_report_climate_state(hass: HomeAssistant) -> None:
|
||||
assert msg["event"]["payload"]["type"] == "INTERNAL_ERROR"
|
||||
|
||||
|
||||
async def test_report_water_heater_state(hass: HomeAssistant) -> None:
|
||||
"""Test ThermostatController also reports state correctly for water heaters."""
|
||||
for operation_mode in (STATE_ECO, STATE_GAS, STATE_HEAT_PUMP):
|
||||
hass.states.async_set(
|
||||
"water_heater.boyler",
|
||||
operation_mode,
|
||||
{
|
||||
"friendly_name": "Boyler",
|
||||
"supported_features": 11,
|
||||
ATTR_CURRENT_TEMPERATURE: 34,
|
||||
ATTR_UNIT_OF_MEASUREMENT: UnitOfTemperature.CELSIUS,
|
||||
ATTR_OPERATION_LIST: [STATE_ECO, STATE_GAS, STATE_HEAT_PUMP],
|
||||
ATTR_OPERATION_MODE: operation_mode,
|
||||
},
|
||||
)
|
||||
properties = await reported_properties(hass, "water_heater.boyler")
|
||||
properties.assert_not_has_property(
|
||||
"Alexa.ThermostatController", "thermostatMode"
|
||||
)
|
||||
properties.assert_equal(
|
||||
"Alexa.ModeController", "mode", f"operation_mode.{operation_mode}"
|
||||
)
|
||||
properties.assert_equal(
|
||||
"Alexa.TemperatureSensor",
|
||||
"temperature",
|
||||
{"value": 34.0, "scale": "CELSIUS"},
|
||||
)
|
||||
|
||||
for off_mode in [STATE_OFF]:
|
||||
hass.states.async_set(
|
||||
"water_heater.boyler",
|
||||
off_mode,
|
||||
{
|
||||
"friendly_name": "Boyler",
|
||||
"supported_features": 11,
|
||||
ATTR_CURRENT_TEMPERATURE: 34,
|
||||
ATTR_UNIT_OF_MEASUREMENT: UnitOfTemperature.CELSIUS,
|
||||
},
|
||||
)
|
||||
properties = await reported_properties(hass, "water_heater.boyler")
|
||||
properties.assert_not_has_property(
|
||||
"Alexa.ThermostatController", "thermostatMode"
|
||||
)
|
||||
properties.assert_not_has_property("Alexa.ModeController", "mode")
|
||||
properties.assert_equal(
|
||||
"Alexa.TemperatureSensor",
|
||||
"temperature",
|
||||
{"value": 34.0, "scale": "CELSIUS"},
|
||||
)
|
||||
|
||||
for state in "unavailable", "unknown":
|
||||
hass.states.async_set(
|
||||
f"water_heater.{state}",
|
||||
state,
|
||||
{"friendly_name": f"Boyler {state}", "supported_features": 11},
|
||||
)
|
||||
properties = await reported_properties(hass, f"water_heater.{state}")
|
||||
properties.assert_not_has_property(
|
||||
"Alexa.ThermostatController", "thermostatMode"
|
||||
)
|
||||
properties.assert_not_has_property("Alexa.ModeController", "mode")
|
||||
|
||||
|
||||
async def test_report_singe_mode_water_heater(hass: HomeAssistant) -> None:
|
||||
"""Test ThermostatController also reports state correctly for water heaters."""
|
||||
operation_mode = STATE_ECO
|
||||
hass.states.async_set(
|
||||
"water_heater.boyler",
|
||||
operation_mode,
|
||||
{
|
||||
"friendly_name": "Boyler",
|
||||
"supported_features": 11,
|
||||
ATTR_CURRENT_TEMPERATURE: 34,
|
||||
ATTR_UNIT_OF_MEASUREMENT: UnitOfTemperature.CELSIUS,
|
||||
ATTR_OPERATION_LIST: [STATE_ECO],
|
||||
ATTR_OPERATION_MODE: operation_mode,
|
||||
},
|
||||
)
|
||||
properties = await reported_properties(hass, "water_heater.boyler")
|
||||
properties.assert_not_has_property("Alexa.ThermostatController", "thermostatMode")
|
||||
properties.assert_equal(
|
||||
"Alexa.ModeController", "mode", f"operation_mode.{operation_mode}"
|
||||
)
|
||||
properties.assert_equal(
|
||||
"Alexa.TemperatureSensor",
|
||||
"temperature",
|
||||
{"value": 34.0, "scale": "CELSIUS"},
|
||||
)
|
||||
|
||||
|
||||
async def test_temperature_sensor_sensor(hass: HomeAssistant) -> None:
|
||||
"""Test TemperatureSensor reports sensor temperature correctly."""
|
||||
for bad_value in (STATE_UNKNOWN, STATE_UNAVAILABLE, "not-number"):
|
||||
@ -823,6 +921,29 @@ async def test_temperature_sensor_climate(hass: HomeAssistant) -> None:
|
||||
)
|
||||
|
||||
|
||||
async def test_temperature_sensor_water_heater(hass: HomeAssistant) -> None:
|
||||
"""Test TemperatureSensor reports climate temperature correctly."""
|
||||
for bad_value in (STATE_UNKNOWN, STATE_UNAVAILABLE, "not-number"):
|
||||
hass.states.async_set(
|
||||
"water_heater.boyler",
|
||||
STATE_ECO,
|
||||
{"supported_features": 11, ATTR_CURRENT_TEMPERATURE: bad_value},
|
||||
)
|
||||
|
||||
properties = await reported_properties(hass, "water_heater.boyler")
|
||||
properties.assert_not_has_property("Alexa.TemperatureSensor", "temperature")
|
||||
|
||||
hass.states.async_set(
|
||||
"water_heater.boyler",
|
||||
STATE_ECO,
|
||||
{"supported_features": 11, ATTR_CURRENT_TEMPERATURE: 34},
|
||||
)
|
||||
properties = await reported_properties(hass, "water_heater.boyler")
|
||||
properties.assert_equal(
|
||||
"Alexa.TemperatureSensor", "temperature", {"value": 34.0, "scale": "CELSIUS"}
|
||||
)
|
||||
|
||||
|
||||
async def test_report_alarm_control_panel_state(hass: HomeAssistant) -> None:
|
||||
"""Test SecurityPanelController implements armState property."""
|
||||
hass.states.async_set("alarm_control_panel.armed_away", STATE_ALARM_ARMED_AWAY, {})
|
||||
|
@ -128,12 +128,14 @@ async def assert_request_calls_service(
|
||||
|
||||
|
||||
async def assert_request_fails(
|
||||
namespace, name, endpoint, service_not_called, hass, payload=None
|
||||
namespace, name, endpoint, service_not_called, hass, payload=None, instance=None
|
||||
):
|
||||
"""Assert an API request returns an ErrorResponse."""
|
||||
request = get_new_request(namespace, name, endpoint)
|
||||
if payload:
|
||||
request["directive"]["payload"] = payload
|
||||
if instance:
|
||||
request["directive"]["header"]["instance"] = instance
|
||||
|
||||
domain, service_name = service_not_called.split(".")
|
||||
call = async_mock_service(hass, domain, service_name)
|
||||
|
@ -2700,6 +2700,181 @@ async def test_thermostat(hass: HomeAssistant) -> None:
|
||||
assert call.data["preset_mode"] == "eco"
|
||||
|
||||
|
||||
async def test_water_heater(hass: HomeAssistant) -> None:
|
||||
"""Test water_heater discovery."""
|
||||
hass.config.units = US_CUSTOMARY_SYSTEM
|
||||
device = (
|
||||
"water_heater.boyler",
|
||||
"gas",
|
||||
{
|
||||
"temperature": 70.0,
|
||||
"target_temp_high": None,
|
||||
"target_temp_low": None,
|
||||
"current_temperature": 75.0,
|
||||
"friendly_name": "Test water heater",
|
||||
"supported_features": 1 | 2 | 8,
|
||||
"operation_list": ["off", "gas", "eco"],
|
||||
"operation_mode": "gas",
|
||||
"min_temp": 50,
|
||||
"max_temp": 90,
|
||||
},
|
||||
)
|
||||
appliance = await discovery_test(device, hass)
|
||||
|
||||
assert appliance["endpointId"] == "water_heater#boyler"
|
||||
assert appliance["displayCategories"][0] == "WATER_HEATER"
|
||||
assert appliance["friendlyName"] == "Test water heater"
|
||||
|
||||
capabilities = assert_endpoint_capabilities(
|
||||
appliance,
|
||||
"Alexa.PowerController",
|
||||
"Alexa.ThermostatController",
|
||||
"Alexa.ModeController",
|
||||
"Alexa.TemperatureSensor",
|
||||
"Alexa.EndpointHealth",
|
||||
"Alexa",
|
||||
)
|
||||
|
||||
properties = await reported_properties(hass, "water_heater#boyler")
|
||||
properties.assert_equal("Alexa.ModeController", "mode", "operation_mode.gas")
|
||||
properties.assert_equal(
|
||||
"Alexa.ThermostatController",
|
||||
"targetSetpoint",
|
||||
{"value": 70.0, "scale": "FAHRENHEIT"},
|
||||
)
|
||||
properties.assert_equal(
|
||||
"Alexa.TemperatureSensor", "temperature", {"value": 75.0, "scale": "FAHRENHEIT"}
|
||||
)
|
||||
|
||||
modes_capability = get_capability(capabilities, "Alexa.ModeController")
|
||||
assert modes_capability is not None
|
||||
configuration = modes_capability["configuration"]
|
||||
|
||||
supported_modes = ["operation_mode.off", "operation_mode.gas", "operation_mode.eco"]
|
||||
for mode in supported_modes:
|
||||
assert mode in [item["value"] for item in configuration["supportedModes"]]
|
||||
|
||||
call, msg = await assert_request_calls_service(
|
||||
"Alexa.ThermostatController",
|
||||
"SetTargetTemperature",
|
||||
"water_heater#boyler",
|
||||
"water_heater.set_temperature",
|
||||
hass,
|
||||
payload={"targetSetpoint": {"value": 69.0, "scale": "FAHRENHEIT"}},
|
||||
)
|
||||
assert call.data["temperature"] == 69.0
|
||||
properties = ReportedProperties(msg["context"]["properties"])
|
||||
properties.assert_equal(
|
||||
"Alexa.ThermostatController",
|
||||
"targetSetpoint",
|
||||
{"value": 69.0, "scale": "FAHRENHEIT"},
|
||||
)
|
||||
|
||||
msg = await assert_request_fails(
|
||||
"Alexa.ThermostatController",
|
||||
"SetTargetTemperature",
|
||||
"water_heater#boyler",
|
||||
"water_heater.set_temperature",
|
||||
hass,
|
||||
payload={"targetSetpoint": {"value": 0.0, "scale": "CELSIUS"}},
|
||||
)
|
||||
assert msg["event"]["payload"]["type"] == "TEMPERATURE_VALUE_OUT_OF_RANGE"
|
||||
|
||||
call, msg = await assert_request_calls_service(
|
||||
"Alexa.ThermostatController",
|
||||
"SetTargetTemperature",
|
||||
"water_heater#boyler",
|
||||
"water_heater.set_temperature",
|
||||
hass,
|
||||
payload={
|
||||
"targetSetpoint": {"value": 30.0, "scale": "CELSIUS"},
|
||||
},
|
||||
)
|
||||
assert call.data["temperature"] == 86.0
|
||||
properties = ReportedProperties(msg["context"]["properties"])
|
||||
properties.assert_equal(
|
||||
"Alexa.ThermostatController",
|
||||
"targetSetpoint",
|
||||
{"value": 86.0, "scale": "FAHRENHEIT"},
|
||||
)
|
||||
|
||||
call, msg = await assert_request_calls_service(
|
||||
"Alexa.ThermostatController",
|
||||
"AdjustTargetTemperature",
|
||||
"water_heater#boyler",
|
||||
"water_heater.set_temperature",
|
||||
hass,
|
||||
payload={"targetSetpointDelta": {"value": -10.0, "scale": "KELVIN"}},
|
||||
)
|
||||
assert call.data["temperature"] == 52.0
|
||||
properties = ReportedProperties(msg["context"]["properties"])
|
||||
properties.assert_equal(
|
||||
"Alexa.ThermostatController",
|
||||
"targetSetpoint",
|
||||
{"value": 52.0, "scale": "FAHRENHEIT"},
|
||||
)
|
||||
|
||||
msg = await assert_request_fails(
|
||||
"Alexa.ThermostatController",
|
||||
"AdjustTargetTemperature",
|
||||
"water_heater#boyler",
|
||||
"water_heater.set_temperature",
|
||||
hass,
|
||||
payload={"targetSetpointDelta": {"value": 20.0, "scale": "CELSIUS"}},
|
||||
)
|
||||
assert msg["event"]["payload"]["type"] == "TEMPERATURE_VALUE_OUT_OF_RANGE"
|
||||
|
||||
# Setting mode, the payload can be an object with a value attribute...
|
||||
call, msg = await assert_request_calls_service(
|
||||
"Alexa.ModeController",
|
||||
"SetMode",
|
||||
"water_heater#boyler",
|
||||
"water_heater.set_operation_mode",
|
||||
hass,
|
||||
payload={"mode": "operation_mode.eco"},
|
||||
instance="water_heater.operation_mode",
|
||||
)
|
||||
assert call.data["operation_mode"] == "eco"
|
||||
properties = ReportedProperties(msg["context"]["properties"])
|
||||
properties.assert_equal("Alexa.ModeController", "mode", "operation_mode.eco")
|
||||
|
||||
call, msg = await assert_request_calls_service(
|
||||
"Alexa.ModeController",
|
||||
"SetMode",
|
||||
"water_heater#boyler",
|
||||
"water_heater.set_operation_mode",
|
||||
hass,
|
||||
payload={"mode": "operation_mode.gas"},
|
||||
instance="water_heater.operation_mode",
|
||||
)
|
||||
assert call.data["operation_mode"] == "gas"
|
||||
properties = ReportedProperties(msg["context"]["properties"])
|
||||
properties.assert_equal("Alexa.ModeController", "mode", "operation_mode.gas")
|
||||
|
||||
# assert unsupported mode
|
||||
msg = await assert_request_fails(
|
||||
"Alexa.ModeController",
|
||||
"SetMode",
|
||||
"water_heater#boyler",
|
||||
"water_heater.set_operation_mode",
|
||||
hass,
|
||||
payload={"mode": "operation_mode.invalid"},
|
||||
instance="water_heater.operation_mode",
|
||||
)
|
||||
assert msg["event"]["payload"]["type"] == "INVALID_VALUE"
|
||||
|
||||
call, _ = await assert_request_calls_service(
|
||||
"Alexa.ModeController",
|
||||
"SetMode",
|
||||
"water_heater#boyler",
|
||||
"water_heater.set_operation_mode",
|
||||
hass,
|
||||
payload={"mode": "operation_mode.off"},
|
||||
instance="water_heater.operation_mode",
|
||||
)
|
||||
assert call.data["operation_mode"] == "off"
|
||||
|
||||
|
||||
async def test_no_current_target_temp_adjusting_temp(hass: HomeAssistant) -> None:
|
||||
"""Test thermostat adjusting temp with no initial target temperature."""
|
||||
hass.config.units = US_CUSTOMARY_SYSTEM
|
||||
|
Loading…
x
Reference in New Issue
Block a user