#!/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']