mirror of
https://github.com/home-assistant/core.git
synced 2025-04-24 09:17:53 +00:00
Add humidifier support for Alexa (#81329)
This commit is contained in:
parent
35e81cf982
commit
5d4c4a1293
@ -8,6 +8,7 @@ from homeassistant.components import (
|
||||
climate,
|
||||
cover,
|
||||
fan,
|
||||
humidifier,
|
||||
image_processing,
|
||||
input_button,
|
||||
input_number,
|
||||
@ -398,6 +399,8 @@ class AlexaPowerController(AlexaCapability):
|
||||
is_on = self.entity.state != climate.HVACMode.OFF
|
||||
elif self.entity.domain == fan.DOMAIN:
|
||||
is_on = self.entity.state == fan.STATE_ON
|
||||
elif self.entity.domain == humidifier.DOMAIN:
|
||||
is_on = self.entity.state == humidifier.STATE_ON
|
||||
elif self.entity.domain == vacuum.DOMAIN:
|
||||
is_on = self.entity.state == vacuum.STATE_CLEANING
|
||||
elif self.entity.domain == timer.DOMAIN:
|
||||
@ -1403,6 +1406,12 @@ class AlexaModeController(AlexaCapability):
|
||||
if mode in self.entity.attributes.get(fan.ATTR_PRESET_MODES, None):
|
||||
return f"{fan.ATTR_PRESET_MODE}.{mode}"
|
||||
|
||||
# Humidifier mode
|
||||
if self.instance == f"{humidifier.DOMAIN}.{humidifier.ATTR_MODE}":
|
||||
mode = self.entity.attributes.get(humidifier.ATTR_MODE, None)
|
||||
if mode in self.entity.attributes.get(humidifier.ATTR_AVAILABLE_MODES, []):
|
||||
return f"{humidifier.ATTR_MODE}.{mode}"
|
||||
|
||||
# Cover Position
|
||||
if self.instance == f"{cover.DOMAIN}.{cover.ATTR_POSITION}":
|
||||
# Return state instead of position when using ModeController.
|
||||
@ -1459,6 +1468,20 @@ class AlexaModeController(AlexaCapability):
|
||||
)
|
||||
return self._resource.serialize_capability_resources()
|
||||
|
||||
# Humidifier modes
|
||||
if self.instance == f"{humidifier.DOMAIN}.{humidifier.ATTR_MODE}":
|
||||
self._resource = AlexaModeResource([AlexaGlobalCatalog.SETTING_MODE], False)
|
||||
modes = self.entity.attributes.get(humidifier.ATTR_AVAILABLE_MODES, [])
|
||||
for mode in modes:
|
||||
self._resource.add_mode(f"{humidifier.ATTR_MODE}.{mode}", [mode])
|
||||
# Humidifiers or Fans with a single mode completely break Alexa discovery, add a
|
||||
# fake preset (see issue #53832).
|
||||
if len(modes) == 1:
|
||||
self._resource.add_mode(
|
||||
f"{humidifier.ATTR_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(
|
||||
@ -1600,6 +1623,12 @@ class AlexaRangeController(AlexaCapability):
|
||||
return self.entity.attributes.get(fan.ATTR_PERCENTAGE)
|
||||
return 100 if self.entity.state == fan.STATE_ON else 0
|
||||
|
||||
# Humidifier target humidity
|
||||
if self.instance == f"{humidifier.DOMAIN}.{humidifier.ATTR_HUMIDITY}":
|
||||
# If the humidifier is turned off the target humidity attribute is not set.
|
||||
# We return 0 to make clear we do not know the current value.
|
||||
return self.entity.attributes.get(humidifier.ATTR_HUMIDITY, 0)
|
||||
|
||||
# Input Number Value
|
||||
if self.instance == f"{input_number.DOMAIN}.{input_number.ATTR_VALUE}":
|
||||
return float(self.entity.state)
|
||||
@ -1640,6 +1669,17 @@ class AlexaRangeController(AlexaCapability):
|
||||
)
|
||||
return self._resource.serialize_capability_resources()
|
||||
|
||||
# Humidifier Target Humidity Resources
|
||||
if self.instance == f"{humidifier.DOMAIN}.{humidifier.ATTR_HUMIDITY}":
|
||||
self._resource = AlexaPresetResource(
|
||||
labels=["Humidity", "Percentage", "Target humidity"],
|
||||
min_value=self.entity.attributes.get(humidifier.ATTR_MIN_HUMIDITY, 10),
|
||||
max_value=self.entity.attributes.get(humidifier.ATTR_MAX_HUMIDITY, 90),
|
||||
precision=1,
|
||||
unit=AlexaGlobalCatalog.UNIT_PERCENT,
|
||||
)
|
||||
return self._resource.serialize_capability_resources()
|
||||
|
||||
# Cover Position Resources
|
||||
if self.instance == f"{cover.DOMAIN}.{cover.ATTR_POSITION}":
|
||||
self._resource = AlexaPresetResource(
|
||||
@ -1764,6 +1804,22 @@ class AlexaRangeController(AlexaCapability):
|
||||
)
|
||||
return self._semantics.serialize_semantics()
|
||||
|
||||
# Target Humidity Percentage
|
||||
if self.instance == f"{humidifier.DOMAIN}.{humidifier.ATTR_HUMIDITY}":
|
||||
lower_labels = [AlexaSemantics.ACTION_LOWER]
|
||||
raise_labels = [AlexaSemantics.ACTION_RAISE]
|
||||
self._semantics = AlexaSemantics()
|
||||
min_value = self.entity.attributes.get(humidifier.ATTR_MIN_HUMIDITY, 10)
|
||||
max_value = self.entity.attributes.get(humidifier.ATTR_MAX_HUMIDITY, 90)
|
||||
|
||||
self._semantics.add_action_to_directive(
|
||||
lower_labels, "SetRangeValue", {"rangeValue": min_value}
|
||||
)
|
||||
self._semantics.add_action_to_directive(
|
||||
raise_labels, "SetRangeValue", {"rangeValue": max_value}
|
||||
)
|
||||
return self._semantics.serialize_semantics()
|
||||
|
||||
return None
|
||||
|
||||
|
||||
|
@ -83,7 +83,8 @@ API_THERMOSTAT_MODES_CUSTOM = {
|
||||
}
|
||||
API_THERMOSTAT_PRESETS = {climate.PRESET_ECO: "ECO"}
|
||||
|
||||
# AlexaModeController does not like a single mode for the fan preset, we add PRESET_MODE_NA if a fan has only one preset_mode
|
||||
# AlexaModeController does not like a single mode for the fan preset or humidifier mode,
|
||||
# we add PRESET_MODE_NA if a fan / humidifier has only one preset_mode
|
||||
PRESET_MODE_NA = "-"
|
||||
|
||||
|
||||
|
@ -15,6 +15,7 @@ from homeassistant.components import (
|
||||
cover,
|
||||
fan,
|
||||
group,
|
||||
humidifier,
|
||||
image_processing,
|
||||
input_boolean,
|
||||
input_button,
|
||||
@ -100,6 +101,9 @@ class DisplayCategory:
|
||||
# to HDMI1. Applies to Scenes
|
||||
ACTIVITY_TRIGGER = "ACTIVITY_TRIGGER"
|
||||
|
||||
# Indicates a device that cools the air in interior spaces.
|
||||
AIR_CONDITIONER = "AIR_CONDITIONER"
|
||||
|
||||
# Indicates a device that emits pleasant odors and masks unpleasant odors in interior spaces.
|
||||
AIR_FRESHENER = "AIR_FRESHENER"
|
||||
|
||||
@ -583,6 +587,30 @@ class FanCapabilities(AlexaEntity):
|
||||
yield Alexa(self.hass)
|
||||
|
||||
|
||||
@ENTITY_ADAPTERS.register(humidifier.DOMAIN)
|
||||
class HumidifierCapabilities(AlexaEntity):
|
||||
"""Class to represent Humidifier capabilities."""
|
||||
|
||||
def default_display_categories(self):
|
||||
"""Return the display categories for this entity."""
|
||||
return [DisplayCategory.OTHER]
|
||||
|
||||
def interfaces(self):
|
||||
"""Yield the supported interfaces."""
|
||||
yield AlexaPowerController(self.entity)
|
||||
supported = self.entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
|
||||
if supported & humidifier.HumidifierEntityFeature.MODES:
|
||||
yield AlexaModeController(
|
||||
self.entity, instance=f"{humidifier.DOMAIN}.{humidifier.ATTR_MODE}"
|
||||
)
|
||||
yield AlexaRangeController(
|
||||
self.entity, instance=f"{humidifier.DOMAIN}.{humidifier.ATTR_HUMIDITY}"
|
||||
)
|
||||
|
||||
yield AlexaEndpointHealth(self.hass, self.entity)
|
||||
yield Alexa(self.hass)
|
||||
|
||||
|
||||
@ENTITY_ADAPTERS.register(lock.DOMAIN)
|
||||
class LockCapabilities(AlexaEntity):
|
||||
"""Class to represent Lock capabilities."""
|
||||
|
@ -14,6 +14,7 @@ from homeassistant.components import (
|
||||
cover,
|
||||
fan,
|
||||
group,
|
||||
humidifier,
|
||||
input_button,
|
||||
input_number,
|
||||
light,
|
||||
@ -154,6 +155,8 @@ async def async_api_turn_on(
|
||||
service = cover.SERVICE_OPEN_COVER
|
||||
elif domain == fan.DOMAIN:
|
||||
service = fan.SERVICE_TURN_ON
|
||||
elif domain == humidifier.DOMAIN:
|
||||
service = humidifier.SERVICE_TURN_ON
|
||||
elif domain == vacuum.DOMAIN:
|
||||
supported = entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
|
||||
if (
|
||||
@ -201,6 +204,8 @@ async def async_api_turn_off(
|
||||
service = cover.SERVICE_CLOSE_COVER
|
||||
elif domain == fan.DOMAIN:
|
||||
service = fan.SERVICE_TURN_OFF
|
||||
elif domain == humidifier.DOMAIN:
|
||||
service = humidifier.SERVICE_TURN_OFF
|
||||
elif domain == vacuum.DOMAIN:
|
||||
supported = entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
|
||||
if (
|
||||
@ -448,20 +453,31 @@ async def async_api_set_percentage(
|
||||
"""Process a set percentage request."""
|
||||
entity = directive.entity
|
||||
|
||||
if entity.domain != fan.DOMAIN:
|
||||
if entity.domain == fan.DOMAIN:
|
||||
percentage = int(directive.payload["percentage"])
|
||||
service = fan.SERVICE_SET_PERCENTAGE
|
||||
data = {
|
||||
ATTR_ENTITY_ID: entity.entity_id,
|
||||
fan.ATTR_PERCENTAGE: percentage,
|
||||
}
|
||||
|
||||
await hass.services.async_call(
|
||||
entity.domain, service, data, blocking=False, context=context
|
||||
)
|
||||
elif entity.domain == humidifier.DOMAIN:
|
||||
percentage = int(directive.payload["percentage"])
|
||||
service = humidifier.SERVICE_SET_HUMIDITY
|
||||
data = {
|
||||
ATTR_ENTITY_ID: entity.entity_id,
|
||||
humidifier.ATTR_HUMIDITY: percentage,
|
||||
}
|
||||
|
||||
await hass.services.async_call(
|
||||
entity.domain, service, data, blocking=False, context=context
|
||||
)
|
||||
else:
|
||||
raise AlexaInvalidDirectiveError(DIRECTIVE_NOT_SUPPORTED)
|
||||
|
||||
percentage = int(directive.payload["percentage"])
|
||||
service = fan.SERVICE_SET_PERCENTAGE
|
||||
data = {
|
||||
ATTR_ENTITY_ID: entity.entity_id,
|
||||
fan.ATTR_PERCENTAGE: percentage,
|
||||
}
|
||||
|
||||
await hass.services.async_call(
|
||||
entity.domain, service, data, blocking=False, context=context
|
||||
)
|
||||
|
||||
return directive.response()
|
||||
|
||||
|
||||
@ -1130,6 +1146,18 @@ async def async_api_set_mode(
|
||||
msg = f"Entity '{entity.entity_id}' does not support Preset '{preset_mode}'"
|
||||
raise AlexaInvalidValueError(msg)
|
||||
|
||||
# Humidifier mode
|
||||
elif instance == f"{humidifier.DOMAIN}.{humidifier.ATTR_MODE}":
|
||||
mode = mode.split(".")[1]
|
||||
if mode != PRESET_MODE_NA and mode in entity.attributes.get(
|
||||
humidifier.ATTR_AVAILABLE_MODES
|
||||
):
|
||||
service = humidifier.SERVICE_SET_MODE
|
||||
data[humidifier.ATTR_MODE] = mode
|
||||
else:
|
||||
msg = f"Entity '{entity.entity_id}' does not support Mode '{mode}'"
|
||||
raise AlexaInvalidValueError(msg)
|
||||
|
||||
# Cover Position
|
||||
elif instance == f"{cover.DOMAIN}.{cover.ATTR_POSITION}":
|
||||
position = mode.split(".")[1]
|
||||
@ -1306,6 +1334,12 @@ async def async_api_set_range(
|
||||
else:
|
||||
service = fan.SERVICE_TURN_ON
|
||||
|
||||
# Humidifier target humidity
|
||||
elif instance == f"{humidifier.DOMAIN}.{humidifier.ATTR_HUMIDITY}":
|
||||
range_value = int(range_value)
|
||||
service = humidifier.SERVICE_SET_HUMIDITY
|
||||
data[humidifier.ATTR_HUMIDITY] = range_value
|
||||
|
||||
# Input Number Value
|
||||
elif instance == f"{input_number.DOMAIN}.{input_number.ATTR_VALUE}":
|
||||
range_value = float(range_value)
|
||||
@ -1414,6 +1448,26 @@ async def async_api_adjust_range(
|
||||
else:
|
||||
service = fan.SERVICE_TURN_OFF
|
||||
|
||||
# Humidifier target humidity
|
||||
elif instance == f"{humidifier.DOMAIN}.{humidifier.ATTR_HUMIDITY}":
|
||||
percentage_step = 5
|
||||
range_delta = (
|
||||
int(range_delta * percentage_step)
|
||||
if range_delta_default
|
||||
else int(range_delta)
|
||||
)
|
||||
service = humidifier.SERVICE_SET_HUMIDITY
|
||||
if not (current := entity.attributes.get(humidifier.ATTR_HUMIDITY)):
|
||||
msg = f"Unable to determine {entity.entity_id} current target humidity"
|
||||
raise AlexaInvalidValueError(msg)
|
||||
min_value = entity.attributes.get(humidifier.ATTR_MIN_HUMIDITY, 10)
|
||||
max_value = entity.attributes.get(humidifier.ATTR_MAX_HUMIDITY, 90)
|
||||
percentage = response_value = min(
|
||||
max_value, max(min_value, range_delta + current)
|
||||
)
|
||||
if percentage:
|
||||
data[humidifier.ATTR_HUMIDITY] = percentage
|
||||
|
||||
# Input Number Value
|
||||
elif instance == f"{input_number.DOMAIN}.{input_number.ATTR_VALUE}":
|
||||
range_delta = float(range_delta)
|
||||
|
@ -411,6 +411,72 @@ async def test_report_fan_speed_state(hass):
|
||||
properties.assert_equal("Alexa.RangeController", "rangeValue", 0)
|
||||
|
||||
|
||||
async def test_report_humidifier_humidity_state(hass):
|
||||
"""Test PercentageController, PowerLevelController reports humidifier humidity correctly."""
|
||||
hass.states.async_set(
|
||||
"humidifier.dry",
|
||||
"on",
|
||||
{
|
||||
"friendly_name": "Humidifier dry",
|
||||
"supported_features": 0,
|
||||
"humidity": 25,
|
||||
"min_humidity": 20,
|
||||
"max_humidity": 90,
|
||||
},
|
||||
)
|
||||
hass.states.async_set(
|
||||
"humidifier.wet",
|
||||
"on",
|
||||
{
|
||||
"friendly_name": "Humidifier wet",
|
||||
"supported_features": 0,
|
||||
"humidity": 80,
|
||||
"min_humidity": 20,
|
||||
"max_humidity": 90,
|
||||
},
|
||||
)
|
||||
properties = await reported_properties(hass, "humidifier.dry")
|
||||
properties.assert_equal("Alexa.RangeController", "rangeValue", 25)
|
||||
|
||||
properties = await reported_properties(hass, "humidifier.wet")
|
||||
properties.assert_equal("Alexa.RangeController", "rangeValue", 80)
|
||||
|
||||
|
||||
async def test_report_humidifier_mode(hass):
|
||||
"""Test ModeController reports humidifier mode correctly."""
|
||||
hass.states.async_set(
|
||||
"humidifier.auto",
|
||||
"on",
|
||||
{
|
||||
"friendly_name": "Humidifier auto",
|
||||
"supported_features": 1,
|
||||
"humidity": 50,
|
||||
"mode": "Auto",
|
||||
"available_modes": ["Auto", "Low", "Medium", "High"],
|
||||
"min_humidity": 20,
|
||||
"max_humidity": 90,
|
||||
},
|
||||
)
|
||||
properties = await reported_properties(hass, "humidifier.auto")
|
||||
properties.assert_equal("Alexa.ModeController", "mode", "mode.Auto")
|
||||
|
||||
hass.states.async_set(
|
||||
"humidifier.medium",
|
||||
"on",
|
||||
{
|
||||
"friendly_name": "Humidifier auto",
|
||||
"supported_features": 1,
|
||||
"humidity": 60,
|
||||
"mode": "Medium",
|
||||
"available_modes": ["Auto", "Low", "Medium", "High"],
|
||||
"min_humidity": 20,
|
||||
"max_humidity": 90,
|
||||
},
|
||||
)
|
||||
properties = await reported_properties(hass, "humidifier.medium")
|
||||
properties.assert_equal("Alexa.ModeController", "mode", "mode.Medium")
|
||||
|
||||
|
||||
async def test_report_fan_preset_mode(hass):
|
||||
"""Test ModeController reports fan preset_mode correctly."""
|
||||
hass.states.async_set(
|
||||
|
@ -950,6 +950,145 @@ async def test_single_preset_mode_fan(hass, caplog):
|
||||
caplog.clear()
|
||||
|
||||
|
||||
@freeze_time("2022-04-19 07:53:05")
|
||||
async def test_humidifier(hass, caplog):
|
||||
"""Test humidifier controller."""
|
||||
device = (
|
||||
"humidifier.test_1",
|
||||
"on",
|
||||
{
|
||||
"friendly_name": "Humidifier test 1",
|
||||
"humidity": 66,
|
||||
"supported_features": 1,
|
||||
"mode": "Auto",
|
||||
"available_modes": ["Auto", "Low", "Medium", "High"],
|
||||
"min_humidity": 20,
|
||||
"max_humidity": 90,
|
||||
},
|
||||
)
|
||||
await discovery_test(device, hass)
|
||||
|
||||
await assert_power_controller_works(
|
||||
"humidifier#test_1",
|
||||
"humidifier.turn_on",
|
||||
"humidifier.turn_off",
|
||||
hass,
|
||||
"2022-04-19T07:53:05Z",
|
||||
)
|
||||
|
||||
call, _ = await assert_request_calls_service(
|
||||
"Alexa.ModeController",
|
||||
"SetMode",
|
||||
"humidifier#test_1",
|
||||
"humidifier.set_mode",
|
||||
hass,
|
||||
payload={"mode": "mode.Auto"},
|
||||
instance="humidifier.mode",
|
||||
)
|
||||
assert call.data["mode"] == "Auto"
|
||||
|
||||
with pytest.raises(AssertionError):
|
||||
await assert_request_calls_service(
|
||||
"Alexa.ModeController",
|
||||
"SetMode",
|
||||
"humidifier#test_1",
|
||||
"humidifier.set_mode",
|
||||
hass,
|
||||
payload={"mode": "mode.-"},
|
||||
instance="humidifier.mode",
|
||||
)
|
||||
assert "Entity 'humidifier.test_1' does not support Mode '-'" in caplog.text
|
||||
caplog.clear()
|
||||
|
||||
call, _ = await assert_request_calls_service(
|
||||
"Alexa.RangeController",
|
||||
"SetRangeValue",
|
||||
"humidifier#test_1",
|
||||
"humidifier.set_humidity",
|
||||
hass,
|
||||
payload={"rangeValue": "67"},
|
||||
instance="humidifier.humidity",
|
||||
)
|
||||
assert call.data["humidity"] == 67
|
||||
call, _ = await assert_request_calls_service(
|
||||
"Alexa.RangeController",
|
||||
"SetRangeValue",
|
||||
"humidifier#test_1",
|
||||
"humidifier.set_humidity",
|
||||
hass,
|
||||
payload={"rangeValue": "33"},
|
||||
instance="humidifier.humidity",
|
||||
)
|
||||
assert call.data["humidity"] == 33
|
||||
|
||||
|
||||
async def test_humidifier_without_modes(hass):
|
||||
"""Test humidifier discovery without modes."""
|
||||
|
||||
device = (
|
||||
"humidifier.test_2",
|
||||
"on",
|
||||
{
|
||||
"friendly_name": "Humidifier test 2",
|
||||
"humidity": 33,
|
||||
"supported_features": 0,
|
||||
"min_humidity": 20,
|
||||
"max_humidity": 90,
|
||||
},
|
||||
)
|
||||
appliance = await discovery_test(device, hass)
|
||||
|
||||
assert appliance["endpointId"] == "humidifier#test_2"
|
||||
assert appliance["displayCategories"][0] == "OTHER"
|
||||
assert appliance["friendlyName"] == "Humidifier test 2"
|
||||
capabilities = assert_endpoint_capabilities(
|
||||
appliance,
|
||||
"Alexa.RangeController",
|
||||
"Alexa.PowerController",
|
||||
"Alexa.EndpointHealth",
|
||||
"Alexa",
|
||||
)
|
||||
|
||||
power_capability = get_capability(capabilities, "Alexa.PowerController")
|
||||
assert "capabilityResources" not in power_capability
|
||||
assert "configuration" not in power_capability
|
||||
|
||||
|
||||
async def test_humidifier_with_modes(hass):
|
||||
"""Test humidifier discovery with modes."""
|
||||
|
||||
device = (
|
||||
"humidifier.test_1",
|
||||
"on",
|
||||
{
|
||||
"friendly_name": "Humidifier test 1",
|
||||
"humidity": 66,
|
||||
"supported_features": 1,
|
||||
"mode": "Auto",
|
||||
"available_modes": ["Auto", "Low", "Medium", "High"],
|
||||
"min_humidity": 20,
|
||||
"max_humidity": 90,
|
||||
},
|
||||
)
|
||||
appliance = await discovery_test(device, hass)
|
||||
|
||||
assert appliance["endpointId"] == "humidifier#test_1"
|
||||
assert appliance["displayCategories"][0] == "OTHER"
|
||||
assert appliance["friendlyName"] == "Humidifier test 1"
|
||||
capabilities = assert_endpoint_capabilities(
|
||||
appliance,
|
||||
"Alexa.ModeController",
|
||||
"Alexa.RangeController",
|
||||
"Alexa.PowerController",
|
||||
"Alexa.EndpointHealth",
|
||||
"Alexa",
|
||||
)
|
||||
|
||||
power_capability = get_capability(capabilities, "Alexa.PowerController")
|
||||
assert "capabilityResources" not in power_capability
|
||||
assert "configuration" not in power_capability
|
||||
|
||||
|
||||
async def test_lock(hass):
|
||||
"""Test lock discovery."""
|
||||
device = ("lock.test", "off", {"friendly_name": "Test lock"})
|
||||
|
@ -209,8 +209,8 @@ async def test_report_state_unsets_authorized_on_access_token_error(
|
||||
config._store.set_authorized.assert_called_once_with(False)
|
||||
|
||||
|
||||
async def test_report_state_instance(hass, aioclient_mock):
|
||||
"""Test proactive state reports with instance."""
|
||||
async def test_report_state_fan(hass, aioclient_mock):
|
||||
"""Test proactive state reports with fan instance."""
|
||||
aioclient_mock.post(TEST_URL, text="", status=202)
|
||||
|
||||
hass.states.async_set(
|
||||
@ -275,6 +275,64 @@ async def test_report_state_instance(hass, aioclient_mock):
|
||||
assert call_json["event"]["endpoint"]["endpointId"] == "fan#test_fan"
|
||||
|
||||
|
||||
async def test_report_state_humidifier(hass, aioclient_mock):
|
||||
"""Test proactive state reports with humidifier instance."""
|
||||
aioclient_mock.post(TEST_URL, text="", status=202)
|
||||
|
||||
hass.states.async_set(
|
||||
"humidifier.test_humidifier",
|
||||
"off",
|
||||
{
|
||||
"friendly_name": "Test humidifier",
|
||||
"supported_features": 1,
|
||||
"mode": None,
|
||||
"available_modes": ["auto", "smart"],
|
||||
},
|
||||
)
|
||||
|
||||
await state_report.async_enable_proactive_mode(hass, get_default_config(hass))
|
||||
|
||||
hass.states.async_set(
|
||||
"humidifier.test_humidifier",
|
||||
"on",
|
||||
{
|
||||
"friendly_name": "Test humidifier",
|
||||
"supported_features": 1,
|
||||
"mode": "smart",
|
||||
"available_modes": ["auto", "smart"],
|
||||
"humidity": 55,
|
||||
},
|
||||
)
|
||||
|
||||
# To trigger event listener
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert len(aioclient_mock.mock_calls) == 1
|
||||
call = aioclient_mock.mock_calls
|
||||
|
||||
call_json = call[0][2]
|
||||
assert call_json["event"]["header"]["namespace"] == "Alexa"
|
||||
assert call_json["event"]["header"]["name"] == "ChangeReport"
|
||||
|
||||
change_reports = call_json["event"]["payload"]["change"]["properties"]
|
||||
|
||||
checks = 0
|
||||
for report in change_reports:
|
||||
if report["name"] == "mode":
|
||||
assert report["value"] == "mode.smart"
|
||||
assert report["instance"] == "humidifier.mode"
|
||||
assert report["namespace"] == "Alexa.ModeController"
|
||||
checks += 1
|
||||
if report["name"] == "rangeValue":
|
||||
assert report["value"] == 55
|
||||
assert report["instance"] == "humidifier.humidity"
|
||||
assert report["namespace"] == "Alexa.RangeController"
|
||||
checks += 1
|
||||
assert checks == 2
|
||||
|
||||
assert call_json["event"]["endpoint"]["endpointId"] == "humidifier#test_humidifier"
|
||||
|
||||
|
||||
async def test_send_add_or_update_message(hass, aioclient_mock):
|
||||
"""Test sending an AddOrUpdateReport message."""
|
||||
aioclient_mock.post(TEST_URL, text="")
|
||||
|
Loading…
x
Reference in New Issue
Block a user