Tinge is a mobile first application for controlling Philips Hue lights on Linux
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.
 
 

103 lines
4.0 KiB

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import http
import os
from typing import Union
import toml
from upnpy import UPnP
from .HueBridge import HueBridge
from .HueUtils import connect, is_valid_config, make_request, is_bridge
from .UserOrError import UserOrError
class Tinge:
"""The class that keeps track of bridges and config
"""
def __init__(self):
"""Constructor
"""
self.m_bridges: list[HueBridge] = 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()
def append_bridge(self, bridge: HueBridge):
self.m_bridges.append(bridge)
self.m_discovered.append(bridge.get_ipaddress())
def create_confdir(self):
"""Create the config dir if it does not allready exist
"""
if not os.path.exists(os.path.dirname(self.m_config)):
os.makedirs(os.path.dirname(self.m_config))
def discover_new_bridges(self) -> Union[None, list[dict]]:
"""Use UPnP to discover bridges on the current network
"""
upnp: UPnP = UPnP()
discovered_devices = list()
try:
discovered_devices = upnp.discover()
except http.client.BadStatusLine:
print("UPnP discovery failed")
return None
discovered_bridges: list[dict] = list()
seen_ips: list[str] = list()
if not discovered_devices:
print("No devices discovered at this time")
return None
for device in discovered_devices:
discovered: bool = False
if (device.host not in self.m_discovered) and (device.host not in seen_ips):
seen_ips.append(device.host)
# Let's check if the device has the default name, if so we assume it's a hue bridge
f_name = device.get_friendly_name()
if f_name:
if f_name.startswith("Philips hue"):
discovered = True
# If not we try to do a request against the api and see if we get an answer we can understand
else:
discovered = is_bridge(device.host)
if discovered:
bridge = {'ipaddress': device.host, 'name': device.get_friendly_name()}
if bridge not in discovered_bridges:
discovered_bridges.append(bridge)
return discovered_bridges
def get_bridges(self) -> list[HueBridge]:
"""Get the bridges
Returns:
list[HueBridge]: The bridges we keep track off
"""
return self.m_bridges
def read_bridges_from_file(self):
"""Read config file and add back previously discovered bridges
"""
if is_valid_config(self.m_config):
with open(self.m_config, 'r') as configfile:
mbridges = toml.loads(configfile.read())
for key, value in mbridges.items():
if key not in self.m_discovered:
response = make_request(key, "{}/".format(value['user']))
if response:
name = "{} ({})".format(response.json()['config']['name'], key)
bridge: HueBridge = HueBridge(key, value['user'], name)
self.m_bridges.append(bridge)
self.m_discovered.append(key)
else:
bridge: HueBridge = HueBridge(key, value['user'], is_reachable=False)
self.m_bridges.append(bridge)
self.m_discovered.append(key)
def write_all_bridges_to_conf(self):
"""Save to file
"""
with open(self.m_config, 'w') as configfile:
for bridge in self.m_bridges:
configfile.write('["{}"]\nuser = "{}"\n'.format(bridge.get_ipaddress(), bridge.get_user()))