You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
455 lines
14 KiB
455 lines
14 KiB
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
import json
|
|
from datetime import datetime
|
|
|
|
import requests
|
|
|
|
from ..HueUtils import make_request
|
|
|
|
|
|
class HueLight:
|
|
"""A class representing a light
|
|
"""
|
|
|
|
class State:
|
|
"""A class representing the state of a light
|
|
"""
|
|
|
|
def __init__(self, data_slice: dict):
|
|
"""Constructor
|
|
|
|
Args:
|
|
data_slice (dict): The part of the data from the API response that concerns this state
|
|
"""
|
|
keys = data_slice.keys()
|
|
self.m_on: bool = data_slice['on']
|
|
if 'bri' in keys:
|
|
self.m_bri: int = data_slice['bri']
|
|
else:
|
|
self.m_bri: int = -1
|
|
if 'ct' in keys:
|
|
self.m_ct: int = data_slice['ct']
|
|
else:
|
|
self.m_ct: int = -1
|
|
if 'hue' in keys:
|
|
self.m_hue: int = data_slice['hue']
|
|
else:
|
|
self.m_hue: int = -1
|
|
if 'sat' in keys:
|
|
self.m_sat: int = data_slice['sat']
|
|
else:
|
|
self.m_sat: int = -1
|
|
self.m_alert: str = data_slice['alert']
|
|
if 'colormode' in keys:
|
|
self.m_colormode: str = data_slice['colormode']
|
|
else:
|
|
self.m_colormode: str = ""
|
|
self.m_mode: str = data_slice['mode']
|
|
self.m_reachable: bool = data_slice['reachable']
|
|
|
|
def get_brightness(self) -> int:
|
|
"""Get current brightness of the light
|
|
|
|
Returns:
|
|
int: -1-254
|
|
"""
|
|
return self.m_bri
|
|
|
|
def get_ct(self) -> int:
|
|
"""Get current color temp of the light
|
|
|
|
Returns:
|
|
int: -1, 153-500, -1 means it cant do color temp
|
|
"""
|
|
return self.m_ct
|
|
|
|
def get_hue(self) -> int:
|
|
"""Get current hue of the light
|
|
|
|
Returns:
|
|
int: -1-65535
|
|
"""
|
|
return self.m_hue
|
|
|
|
def get_sat(self):
|
|
"""Get current saturation of the light
|
|
|
|
Returns:
|
|
int: -1-254
|
|
"""
|
|
return self.m_hue
|
|
|
|
def is_on(self) -> bool:
|
|
"""Is this thing on?
|
|
|
|
Returns:
|
|
bool: True if it is on, otherwise False
|
|
"""
|
|
return self.m_on
|
|
|
|
def is_reachable(self) -> bool:
|
|
"""Can we reach this light, if not it has most likely been turned off with physical switch
|
|
|
|
Returns:
|
|
bool: True if it is reachable, otherwise False
|
|
"""
|
|
return self.m_reachable
|
|
|
|
class SwUpdate:
|
|
"""A class to describe software updates
|
|
"""
|
|
|
|
def __init__(self, data_slice: dict):
|
|
"""Constructor
|
|
|
|
Args:
|
|
data_slice (dict): The part of the data from the API that concerns this SwUpdate
|
|
"""
|
|
self.m_state: str = data_slice['state']
|
|
self.m_lastinstall: datetime = datetime.strptime(data_slice['lastinstall'], "%Y-%m-%dT%H:%M:%S")
|
|
|
|
class Capabilites:
|
|
"""A class for the light capabilities
|
|
"""
|
|
|
|
class Control:
|
|
"""A class for the control object
|
|
"""
|
|
|
|
class ColorTemp:
|
|
"""A class for the color temperature object
|
|
"""
|
|
|
|
def __init__(self, data_slice: dict):
|
|
"""Constructor
|
|
|
|
Args:
|
|
data_slice (dict): The part of the data from the API that concerns this ColorTemp
|
|
"""
|
|
keys = data_slice.keys()
|
|
if 'min' in keys:
|
|
self.m_min: int = data_slice['min']
|
|
else:
|
|
self.m_min: int = -1
|
|
if 'max' in keys:
|
|
self.m_max: int = data_slice['max']
|
|
else:
|
|
self.m_max: int = -1
|
|
|
|
def get_max(self) -> int:
|
|
"""Get the max colortemp of this light
|
|
|
|
Returns:
|
|
int: Max colortemp of this light, -1 means you can't change colortemp
|
|
"""
|
|
return self.m_max
|
|
|
|
def __init__(self, data_slice: dict):
|
|
"""Constructor
|
|
|
|
Args:
|
|
data_slice (dict): The part of the Hue API data that concerns this Control
|
|
"""
|
|
keys = data_slice.keys()
|
|
if 'mindimlevel' in keys:
|
|
self.m_mindimlevel: int = data_slice['mindimlevel']
|
|
else:
|
|
self.m_mindimlevel: int = -1
|
|
if 'maxlumen' in keys:
|
|
self.m_maxlumen: int = data_slice['maxlumen']
|
|
else:
|
|
self.m_maxlumen: int = -1
|
|
if 'ct' in data_slice.keys():
|
|
self.m_ct = HueLight.Capabilites.Control.ColorTemp(data_slice['ct'])
|
|
else:
|
|
self.m_ct = HueLight.Capabilites.Control.ColorTemp({})
|
|
|
|
def get_colortemp(self):
|
|
"""Get the colortemp object
|
|
|
|
Returns:
|
|
HueLight.Capabilities.Control.ColorTemp: the colortemp object of this capability
|
|
"""
|
|
return self.m_ct
|
|
|
|
class Streaming:
|
|
"""A class for the streaming object
|
|
"""
|
|
|
|
def __init__(self, data_slice: dict):
|
|
"""Constructor
|
|
|
|
Args:
|
|
data_slice (dict): The part of the Hue API data that concerns this streaming object
|
|
"""
|
|
self.m_renderer: bool = data_slice['renderer']
|
|
self.m_proxy: bool = data_slice['proxy']
|
|
|
|
def __init__(self, data_slice: dict):
|
|
"""Constructor
|
|
|
|
Args:
|
|
data_slice (dict): The part of the Hue API data that concerns this capabilities object
|
|
"""
|
|
self.m_certified: bool = data_slice['certified']
|
|
self.m_control = HueLight.Capabilites.Control(data_slice['control'])
|
|
self.m_streaming = HueLight.Capabilites.Streaming(data_slice['streaming'])
|
|
|
|
def get_control(self):
|
|
"""Get the control object
|
|
|
|
Returns:
|
|
HueLight.Capabilities.Control: the control object of this capability
|
|
"""
|
|
|
|
class Config:
|
|
"""A class for the config object
|
|
"""
|
|
|
|
class Startup:
|
|
"""A class for the startup object
|
|
"""
|
|
|
|
def __init__(self, data_slice: dict):
|
|
"""Constructor
|
|
|
|
Args:
|
|
data_slice (dict): The part of the Hue API data that concerns this startup
|
|
"""
|
|
self.m_mode: str = data_slice['mode']
|
|
self.m_configured: bool = data_slice['configured']
|
|
|
|
def __init__(self, data_slice: dict):
|
|
"""Constructor
|
|
|
|
Args:
|
|
data_slice (dict): The part of the Hue API response that concerns this config object
|
|
"""
|
|
self.m_archetype: str = data_slice['archetype']
|
|
self.m_function: str = data_slice['function']
|
|
self.m_direction: str = data_slice['direction']
|
|
self.m_startup = HueLight.Config.Startup(data_slice['startup'])
|
|
|
|
def __init__(self, light_id: int, data: dict, parent_bridge_ip: str, parent_bridge_user: str):
|
|
"""Constructor
|
|
|
|
Args:
|
|
light_id (int): The id of this light
|
|
data (dict): The response data from the Hue API
|
|
parent_bridge_ip (str): ip address of the parent bridge
|
|
parent_bridge_user (str): username of the parent bridge
|
|
"""
|
|
self.m_id: int = light_id
|
|
self.m_parent_bridge_ip = parent_bridge_ip
|
|
self.m_parent_bridge_user = parent_bridge_user
|
|
self.m_state = HueLight.State(data['state'])
|
|
self.m_swupdate = HueLight.SwUpdate(data['swupdate'])
|
|
self.m_type: str = data['type']
|
|
self.m_name: str = data['name']
|
|
self.m_modelid: str = data['modelid']
|
|
self.m_manufacturername: str = data['manufacturername']
|
|
self.m_productname: str = data['productname']
|
|
self.m_capabilites = HueLight.Capabilites(data['capabilities'])
|
|
self.m_config = HueLight.Config(data['config'])
|
|
self.m_uniqueid: str = data['uniqueid']
|
|
self.m_swconfigid: str = data['swconfigid']
|
|
self.m_productid: str = data['productid']
|
|
|
|
def __str__(self) -> str:
|
|
"""String representation of this light
|
|
|
|
Returns:
|
|
str: The name of the light
|
|
"""
|
|
return self.m_name
|
|
|
|
def can_set_brightness(self):
|
|
"""Check if we can set brightness for this light
|
|
Docs says min brightness is 1, so 0 (suspected default for uncapable light ) or -1 (our default) means not able
|
|
|
|
Returns:
|
|
bool: True if we can, otherwise False
|
|
"""
|
|
return self.get_brightness() > 0
|
|
|
|
def can_set_ct(self) -> bool:
|
|
"""Check if we can set a color temp for this light
|
|
|
|
Returns:
|
|
bool: True if we can, otherwise False
|
|
"""
|
|
return self.get_ct() > -1
|
|
|
|
def can_set_hue(self) -> bool:
|
|
"""Check if we can set hue for this light
|
|
|
|
Returns:
|
|
bool: True if we can, otherwise False
|
|
"""
|
|
return self.get_hue() != -1
|
|
|
|
def can_set_sat(self) -> bool:
|
|
"""Check if we can set saturation for this light
|
|
|
|
Returns:
|
|
bool: True if we can, otherwise False
|
|
"""
|
|
return self.get_sat() != -1
|
|
|
|
def delete(self):
|
|
"""Delete the light
|
|
|
|
"""
|
|
method: str = "DELETE"
|
|
path = "{}/lights/{}".format(self.m_parent_bridge_user, self.get_id())
|
|
make_request(self.m_parent_bridge_ip, path, method)
|
|
|
|
def get_brightness(self) -> int:
|
|
"""Get currrent brightness
|
|
|
|
Returns:
|
|
int: 0-254
|
|
"""
|
|
return self.get_state().get_brightness()
|
|
|
|
def get_ct(self) -> int:
|
|
"""Get current colortemp
|
|
|
|
Returns:
|
|
int: -1,153-500
|
|
"""
|
|
return self.get_state().get_ct()
|
|
|
|
def get_hue(self) -> int:
|
|
"""Get current hue
|
|
|
|
Returns:
|
|
int: -1-65535
|
|
"""
|
|
return self.get_state().get_hue()
|
|
|
|
def get_id(self) -> int:
|
|
"""Get the id of this light
|
|
|
|
Returns:
|
|
int: The light id
|
|
"""
|
|
return self.m_id
|
|
|
|
def get_sat(self):
|
|
"""Get current saturation
|
|
|
|
Returns:
|
|
int: -1-254
|
|
"""
|
|
return self.get_state().get_sat()
|
|
|
|
def get_state(self):
|
|
"""Get the state object for this light
|
|
|
|
Returns:
|
|
HueLight.State: The current state of affairs
|
|
"""
|
|
return self.m_state
|
|
|
|
def is_on(self) -> bool:
|
|
"""Is it on though?
|
|
|
|
Returns:
|
|
bool: True if it is both on and reachable, otherwise False
|
|
"""
|
|
# A light has to be both on and reachable
|
|
return self.get_state().is_on() and self.is_reachable()
|
|
|
|
def is_reachable(self) -> bool:
|
|
"""Is it reachable
|
|
|
|
Returns:
|
|
bool: True if it is, False otherwise
|
|
"""
|
|
return self.get_state().is_reachable()
|
|
|
|
def rename(self, name):
|
|
"""Set the name of the light
|
|
|
|
Args:
|
|
name (str): The new name for the light
|
|
"""
|
|
method: str = "PUT"
|
|
payload = '{{"name":"{0}"}}'.format(name)
|
|
path = "{}/lights/{}".format(self.m_parent_bridge_user, self.get_id())
|
|
make_request(self.m_parent_bridge_ip, path, method, payload)
|
|
self.update_state()
|
|
|
|
def set_brightness(self, bri: int):
|
|
"""Set the brightness of the light
|
|
|
|
Args:
|
|
bri (int): 0-254
|
|
"""
|
|
state = '{{"bri":{0}}}'.format(bri)
|
|
self.set_state(state)
|
|
|
|
def set_ct(self, colortemp: int):
|
|
"""Set the colortemp of the light, if possible
|
|
|
|
Args:
|
|
colortemp (int): 153-500
|
|
"""
|
|
if self.can_set_ct():
|
|
state = '{{"ct":{0}}}'.format(colortemp)
|
|
self.set_state(state)
|
|
|
|
def set_hue(self, hue: int):
|
|
"""Set the hue of the light, if possible
|
|
|
|
Args:
|
|
hue (int): 0-65535
|
|
"""
|
|
if self.can_set_hue():
|
|
state = '{{"hue":{0}}}'.format(hue)
|
|
self.set_state(state)
|
|
|
|
def set_sat(self, sat: int):
|
|
"""Set the saturation of the light, if possible
|
|
|
|
Args:
|
|
sat (int): 0-254
|
|
"""
|
|
if self.can_set_sat():
|
|
state = '{{"sat":{0}}}'.format(sat)
|
|
self.set_state(state)
|
|
|
|
def set_state(self, state: str) -> requests.Response:
|
|
"""A helper method to set the state of the light
|
|
|
|
Args:
|
|
state (str): The state of the light
|
|
|
|
Returns:
|
|
requests.Response: The response from the Hue API
|
|
"""
|
|
path: str = "{}/lights/{}/state".format(self.m_parent_bridge_user, self.m_id)
|
|
method: str = "PUT"
|
|
response = make_request(self.m_parent_bridge_ip, path, method, state)
|
|
self.update_state()
|
|
return response
|
|
|
|
def toggle(self):
|
|
"""Toggle light
|
|
"""
|
|
if self.is_reachable():
|
|
state: str = '{"on":true}'
|
|
if self.is_on():
|
|
state = '{"on":false}'
|
|
self.set_state(state)
|
|
|
|
def update_state(self):
|
|
"""See if anything has changed
|
|
"""
|
|
path: str = "{}/lights/{}".format(self.m_parent_bridge_user, self.m_id)
|
|
response = make_request(self.m_parent_bridge_ip, path)
|
|
self.m_state = HueLight.State(json.loads(response.text)['state'])
|
|
self.m_name = json.loads(response.text)['name']
|