Home Assistant Unofficial Reference 2024.12.1
services.py
Go to the documentation of this file.
1 """Handle Hue Service calls."""
2 
3 from __future__ import annotations
4 
5 import asyncio
6 import logging
7 
8 from aiohue import HueBridgeV1, HueBridgeV2
9 import voluptuous as vol
10 
11 from homeassistant.core import HomeAssistant, ServiceCall
13 from homeassistant.helpers.service import verify_domain_control
14 
15 from .bridge import HueBridge
16 from .const import (
17  ATTR_DYNAMIC,
18  ATTR_GROUP_NAME,
19  ATTR_SCENE_NAME,
20  ATTR_TRANSITION,
21  DOMAIN,
22  SERVICE_HUE_ACTIVATE_SCENE,
23 )
24 
25 LOGGER = logging.getLogger(__name__)
26 
27 
28 def async_register_services(hass: HomeAssistant) -> None:
29  """Register services for Hue integration."""
30 
31  async def hue_activate_scene(call: ServiceCall, skip_reload=True) -> None:
32  """Handle activation of Hue scene."""
33  # Get parameters
34  group_name = call.data[ATTR_GROUP_NAME]
35  scene_name = call.data[ATTR_SCENE_NAME]
36  transition = call.data.get(ATTR_TRANSITION)
37  dynamic = call.data.get(ATTR_DYNAMIC, False)
38 
39  # Call the set scene function on each bridge
40  tasks = [
41  hue_activate_scene_v1(bridge, group_name, scene_name, transition)
42  if bridge.api_version == 1
44  bridge, group_name, scene_name, transition, dynamic
45  )
46  for bridge in hass.data[DOMAIN].values()
47  if isinstance(bridge, HueBridge)
48  ]
49  results = await asyncio.gather(*tasks)
50 
51  # Did *any* bridge succeed?
52  # Note that we'll get a "True" value for a successful call
53  if True not in results:
54  LOGGER.warning(
55  "No bridge was able to activate scene %s in group %s",
56  scene_name,
57  group_name,
58  )
59 
60  if not hass.services.has_service(DOMAIN, SERVICE_HUE_ACTIVATE_SCENE):
61  # Register a local handler for scene activation
62  hass.services.async_register(
63  DOMAIN,
64  SERVICE_HUE_ACTIVATE_SCENE,
65  verify_domain_control(hass, DOMAIN)(hue_activate_scene),
66  schema=vol.Schema(
67  {
68  vol.Required(ATTR_GROUP_NAME): cv.string,
69  vol.Required(ATTR_SCENE_NAME): cv.string,
70  vol.Optional(ATTR_TRANSITION): cv.positive_int,
71  vol.Optional(ATTR_DYNAMIC): cv.boolean,
72  }
73  ),
74  )
75 
76 
78  bridge: HueBridge,
79  group_name: str,
80  scene_name: str,
81  transition: int | None = None,
82  is_retry: bool = False,
83 ) -> bool:
84  """Service for V1 bridge to call directly into bridge to set scenes."""
85  api: HueBridgeV1 = bridge.api
86  if api.scenes is None:
87  LOGGER.warning("Hub %s does not support scenes", api.host)
88  return False
89 
90  group = next(
91  (group for group in api.groups.values() if group.name == group_name),
92  None,
93  )
94  # Additional scene logic to handle duplicate scene names across groups
95  scene = next(
96  (
97  scene
98  for scene in api.scenes.values()
99  if scene.name == scene_name
100  and group is not None
101  and sorted(scene.lights) == sorted(group.lights)
102  ),
103  None,
104  )
105  # If we can't find it, fetch latest info and try again
106  if not is_retry and (group is None or scene is None):
107  await bridge.async_request_call(api.groups.update)
108  await bridge.async_request_call(api.scenes.update)
109  return await hue_activate_scene_v1(
110  bridge, group_name, scene_name, transition, is_retry=True
111  )
112 
113  if group is None or scene is None:
114  LOGGER.debug(
115  "Unable to find scene %s for group %s on bridge %s",
116  scene_name,
117  group_name,
118  bridge.host,
119  )
120  return False
121 
122  await bridge.async_request_call(
123  group.set_action, scene=scene.id, transitiontime=transition
124  )
125  return True
126 
127 
129  bridge: HueBridge,
130  group_name: str,
131  scene_name: str,
132  transition: int | None = None,
133  dynamic: bool = True,
134 ) -> bool:
135  """Service for V2 bridge to call scene by name."""
136  LOGGER.warning(
137  (
138  "Use of service_call '%s' is deprecated and will be removed "
139  "in a future release. Please use scene entities instead"
140  ),
141  SERVICE_HUE_ACTIVATE_SCENE,
142  )
143  api: HueBridgeV2 = bridge.api
144  for scene in api.scenes:
145  if scene.metadata.name.lower() != scene_name.lower():
146  continue
147  group = api.scenes.get_group(scene.id)
148  if group.metadata.name.lower() != group_name.lower():
149  continue
150  # found match!
151  if transition:
152  transition = transition * 1000 # transition is in ms
153  await bridge.async_request_call(
154  api.scenes.recall, scene.id, dynamic=dynamic, duration=transition
155  )
156  return True
157  LOGGER.debug(
158  "Unable to find scene %s for group %s on bridge %s",
159  scene_name,
160  group_name,
161  bridge.host,
162  )
163  return False
None async_register_services(HomeAssistant hass)
Definition: services.py:28
bool hue_activate_scene_v1(HueBridge bridge, str group_name, str scene_name, int|None transition=None, bool is_retry=False)
Definition: services.py:83
bool hue_activate_scene_v2(HueBridge bridge, str group_name, str scene_name, int|None transition=None, bool dynamic=True)
Definition: services.py:134
Callable[[Callable[[ServiceCall], Any]], Callable[[ServiceCall], Any]] verify_domain_control(HomeAssistant hass, str domain)
Definition: service.py:1139