1 """Alexa message handlers."""
3 from __future__
import annotations
6 from collections.abc
import Callable, Coroutine
11 from homeassistant
import core
as ha
35 ATTR_SUPPORTED_FEATURES,
37 SERVICE_ALARM_ARM_AWAY,
38 SERVICE_ALARM_ARM_HOME,
39 SERVICE_ALARM_ARM_NIGHT,
42 SERVICE_MEDIA_NEXT_TRACK,
45 SERVICE_MEDIA_PREVIOUS_TRACK,
47 SERVICE_SET_COVER_POSITION,
48 SERVICE_SET_COVER_TILT_POSITION,
63 from .config
import AbstractConfig
67 API_THERMOSTAT_MODES_CUSTOM,
68 API_THERMOSTAT_PRESETS,
74 from .entities
import async_get_entities
76 AlexaInvalidDirectiveError,
77 AlexaInvalidValueError,
78 AlexaSecurityPanelAuthorizationRequired,
80 AlexaUnsupportedThermostatModeError,
81 AlexaUnsupportedThermostatTargetStateError,
82 AlexaVideoActionNotPermittedForContentError,
84 from .state_report
import AlexaDirective, AlexaResponse, async_enable_proactive_mode
86 _LOGGER = logging.getLogger(__name__)
87 DIRECTIVE_NOT_SUPPORTED =
"Entity does not support directive"
91 "min_temp": climate.ATTR_MIN_TEMP,
92 "max_temp": climate.ATTR_MAX_TEMP,
94 water_heater.DOMAIN: {
95 "min_temp": water_heater.ATTR_MIN_TEMP,
96 "max_temp": water_heater.ATTR_MAX_TEMP,
100 SERVICE_SET_TEMPERATURE = {
101 climate.DOMAIN: climate.SERVICE_SET_TEMPERATURE,
102 water_heater.DOMAIN: water_heater.SERVICE_SET_TEMPERATURE,
108 [ha.HomeAssistant, AbstractConfig, AlexaDirective, ha.Context],
109 Coroutine[Any, Any, AlexaResponse],
114 @HANDLERS.register(("Alexa.Discovery", "Discover"))
116 hass: ha.HomeAssistant,
117 config: AbstractConfig,
118 directive: AlexaDirective,
121 """Create a API formatted discovery response.
125 discovery_endpoints: list[dict[str, Any]] = []
127 if not config.should_expose(alexa_entity.entity_id):
130 discovered_serialized_entity = alexa_entity.serialize_discovery()
133 "Unable to serialize %s for discovery", alexa_entity.entity_id
136 discovery_endpoints.append(discovered_serialized_entity)
138 return directive.response(
139 name=
"Discover.Response",
140 namespace=
"Alexa.Discovery",
141 payload={
"endpoints": discovery_endpoints},
145 @HANDLERS.register(("Alexa.Authorization", "AcceptGrant"))
147 hass: ha.HomeAssistant,
148 config: AbstractConfig,
149 directive: AlexaDirective,
152 """Create a API formatted AcceptGrant response.
156 auth_code: str = directive.payload[
"grant"][
"code"]
158 if config.supports_auth:
159 await config.async_accept_grant(auth_code)
161 if config.should_report_state:
164 return directive.response(
165 name=
"AcceptGrant.Response", namespace=
"Alexa.Authorization", payload={}
169 @HANDLERS.register(("Alexa.PowerController", "TurnOn"))
171 hass: ha.HomeAssistant,
172 config: AbstractConfig,
173 directive: AlexaDirective,
176 """Process a turn on request."""
177 entity = directive.entity
178 if (domain := entity.domain) == group.DOMAIN:
181 service = SERVICE_TURN_ON
182 if domain == cover.DOMAIN:
183 service = cover.SERVICE_OPEN_COVER
184 elif domain == climate.DOMAIN:
185 service = climate.SERVICE_TURN_ON
186 elif domain == fan.DOMAIN:
187 service = fan.SERVICE_TURN_ON
188 elif domain == humidifier.DOMAIN:
189 service = humidifier.SERVICE_TURN_ON
190 elif domain == remote.DOMAIN:
191 service = remote.SERVICE_TURN_ON
192 elif domain == vacuum.DOMAIN:
193 supported = entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
195 not supported & vacuum.VacuumEntityFeature.TURN_ON
196 and supported & vacuum.VacuumEntityFeature.START
198 service = vacuum.SERVICE_START
199 elif domain == timer.DOMAIN:
200 service = timer.SERVICE_START
201 elif domain == media_player.DOMAIN:
202 supported = entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
204 media_player.MediaPlayerEntityFeature.TURN_ON
205 | media_player.MediaPlayerEntityFeature.TURN_OFF
207 if not supported & power_features:
208 service = media_player.SERVICE_MEDIA_PLAY
210 await hass.services.async_call(
213 {ATTR_ENTITY_ID: entity.entity_id},
218 return directive.response()
221 @HANDLERS.register(("Alexa.PowerController", "TurnOff"))
223 hass: ha.HomeAssistant,
224 config: AbstractConfig,
225 directive: AlexaDirective,
228 """Process a turn off request."""
229 entity = directive.entity
230 domain = entity.domain
231 if entity.domain == group.DOMAIN:
234 service = SERVICE_TURN_OFF
235 if entity.domain == cover.DOMAIN:
236 service = cover.SERVICE_CLOSE_COVER
237 elif domain == climate.DOMAIN:
238 service = climate.SERVICE_TURN_OFF
239 elif domain == fan.DOMAIN:
240 service = fan.SERVICE_TURN_OFF
241 elif domain == remote.DOMAIN:
242 service = remote.SERVICE_TURN_OFF
243 elif domain == humidifier.DOMAIN:
244 service = humidifier.SERVICE_TURN_OFF
245 elif domain == vacuum.DOMAIN:
246 supported = entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
248 not supported & vacuum.VacuumEntityFeature.TURN_OFF
249 and supported & vacuum.VacuumEntityFeature.RETURN_HOME
251 service = vacuum.SERVICE_RETURN_TO_BASE
252 elif domain == timer.DOMAIN:
253 service = timer.SERVICE_CANCEL
254 elif domain == media_player.DOMAIN:
255 supported = entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
257 media_player.MediaPlayerEntityFeature.TURN_ON
258 | media_player.MediaPlayerEntityFeature.TURN_OFF
260 if not supported & power_features:
261 service = media_player.SERVICE_MEDIA_STOP
263 await hass.services.async_call(
266 {ATTR_ENTITY_ID: entity.entity_id},
271 return directive.response()
274 @HANDLERS.register(("Alexa.BrightnessController", "SetBrightness"))
276 hass: ha.HomeAssistant,
277 config: AbstractConfig,
278 directive: AlexaDirective,
281 """Process a set brightness request."""
282 entity = directive.entity
283 brightness =
int(directive.payload[
"brightness"])
285 await hass.services.async_call(
288 {ATTR_ENTITY_ID: entity.entity_id, light.ATTR_BRIGHTNESS_PCT: brightness},
293 return directive.response()
296 @HANDLERS.register(("Alexa.BrightnessController", "AdjustBrightness"))
298 hass: ha.HomeAssistant,
299 config: AbstractConfig,
300 directive: AlexaDirective,
303 """Process an adjust brightness request."""
304 entity = directive.entity
305 brightness_delta =
int(directive.payload[
"brightnessDelta"])
308 await hass.services.async_call(
312 ATTR_ENTITY_ID: entity.entity_id,
313 light.ATTR_BRIGHTNESS_STEP_PCT: brightness_delta,
319 return directive.response()
322 @HANDLERS.register(("Alexa.ColorController", "SetColor"))
324 hass: ha.HomeAssistant,
325 config: AbstractConfig,
326 directive: AlexaDirective,
329 """Process a set color request."""
330 entity = directive.entity
331 rgb = color_util.color_hsb_to_RGB(
332 float(directive.payload[
"color"][
"hue"]),
333 float(directive.payload[
"color"][
"saturation"]),
334 float(directive.payload[
"color"][
"brightness"]),
337 await hass.services.async_call(
340 {ATTR_ENTITY_ID: entity.entity_id, light.ATTR_RGB_COLOR: rgb},
345 return directive.response()
348 @HANDLERS.register(("Alexa.ColorTemperatureController", "SetColorTemperature"))
350 hass: ha.HomeAssistant,
351 config: AbstractConfig,
352 directive: AlexaDirective,
355 """Process a set color temperature request."""
356 entity = directive.entity
357 kelvin =
int(directive.payload[
"colorTemperatureInKelvin"])
359 await hass.services.async_call(
362 {ATTR_ENTITY_ID: entity.entity_id, light.ATTR_KELVIN: kelvin},
367 return directive.response()
370 @HANDLERS.register(("Alexa.ColorTemperatureController", "DecreaseColorTemperature"))
372 hass: ha.HomeAssistant,
373 config: AbstractConfig,
374 directive: AlexaDirective,
377 """Process a decrease color temperature request."""
378 entity = directive.entity
379 current =
int(entity.attributes[light.ATTR_COLOR_TEMP])
380 max_mireds =
int(entity.attributes[light.ATTR_MAX_MIREDS])
382 value =
min(max_mireds, current + 50)
383 await hass.services.async_call(
386 {ATTR_ENTITY_ID: entity.entity_id, light.ATTR_COLOR_TEMP: value},
391 return directive.response()
394 @HANDLERS.register(("Alexa.ColorTemperatureController", "IncreaseColorTemperature"))
396 hass: ha.HomeAssistant,
397 config: AbstractConfig,
398 directive: AlexaDirective,
401 """Process an increase color temperature request."""
402 entity = directive.entity
403 current =
int(entity.attributes[light.ATTR_COLOR_TEMP])
404 min_mireds =
int(entity.attributes[light.ATTR_MIN_MIREDS])
406 value =
max(min_mireds, current - 50)
407 await hass.services.async_call(
410 {ATTR_ENTITY_ID: entity.entity_id, light.ATTR_COLOR_TEMP: value},
415 return directive.response()
418 @HANDLERS.register(("Alexa.SceneController", "Activate"))
420 hass: ha.HomeAssistant,
421 config: AbstractConfig,
422 directive: AlexaDirective,
425 """Process an activate request."""
426 entity = directive.entity
427 domain = entity.domain
429 service = SERVICE_TURN_ON
430 if domain == button.DOMAIN:
431 service = button.SERVICE_PRESS
432 elif domain == input_button.DOMAIN:
433 service = input_button.SERVICE_PRESS
435 await hass.services.async_call(
438 {ATTR_ENTITY_ID: entity.entity_id},
443 payload: dict[str, Any] = {
444 "cause": {
"type": Cause.VOICE_INTERACTION},
445 "timestamp": dt_util.utcnow().strftime(DATE_FORMAT),
448 return directive.response(
449 name=
"ActivationStarted", namespace=
"Alexa.SceneController", payload=payload
453 @HANDLERS.register(("Alexa.SceneController", "Deactivate"))
455 hass: ha.HomeAssistant,
456 config: AbstractConfig,
457 directive: AlexaDirective,
460 """Process a deactivate request."""
461 entity = directive.entity
462 domain = entity.domain
464 await hass.services.async_call(
467 {ATTR_ENTITY_ID: entity.entity_id},
472 payload: dict[str, Any] = {
473 "cause": {
"type": Cause.VOICE_INTERACTION},
474 "timestamp": dt_util.utcnow().strftime(DATE_FORMAT),
477 return directive.response(
478 name=
"DeactivationStarted", namespace=
"Alexa.SceneController", payload=payload
482 @HANDLERS.register(("Alexa.LockController", "Lock"))
484 hass: ha.HomeAssistant,
485 config: AbstractConfig,
486 directive: AlexaDirective,
489 """Process a lock request."""
490 entity = directive.entity
491 await hass.services.async_call(
494 {ATTR_ENTITY_ID: entity.entity_id},
499 response = directive.response()
500 response.add_context_property(
501 {
"name":
"lockState",
"namespace":
"Alexa.LockController",
"value":
"LOCKED"}
506 @HANDLERS.register(("Alexa.LockController", "Unlock"))
508 hass: ha.HomeAssistant,
509 config: AbstractConfig,
510 directive: AlexaDirective,
513 """Process an unlock request."""
514 if config.locale
not in {
533 "The unlock directive is not supported for the following locales:"
538 entity = directive.entity
539 await hass.services.async_call(
542 {ATTR_ENTITY_ID: entity.entity_id},
547 response = directive.response()
548 response.add_context_property(
549 {
"namespace":
"Alexa.LockController",
"name":
"lockState",
"value":
"UNLOCKED"}
555 @HANDLERS.register(("Alexa.Speaker", "SetVolume"))
557 hass: ha.HomeAssistant,
558 config: AbstractConfig,
559 directive: AlexaDirective,
562 """Process a set volume request."""
563 volume = round(
float(directive.payload[
"volume"] / 100), 2)
564 entity = directive.entity
566 data: dict[str, Any] = {
567 ATTR_ENTITY_ID: entity.entity_id,
568 media_player.const.ATTR_MEDIA_VOLUME_LEVEL: volume,
571 await hass.services.async_call(
572 entity.domain, SERVICE_VOLUME_SET, data, blocking=
False, context=context
575 return directive.response()
578 @HANDLERS.register(("Alexa.InputController", "SelectInput"))
580 hass: ha.HomeAssistant,
581 config: AbstractConfig,
582 directive: AlexaDirective,
585 """Process a set input request."""
586 media_input = directive.payload[
"input"]
587 entity = directive.entity
591 source_list = entity.attributes.get(media_player.const.ATTR_INPUT_SOURCE_LIST)
or []
592 for source
in source_list:
594 source.lower().replace(
"-",
"").replace(
"_",
"").replace(
" ",
"")
596 media_input = media_input.lower().replace(
" ",
"")
598 formatted_source
in Inputs.VALID_SOURCE_NAME_MAP
599 and formatted_source == media_input
601 media_input.endswith(
"1")
and formatted_source == media_input.rstrip(
"1")
607 f
"failed to map input {media_input} to a media source on {entity.entity_id}"
611 data: dict[str, Any] = {
612 ATTR_ENTITY_ID: entity.entity_id,
613 media_player.const.ATTR_INPUT_SOURCE: media_input,
616 await hass.services.async_call(
618 media_player.SERVICE_SELECT_SOURCE,
624 return directive.response()
627 @HANDLERS.register(("Alexa.Speaker", "AdjustVolume"))
629 hass: ha.HomeAssistant,
630 config: AbstractConfig,
631 directive: AlexaDirective,
634 """Process an adjust volume request."""
635 volume_delta =
int(directive.payload[
"volume"])
637 entity = directive.entity
638 current_level = entity.attributes[media_player.const.ATTR_MEDIA_VOLUME_LEVEL]
642 current = math.floor(
int(current_level * 100))
643 except ZeroDivisionError:
646 volume =
float(
max(0, volume_delta + current) / 100)
648 data: dict[str, Any] = {
649 ATTR_ENTITY_ID: entity.entity_id,
650 media_player.const.ATTR_MEDIA_VOLUME_LEVEL: volume,
653 await hass.services.async_call(
654 entity.domain, SERVICE_VOLUME_SET, data, blocking=
False, context=context
657 return directive.response()
660 @HANDLERS.register(("Alexa.StepSpeaker", "AdjustVolume"))
662 hass: ha.HomeAssistant,
663 config: AbstractConfig,
664 directive: AlexaDirective,
667 """Process an adjust volume step request."""
674 entity = directive.entity
675 volume_int =
int(directive.payload[
"volumeSteps"])
676 is_default = bool(directive.payload[
"volumeStepsDefault"])
680 service_volume = SERVICE_VOLUME_DOWN
682 volume_int = -default_steps
684 service_volume = SERVICE_VOLUME_UP
686 volume_int = default_steps
688 data: dict[str, Any] = {ATTR_ENTITY_ID: entity.entity_id}
690 for _
in range(abs(volume_int)):
691 await hass.services.async_call(
692 entity.domain, service_volume, data, blocking=
False, context=context
695 return directive.response()
698 @HANDLERS.register(("Alexa.StepSpeaker", "SetMute"))
699 @HANDLERS.register(("Alexa.Speaker", "SetMute"))
701 hass: ha.HomeAssistant,
702 config: AbstractConfig,
703 directive: AlexaDirective,
706 """Process a set mute request."""
707 mute = bool(directive.payload[
"mute"])
708 entity = directive.entity
709 data: dict[str, Any] = {
710 ATTR_ENTITY_ID: entity.entity_id,
711 media_player.const.ATTR_MEDIA_VOLUME_MUTED: mute,
714 await hass.services.async_call(
715 entity.domain, SERVICE_VOLUME_MUTE, data, blocking=
False, context=context
718 return directive.response()
721 @HANDLERS.register(("Alexa.PlaybackController", "Play"))
723 hass: ha.HomeAssistant,
724 config: AbstractConfig,
725 directive: AlexaDirective,
728 """Process a play request."""
729 entity = directive.entity
730 data: dict[str, Any] = {ATTR_ENTITY_ID: entity.entity_id}
732 await hass.services.async_call(
733 entity.domain, SERVICE_MEDIA_PLAY, data, blocking=
False, context=context
736 return directive.response()
739 @HANDLERS.register(("Alexa.PlaybackController", "Pause"))
741 hass: ha.HomeAssistant,
742 config: AbstractConfig,
743 directive: AlexaDirective,
746 """Process a pause request."""
747 entity = directive.entity
748 data: dict[str, Any] = {ATTR_ENTITY_ID: entity.entity_id}
750 await hass.services.async_call(
751 entity.domain, SERVICE_MEDIA_PAUSE, data, blocking=
False, context=context
754 return directive.response()
757 @HANDLERS.register(("Alexa.PlaybackController", "Stop"))
759 hass: ha.HomeAssistant,
760 config: AbstractConfig,
761 directive: AlexaDirective,
764 """Process a stop request."""
765 entity = directive.entity
766 data: dict[str, Any] = {ATTR_ENTITY_ID: entity.entity_id}
768 if entity.domain == cover.DOMAIN:
769 supported: int = entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
770 feature_services: dict[int, str] = {
771 cover.CoverEntityFeature.STOP.value: cover.SERVICE_STOP_COVER,
772 cover.CoverEntityFeature.STOP_TILT.value: cover.SERVICE_STOP_COVER_TILT,
774 await asyncio.gather(
776 hass.services.async_call(
777 entity.domain, service, data, blocking=
False, context=context
779 for feature, service
in feature_services.items()
780 if feature & supported
784 await hass.services.async_call(
785 entity.domain, SERVICE_MEDIA_STOP, data, blocking=
False, context=context
788 return directive.response()
791 @HANDLERS.register(("Alexa.PlaybackController", "Next"))
793 hass: ha.HomeAssistant,
794 config: AbstractConfig,
795 directive: AlexaDirective,
798 """Process a next request."""
799 entity = directive.entity
800 data: dict[str, Any] = {ATTR_ENTITY_ID: entity.entity_id}
802 await hass.services.async_call(
803 entity.domain, SERVICE_MEDIA_NEXT_TRACK, data, blocking=
False, context=context
806 return directive.response()
809 @HANDLERS.register(("Alexa.PlaybackController", "Previous"))
811 hass: ha.HomeAssistant,
812 config: AbstractConfig,
813 directive: AlexaDirective,
816 """Process a previous request."""
817 entity = directive.entity
818 data: dict[str, Any] = {ATTR_ENTITY_ID: entity.entity_id}
820 await hass.services.async_call(
822 SERVICE_MEDIA_PREVIOUS_TRACK,
828 return directive.response()
832 hass: ha.HomeAssistant, temp_obj: dict[str, Any], interval: bool =
False
834 """Get temperature from Temperature object in requested unit."""
835 to_unit = hass.config.units.temperature_unit
836 from_unit = UnitOfTemperature.CELSIUS
837 temp =
float(temp_obj[
"value"])
839 if temp_obj[
"scale"] ==
"FAHRENHEIT":
840 from_unit = UnitOfTemperature.FAHRENHEIT
841 elif temp_obj[
"scale"] ==
"KELVIN" and not interval:
846 return TemperatureConverter.convert_interval(temp, from_unit, to_unit)
847 return TemperatureConverter.convert(temp, from_unit, to_unit)
850 @HANDLERS.register(("Alexa.ThermostatController", "SetTargetTemperature"))
852 hass: ha.HomeAssistant,
853 config: AbstractConfig,
854 directive: AlexaDirective,
857 """Process a set target temperature request."""
858 entity = directive.entity
859 domain = entity.domain
861 min_temp = entity.attributes[MIN_MAX_TEMP[domain][
"min_temp"]]
862 max_temp = entity.attributes[
"max_temp"]
863 unit = hass.config.units.temperature_unit
865 data: dict[str, Any] = {ATTR_ENTITY_ID: entity.entity_id}
867 payload = directive.payload
868 response = directive.response()
869 if "targetSetpoint" in payload:
871 if temp < min_temp
or temp > max_temp:
873 data[ATTR_TEMPERATURE] = temp
874 response.add_context_property(
876 "name":
"targetSetpoint",
877 "namespace":
"Alexa.ThermostatController",
878 "value": {
"value": temp,
"scale": API_TEMP_UNITS[unit]},
881 if "lowerSetpoint" in payload:
883 if temp_low < min_temp
or temp_low > max_temp:
885 data[climate.ATTR_TARGET_TEMP_LOW] = temp_low
886 response.add_context_property(
888 "name":
"lowerSetpoint",
889 "namespace":
"Alexa.ThermostatController",
890 "value": {
"value": temp_low,
"scale": API_TEMP_UNITS[unit]},
893 if "upperSetpoint" in payload:
895 if temp_high < min_temp
or temp_high > max_temp:
897 data[climate.ATTR_TARGET_TEMP_HIGH] = temp_high
898 response.add_context_property(
900 "name":
"upperSetpoint",
901 "namespace":
"Alexa.ThermostatController",
902 "value": {
"value": temp_high,
"scale": API_TEMP_UNITS[unit]},
906 service = SERVICE_SET_TEMPERATURE[domain]
908 await hass.services.async_call(
919 @HANDLERS.register(("Alexa.ThermostatController", "AdjustTargetTemperature"))
921 hass: ha.HomeAssistant,
922 config: AbstractConfig,
923 directive: AlexaDirective,
926 """Process an adjust target temperature request for climates and water heaters."""
928 entity = directive.entity
929 domain = entity.domain
930 min_temp = entity.attributes[MIN_MAX_TEMP[domain][
"min_temp"]]
931 max_temp = entity.attributes[MIN_MAX_TEMP[domain][
"max_temp"]]
932 unit = hass.config.units.temperature_unit
935 hass, directive.payload[
"targetSetpointDelta"], interval=
True
938 response = directive.response()
940 current_target_temp_high = entity.attributes.get(climate.ATTR_TARGET_TEMP_HIGH)
941 current_target_temp_low = entity.attributes.get(climate.ATTR_TARGET_TEMP_LOW)
942 if current_target_temp_high
is not None and current_target_temp_low
is not None:
943 target_temp_high =
float(current_target_temp_high) + temp_delta
944 if target_temp_high < min_temp
or target_temp_high > max_temp:
947 target_temp_low =
float(current_target_temp_low) + temp_delta
948 if target_temp_low < min_temp
or target_temp_low > max_temp:
952 ATTR_ENTITY_ID: entity.entity_id,
953 climate.ATTR_TARGET_TEMP_HIGH: target_temp_high,
954 climate.ATTR_TARGET_TEMP_LOW: target_temp_low,
957 response.add_context_property(
959 "name":
"upperSetpoint",
960 "namespace":
"Alexa.ThermostatController",
961 "value": {
"value": target_temp_high,
"scale": API_TEMP_UNITS[unit]},
964 response.add_context_property(
966 "name":
"lowerSetpoint",
967 "namespace":
"Alexa.ThermostatController",
968 "value": {
"value": target_temp_low,
"scale": API_TEMP_UNITS[unit]},
972 current_target_temp: str |
None = entity.attributes.get(ATTR_TEMPERATURE)
973 if current_target_temp
is None:
975 "The current target temperature is not set, "
976 "cannot adjust target temperature"
978 target_temp =
float(current_target_temp) + temp_delta
980 if target_temp < min_temp
or target_temp > max_temp:
983 data = {ATTR_ENTITY_ID: entity.entity_id, ATTR_TEMPERATURE: target_temp}
984 response.add_context_property(
986 "name":
"targetSetpoint",
987 "namespace":
"Alexa.ThermostatController",
988 "value": {
"value": target_temp,
"scale": API_TEMP_UNITS[unit]},
992 service = SERVICE_SET_TEMPERATURE[domain]
994 await hass.services.async_call(
1005 @HANDLERS.register(("Alexa.ThermostatController", "SetThermostatMode"))
1007 hass: ha.HomeAssistant,
1008 config: AbstractConfig,
1009 directive: AlexaDirective,
1010 context: ha.Context,
1012 """Process a set thermostat mode request."""
1013 operation_list: list[str]
1015 entity = directive.entity
1016 mode = directive.payload[
"thermostatMode"]
1017 mode = mode
if isinstance(mode, str)
else mode[
"value"]
1019 data: dict[str, Any] = {ATTR_ENTITY_ID: entity.entity_id}
1021 ha_preset = next((k
for k, v
in API_THERMOSTAT_PRESETS.items()
if v == mode),
None)
1024 presets = entity.attributes.get(climate.ATTR_PRESET_MODES)
or []
1026 if ha_preset
not in presets:
1027 msg = f
"The requested thermostat mode {ha_preset} is not supported"
1030 service = climate.SERVICE_SET_PRESET_MODE
1031 data[climate.ATTR_PRESET_MODE] = ha_preset
1033 elif mode ==
"CUSTOM":
1034 operation_list = entity.attributes.get(climate.ATTR_HVAC_MODES)
or []
1035 custom_mode = directive.payload[
"thermostatMode"][
"customName"]
1037 (k
for k, v
in API_THERMOSTAT_MODES_CUSTOM.items()
if v == custom_mode),
1040 if custom_mode
not in operation_list:
1042 f
"The requested thermostat mode {mode}: {custom_mode} is not supported"
1046 service = climate.SERVICE_SET_HVAC_MODE
1047 data[climate.ATTR_HVAC_MODE] = custom_mode
1050 operation_list = entity.attributes.get(climate.ATTR_HVAC_MODES)
or []
1051 ha_modes: dict[str, str] = {
1052 k: v
for k, v
in API_THERMOSTAT_MODES.items()
if v == mode
1054 ha_mode: str |
None = next(
1055 iter(set(ha_modes).intersection(operation_list)),
None
1057 if ha_mode
not in operation_list:
1058 msg = f
"The requested thermostat mode {mode} is not supported"
1061 service = climate.SERVICE_SET_HVAC_MODE
1062 data[climate.ATTR_HVAC_MODE] = ha_mode
1064 response = directive.response()
1065 await hass.services.async_call(
1066 climate.DOMAIN, service, data, blocking=
False, context=context
1068 response.add_context_property(
1070 "name":
"thermostatMode",
1071 "namespace":
"Alexa.ThermostatController",
1079 @HANDLERS.register(("Alexa", "ReportState"))
1081 hass: ha.HomeAssistant,
1082 config: AbstractConfig,
1083 directive: AlexaDirective,
1084 context: ha.Context,
1086 """Process a ReportState request."""
1087 return directive.response(name=
"StateReport")
1090 @HANDLERS.register(("Alexa.SecurityPanelController", "Arm"))
1092 hass: ha.HomeAssistant,
1093 config: AbstractConfig,
1094 directive: AlexaDirective,
1095 context: ha.Context,
1097 """Process a Security Panel Arm request."""
1098 entity = directive.entity
1100 arm_state = directive.payload[
"armState"]
1101 data: dict[str, Any] = {ATTR_ENTITY_ID: entity.entity_id}
1107 entity.state == alarm_control_panel.AlarmControlPanelState.ARMED_AWAY
1108 and arm_state !=
"ARMED_AWAY"
1110 msg =
"You must disarm the system before you can set the requested arm state."
1113 if arm_state ==
"ARMED_AWAY":
1114 service = SERVICE_ALARM_ARM_AWAY
1115 elif arm_state ==
"ARMED_NIGHT":
1116 service = SERVICE_ALARM_ARM_NIGHT
1117 elif arm_state ==
"ARMED_STAY":
1118 service = SERVICE_ALARM_ARM_HOME
1122 await hass.services.async_call(
1123 entity.domain, service, data, blocking=
False, context=context
1127 payload: dict[str, Any] = {
"exitDelayInSeconds": 0}
1129 response = directive.response(
1130 name=
"Arm.Response", namespace=
"Alexa.SecurityPanelController", payload=payload
1133 response.add_context_property(
1136 "namespace":
"Alexa.SecurityPanelController",
1144 @HANDLERS.register(("Alexa.SecurityPanelController", "Disarm"))
1146 hass: ha.HomeAssistant,
1147 config: AbstractConfig,
1148 directive: AlexaDirective,
1149 context: ha.Context,
1151 """Process a Security Panel Disarm request."""
1152 entity = directive.entity
1153 data: dict[str, Any] = {ATTR_ENTITY_ID: entity.entity_id}
1154 response = directive.response()
1159 if entity.state == alarm_control_panel.AlarmControlPanelState.DISARMED:
1162 payload = directive.payload
1163 if "authorization" in payload:
1164 value = payload[
"authorization"][
"value"]
1165 if payload[
"authorization"][
"type"] ==
"FOUR_DIGIT_PIN":
1166 data[
"code"] = value
1168 await hass.services.async_call(
1169 entity.domain, SERVICE_ALARM_DISARM, data, blocking=
True, context=context
1172 response.add_context_property(
1175 "namespace":
"Alexa.SecurityPanelController",
1176 "value":
"DISARMED",
1183 @HANDLERS.register(("Alexa.ModeController", "SetMode"))
1185 hass: ha.HomeAssistant,
1186 config: AbstractConfig,
1187 directive: AlexaDirective,
1188 context: ha.Context,
1190 """Process a SetMode directive."""
1191 entity = directive.entity
1192 instance = directive.instance
1193 domain = entity.domain
1195 data: dict[str, Any] = {ATTR_ENTITY_ID: entity.entity_id}
1196 mode = directive.payload[
"mode"]
1199 if instance == f
"{fan.DOMAIN}.{fan.ATTR_DIRECTION}":
1200 direction = mode.split(
".")[1]
1201 if direction
in (fan.DIRECTION_REVERSE, fan.DIRECTION_FORWARD):
1202 service = fan.SERVICE_SET_DIRECTION
1203 data[fan.ATTR_DIRECTION] = direction
1206 elif instance == f
"{fan.DOMAIN}.{fan.ATTR_PRESET_MODE}":
1207 preset_mode = mode.split(
".")[1]
1208 preset_modes: list[str] |
None = entity.attributes.get(fan.ATTR_PRESET_MODES)
1210 preset_mode != PRESET_MODE_NA
1212 and preset_mode
in preset_modes
1214 service = fan.SERVICE_SET_PRESET_MODE
1215 data[fan.ATTR_PRESET_MODE] = preset_mode
1217 msg = f
"Entity '{entity.entity_id}' does not support Preset '{preset_mode}'"
1221 elif instance == f
"{humidifier.DOMAIN}.{humidifier.ATTR_MODE}":
1222 mode = mode.split(
".")[1]
1223 modes: list[str] |
None = entity.attributes.get(humidifier.ATTR_AVAILABLE_MODES)
1224 if mode != PRESET_MODE_NA
and modes
and mode
in modes:
1225 service = humidifier.SERVICE_SET_MODE
1226 data[humidifier.ATTR_MODE] = mode
1228 msg = f
"Entity '{entity.entity_id}' does not support Mode '{mode}'"
1232 elif instance == f
"{remote.DOMAIN}.{remote.ATTR_ACTIVITY}":
1233 activity = mode.split(
".")[1]
1234 activities: list[str] |
None = entity.attributes.get(remote.ATTR_ACTIVITY_LIST)
1235 if activity != PRESET_MODE_NA
and activities
and activity
in activities:
1236 service = remote.SERVICE_TURN_ON
1237 data[remote.ATTR_ACTIVITY] = activity
1239 msg = f
"Entity '{entity.entity_id}' does not support Mode '{mode}'"
1243 elif instance == f
"{water_heater.DOMAIN}.{water_heater.ATTR_OPERATION_MODE}":
1244 operation_mode = mode.split(
".")[1]
1245 operation_modes: list[str] |
None = entity.attributes.get(
1246 water_heater.ATTR_OPERATION_LIST
1249 operation_mode != PRESET_MODE_NA
1251 and operation_mode
in operation_modes
1253 service = water_heater.SERVICE_SET_OPERATION_MODE
1254 data[water_heater.ATTR_OPERATION_MODE] = operation_mode
1256 msg = f
"Entity '{entity.entity_id}' does not support Operation mode '{operation_mode}'"
1260 elif instance == f
"{cover.DOMAIN}.{cover.ATTR_POSITION}":
1261 position = mode.split(
".")[1]
1263 if position == cover.STATE_CLOSED:
1264 service = cover.SERVICE_CLOSE_COVER
1265 elif position == cover.STATE_OPEN:
1266 service = cover.SERVICE_OPEN_COVER
1267 elif position ==
"custom":
1268 service = cover.SERVICE_STOP_COVER
1271 elif instance == f
"{valve.DOMAIN}.state":
1272 position = mode.split(
".")[1]
1274 if position == valve.STATE_CLOSED:
1275 service = valve.SERVICE_CLOSE_VALVE
1276 elif position == valve.STATE_OPEN:
1277 service = valve.SERVICE_OPEN_VALVE
1282 await hass.services.async_call(
1283 domain, service, data, blocking=
False, context=context
1286 response = directive.response()
1287 response.add_context_property(
1289 "namespace":
"Alexa.ModeController",
1290 "instance": instance,
1299 @HANDLERS.register(("Alexa.ModeController", "AdjustMode"))
1301 hass: ha.HomeAssistant,
1302 config: AbstractConfig,
1303 directive: AlexaDirective,
1304 context: ha.Context,
1306 """Process a AdjustMode request.
1308 Requires capabilityResources supportedModes to be ordered.
1309 Only supportedModes with ordered=True support the adjustMode directive.
1317 @HANDLERS.register(("Alexa.ToggleController", "TurnOn"))
1319 hass: ha.HomeAssistant,
1320 config: AbstractConfig,
1321 directive: AlexaDirective,
1322 context: ha.Context,
1324 """Process a toggle on request."""
1325 entity = directive.entity
1326 instance = directive.instance
1327 domain = entity.domain
1329 data: dict[str, Any]
1332 if instance == f
"{fan.DOMAIN}.{fan.ATTR_OSCILLATING}":
1333 service = fan.SERVICE_OSCILLATE
1335 ATTR_ENTITY_ID: entity.entity_id,
1336 fan.ATTR_OSCILLATING:
True,
1338 elif instance == f
"{valve.DOMAIN}.stop":
1339 service = valve.SERVICE_STOP_VALVE
1341 ATTR_ENTITY_ID: entity.entity_id,
1346 await hass.services.async_call(
1347 domain, service, data, blocking=
False, context=context
1350 response = directive.response()
1351 response.add_context_property(
1353 "namespace":
"Alexa.ToggleController",
1354 "instance": instance,
1355 "name":
"toggleState",
1363 @HANDLERS.register(("Alexa.ToggleController", "TurnOff"))
1365 hass: ha.HomeAssistant,
1366 config: AbstractConfig,
1367 directive: AlexaDirective,
1368 context: ha.Context,
1370 """Process a toggle off request."""
1371 entity = directive.entity
1372 instance = directive.instance
1373 domain = entity.domain
1376 if instance != f
"{fan.DOMAIN}.{fan.ATTR_OSCILLATING}":
1379 service = fan.SERVICE_OSCILLATE
1380 data: dict[str, Any] = {
1381 ATTR_ENTITY_ID: entity.entity_id,
1382 fan.ATTR_OSCILLATING:
False,
1385 await hass.services.async_call(
1386 domain, service, data, blocking=
False, context=context
1389 response = directive.response()
1390 response.add_context_property(
1392 "namespace":
"Alexa.ToggleController",
1393 "instance": instance,
1394 "name":
"toggleState",
1402 @HANDLERS.register(("Alexa.RangeController", "SetRangeValue"))
1404 hass: ha.HomeAssistant,
1405 config: AbstractConfig,
1406 directive: AlexaDirective,
1407 context: ha.Context,
1409 """Process a next request."""
1410 entity = directive.entity
1411 instance = directive.instance
1412 domain = entity.domain
1414 data: dict[str, Any] = {ATTR_ENTITY_ID: entity.entity_id}
1415 range_value = directive.payload[
"rangeValue"]
1416 supported = entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
1419 if instance == f
"{cover.DOMAIN}.{cover.ATTR_POSITION}":
1420 range_value =
int(range_value)
1421 if supported & cover.CoverEntityFeature.CLOSE
and range_value == 0:
1422 service = cover.SERVICE_CLOSE_COVER
1423 elif supported & cover.CoverEntityFeature.OPEN
and range_value == 100:
1424 service = cover.SERVICE_OPEN_COVER
1426 service = cover.SERVICE_SET_COVER_POSITION
1427 data[cover.ATTR_POSITION] = range_value
1430 elif instance == f
"{cover.DOMAIN}.tilt":
1431 range_value =
int(range_value)
1432 if supported & cover.CoverEntityFeature.CLOSE_TILT
and range_value == 0:
1433 service = cover.SERVICE_CLOSE_COVER_TILT
1434 elif supported & cover.CoverEntityFeature.OPEN_TILT
and range_value == 100:
1435 service = cover.SERVICE_OPEN_COVER_TILT
1437 service = cover.SERVICE_SET_COVER_TILT_POSITION
1438 data[cover.ATTR_TILT_POSITION] = range_value
1441 elif instance == f
"{fan.DOMAIN}.{fan.ATTR_PERCENTAGE}":
1442 range_value =
int(range_value)
1443 if range_value == 0:
1444 service = fan.SERVICE_TURN_OFF
1445 elif supported & fan.FanEntityFeature.SET_SPEED:
1446 service = fan.SERVICE_SET_PERCENTAGE
1447 data[fan.ATTR_PERCENTAGE] = range_value
1449 service = fan.SERVICE_TURN_ON
1452 elif instance == f
"{humidifier.DOMAIN}.{humidifier.ATTR_HUMIDITY}":
1453 range_value =
int(range_value)
1454 service = humidifier.SERVICE_SET_HUMIDITY
1455 data[humidifier.ATTR_HUMIDITY] = range_value
1458 elif instance == f
"{input_number.DOMAIN}.{input_number.ATTR_VALUE}":
1459 range_value =
float(range_value)
1460 service = input_number.SERVICE_SET_VALUE
1461 min_value =
float(entity.attributes[input_number.ATTR_MIN])
1462 max_value =
float(entity.attributes[input_number.ATTR_MAX])
1463 data[input_number.ATTR_VALUE] =
min(max_value,
max(min_value, range_value))
1466 elif instance == f
"{number.DOMAIN}.{number.ATTR_VALUE}":
1467 range_value =
float(range_value)
1468 service = number.SERVICE_SET_VALUE
1469 min_value =
float(entity.attributes[number.ATTR_MIN])
1470 max_value =
float(entity.attributes[number.ATTR_MAX])
1471 data[number.ATTR_VALUE] =
min(max_value,
max(min_value, range_value))
1474 elif instance == f
"{vacuum.DOMAIN}.{vacuum.ATTR_FAN_SPEED}":
1475 service = vacuum.SERVICE_SET_FAN_SPEED
1476 speed_list = entity.attributes[vacuum.ATTR_FAN_SPEED_LIST]
1478 (v
for i, v
in enumerate(speed_list)
if i ==
int(range_value)),
None
1482 msg =
"Entity does not support value"
1485 data[vacuum.ATTR_FAN_SPEED] = speed
1488 elif instance == f
"{valve.DOMAIN}.{valve.ATTR_POSITION}":
1489 range_value =
int(range_value)
1490 if supported & valve.ValveEntityFeature.CLOSE
and range_value == 0:
1491 service = valve.SERVICE_CLOSE_VALVE
1492 elif supported & valve.ValveEntityFeature.OPEN
and range_value == 100:
1493 service = valve.SERVICE_OPEN_VALVE
1495 service = valve.SERVICE_SET_VALVE_POSITION
1496 data[valve.ATTR_POSITION] = range_value
1501 await hass.services.async_call(
1502 domain, service, data, blocking=
False, context=context
1505 response = directive.response()
1506 response.add_context_property(
1508 "namespace":
"Alexa.RangeController",
1509 "instance": instance,
1510 "name":
"rangeValue",
1511 "value": range_value,
1518 @HANDLERS.register(("Alexa.RangeController", "AdjustRangeValue"))
1520 hass: ha.HomeAssistant,
1521 config: AbstractConfig,
1522 directive: AlexaDirective,
1523 context: ha.Context,
1525 """Process a next request."""
1526 entity = directive.entity
1527 instance = directive.instance
1528 domain = entity.domain
1530 data: dict[str, Any] = {ATTR_ENTITY_ID: entity.entity_id}
1531 range_delta = directive.payload[
"rangeValueDelta"]
1532 range_delta_default = bool(directive.payload[
"rangeValueDeltaDefault"])
1533 response_value: int |
None = 0
1536 if instance == f
"{cover.DOMAIN}.{cover.ATTR_POSITION}":
1537 range_delta =
int(range_delta * 20)
if range_delta_default
else int(range_delta)
1538 service = SERVICE_SET_COVER_POSITION
1539 if not (current := entity.attributes.get(cover.ATTR_CURRENT_POSITION)):
1540 msg = f
"Unable to determine {entity.entity_id} current position"
1542 position = response_value =
min(100,
max(0, range_delta + current))
1544 service = cover.SERVICE_OPEN_COVER
1546 service = cover.SERVICE_CLOSE_COVER
1548 data[cover.ATTR_POSITION] = position
1551 elif instance == f
"{cover.DOMAIN}.tilt":
1552 range_delta =
int(range_delta * 20)
if range_delta_default
else int(range_delta)
1553 service = SERVICE_SET_COVER_TILT_POSITION
1554 current = entity.attributes.get(cover.ATTR_TILT_POSITION)
1556 msg = f
"Unable to determine {entity.entity_id} current tilt position"
1558 tilt_position = response_value =
min(100,
max(0, range_delta + current))
1559 if tilt_position == 100:
1560 service = cover.SERVICE_OPEN_COVER_TILT
1561 elif tilt_position == 0:
1562 service = cover.SERVICE_CLOSE_COVER_TILT
1564 data[cover.ATTR_TILT_POSITION] = tilt_position
1567 elif instance == f
"{fan.DOMAIN}.{fan.ATTR_PERCENTAGE}":
1568 percentage_step = entity.attributes.get(fan.ATTR_PERCENTAGE_STEP)
or 20
1570 int(range_delta * percentage_step)
1571 if range_delta_default
1572 else int(range_delta)
1574 service = fan.SERVICE_SET_PERCENTAGE
1575 if not (current := entity.attributes.get(fan.ATTR_PERCENTAGE)):
1576 msg = f
"Unable to determine {entity.entity_id} current fan speed"
1578 percentage = response_value =
min(100,
max(0, range_delta + current))
1580 data[fan.ATTR_PERCENTAGE] = percentage
1582 service = fan.SERVICE_TURN_OFF
1585 elif instance == f
"{humidifier.DOMAIN}.{humidifier.ATTR_HUMIDITY}":
1588 int(range_delta * percentage_step)
1589 if range_delta_default
1590 else int(range_delta)
1592 service = humidifier.SERVICE_SET_HUMIDITY
1593 if not (current := entity.attributes.get(humidifier.ATTR_HUMIDITY)):
1594 msg = f
"Unable to determine {entity.entity_id} current target humidity"
1596 min_value = entity.attributes.get(humidifier.ATTR_MIN_HUMIDITY, 10)
1597 max_value = entity.attributes.get(humidifier.ATTR_MAX_HUMIDITY, 90)
1598 percentage = response_value =
min(
1599 max_value,
max(min_value, range_delta + current)
1602 data[humidifier.ATTR_HUMIDITY] = percentage
1605 elif instance == f
"{input_number.DOMAIN}.{input_number.ATTR_VALUE}":
1606 range_delta =
float(range_delta)
1607 service = input_number.SERVICE_SET_VALUE
1608 min_value =
float(entity.attributes[input_number.ATTR_MIN])
1609 max_value =
float(entity.attributes[input_number.ATTR_MAX])
1610 current =
float(entity.state)
1611 data[input_number.ATTR_VALUE] = response_value =
min(
1612 max_value,
max(min_value, range_delta + current)
1616 elif instance == f
"{number.DOMAIN}.{number.ATTR_VALUE}":
1617 range_delta =
float(range_delta)
1618 service = number.SERVICE_SET_VALUE
1619 min_value =
float(entity.attributes[number.ATTR_MIN])
1620 max_value =
float(entity.attributes[number.ATTR_MAX])
1621 current =
float(entity.state)
1622 data[number.ATTR_VALUE] = response_value =
min(
1623 max_value,
max(min_value, range_delta + current)
1627 elif instance == f
"{vacuum.DOMAIN}.{vacuum.ATTR_FAN_SPEED}":
1628 range_delta =
int(range_delta)
1629 service = vacuum.SERVICE_SET_FAN_SPEED
1630 speed_list = entity.attributes[vacuum.ATTR_FAN_SPEED_LIST]
1631 current_speed = entity.attributes[vacuum.ATTR_FAN_SPEED]
1632 current_speed_index = next(
1633 (i
for i, v
in enumerate(speed_list)
if v == current_speed), 0
1635 new_speed_index =
min(
1636 len(speed_list) - 1,
max(0, current_speed_index + range_delta)
1639 (v
for i, v
in enumerate(speed_list)
if i == new_speed_index),
None
1641 data[vacuum.ATTR_FAN_SPEED] = response_value = speed
1644 elif instance == f
"{valve.DOMAIN}.{valve.ATTR_POSITION}":
1645 range_delta =
int(range_delta * 20)
if range_delta_default
else int(range_delta)
1646 service = valve.SERVICE_SET_VALVE_POSITION
1647 if not (current := entity.attributes.get(valve.ATTR_POSITION)):
1648 msg = f
"Unable to determine {entity.entity_id} current position"
1650 position = response_value =
min(100,
max(0, range_delta + current))
1652 service = valve.SERVICE_OPEN_VALVE
1654 service = valve.SERVICE_CLOSE_VALVE
1656 data[valve.ATTR_POSITION] = position
1661 await hass.services.async_call(
1662 domain, service, data, blocking=
False, context=context
1665 response = directive.response()
1666 response.add_context_property(
1668 "namespace":
"Alexa.RangeController",
1669 "instance": instance,
1670 "name":
"rangeValue",
1671 "value": response_value,
1678 @HANDLERS.register(("Alexa.ChannelController", "ChangeChannel"))
1680 hass: ha.HomeAssistant,
1681 config: AbstractConfig,
1682 directive: AlexaDirective,
1683 context: ha.Context,
1685 """Process a change channel request."""
1687 entity = directive.entity
1688 channel_payload = directive.payload[
"channel"]
1689 metadata_payload = directive.payload[
"channelMetadata"]
1690 payload_name =
"number"
1692 if "number" in channel_payload:
1693 channel = channel_payload[
"number"]
1694 payload_name =
"number"
1695 elif "callSign" in channel_payload:
1696 channel = channel_payload[
"callSign"]
1697 payload_name =
"callSign"
1698 elif "affiliateCallSign" in channel_payload:
1699 channel = channel_payload[
"affiliateCallSign"]
1700 payload_name =
"affiliateCallSign"
1701 elif "uri" in channel_payload:
1702 channel = channel_payload[
"uri"]
1703 payload_name =
"uri"
1704 elif "name" in metadata_payload:
1705 channel = metadata_payload[
"name"]
1706 payload_name =
"callSign"
1708 data: dict[str, Any] = {
1709 ATTR_ENTITY_ID: entity.entity_id,
1710 media_player.const.ATTR_MEDIA_CONTENT_ID: channel,
1711 media_player.const.ATTR_MEDIA_CONTENT_TYPE: (
1712 media_player.const.MEDIA_TYPE_CHANNEL
1716 await hass.services.async_call(
1718 media_player.const.SERVICE_PLAY_MEDIA,
1724 response = directive.response()
1726 response.add_context_property(
1728 "namespace":
"Alexa.ChannelController",
1730 "value": {payload_name: channel},
1737 @HANDLERS.register(("Alexa.ChannelController", "SkipChannels"))
1739 hass: ha.HomeAssistant,
1740 config: AbstractConfig,
1741 directive: AlexaDirective,
1742 context: ha.Context,
1744 """Process a skipchannel request."""
1745 channel =
int(directive.payload[
"channelCount"])
1746 entity = directive.entity
1748 data: dict[str, Any] = {ATTR_ENTITY_ID: entity.entity_id}
1751 service_media = SERVICE_MEDIA_PREVIOUS_TRACK
1753 service_media = SERVICE_MEDIA_NEXT_TRACK
1755 for _
in range(abs(channel)):
1756 await hass.services.async_call(
1757 entity.domain, service_media, data, blocking=
False, context=context
1760 response = directive.response()
1762 response.add_context_property(
1764 "namespace":
"Alexa.ChannelController",
1766 "value": {
"number":
""},
1773 @HANDLERS.register(("Alexa.SeekController", "AdjustSeekPosition"))
1775 hass: ha.HomeAssistant,
1776 config: AbstractConfig,
1777 directive: AlexaDirective,
1778 context: ha.Context,
1780 """Process a seek request."""
1781 entity = directive.entity
1782 position_delta =
int(directive.payload[
"deltaPositionMilliseconds"])
1784 current_position = entity.attributes.get(media_player.ATTR_MEDIA_POSITION)
1785 if not current_position:
1786 msg = f
"{entity} did not return the current media position."
1789 seek_position =
max(
int(current_position) +
int(position_delta / 1000), 0)
1791 media_duration = entity.attributes.get(media_player.ATTR_MEDIA_DURATION)
1792 if media_duration
and 0 <
int(media_duration) < seek_position:
1793 seek_position = media_duration
1795 data: dict[str, Any] = {
1796 ATTR_ENTITY_ID: entity.entity_id,
1797 media_player.ATTR_MEDIA_SEEK_POSITION: seek_position,
1800 await hass.services.async_call(
1801 media_player.DOMAIN,
1802 media_player.SERVICE_MEDIA_SEEK,
1809 seek_position =
int(seek_position * 1000)
1811 payload: dict[str, Any] = {
1812 "properties": [{
"name":
"positionMilliseconds",
"value": seek_position}]
1814 return directive.response(
1815 name=
"StateReport", namespace=
"Alexa.SeekController", payload=payload
1819 @HANDLERS.register(("Alexa.EqualizerController", "SetMode"))
1821 hass: ha.HomeAssistant,
1822 config: AbstractConfig,
1823 directive: AlexaDirective,
1824 context: ha.Context,
1826 """Process a SetMode request for EqualizerController."""
1827 mode = directive.payload[
"mode"]
1828 entity = directive.entity
1829 data: dict[str, Any] = {ATTR_ENTITY_ID: entity.entity_id}
1831 sound_mode_list = entity.attributes.get(media_player.const.ATTR_SOUND_MODE_LIST)
1832 if sound_mode_list
and mode.lower()
in sound_mode_list:
1833 data[media_player.const.ATTR_SOUND_MODE] = mode.lower()
1835 msg = f
"failed to map sound mode {mode} to a mode on {entity.entity_id}"
1838 await hass.services.async_call(
1840 media_player.SERVICE_SELECT_SOUND_MODE,
1846 return directive.response()
1849 @HANDLERS.register(("Alexa.EqualizerController", "AdjustBands"))
1850 @HANDLERS.register(("Alexa.EqualizerController", "ResetBands"))
1851 @HANDLERS.register(("Alexa.EqualizerController", "SetBands"))
1853 hass: ha.HomeAssistant,
1854 config: AbstractConfig,
1855 directive: AlexaDirective,
1856 context: ha.Context,
1858 """Handle an AdjustBands, ResetBands, SetBands request.
1860 Only mode directives are currently supported for the EqualizerController.
1866 @HANDLERS.register(("Alexa.TimeHoldController", "Hold"))
1868 hass: ha.HomeAssistant,
1869 config: AbstractConfig,
1870 directive: AlexaDirective,
1871 context: ha.Context,
1873 """Process a TimeHoldController Hold request."""
1874 entity = directive.entity
1875 data: dict[str, Any] = {ATTR_ENTITY_ID: entity.entity_id}
1877 if entity.domain == timer.DOMAIN:
1878 service = timer.SERVICE_PAUSE
1880 elif entity.domain == vacuum.DOMAIN:
1881 service = vacuum.SERVICE_START_PAUSE
1886 await hass.services.async_call(
1887 entity.domain, service, data, blocking=
False, context=context
1890 return directive.response()
1893 @HANDLERS.register(("Alexa.TimeHoldController", "Resume"))
1895 hass: ha.HomeAssistant,
1896 config: AbstractConfig,
1897 directive: AlexaDirective,
1898 context: ha.Context,
1900 """Process a TimeHoldController Resume request."""
1901 entity = directive.entity
1902 data: dict[str, Any] = {ATTR_ENTITY_ID: entity.entity_id}
1904 if entity.domain == timer.DOMAIN:
1905 service = timer.SERVICE_START
1907 elif entity.domain == vacuum.DOMAIN:
1908 service = vacuum.SERVICE_START_PAUSE
1913 await hass.services.async_call(
1914 entity.domain, service, data, blocking=
False, context=context
1917 return directive.response()
1920 @HANDLERS.register(("Alexa.CameraStreamController", "InitializeCameraStreams"))
1922 hass: ha.HomeAssistant,
1923 config: AbstractConfig,
1924 directive: AlexaDirective,
1925 context: ha.Context,
1927 """Process a InitializeCameraStreams request."""
1928 entity = directive.entity
1929 stream_source = await camera.async_request_stream(hass, entity.entity_id, fmt=
"hls")
1930 state = hass.states.get(entity.entity_id)
1932 camera_image = state.attributes[ATTR_ENTITY_PICTURE]
1935 external_url = network.get_url(
1937 allow_internal=
False,
1940 require_standard_port=
True,
1942 except network.NoURLAvailableError
as err:
1944 "Failed to find suitable URL to serve to Alexa"
1947 payload: dict[str, Any] = {
1950 "uri": f
"{external_url}{stream_source}",
1952 "resolution": {
"width": 1280,
"height": 720},
1953 "authorizationType":
"NONE",
1954 "videoCodec":
"H264",
1955 "audioCodec":
"AAC",
1958 "imageUri": f
"{external_url}{camera_image}",
1960 return directive.response(
1961 name=
"Response", namespace=
"Alexa.CameraStreamController", payload=payload
list[AlexaEntity] async_get_entities(HomeAssistant hass, AbstractConfig config)
AlexaResponse async_api_seek(ha.HomeAssistant hass, AbstractConfig config, AlexaDirective directive, ha.Context context)
AlexaResponse async_api_select_input(ha.HomeAssistant hass, AbstractConfig config, AlexaDirective directive, ha.Context context)
AlexaResponse async_api_activate(ha.HomeAssistant hass, AbstractConfig config, AlexaDirective directive, ha.Context context)
AlexaResponse async_api_toggle_on(ha.HomeAssistant hass, AbstractConfig config, AlexaDirective directive, ha.Context context)
AlexaResponse async_api_disarm(ha.HomeAssistant hass, AbstractConfig config, AlexaDirective directive, ha.Context context)
AlexaResponse async_api_resume(ha.HomeAssistant hass, AbstractConfig config, AlexaDirective directive, ha.Context context)
AlexaResponse async_api_adjust_target_temp(ha.HomeAssistant hass, AbstractConfig config, AlexaDirective directive, ha.Context context)
AlexaResponse async_api_accept_grant(ha.HomeAssistant hass, AbstractConfig config, AlexaDirective directive, ha.Context context)
AlexaResponse async_api_discovery(ha.HomeAssistant hass, AbstractConfig config, AlexaDirective directive, ha.Context context)
AlexaResponse async_api_toggle_off(ha.HomeAssistant hass, AbstractConfig config, AlexaDirective directive, ha.Context context)
AlexaResponse async_api_turn_on(ha.HomeAssistant hass, AbstractConfig config, AlexaDirective directive, ha.Context context)
AlexaResponse async_api_reportstate(ha.HomeAssistant hass, AbstractConfig config, AlexaDirective directive, ha.Context context)
AlexaResponse async_api_set_target_temp(ha.HomeAssistant hass, AbstractConfig config, AlexaDirective directive, ha.Context context)
AlexaResponse async_api_set_color(ha.HomeAssistant hass, AbstractConfig config, AlexaDirective directive, ha.Context context)
AlexaResponse async_api_changechannel(ha.HomeAssistant hass, AbstractConfig config, AlexaDirective directive, ha.Context context)
AlexaResponse async_api_adjust_volume_step(ha.HomeAssistant hass, AbstractConfig config, AlexaDirective directive, ha.Context context)
AlexaResponse async_api_previous(ha.HomeAssistant hass, AbstractConfig config, AlexaDirective directive, ha.Context context)
AlexaResponse async_api_bands_directive(ha.HomeAssistant hass, AbstractConfig config, AlexaDirective directive, ha.Context context)
AlexaResponse async_api_adjust_brightness(ha.HomeAssistant hass, AbstractConfig config, AlexaDirective directive, ha.Context context)
AlexaResponse async_api_skipchannel(ha.HomeAssistant hass, AbstractConfig config, AlexaDirective directive, ha.Context context)
AlexaResponse async_api_initialize_camera_stream(ha.HomeAssistant hass, AbstractConfig config, AlexaDirective directive, ha.Context context)
AlexaResponse async_api_adjust_range(ha.HomeAssistant hass, AbstractConfig config, AlexaDirective directive, ha.Context context)
AlexaResponse async_api_set_range(ha.HomeAssistant hass, AbstractConfig config, AlexaDirective directive, ha.Context context)
AlexaResponse async_api_decrease_color_temp(ha.HomeAssistant hass, AbstractConfig config, AlexaDirective directive, ha.Context context)
float temperature_from_object(ha.HomeAssistant hass, dict[str, Any] temp_obj, bool interval=False)
AlexaResponse async_api_increase_color_temp(ha.HomeAssistant hass, AbstractConfig config, AlexaDirective directive, ha.Context context)
AlexaResponse async_api_set_mode(ha.HomeAssistant hass, AbstractConfig config, AlexaDirective directive, ha.Context context)
AlexaResponse async_api_set_mute(ha.HomeAssistant hass, AbstractConfig config, AlexaDirective directive, ha.Context context)
AlexaResponse async_api_next(ha.HomeAssistant hass, AbstractConfig config, AlexaDirective directive, ha.Context context)
AlexaResponse async_api_set_brightness(ha.HomeAssistant hass, AbstractConfig config, AlexaDirective directive, ha.Context context)
AlexaResponse async_api_adjust_mode(ha.HomeAssistant hass, AbstractConfig config, AlexaDirective directive, ha.Context context)
AlexaResponse async_api_turn_off(ha.HomeAssistant hass, AbstractConfig config, AlexaDirective directive, ha.Context context)
AlexaResponse async_api_set_volume(ha.HomeAssistant hass, AbstractConfig config, AlexaDirective directive, ha.Context context)
AlexaResponse async_api_set_eq_mode(ha.HomeAssistant hass, AbstractConfig config, AlexaDirective directive, ha.Context context)
AlexaResponse async_api_lock(ha.HomeAssistant hass, AbstractConfig config, AlexaDirective directive, ha.Context context)
AlexaResponse async_api_play(ha.HomeAssistant hass, AbstractConfig config, AlexaDirective directive, ha.Context context)
AlexaResponse async_api_deactivate(ha.HomeAssistant hass, AbstractConfig config, AlexaDirective directive, ha.Context context)
AlexaResponse async_api_set_thermostat_mode(ha.HomeAssistant hass, AbstractConfig config, AlexaDirective directive, ha.Context context)
AlexaResponse async_api_stop(ha.HomeAssistant hass, AbstractConfig config, AlexaDirective directive, ha.Context context)
AlexaResponse async_api_adjust_volume(ha.HomeAssistant hass, AbstractConfig config, AlexaDirective directive, ha.Context context)
AlexaResponse async_api_pause(ha.HomeAssistant hass, AbstractConfig config, AlexaDirective directive, ha.Context context)
AlexaResponse async_api_unlock(ha.HomeAssistant hass, AbstractConfig config, AlexaDirective directive, ha.Context context)
AlexaResponse async_api_set_color_temperature(ha.HomeAssistant hass, AbstractConfig config, AlexaDirective directive, ha.Context context)
AlexaResponse async_api_hold(ha.HomeAssistant hass, AbstractConfig config, AlexaDirective directive, ha.Context context)
AlexaResponse async_api_arm(ha.HomeAssistant hass, AbstractConfig config, AlexaDirective directive, ha.Context context)
CALLBACK_TYPE|None async_enable_proactive_mode(HomeAssistant hass, AbstractConfig smart_home_config)