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.
tinge/tinge/HueLight/__init__.py

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']