2023-07-21 20:40:19 +02:00
# Copyright (c) 2023 Julian Müller (ChaoticByte)
from json import dumps as _json_dumps
from json import loads as _json_loads
from urllib import request as _request
from . exceptions import *
# Have to use the following SSL context because the
# TLS certificate of a Hue Bridge is self signed
ssl_context_unverified = _request . ssl . _create_unverified_context ( )
class _BaseController :
_api_endpoint_all = " "
_api_endpoint_specific = " "
def __init__ ( self , number : int , bridge_ip_address : str , bridge_api_user : str ) :
assert type ( number ) == int
assert type ( bridge_ip_address ) == str
assert type ( bridge_api_user ) == str
self . number = number
self . bridge_ip_address = bridge_ip_address
self . bridge_api_user = bridge_api_user
@classmethod
def from_name ( cls , name : str , bridge_ip_address : str , bridge_api_user : str ) :
assert type ( bridge_ip_address ) == str
assert type ( bridge_api_user ) == str
assert type ( name ) == str
api_request = _request . Request ( cls . _api_endpoint_all . format (
bridge_ip_address = bridge_ip_address ,
bridge_api_user = bridge_api_user ) )
with _request . urlopen ( api_request , context = ssl_context_unverified ) as r :
data = _json_loads ( r . read ( ) )
for n in data :
if data [ n ] [ " name " ] == name :
return cls ( int ( n ) , bridge_ip_address , bridge_api_user )
raise LightOrGroupNotFound ( )
def _api_request ( self , method = " GET " , path : str = " " , data : dict = { } ) :
assert type ( method ) == str
assert type ( path ) == str
assert type ( data ) == dict
api_request = _request . Request (
self . _api_endpoint_specific . format (
bridge_ip_address = self . bridge_ip_address ,
bridge_api_user = self . bridge_api_user ,
number = self . number
) + path ,
method = method ,
data = _json_dumps ( data ) . encode ( ) )
with _request . urlopen ( api_request , context = ssl_context_unverified ) as r :
response_data = _json_loads ( r . read ( ) )
if type ( response_data ) == list and len ( response_data ) > 0 :
if " error " in response_data [ 0 ] :
raise APIError ( response_data )
return response_data
class LightController ( _BaseController ) :
2023-07-21 22:35:31 +02:00
''' Control a Philips Hue Light using the API of your Hue Bridge.
Args :
- ` number : int ` - The number of your light
- ` bridge_ip_address : str ` - The IP address of your Hue Bridge
- ` bridge_api_user : str ` - The user used to authenticate to the API
Use the class method ` . from_name ( name : str , . . . ) ` to use the name of a light instead of the number .
'''
2023-07-21 20:40:19 +02:00
_api_endpoint_all = " https:// {bridge_ip_address} /api/ {bridge_api_user} /lights "
_api_endpoint_specific = " https:// {bridge_ip_address} /api/ {bridge_api_user} /lights/ {number} "
@property
def reachable ( self ) - > bool :
2023-07-21 22:35:31 +02:00
''' Check if the light is reachable using `LightController.reachable` '''
2023-07-21 20:40:19 +02:00
data = self . _api_request ( )
return data [ " state " ] [ " reachable " ]
@property
def on ( self ) - > bool :
2023-07-21 22:35:31 +02:00
''' Check if the light is on using `LightController.on` '''
2023-07-21 20:40:19 +02:00
data = self . _api_request ( )
return data [ " state " ] [ " on " ]
@on.setter
def on ( self , on : bool ) :
2023-07-21 22:35:31 +02:00
''' Turn the light on/off using `LightController.on = ...` '''
2023-07-21 20:40:19 +02:00
assert type ( on ) == bool
self . _api_request ( " PUT " , " /state " , { " on " : on } )
2023-07-21 22:35:31 +02:00
2023-07-21 20:40:19 +02:00
@property
2023-07-21 22:35:31 +02:00
def brightness ( self ) - > int :
''' Get the brightness using `LightController.brightness` '''
2023-07-21 20:40:19 +02:00
data = self . _api_request ( )
return data [ " state " ] [ " bri " ]
2023-07-21 22:35:31 +02:00
2023-07-21 20:40:19 +02:00
@brightness.setter
def brightness ( self , brightness : float ) :
2023-07-21 22:35:31 +02:00
''' Set the brightness using `LightController.brightness = ...` '''
2023-07-21 20:40:19 +02:00
assert type ( brightness ) == float or type ( brightness ) == int
bri_ = min ( max ( int ( brightness * 254 ) , 0 ) , 254 )
self . _api_request ( " PUT " , " /state " , { " bri " : bri_ } )
2023-07-21 22:35:31 +02:00
2023-07-21 20:40:19 +02:00
@property
2023-07-21 22:35:31 +02:00
def hue ( self ) - > int :
''' Get the hue using `LightController.hue` '''
2023-07-21 20:40:19 +02:00
data = self . _api_request ( )
return data [ " state " ] [ " hue " ]
2023-07-21 22:35:31 +02:00
2023-07-21 20:40:19 +02:00
@hue.setter
def hue ( self , hue : float ) :
2023-07-21 22:35:31 +02:00
''' Set the hue using `LightController.hue = ...` '''
2023-07-21 20:40:19 +02:00
assert type ( hue ) == float or type ( hue ) == int
hue_ = min ( max ( int ( hue * 65535 ) , 0 ) , 65535 )
self . _api_request ( " PUT " , " /state " , { " hue " : hue_ } )
@property
2023-07-21 22:35:31 +02:00
def saturation ( self ) - > int :
''' Get the saturation using `LightController.saturation` '''
2023-07-21 20:40:19 +02:00
data = self . _api_request ( )
return data [ " state " ] [ " sat " ]
2023-07-21 22:35:31 +02:00
2023-07-21 20:40:19 +02:00
@saturation.setter
def saturation ( self , saturation : float ) :
2023-07-21 22:35:31 +02:00
''' Set the saturation using `LightController.saturation = ...` '''
2023-07-21 20:40:19 +02:00
assert type ( saturation ) == float or type ( saturation ) == int
sat_ = min ( max ( int ( saturation * 254 ) , 0 ) , 254 )
self . _api_request ( " PUT " , " /state " , { " sat " : sat_ } )
2023-07-21 22:35:31 +02:00
2023-07-23 10:32:00 +02:00
@property
def color_temperature ( self ) :
''' Get the white color temperature in Mired using `LightController.color_temperature` '''
data = self . _api_request ( )
return data [ " state " ] [ " ct " ]
@color_temperature.setter
def color_temperature ( self , mired : int ) :
''' Set the white color temperature in Mired (`154` - `500`) using `LightController.color_temperature = ...` '''
assert type ( mired ) == int
ct_ = min ( max ( mired , 154 ) , 500 )
self . _api_request ( " PUT " , " /state " , { " ct " : ct_ } )
2023-07-21 20:40:19 +02:00
def alert ( self ) :
2023-07-21 22:35:31 +02:00
''' Flash the light once. '''
2023-07-21 20:40:19 +02:00
self . _api_request ( " PUT " , " /state " , { " alert " : " select " } )
def alert_long ( self ) :
2023-07-21 22:35:31 +02:00
''' Flash the light for 10 seconds. '''
2023-07-21 20:40:19 +02:00
self . _api_request ( " PUT " , " /state " , { " alert " : " lselect " } )
class GroupController ( _BaseController ) :
2023-07-21 22:35:31 +02:00
''' Control a Philips Hue Light Group (Room/Zone) using the API of your Hue Bridge.
Args :
- ` number : int ` - The number of your light group
- ` bridge_ip_address : str ` - The IP address of your Hue Bridge
- ` bridge_api_user : str ` - The user used to authenticate to the API
Use the class method ` . from_name ( name : str , . . . ) ` to use the name of a group instead of the number .
'''
2023-07-21 20:40:19 +02:00
_api_endpoint_all = " https:// {bridge_ip_address} /api/ {bridge_api_user} /groups "
_api_endpoint_specific = " https:// {bridge_ip_address} /api/ {bridge_api_user} /groups/ {number} "
@property
def any_on ( self ) - > bool :
2023-07-21 22:35:31 +02:00
''' Check if any light in this group is on using `GroupController.any_on` '''
2023-07-21 20:40:19 +02:00
data = self . _api_request ( )
return data [ " state " ] [ " any_on " ]
@property
def all_on ( self ) - > bool :
2023-07-21 22:35:31 +02:00
''' Check if all lights in this group are on using `GroupController.all_on` '''
2023-07-21 20:40:19 +02:00
data = self . _api_request ( )
return data [ " state " ] [ " all_on " ]
@all_on.setter
def all_on ( self , on : bool ) :
2023-07-21 22:35:31 +02:00
''' Turn on/off all lights in this group using `GroupController.all_on = ...` '''
2023-07-21 20:40:19 +02:00
assert type ( on ) == bool
self . _api_request ( " PUT " , " /action " , { " on " : on } )
@property
2023-07-21 22:35:31 +02:00
def brightness ( self ) - > int :
''' Get the last set brightness in this group using `GroupController.brightness` '''
2023-07-21 20:40:19 +02:00
data = self . _api_request ( )
return data [ " action " ] [ " bri " ]
2023-07-21 22:35:31 +02:00
2023-07-21 20:40:19 +02:00
@brightness.setter
def brightness ( self , brightness : float ) :
2023-07-21 22:35:31 +02:00
''' Set the brightness of all lights in this group using `GroupController.brightness = ...` '''
2023-07-21 20:40:19 +02:00
assert type ( brightness ) == float or type ( brightness ) == int
bri_ = min ( max ( int ( brightness * 254 ) , 0 ) , 254 )
self . _api_request ( " PUT " , " /action " , { " bri " : bri_ } )
@property
2023-07-21 22:35:31 +02:00
def hue ( self ) - > int :
''' Get the last set hue in this group using `GroupController.hue` '''
2023-07-21 20:40:19 +02:00
data = self . _api_request ( )
return data [ " action " ] [ " hue " ]
2023-07-21 22:35:31 +02:00
2023-07-21 20:40:19 +02:00
@hue.setter
def hue ( self , hue : float ) :
2023-07-21 22:35:31 +02:00
''' Set the hue of all lights in this group using `GroupController.hue = ...` '''
2023-07-21 20:40:19 +02:00
assert type ( hue ) == float or type ( hue ) == int
hue_ = min ( max ( int ( hue * 65535 ) , 0 ) , 65535 )
self . _api_request ( " PUT " , " /action " , { " hue " : hue_ } )
@property
2023-07-21 22:35:31 +02:00
def saturation ( self ) - > int :
''' Get the last set saturation in this group using `GroupController.saturation` '''
2023-07-21 20:40:19 +02:00
data = self . _api_request ( )
return data [ " action " ] [ " sat " ]
2023-07-21 22:35:31 +02:00
2023-07-21 20:40:19 +02:00
@saturation.setter
def saturation ( self , saturation : float ) :
2023-07-21 22:35:31 +02:00
''' Set the saturation of all lights in this group using `GroupController.saturation = ...` '''
2023-07-21 20:40:19 +02:00
assert type ( saturation ) == float or type ( saturation ) == int
sat_ = min ( max ( int ( saturation * 254 ) , 0 ) , 254 )
self . _api_request ( " PUT " , " /action " , { " sat " : sat_ } )
2023-07-21 22:35:31 +02:00
2023-07-23 10:32:00 +02:00
@property
def color_temperature ( self ) :
''' Get the last set white color temperature in Mired using `GroupController.color_temperature` '''
data = self . _api_request ( )
return data [ " action " ] [ " ct " ]
@color_temperature.setter
def color_temperature ( self , mired : int ) :
''' Set the white color temperature in Mired (`154` - `500`) for all lights in this group using `GroupController.color_temperature = ...` '''
assert type ( mired ) == int
ct_ = min ( max ( mired , 154 ) , 500 )
self . _api_request ( " PUT " , " /action " , { " ct " : ct_ } )
2023-07-21 20:40:19 +02:00
def alert ( self ) :
2023-07-21 22:35:31 +02:00
''' Flash all lights in the group once. '''
2023-07-21 20:40:19 +02:00
self . _api_request ( " PUT " , " /action " , { " alert " : " select " } )
def alert_long ( self ) :
2023-07-21 22:35:31 +02:00
''' Flash all lights in the group for 10 seconds. '''
2023-07-21 20:40:19 +02:00
self . _api_request ( " PUT " , " /action " , { " alert " : " lselect " } )