#!/usr/bin/env python3 # -*- coding: utf-8 -*- import http import os from typing import Union import simplejson 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 if device.get_friendly_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()))