66"""
77
88import logging
9+ import math
910from . import config
1011from . import hub_api
1112from enum import Enum
1213
1314
1415from .Error import APIError
1516
16- capability = Enum ('capability' , 'ALERT BASS BATTERY_U BRIGHTNESS COLOR_HS COLOR_LOOP COLOR_TEMP CONTACT CONTROL_LIGHT CONTROL_POWER DEVICE DIMMER_CONTROL GENERATE_ALERT HUMIDITY IDENTIFY LOUDNESS MOISTURE MUTE NEXT ON_OFF PAUSE PLAY PREVIOUS PUSH_NOTIFICATION REMOTE_CONTROL SEEK SMOKE STOP TEMPERATURE TRANSITION TREBLE TWILIGHT USER_PRESENCE VOLUME' )
17+ capability = Enum ('capability' , 'ALERT BASS BATTERY_U BRIGHTNESS COLOR_HS COLOR_LOOP COLOR_TEMP CONTACT CONTROL_LIGHT CONTROL_POWER DEVICE DIMMER_CONTROL GENERATE_ALERT HUMIDITY IDENTIFY LOUDNESS LUX MOISTURE MOTION MUTE NEXT ON_OFF PAUSE PLAY PREVIOUS PUSH_NOTIFICATION REMOTE_CONTROL SEEK SMOKE STOP TEMPERATURE TRANSITION TREBLE TWILIGHT USER_PRESENCE VOLUME' )
1718
1819def getDevices (** kwargs ):
1920 """Deprecated, will be removed in v0.3. Get up to date full devices data set as a dict.
@@ -62,7 +63,6 @@ def devices(*, capabilities=None, and_filter=False, **kwargs):
6263 devs = hub_api .devices (** kwargs )
6364 if capabilities :
6465 if isinstance (capabilities , capability ): # single capability given
65- logging .debug ("single capability {0}" .format (capabilities .name ))
6666 return { key : value for key , value in devs .items () if capabilities .name in value ['capabilities' ]['values' ] }
6767 else : # multi-filter
6868 if and_filter :
@@ -72,11 +72,11 @@ def devices(*, capabilities=None, and_filter=False, **kwargs):
7272 else : # no filtering
7373 return devs
7474
75- def toggle (device_id , ** kwargs ):
75+ def device_toggle (device_id , ** kwargs ):
7676 """Toggle power state of any device capable of it such as lamps. Eligibility is determined by the capability ON_OFF.
7777
7878 Args:
79- device_id: ID of the device to toggle.
79+ device_id(str) : ID of the device to toggle.
8080 **hub_id(str): optional id of hub to operate on. A specified hub_id takes presedence over a hub_name or default Hub.
8181 **hub_name(str): optional name of hub to operate on.
8282 **remote(bool): Remote or local query.
@@ -86,16 +86,131 @@ def toggle(device_id, **kwargs):
8686 # Get list of devices known to support toggle and find the device and it's state.
8787 devs = devices (capabilities = capability .ON_OFF , ** kwargs )
8888 dev_state = devs [device_id ]['state' ]
89- current_state = dev_state ['isOn' ]
89+ current_power = dev_state ['isOn' ]
9090 new_state = _clean_state (dev_state )
91- new_state ['isOn' ] = not current_state # reverse state
91+ new_state ['isOn' ] = not current_power # reverse power state
92+ hub_api .devices_command_state (device_id = device_id , state = new_state , ** kwargs )
93+
94+ def device_on (device_id , ** kwargs ):
95+ """Turn on a device that is capable of turning on. Eligibility is determined by the capability ON_OFF.
96+
97+ Args:
98+ device_id(str): ID of the device to operate on.
99+ """
100+ _fill_kwargs (kwargs )
101+ if _is_eligible (device_id , capability .ON_OFF , ** kwargs ):
102+ hub_api .devices_command_on (device_id , ** kwargs )
103+ else :
104+ raise AttributeError ('Device not found or not eligible for action.' )
105+
106+ def device_off (device_id , ** kwargs ):
107+ """Turn off a device that is capable of turning off. Eligibility is determined by the capability ON_OFF.
108+
109+ Args:
110+ device_id(str): ID of the device to operate on.
111+ """
112+ _fill_kwargs (kwargs )
113+ if _is_eligible (device_id , capability .ON_OFF , ** kwargs ):
114+ hub_api .devices_command_off (device_id , ** kwargs )
115+ else :
116+ raise AttributeError ('Device not found or not eligible for action.' )
117+
118+ def light_temperature (device_id , temperature = 2700 , transition = 0 , ** kwargs ):
119+ """Set temperature of a light.
120+
121+ Args:
122+ device_id(str): ID of the device to operate on.
123+ temperature(float): Temperature in Kelvins. If outside the operating range of the device the extreme value is used. Defaults to 2700K.
124+ transition(int): Transition length in milliseconds. Defaults to instant.
125+ """
126+ _fill_kwargs (kwargs )
127+ state = {} # will be populated by _is_eligible
128+ if _is_eligible (device_id , capability .COLOR_TEMP , state = state , ** kwargs ):
129+ # Make sure temperature is within bounds [state.minTemperature, state.maxTemperature]
130+ minimum = state ['minTemperature' ]
131+ maximum = state ['maxTemperature' ]
132+ if temperature < minimum :
133+ logging .warn ('Device does not support temperature {0}K, using minimum instead: {1}' .format (temperature , minimum ))
134+ temperature = minimum
135+ elif temperature > maximum :
136+ logging .warn ('Device does not support temperature {0}K, using maximum instead: {1}' .format (temperature , maximum ))
137+ temperature = maximum
138+
139+ state = _clean_state (state )
140+ state ['colorMode' ] = 'ct'
141+ state ['temperature' ] = temperature
142+ state ['transitionMsec' ] = transition
143+ hub_api .devices_command_state (device_id = device_id , state = state , ** kwargs )
144+ else :
145+ raise AttributeError ('Device not found or not eligible for action.' )
146+
147+ def light_color (device_id , hue , saturation = 1.0 , transition = 0 , ** kwargs ):
148+ """Set color (hue & saturation) of a light.
149+
150+ Args:
151+ device_id(str): ID of the device to operate on.
152+ hue(float): Hue in the range of [0, Pi*2]. If outside the range an AttributeError is raised.
153+ saturation(float): Saturation in the range of [0, 1]. If outside the range an AttributeError is raised. Defaults to 1.0 (full saturation.)
154+ transition(int): Transition length in milliseconds. Defaults to instant.
155+ """
156+ _fill_kwargs (kwargs )
157+ state = {} # will be populated by _is_eligible
158+ if _is_eligible (device_id , capability .COLOR_HS , state = state , ** kwargs ):
159+ # Make sure hue & saturation are within bounds
160+ if hue < 0 or hue > math .pi * 2 :
161+ raise AttributeError ('Hue out of bounds [0, pi*2]: {0}' .format (hue ))
162+ elif saturation < 0 or saturation > 1.0 :
163+ raise AttributeError ('Saturation out of bounds [0, 1.0]: {0}' .format (saturation ))
164+
165+ state = _clean_state (state )
166+ state ['colorMode' ] = 'hs'
167+ state ['hue' ] = hue
168+ state ['saturation' ] = saturation
169+ hub_api .devices_command_state (device_id = device_id , state = state , ** kwargs )
170+ else :
171+ raise AttributeError ('Device not found or not eligible for action.' )
172+
173+ def light_brightness (device_id , brightness , transition = 0 , ** kwargs ):
174+ """Set brightness of a light.
175+
176+ Args:
177+ device_id(str): ID of the device to operate on.
178+ brightness(float): Brightness in the range of [0, 1]. If outside the range an AttributeError is raised.
179+ transition(int): Transition length in milliseconds. Defaults to instant.
180+ """
181+ _fill_kwargs (kwargs )
182+ state = {} # will be populated by _is_eligible
183+ if _is_eligible (device_id , capability .BRIGHTNESS , state = state , ** kwargs ):
184+ # Make sure hue & saturation are within bounds
185+ if brightness < 0 or brightness > 1.0 :
186+ raise AttributeError ('Brightness out of bounds [0, 1.0]: {0}' .format (brightness ))
187+
188+ state = _clean_state (state )
189+ state ['brightness' ] = brightness
190+ hub_api .devices_command_state (device_id = device_id , state = state , ** kwargs )
191+ else :
192+ raise AttributeError ('Device not found or not eligible for action.' )
193+
194+ def _is_eligible (device_id , capability_filter , devs = None , state = None , ** kwargs ):
195+ """Check if device matches a AND devices filter.
196+
197+ Args:
198+ device_id(str): ID of the device to check.
199+ filter(hub.capability): Single hub.capability or a list of them to match against.
200+ devs(dict): Optional devices dictionary to use. If not defined, will be retrieved live.
201+ state(dict): Optional state dictionary, will be populated with state of checked device if device is eligible.
202+ Returns:
203+ bool: True if filter matches.
204+ """
205+ if devs is None : # only retrieve if we didn't get them
206+ devs = devices (capabilities = capability_filter , ** kwargs )
207+ if device_id in devs :
208+ state .update (devs [device_id ]['state' ])
209+ logging .debug ('Implicitly returning state: {0}' .format (state ))
210+ return True
211+ else :
212+ return False
92213
93- command = {
94- "type" : "CMD_DEVICE" ,
95- "id" : device_id ,
96- "state" : new_state
97- }
98- hub_api .devices_command (command , ** kwargs )
99214
100215def _get_id (** kwargs ):
101216 """Get a hub_id from various sources, meant so that you can just throw kwargs at it and get a valid id.
@@ -114,7 +229,7 @@ def _get_id(**kwargs):
114229 return kwargs ['hubId' ]
115230 if 'hub_name' in kwargs or 'hubName' in kwargs :
116231 if 'hub_name' in kwargs :
117- return getHubId (kwargs ['hub_name' ])
232+ return hub_id (kwargs ['hub_name' ])
118233 return getHubId (kwargs ['hubName' ])
119234 return default ()
120235
@@ -175,21 +290,34 @@ def default():
175290 return config .state ['Hubs' ]['default' ]
176291
177292def getHubId (hub_name ):
293+ """Deprecated, use hub_id(). Return id of hub by it's name.
294+
295+ Args:
296+ hub_name(str): Name of hub to query. The name is given when registering a hub to an account.
297+ str: hub_id on success, raises an attributeerror on failure.
298+
299+ Returns:
300+ str: Hub id or raises
301+ """
302+ logging .warn ('hub.getHubId is deprecated and will be removed soon. Use hub.hub_id()' )
303+ return hub_id (hub_name )
304+
305+ def hub_id (hub_name ):
178306 """Get hub id by it's name.
179307
180308 Args:
181309 hub_name(str): Name of hub to query. The name is given when registering a hub to an account.
182310
183311 Returns:
184- str: Hub id or None if the hub wasn't found .
312+ str: hub_id on success, raises an attributeerror on failure .
185313 """
186314
187315 for section in config .state .sections ():
188316 if section .startswith ("Hubs." ):
189- logging .debug ('Found hub {0}' .format (section ))
317+ logging .debug ('Found hub: {0}' .format (section ))
190318 if config .state [section ]['hubname' ] == hub_name :
191319 return section [5 :] # cut out "Hubs."
192- return None
320+ raise AttributeError ( 'Hub not found: {0}' . format ( hub_name ))
193321
194322def _getAttr (hub_id , attr , default = None , boolean = False ):
195323 """Get hub state attributes by attr name. Optionally set a default value if attribute not found.
0 commit comments