parent
a582539b41
commit
91675efb16
@ -0,0 +1,11 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from tinge import Tinge
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
m_tinge = Tinge()
|
||||||
|
bridge = m_tinge.get_bridges()[0]
|
||||||
|
for light in bridge.get_lights():
|
||||||
|
print("Light: {} is reachable: {} and on:{}".format(light, light.is_reachable(), light.is_on()))
|
||||||
|
light.toggle()
|
||||||
|
print("Light: {} is reachable: {} and on:{}".format(light, light.is_reachable(), light.is_on()))
|
@ -1,14 +1,35 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
import json
|
||||||
|
|
||||||
|
from ..HueLight import HueLight
|
||||||
|
from ..HueUtils import make_request
|
||||||
|
|
||||||
|
|
||||||
class HueBridge():
|
class HueBridge():
|
||||||
def __init__(self, ipaddress: str, username: str):
|
def __init__(self, ipaddress: str, username: str):
|
||||||
self.mipaddress: str = ipaddress
|
self.m_ipaddress: str = ipaddress
|
||||||
self.musername: str = username
|
self.m_username: str = username
|
||||||
|
self.m_lights: list[HueLight] = self.discover_lights()
|
||||||
|
|
||||||
|
def discover_lights(self) -> list[HueLight]:
|
||||||
|
path: str = "{}/lights".format(self.m_username)
|
||||||
|
response = make_request(self.m_ipaddress, path)
|
||||||
|
lights: list[HueLight] = list()
|
||||||
|
for key, value in json.loads(response.text).items():
|
||||||
|
lights.append(HueLight(key, value, self.get_ipaddress(), self.get_user()))
|
||||||
|
return lights
|
||||||
|
|
||||||
|
def get_lights(self):
|
||||||
|
return self.m_lights
|
||||||
|
|
||||||
|
def get_light_by_id(self, id: int) -> HueLight:
|
||||||
|
for light in self.m_lights:
|
||||||
|
if light.get_id() == id:
|
||||||
|
return light
|
||||||
|
|
||||||
def get_ipaddress(self):
|
def get_ipaddress(self):
|
||||||
return self.mipaddress
|
return self.m_ipaddress
|
||||||
|
|
||||||
def get_user(self):
|
def get_user(self):
|
||||||
return self.musername
|
return self.m_username
|
||||||
|
@ -0,0 +1,143 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import json
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
|
from ..HueUtils import make_request
|
||||||
|
|
||||||
|
|
||||||
|
class HueLight:
|
||||||
|
class State:
|
||||||
|
def __init__(self, data_slice: dict):
|
||||||
|
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 = 0
|
||||||
|
if 'ct' in keys:
|
||||||
|
self.m_ct: int = data_slice['ct']
|
||||||
|
else:
|
||||||
|
self.m_ct: int = 0
|
||||||
|
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 is_on(self) -> bool:
|
||||||
|
return self.m_on
|
||||||
|
|
||||||
|
def is_reachable(self) -> bool:
|
||||||
|
return self.m_reachable
|
||||||
|
|
||||||
|
class SwUpdate:
|
||||||
|
def __init__(self, data_slice: dict):
|
||||||
|
self.m_state: str = data_slice['state']
|
||||||
|
self.m_lastinstall: datetime = datetime.strptime(data_slice['lastinstall'], "%Y-%m-%dT%H:%M:%S")
|
||||||
|
|
||||||
|
class Capabilites:
|
||||||
|
class Control:
|
||||||
|
class ColorTemp:
|
||||||
|
def __init__(self, data_slice: dict):
|
||||||
|
keys = data_slice.keys()
|
||||||
|
if 'min' in keys:
|
||||||
|
self.m_min: int = data_slice['min']
|
||||||
|
else:
|
||||||
|
self.m_min: int = 0
|
||||||
|
if 'max' in keys:
|
||||||
|
self.m_max: int = data_slice['max']
|
||||||
|
else:
|
||||||
|
self.m_max: int = 0
|
||||||
|
|
||||||
|
def __init__(self, data_slice: dict):
|
||||||
|
keys = data_slice.keys()
|
||||||
|
if 'mindimlevel' in keys:
|
||||||
|
self.m_mindimlevel: int = data_slice['mindimlevel']
|
||||||
|
else:
|
||||||
|
self.m_mindimlevel: int = 0
|
||||||
|
if 'maxlumen' in keys:
|
||||||
|
self.m_maxlumen: int = data_slice['maxlumen']
|
||||||
|
else:
|
||||||
|
self.m_maxlumen: int = 0
|
||||||
|
if 'ct' in data_slice.keys():
|
||||||
|
self.m_ct = HueLight.Capabilites.Control.ColorTemp(data_slice['ct'])
|
||||||
|
else:
|
||||||
|
self.m_ct = HueLight.Capabilites.Control.ColorTemp({})
|
||||||
|
|
||||||
|
class Streaming:
|
||||||
|
def __init__(self, data_slice: dict):
|
||||||
|
self.m_renderer: bool = data_slice['renderer']
|
||||||
|
self.m_proxy: bool = data_slice['proxy']
|
||||||
|
|
||||||
|
def __init__(self, data_slice: dict):
|
||||||
|
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'])
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
class Startup:
|
||||||
|
def __init__(self, data_slice: dict):
|
||||||
|
self.m_mode: str = data_slice['mode']
|
||||||
|
self.m_configured: bool = data_slice['configured']
|
||||||
|
|
||||||
|
def __init__(self, data_slice: dict):
|
||||||
|
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, id: int, data: dict, parent_bridge_ip: str, parent_bridge_user: str):
|
||||||
|
self.m_id: int = 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:
|
||||||
|
return self.m_name
|
||||||
|
|
||||||
|
def update_state(self):
|
||||||
|
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'])
|
||||||
|
|
||||||
|
def get_state(self):
|
||||||
|
return self.m_state
|
||||||
|
|
||||||
|
def get_id(self):
|
||||||
|
return self.m_id
|
||||||
|
|
||||||
|
def is_on(self) -> bool:
|
||||||
|
return self.get_state().is_on()
|
||||||
|
|
||||||
|
def is_reachable(self) -> bool:
|
||||||
|
return self.get_state().is_reachable()
|
||||||
|
|
||||||
|
def set_state(self, state: str) -> requests.Response:
|
||||||
|
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):
|
||||||
|
if self.is_reachable():
|
||||||
|
state: str = '{"on":true}'
|
||||||
|
if self.is_on():
|
||||||
|
state = '{"on":false}'
|
||||||
|
self.set_state(state)
|
@ -0,0 +1,47 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import os
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
|
from ..UserOrError import UserOrError
|
||||||
|
|
||||||
|
|
||||||
|
def connect(ipaddress: str) -> UserOrError:
|
||||||
|
user_or_error: UserOrError = UserOrError()
|
||||||
|
body: str = '{{"devicetype":"{0}"}}'.format("tinge")
|
||||||
|
path: str = ""
|
||||||
|
method: str = "POST"
|
||||||
|
response: requests.Response = make_request(ipaddress, path, method, body)
|
||||||
|
|
||||||
|
data: dict = response.json()[0]
|
||||||
|
if 'error' in data.keys():
|
||||||
|
user_or_error.set_error(data['error']['type'])
|
||||||
|
elif 'success' in data.keys():
|
||||||
|
user_or_error.set_user(data['success']['username'])
|
||||||
|
else:
|
||||||
|
user_or_error.set_error(user_or_error.UNKNOWNERROR)
|
||||||
|
return user_or_error
|
||||||
|
|
||||||
|
|
||||||
|
def is_valid_config(filename: str) -> bool:
|
||||||
|
return os.path.exists(filename) and os.path.getsize(filename) > 0
|
||||||
|
|
||||||
|
|
||||||
|
def make_request(ipaddress: str, path: str, method: str = "GET",
|
||||||
|
body: str = '') -> requests.Response:
|
||||||
|
rfunct = requests.get
|
||||||
|
url = "http://{}/api/{}".format(ipaddress, path)
|
||||||
|
if method == "PUT":
|
||||||
|
rfunct = requests.put
|
||||||
|
elif method == "POST":
|
||||||
|
rfunct = requests.post
|
||||||
|
elif method == "DELETE":
|
||||||
|
rfunct = requests.delete
|
||||||
|
if body and method == "GET":
|
||||||
|
response = rfunct(url, params=body)
|
||||||
|
elif body:
|
||||||
|
response = rfunct(url, data=body)
|
||||||
|
else:
|
||||||
|
response = rfunct(url)
|
||||||
|
return response
|
@ -1,89 +1,66 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import json
|
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
import uuid
|
|
||||||
|
|
||||||
import requests
|
|
||||||
import toml
|
import toml
|
||||||
from upnpy import UPnP
|
from upnpy import UPnP
|
||||||
|
|
||||||
import tinge
|
from .HueBridge import HueBridge
|
||||||
|
from .HueUtils import connect, is_valid_config
|
||||||
|
from .UserOrError import UserOrError
|
||||||
def connect(ipaddress: str, appid: uuid.UUID = uuid.uuid4()) -> UserOrError:
|
|
||||||
user_or_error = UserOrError()
|
|
||||||
body: dict = json.loads('{"devicetype":"{}#{}"}'.format("tinge", appid))
|
|
||||||
path: str = "api"
|
|
||||||
method: str = "POST"
|
|
||||||
response: requests.Response = make_request(ipaddress, path, method, body)
|
|
||||||
|
|
||||||
data: dict = response.json()[0]
|
|
||||||
if 'error' in data.keys():
|
|
||||||
user_or_error.set_error(data['error']['type'])
|
|
||||||
elif 'success' in data.keys():
|
|
||||||
user_or_error.set_user(data['success']['username'])
|
|
||||||
else:
|
|
||||||
user_or_error.set_error(9999)
|
|
||||||
return user_or_error
|
|
||||||
|
|
||||||
|
|
||||||
def is_valid_config(filename: str) -> bool:
|
|
||||||
return os.path.exists(filename) and os.path.getsize(filename) > 0
|
|
||||||
|
|
||||||
|
|
||||||
def make_request(ipaddress: str, path: str, method: str = "GET",
|
|
||||||
body: dict = json.loads('{}')) -> requests.Response:
|
|
||||||
rfunct = requests.get
|
|
||||||
url = "http://{}/{}".format(ipaddress, path)
|
|
||||||
if method == "PUT":
|
|
||||||
rfunct = requests.put
|
|
||||||
elif method == "POST":
|
|
||||||
rfunct = requests.post
|
|
||||||
elif method == "DELETE":
|
|
||||||
rfunct = requests.delete
|
|
||||||
response: requests.Response = requests.Response()
|
|
||||||
if body:
|
|
||||||
response = rfunct(url, data=body)
|
|
||||||
else:
|
|
||||||
response = rfunct(url)
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
class Tinge:
|
class Tinge:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.mbridges: list[HueBridge] = list()
|
self.m_bridges: list[HueBridge] = list()
|
||||||
self.mdiscovered: list[str] = list()
|
self.m_discovered: list[str] = list()
|
||||||
|
self.m_config = os.path.join(os.environ['HOME'], ".config/tinge/config")
|
||||||
|
self.create_confdir()
|
||||||
|
self.read_bridges_from_file()
|
||||||
|
self.discover_new_bridges()
|
||||||
|
self.write_all_bridges_to_conf()
|
||||||
|
|
||||||
|
def create_confdir(self):
|
||||||
|
if not os.path.exists(os.path.dirname(self.m_config)):
|
||||||
|
os.makedirs(os.path.dirname(self.m_config))
|
||||||
|
|
||||||
def discover_new_bridges(self):
|
def discover_new_bridges(self):
|
||||||
upnp: UPnP = UPnP()
|
upnp: UPnP = UPnP()
|
||||||
for device in upnp.discover():
|
discovered_devices = upnp.discover()
|
||||||
if device.get_friendly_name().startswith("Philips hue") and device.host not in self.mdiscovered:
|
if not discovered_devices:
|
||||||
|
print("No devices discovered at this time")
|
||||||
|
return
|
||||||
|
for device in discovered_devices:
|
||||||
|
if device.get_friendly_name().startswith("Philips hue") and device.host not in self.m_discovered:
|
||||||
user_or_error: UserOrError = connect(device.host)
|
user_or_error: UserOrError = connect(device.host)
|
||||||
|
print("Is error: {}".format(str(user_or_error.is_error())))
|
||||||
while user_or_error.is_error():
|
while user_or_error.is_error():
|
||||||
|
print("Is error: {}".format(str(user_or_error.get_error_code())))
|
||||||
if user_or_error.get_error_code() == 101:
|
if user_or_error.get_error_code() == 101:
|
||||||
print("Please press the button on your Hue Bridge")
|
print("Please press the button on your Hue Bridge")
|
||||||
time.sleep(5)
|
time.sleep(5)
|
||||||
user_or_error = connect(device.host)
|
user_or_error = connect(device.host)
|
||||||
bridge: HueBridge = HueBridge(device.host, user_or_error.get_user)
|
bridge: HueBridge = HueBridge(device.host, user_or_error.get_user())
|
||||||
|
|
||||||
|
self.m_bridges.append(bridge)
|
||||||
|
self.m_discovered.append(device.host)
|
||||||
|
return
|
||||||
|
|
||||||
self.mbridges.append(bridge)
|
def get_bridges(self):
|
||||||
self.mdiscovered.append(device.host)
|
return self.m_bridges
|
||||||
|
|
||||||
def get_bridges_from_file(self):
|
def read_bridges_from_file(self):
|
||||||
if is_valid_config(self.config):
|
if is_valid_config(self.m_config):
|
||||||
with open(self.config, 'r') as configfile:
|
with open(self.m_config, 'r') as configfile:
|
||||||
mbridges = toml.loads(configfile.read())
|
mbridges = toml.loads(configfile.read())
|
||||||
for bridge, value in mbridges.items():
|
for key, value in mbridges.items():
|
||||||
print(bridge, value['user'])
|
if key not in self.m_discovered:
|
||||||
if bridge not in self.mdiscovered:
|
bridge: HueBridge = HueBridge(key, value['user'])
|
||||||
bridge: HueBridge = HueBridge(bridge, value['user'])
|
self.m_bridges.append(bridge)
|
||||||
bridge.connect()
|
self.m_discovered.append(key)
|
||||||
self.mbridges.append(bridge)
|
|
||||||
self.mdiscovered.append(bridge)
|
|
||||||
|
|
||||||
def write_all_bridges_to_conf(self):
|
def write_all_bridges_to_conf(self):
|
||||||
with open(self.config, 'w') as configfile:
|
with open(self.m_config, 'w') as configfile:
|
||||||
for bridge in self.mbridges:
|
for bridge in self.m_bridges:
|
||||||
configfile.write('["{}"]\nuser = "{}"\n'.format(bridge.ip, bridge.username))
|
configfile.write('["{}"]\nuser = "{}"\n'.format(bridge.get_ipaddress(), bridge.get_user()))
|
||||||
|
@ -1,48 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
import os
|
|
||||||
|
|
||||||
import time
|
|
||||||
import toml
|
|
||||||
import wx
|
|
||||||
import HueBridge
|
|
||||||
from upnpy import UPnP
|
|
||||||
|
|
||||||
|
|
||||||
def connect_new_bridge(ipaddress) -> Bridge:
|
|
||||||
bridge: Bridge = Bridge(ipaddress)
|
|
||||||
connected: bool = False
|
|
||||||
while not connected:
|
|
||||||
try:
|
|
||||||
bridge.register_app()
|
|
||||||
except Exception as mexception: # PhueRegistrationException as mexception:
|
|
||||||
print(mexception)
|
|
||||||
time.sleep(5)
|
|
||||||
else:
|
|
||||||
connected = True
|
|
||||||
bridge.connect()
|
|
||||||
return bridge
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Hui(wx.Frame):
|
|
||||||
def __init__(self, *args, **kw):
|
|
||||||
super().__init__(*args, **kw)
|
|
||||||
self.bridges: list[Bridge] = list()
|
|
||||||
self.discovered: list[str] = list()
|
|
||||||
self.config = os.path.join(os.getenv('HOME'), '.tinge')
|
|
||||||
if is_valid_config(self.config):
|
|
||||||
self.get_bridges_from_file()
|
|
||||||
self.discover_new_bridges()
|
|
||||||
self.write_all_bridges_to_conf()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
app = wx.App()
|
|
||||||
frm = Hui()
|
|
||||||
# frm.show()
|
|
||||||
app.MainLoop()
|
|
Loading…
Reference in new issue