def run_pytradfri(): api_factory = APIFactory('192.168.1.123', 'myUserName', 'password123456789') api = api_factory.request gateway = Gateway() devices_command = gateway.get_devices() devices_commands = yield from api(devices_command) devices = yield from api(devices_commands) groups_command = gateway.get_groups() groups_commands = yield from api(groups_command) groups = yield from api(groups_commands) for device in devices: observe_command = device.observe(observe_device_callback, observe_err_callback, duration=0) # Start observation as a second task on the loop. ensure_future(api(observe_command)) yield from asyncio.sleep(0) for group in groups: observe_command = group.observe(observe_group_callback, observe_err_callback, duration=0) # Start observation as a second task on the loop. ensure_future(api(observe_command)) yield from asyncio.sleep(0) # Sleep in an infinite loop to keep this running but also allow other tasks to execute while True: yield from asyncio.sleep(1)
async def async_setup_entry(hass, entry): """Create a gateway.""" # host, identity, key, allow_tradfri_groups tradfri_data = hass.data.setdefault(DOMAIN, {})[entry.entry_id] = {} listeners = tradfri_data[LISTENERS] = [] factory = await APIFactory.init( entry.data[CONF_HOST], psk_id=entry.data[CONF_IDENTITY], psk=entry.data[CONF_KEY], ) async def on_hass_stop(event): """Close connection when hass stops.""" await factory.shutdown() listeners.append( hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, on_hass_stop)) api = factory.request gateway = Gateway() try: gateway_info = await api(gateway.get_gateway_info()) devices_commands = await api(gateway.get_devices()) devices = await api(devices_commands) groups_commands = await api(gateway.get_groups()) groups = await api(groups_commands) except RequestError as err: await factory.shutdown() raise ConfigEntryNotReady from err tradfri_data[KEY_API] = api tradfri_data[FACTORY] = factory tradfri_data[DEVICES] = devices tradfri_data[GROUPS] = groups dev_reg = await hass.helpers.device_registry.async_get_registry() dev_reg.async_get_or_create( config_entry_id=entry.entry_id, connections=set(), identifiers={(DOMAIN, entry.data[CONF_GATEWAY_ID])}, manufacturer=ATTR_TRADFRI_MANUFACTURER, name=ATTR_TRADFRI_GATEWAY, # They just have 1 gateway model. Type is not exposed yet. model=ATTR_TRADFRI_GATEWAY_MODEL, sw_version=gateway_info.firmware_version, ) for component in PLATFORMS: hass.async_create_task( hass.config_entries.async_forward_entry_setup(entry, component)) return True
def connect(): print("connect") global conf global api_factory global lights global groups global api try: identity = conf[ip_host].get('identity') psk = conf[ip_host].get('key') api_factory = APIFactory(host=ip_host, psk_id=identity, psk=psk) except KeyError: identity = uuid.uuid4().hex api_factory = APIFactory(host=ip_host, psk_id=identity) try: psk = api_factory.generate_psk(key) print('Generated PSK: ', psk) conf[ip_host] = {'identity': identity,'key': psk} save_json(CONFIG_FILE, conf) except AttributeError: raise PytradfriError("Please provide the 'Security Code' on the back of your Tradfri gateway using the -K flag.") api = api_factory.request gateway = Gateway() #get all devices devices_command = gateway.get_devices() devices_commands = api(devices_command) devices = api(devices_commands) # create list of available bulbs lamps = [dev for dev in devices if dev.has_light_control] # get all available groups groups_command = gateway.get_groups() groups_commands = api(groups_command) groupc = api(groups_commands) groups = [dev for dev in groupc] lights = [dev for dev in devices if dev.has_light_control]
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Create a gateway.""" # host, identity, key, allow_tradfri_groups tradfri_data: dict[str, Any] = {} hass.data.setdefault(DOMAIN, {})[entry.entry_id] = tradfri_data listeners = tradfri_data[LISTENERS] = [] factory = await APIFactory.init( entry.data[CONF_HOST], psk_id=entry.data[CONF_IDENTITY], psk=entry.data[CONF_KEY], ) async def on_hass_stop(event): """Close connection when hass stops.""" await factory.shutdown() listeners.append(hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, on_hass_stop)) api = factory.request gateway = Gateway() try: gateway_info = await api(gateway.get_gateway_info()) devices_commands = await api(gateway.get_devices()) devices = await api(devices_commands) groups_commands = await api(gateway.get_groups()) groups = await api(groups_commands) except RequestError as err: await factory.shutdown() raise ConfigEntryNotReady from err tradfri_data[KEY_API] = api tradfri_data[FACTORY] = factory tradfri_data[DEVICES] = devices tradfri_data[GROUPS] = groups dev_reg = await hass.helpers.device_registry.async_get_registry() dev_reg.async_get_or_create( config_entry_id=entry.entry_id, connections=set(), identifiers={(DOMAIN, entry.data[CONF_GATEWAY_ID])}, manufacturer=ATTR_TRADFRI_MANUFACTURER, name=ATTR_TRADFRI_GATEWAY, # They just have 1 gateway model. Type is not exposed yet. model=ATTR_TRADFRI_GATEWAY_MODEL, sw_version=gateway_info.firmware_version, ) hass.config_entries.async_setup_platforms(entry, PLATFORMS) async def async_keep_alive(now): if hass.is_stopping: return try: await api(gateway.get_gateway_info()) except RequestError: _LOGGER.error("Keep-alive failed") listeners.append( async_track_time_interval(hass, async_keep_alive, timedelta(seconds=60)) ) return True
class RequestHandler(BaseHTTPRequestHandler): key = None hubip = None BAD_REQUEST = -1 SUCCESS = 0 # these values are not double-checked # if any mistakes are made here, things might blow up in your face COLOR = 'color' SWITCH = 'switch' BRIGHTNESS = 'brightness' # these values are the specific ones that the white spectrum tradfri bulbs use # they should not be changed, since the bulbs physically can't display other colors # i haven't tested the full-spectrum bulbs, but they should presumably support any color COLOR_TO_HEX_MAP = { 'warm': 'efd275', 'orange': 'efd275', 'red': 'efd275', 'normal': 'f1e0b5', 'yellow': 'f1e0b5', 'cool': 'f5faf6', 'cold': 'f5faf6', 'white': 'f5faf6', 'blue': 'f5faf6' } COMMAND_PART_TYPE_TO_VALUES = { COLOR: list(COLOR_TO_HEX_MAP.keys()), SWITCH: ['on', 'off'], BRIGHTNESS: list(str(i) for i in range(101)) } ZONES = ['living', 'living room', 'bathroom', 'bedroom', 'office'] ZONE_ALIAS_MAP = { 'living room': 'Living Room', 'living': 'Living Room', 'bedroom': 'Bedroom', 'bathroom': 'Bathroom', 'office': 'Office' } # ?[zone] [on|off] # ?[zone] [color] # ?[zone] [brightness] # ?[zone] [color] [brightness] # ?[zone] [brightness] [color] FORMATS = [ [SWITCH], [COLOR], [BRIGHTNESS], [COLOR, BRIGHTNESS], [BRIGHTNESS, COLOR] ] # def __init__(self, request, client_address, server): # BaseHTTPRequestHandler.__init__(self, request, client_address, server) # # print("hub" + str(self.hubip)) def init(self): if self.hubip is None: conf = configparser.ConfigParser() conf.read('tradfri.cfg') #print(conf) self.hubip = conf.get('tradfri', 'hubip') self.securityid = conf.get('tradfri', 'securityid') self.api = api_factory(self.hubip, self.securityid) self.gateway = Gateway() groups_command = self.gateway.get_groups() groups_commands = self.api(groups_command) groups = self.api(*groups_commands) self.groups = dict((g.name, g) for g in groups) print(str(self.groups)) def add_zone(self, zone_ids, inverted, new_zone): if inverted: zone_ids.remove(self.ZONE_ALIAS_MAP[new_zone]) else: zone_ids.add(self.ZONE_ALIAS_MAP[new_zone]) return zone_ids def _parse_request(self): #print("hub2" + self.hubip) self.init() parsed_req = urlparse(self.path) args = parse_qs(parsed_req.query) if self.headers.get('content-type', '') \ == 'application/x-www-form-urlencoded': body = self.rfile.read(int(self.headers.get('content-length'))) args = parse_qs(body) args = dict((k, v[0]) for k, v in args.items()) return (parsed_req.path, args) def isvalid(self, cmd, format): for i in range(len(format)): part_type = format[i] part = cmd[i] for acceptable_value in self.COMMAND_PART_TYPE_TO_VALUES[part_type]: if part.lower() == acceptable_value.lower(): break else: print("invalid: " + str(cmd) + " against " + str(format)) return False print("valid: " + str(cmd) + " against " + str(format)) return True def run_command(self, zone_ids, cmd, format): print("cmd:" + str(cmd)) print("format:" + str(format)) # zones = list(self.ZONES) # if zone_ is not None: # zones = [zone_] print("zone_ids: " + str(zone_ids)) for i in range(len(format)): part_type = format[i] part = cmd[i].lower() #zone_ids = set(self.ZONE_ALIAS_MAP[zone] for zone in zones) #print("zone ids: " + str(zone_ids)) #for zone in zones: for zone_id in zone_ids: #zone_id = self.ZONE_ALIAS_MAP[zone] if part_type == self.SWITCH: # print('performing now') # print('hub ip is ' + str(type(self.hubip))) # print('key is ' + str(type(self.securityid))) # print('zone id is ' + str(type(zone_id))) # print('part is ' + str(type(part))) self.api(self.groups[zone_id].set_state(1 if part == 'on' else 0)) # print('done') elif part_type == self.COLOR: # need to do it per light... for devcmd in self.groups[zone_id].members(): dev = self.api(devcmd) print(str(dev) + ": " + str(dev.has_light_control)) if not dev.has_light_control: continue self.api(dev.light_control.set_hex_color(self.COLOR_TO_HEX_MAP[part])) # self.groups[zone_id].set_hex_color(self.COLOR_TO_HEX_MAP[part]) elif part_type == self.BRIGHTNESS: rawval = int(float(part) * 2.55) self.api(self.groups[zone_id].set_dimmer(rawval)) if rawval > 0: self.api(self.groups[zone_id].set_state(1)) #sleep(0.2) #sleep(0.2) return self.SUCCESS def process(self, input): command = input.lower().split(' ') # print(str(command)) zone_ids = set() inverted = False if len(command) == 0: return self.BAD_REQUEST if command[0] == 'except': # magic "invert" keyword zone_ids = set(self.ZONE_ALIAS_MAP.values()) inverted = True command = command[1:] if len(command) >= 3 and command[0] + ' ' + command[1] in self.ZONES: # hacky support for two-word zone names #zones.append(command[0] + ' ' + command[1]) self.add_zone(zone_ids, inverted, command[0] + ' ' + command[1]) command = command[2:] if command[0] in self.ZONES: #zones.append(command[0]) self.add_zone(zone_ids, inverted, command[0]) command = command[1:] if len(command) == 0: return self.BAD_REQUEST if len(zone_ids) == 0: zone_ids = set(self.ZONE_ALIAS_MAP.values()) for format in self.FORMATS: if len(format) == len(command) and self.isvalid(command, format): return self.run_command(zone_ids, command, format) return self.BAD_REQUEST # def do_POST(self): # path, args = self._parse_request() # self.do('POST', path, args) def do_GET(self): path, args = self._parse_request() self.do('GET', path, args) def do(self, method, path, args): if args.get('key') != RequestHandler.key or 'command' not in args: self.send_error(400, 'Bad Request') return retval = self.process(args['command'].strip()) if retval == self.SUCCESS: self.send_response(200) self.end_headers() elif retval == self.BAD_REQUEST: self.send_error(400, 'Bad request') else: self.send_error(500, 'Trigger command failed')
print(str(datetime.datetime.now().time())[:8] + " "+ str(x)) sys.stdout.flush() api = api_factory.request gateway = Gateway() #get all devices devices_command = gateway.get_devices() devices_commands = api(devices_command) devices = api(devices_commands) # create list of available bulbs lamps = [dev for dev in devices if dev.has_light_control] # get all available groups groups_command = gateway.get_groups() groups_commands = api(groups_command) groupc = api(groups_commands) groups = [dev for dev in groupc] lights = [dev for dev in devices if dev.has_light_control] #------------------------------------------------------------------- # supported_features 1=mono 23=color #device info TRADFRI bulb E27 W opal 1000lm #device info TRADFRI bulb E27 opal 1000lm #device info TRADFRI bulb E27 opal 1000lm #device info TRADFRI bulb GU10 WS 400lm #device info TRADFRI bulb GU10 WS 400lm #device info TRADFRI bulb GU10 WS 400lm #device info LCT001
class MainWindow(QWidget): def __init__(self, appctx): super().__init__() self.appctx = appctx self.api = None self.settings = Settings() self.gateway = Gateway() self.timers: Dict[str, QTimer] = {} self.init_ui() def init_ui(self): self.vbox = QVBoxLayout() self.hbox = QHBoxLayout() # Set layout self.setLayout(self.vbox) # Group list vbox3 = QVBoxLayout() group_frame = QGroupBox("Device Groups") group_frame.setLayout(QVBoxLayout()) self.group_list = QListWidget() self.group_list.itemPressed.connect(self.group_selected) group_frame.layout().addWidget(self.group_list) vbox3.addWidget(group_frame) # Sliders self.group_toggle = QCheckBox("Power") self.group_toggle.setEnabled(False) vbox3.addWidget(self.group_toggle) vbox3.addWidget(QLabel("Brightness")) self.group_brightness_slider = QSlider(Qt.Orientation.Horizontal) self.group_brightness_slider.setEnabled(False) self.group_brightness_slider.sliderMoved.connect( self.group_brightness_changed) vbox3.addWidget(self.group_brightness_slider) vbox3.addWidget(QLabel("Color Temperature")) self.group_color_slider = QSlider(Qt.Orientation.Horizontal) self.group_color_slider.setEnabled(False) self.group_color_slider.sliderMoved.connect(self.group_color_changed) vbox3.addWidget(self.group_color_slider) self.hbox.addLayout(vbox3) # moods mood_frame = QGroupBox("Moods") mood_frame.setLayout(QVBoxLayout()) self.mood_list = QListWidget() self.mood_list.itemPressed.connect(self.mood_selected) mood_frame.layout().addWidget(self.mood_list) self.hbox.addWidget(mood_frame) # Devices in group vbox2 = QVBoxLayout() device_frame = QGroupBox("Devices in Group") device_frame.setLayout(QVBoxLayout()) self.device_list = QListWidget() self.device_list.setEnabled(False) self.device_list.itemPressed.connect(self.device_selected) device_frame.layout().addWidget(self.device_list) vbox2.addWidget(device_frame) # Sliders self.device_toggle = QCheckBox("Power") self.device_toggle.setEnabled(False) vbox2.addWidget(self.device_toggle) vbox2.addWidget(QLabel("Brightness")) self.brightness_slider = QSlider(Qt.Orientation.Horizontal) self.brightness_slider.setEnabled(False) self.brightness_slider.sliderMoved.connect(self.brightness_changed) vbox2.addWidget(self.brightness_slider) vbox2.addWidget(QLabel("Color Temperature")) self.color_slider = QSlider(Qt.Orientation.Horizontal) self.color_slider.setEnabled(False) self.color_slider.sliderMoved.connect(self.color_changed) vbox2.addWidget(self.color_slider) self.hbox.addLayout(vbox2) self.vbox.addLayout(self.hbox) # Settings button icon = QIcon(resource_path('icons/settings.png')) self.settings_button = QPushButton(icon, "Settings") self.settings_button.pressed.connect(self.settings_pressed) self.vbox.addWidget(self.settings_button) self.setWindowTitle('TradfriGUI') self.re_init() def re_init(self): if self.settings.gateway_ip is None or self.settings.gateway_ip == '': self.settings_pressed() self.api = get_api(self.settings) self.device_list.clear() self.group_list.clear() if self.api is None: return groups = self.api(self.gateway.get_groups()) if len(groups) == 0: self.group_list.setEnabled(False) # TODO: load devices directly for group in groups: item = self.api(group) list_item = QListWidgetItem(item.name, self.group_list) setattr(list_item, 'api_item', item) def group_selected(self): current_item = self.group_list.currentItem() item = getattr(current_item, 'api_item', None) if item is None: return # refresh from gateway item = self.api(self.gateway.get_group(item.id)) # load moods self.mood_list.clear() moods = self.api(item.moods()) for m in moods: mood = self.api(m) list_item = QListWidgetItem(mood.name, self.mood_list) setattr(list_item, 'api_item', mood) # load devices devices = item.members() self.device_list.clear() # determine shared state and add devices to list state = False color_temp = False min_color = 10000 max_color = 0 color = [] brightness = [] for d in devices: device = self.api(d) if device.has_light_control: if device.light_control.lights[0].state: state = True if device.light_control.can_set_dimmer: if device.light_control.lights[0].state: brightness.append( device.light_control.lights[0].dimmer) else: brightness.append(0) if device.light_control.can_set_temp: color_temp = True min_color = min(min_color, device.light_control.min_mireds) max_color = max(max_color, device.light_control.max_mireds) color.append(device.light_control.lights[0].color_temp) list_item = QListWidgetItem(device.name, self.device_list) setattr(list_item, 'api_item', device) if len(brightness) > 0: brightness = int(sum(brightness) / len(brightness)) else: brightness = 0 if len(color) > 0: color = int(sum(color) / len(color)) else: color = min_color # enable device list and controls self.device_list.setEnabled(True) self.group_brightness_slider.setEnabled(True) self.group_brightness_slider.setMinimum(0) self.group_brightness_slider.setMaximum(254) self.group_brightness_slider.setSingleStep(16) self.group_brightness_slider.setValue(brightness) if color_temp: self.group_color_slider.setEnabled(True) self.group_color_slider.setMinimum(min_color) self.group_color_slider.setMaximum(max_color) self.group_color_slider.setSingleStep( int((max_color - min_color) / 10)) self.group_color_slider.setValue(color) else: self.group_color_slider.setEnabled(False) self.group_toggle.setEnabled(True) try: self.group_toggle.stateChanged.disconnect(self.group_toggled) except RuntimeError: pass # Disconnect failed because nothing was connected self.group_toggle.setCheckState( Qt.CheckState.Checked if state else Qt.CheckState.Unchecked) self.group_toggle.stateChanged.connect(self.group_toggled) self.brightness_slider.setEnabled(False) self.color_slider.setEnabled(False) self.device_toggle.setEnabled(False) def device_selected(self): current_item = self.device_list.currentItem() item = getattr(current_item, 'api_item', None) if item is None: return # refresh from gateway item = self.api(self.gateway.get_device(item.id)) # enable appropriate controls if item.has_light_control: ctrl = item.light_control if ctrl.can_set_dimmer: self.brightness_slider.setEnabled(True) self.brightness_slider.setMinimum(0) self.brightness_slider.setMaximum(254) self.brightness_slider.setSingleStep(16) self.brightness_slider.setValue(ctrl.lights[0].dimmer) else: self.brightness_slider.setEnabled(False) if ctrl.can_set_temp: self.color_slider.setEnabled(True) self.color_slider.setMinimum(ctrl.min_mireds) self.color_slider.setMaximum(ctrl.max_mireds) self.color_slider.setSingleStep( int((ctrl.max_mireds - ctrl.min_mireds) / 10)) self.color_slider.setValue(ctrl.lights[0].color_temp) else: self.color_slider.setEnabled(False) self.device_toggle.setEnabled(True) try: self.device_toggle.stateChanged.disconnect(self.device_toggled) except RuntimeError: pass # disconnect failed because nothing was connected self.device_toggle.setCheckState( Qt.CheckState.Checked if ctrl.lights[0].state else Qt. CheckState.Unchecked) self.device_toggle.stateChanged.connect(self.device_toggled) else: self.brightness_slider.setEnabled(False) self.color_slider.setEnabled(False) self.device_toggle.setEnabled(False) def mood_selected(self): current_group = self.group_list.currentItem() group = getattr(current_group, 'api_item', None) if group is None: return # refresh from gateway group = self.api(self.gateway.get_group(group.id)) current_mood = self.mood_list.currentItem() mood = getattr(current_mood, 'api_item', None) if mood is None: return self.api(group.activate_mood(mood.id)) def group_brightness_changed(self): current_item = self.group_list.currentItem() if current_item is None: return item = getattr(current_item, 'api_item', None) command = item.set_dimmer(self.group_brightness_slider.value(), transition_time=2) self.queue_command('group_brightness', command) def group_color_changed(self): current_item = self.group_list.currentItem() if current_item is None: return item = getattr(current_item, 'api_item', None) command = item.set_color_temp(self.group_color_slider.value(), transition_time=2) self.queue_command('group_color', command) def brightness_changed(self): current_item = self.device_list.currentItem() if current_item is None: return item = getattr(current_item, 'api_item', None) command = item.light_control.set_dimmer(self.brightness_slider.value(), transition_time=2) self.queue_command('device_brightness_{}'.format(item.id), command) def color_changed(self): current_item = self.device_list.currentItem() if current_item is None: return item = getattr(current_item, 'api_item', None) command = item.light_control.set_color_temp(self.color_slider.value(), transition_time=2) self.queue_command('device_color_{}'.format(item.id), command) def device_toggled(self): current_item = self.device_list.currentItem() if current_item is None: return item = getattr(current_item, 'api_item', None) command = item.light_control.set_state( self.device_toggle.checkState() == Qt.CheckState.Checked) self.api(command) def group_toggled(self): current_item = self.group_list.currentItem() if current_item is None: return item = getattr(current_item, 'api_item', None) command = item.set_state( self.group_toggle.checkState() == Qt.CheckState.Checked) self.api(command) def settings_pressed(self): config = ConfigWindow(self.appctx, self) config.setWindowModality(Qt.ApplicationModal) config.exec_() # reload settings self.settings = config.settings # re-initialize window self.re_init() def queue_command(self, name, command): timer = self.timers.get(name, None) if timer is None: timer = QTimer() timer.setInterval(200) timer.setSingleShot(True) timer.timeout.connect(self.timeout) timer.start() setattr(timer, 'command', command) self.timers[name] = timer def timeout(self): remove = [] for key, item in self.timers.items(): if item.isActive() == False: cmd = getattr(item, 'command') self.api(cmd) remove.append(key) for key in remove: del self.timers[key]
base_name + "/" + str(blind.id) + battery_name, blind.device_info.battery_level) if blind.blind_control.blinds[ 0].current_cover_position >= 50.0: cp = ON else: cp = OFF if cp: client.publish( base_name + "/" + str(blind.id) + status_name, cp) else: print("No devices") client.publish("esp/text", "tradfri: no devices") try: groups_command = gateway.get_groups() groups_commands = api(groups_command) groups = api(groups_commands) #print(groups) last_time = time.time() except: client.disconnect() client.reconnect() client.publish("esp/text", "tradfri: error while getting groups") print("error groups") continue #pass time.sleep(SLEEP) if groups: print("Id\tState\tDimmer\tName")
async def async_setup_entry( hass: HomeAssistant, entry: ConfigEntry, ) -> bool: """Create a gateway.""" tradfri_data: dict[str, Any] = {} hass.data.setdefault(DOMAIN, {})[entry.entry_id] = tradfri_data listeners = tradfri_data[LISTENERS] = [] factory = await APIFactory.init( entry.data[CONF_HOST], psk_id=entry.data[CONF_IDENTITY], psk=entry.data[CONF_KEY], ) tradfri_data[FACTORY] = factory # Used for async_unload_entry async def on_hass_stop(event: Event) -> None: """Close connection when hass stops.""" await factory.shutdown() # Setup listeners listeners.append( hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, on_hass_stop)) api = factory.request gateway = Gateway() groups: list[Group] = [] try: gateway_info = await api(gateway.get_gateway_info(), timeout=TIMEOUT_API) devices_commands: Command = await api(gateway.get_devices(), timeout=TIMEOUT_API) devices: list[Device] = await api(devices_commands, timeout=TIMEOUT_API) if entry.data[CONF_IMPORT_GROUPS]: # Note: we should update this page when deprecating: # https://www.home-assistant.io/integrations/tradfri/ _LOGGER.warning( "Importing of Tradfri groups has been deprecated due to stability issues " "and will be removed in Home Assistant core 2022.4") # No need to load groups if the user hasn't requested it groups_commands: Command = await api(gateway.get_groups(), timeout=TIMEOUT_API) groups = await api(groups_commands, timeout=TIMEOUT_API) except RequestError as exc: await factory.shutdown() raise ConfigEntryNotReady from exc dev_reg = await hass.helpers.device_registry.async_get_registry() dev_reg.async_get_or_create( config_entry_id=entry.entry_id, connections=set(), identifiers={(DOMAIN, entry.data[CONF_GATEWAY_ID])}, manufacturer=ATTR_TRADFRI_MANUFACTURER, name=ATTR_TRADFRI_GATEWAY, # They just have 1 gateway model. Type is not exposed yet. model=ATTR_TRADFRI_GATEWAY_MODEL, sw_version=gateway_info.firmware_version, ) remove_stale_devices(hass, entry, devices) # Setup the device coordinators coordinator_data = { CONF_GATEWAY_ID: gateway, KEY_API: api, COORDINATOR_LIST: [], GROUPS_LIST: [], } for device in devices: coordinator = TradfriDeviceDataUpdateCoordinator(hass=hass, api=api, device=device) await coordinator.async_config_entry_first_refresh() entry.async_on_unload( async_dispatcher_connect(hass, SIGNAL_GW, coordinator.set_hub_available)) coordinator_data[COORDINATOR_LIST].append(coordinator) for group in groups: group_coordinator = TradfriGroupDataUpdateCoordinator(hass=hass, api=api, group=group) await group_coordinator.async_config_entry_first_refresh() entry.async_on_unload( async_dispatcher_connect(hass, SIGNAL_GW, group_coordinator.set_hub_available)) coordinator_data[GROUPS_LIST].append(group_coordinator) tradfri_data[COORDINATOR] = coordinator_data async def async_keep_alive(now: datetime) -> None: if hass.is_stopping: return gw_status = True try: await api(gateway.get_gateway_info()) except RequestError: _LOGGER.error("Keep-alive failed") gw_status = False async_dispatcher_send(hass, SIGNAL_GW, gw_status) listeners.append( async_track_time_interval(hass, async_keep_alive, timedelta(seconds=60))) hass.config_entries.async_setup_platforms(entry, PLATFORMS) return True
class TradfriManager(metaclass=Singleton): api_factory = None api = None gateway = None enabled = False initialized = False last_init = 0 def __init__(self): self.tradfri_state = TradfriState() self.observing_end = 0 self.observing = False self.observe_thread = None def init(self): if self.initialized: Logger().write(LogVerbosity.Info, "already init") return if sys.platform != "linux" and sys.platform != "linux2": Logger().write(LogVerbosity.Info, "Lighting: Not initializing, no coap client available on windows") self.initialized = True self.tradfri_state.update_group(DeviceGroup(1, "Test group", True, 128, 6)) self.tradfri_state.update_group(DeviceGroup(2, "Test group 2", False, 18, 6)) return if current_time() - self.last_init < 5000: Logger().write(LogVerbosity.Info, "Tried initialization less than 5s ago") return Logger().write(LogVerbosity.All, "Start LightManager init") self.enabled = True if not self.initialized: ip = Settings.get_string("tradfri_hub_ip") identity = Database().get_stat_string("LightingId") key = Database().get_stat_string("LightingKey") if identity is None or key is None: Logger().write(LogVerbosity.Info, "Lighting: No identity/key found, going to generate new") # We don't have all information to connect, reset and start from scratch Database().remove_stat("LightingId") Database().remove_stat("LightingKey") key = None identity = uuid.uuid4().hex Database().update_stat("LightingId", identity) # Generate and save a new id self.api_factory = APIFactory(host=ip, psk_id=identity) else: self.api_factory = APIFactory(host=ip, psk_id=identity, psk=key) self.api = self.api_factory.request self.gateway = Gateway() if key is None: try: security_code = SecureSettings.get_string("tradfri_hub_code") # the code at the bottom of the hub key = self.api_factory.generate_psk(security_code) Database().update_stat("LightingKey", key) # Save the new key Logger().write(LogVerbosity.Info, "Lighting: New key retrieved") except Exception as e: Logger().write_error(e, "Unhandled exception") return else: Logger().write(LogVerbosity.Info, "Lighting: Previously saved key found") try: self.initialized = True groups = self.get_device_groups() for group in groups: self.tradfri_state.update_group(group) except Exception as e: Logger().write(LogVerbosity.Info, "Failed to init tradfri, clearing previous key to try generate new") self.initialized = False Database().remove_stat("LightingId") Database().remove_stat("LightingKey") Logger().write_error(e, "Failed to get groups from hub") def start_observing(self): Logger().write(LogVerbosity.Debug, "Start observing light data") self.observing = True if self.observing_end > current_time(): Logger().write(LogVerbosity.All, "Still observing, not starting again") return # still observing, the check observing thread will renew if not self.check_state(): return self.observing_end = current_time() + 30000 groups_commands = self.api(self.gateway.get_groups()) result = self.api(groups_commands) for group in result: self.observe_group(group) def observe_group(self, group): Logger().write(LogVerbosity.All, "Starting observe for group " + group.name) self.observe_thread = CustomThread(lambda: self.api(group.observe( self.tradfri_state.update_group, lambda x: self.check_observe(group), duration=30)), "Light group observer", []) self.observe_thread.start() def check_observe(self, group): if self.observing: # Restart observing since it timed out Logger().write(LogVerbosity.Debug, "Restarting observing for group " + str(group.name)) self.observe_group(group) def stop_observing(self): Logger().write(LogVerbosity.Debug, "Stop observing light data") self.observing = False def get_devices(self): if not self.check_state(): return [] Logger().write(LogVerbosity.All, "Get devices") devices_commands = self.api(self.gateway.get_devices()) devices = self.api(devices_commands) return [d for d in [self.parse_device(x) for x in devices] if d is not None] def set_state(self, device_id, state): if not self.check_state(): return Logger().write(LogVerbosity.All, "Set device state") device = self.api(self.gateway.get_device(device_id)) if device.has_light_control: self.api(device.light_control.set_state(state)) else: self.api(device.socket_control.set_state(state)) def set_light_warmth(self, device_id, warmth): if not self.check_state(): return Logger().write(LogVerbosity.All, "Set light warmth") device = self.api(self.gateway.get_device(device_id)) self.api(device.light_control.set_color_temp(warmth)) def set_light_dimmer(self, device_id, amount): if not self.check_state(): return Logger().write(LogVerbosity.All, "Set light dimmer") device = self.api(self.gateway.get_device(device_id)) self.api(device.light_control.set_dimmer(amount)) def set_device_name(self, device_id, name): if not self.check_state(): return Logger().write(LogVerbosity.All, "Set device name") device = self.api(self.gateway.get_device(device_id)) self.api(device.set_name(name)) def get_device_groups(self): if not self.check_state(): return [] Logger().write(LogVerbosity.All, "Get device groups") groups_commands = self.api(self.gateway.get_groups()) result = self.api(groups_commands) Logger().write(LogVerbosity.All, "Get device groups: " + str([x.raw for x in result])) return [DeviceGroup(group.id, group.name, group.state, group.dimmer, len(group.member_ids)) for group in result] def get_devices_in_group(self, group): if not self.check_state(): return [] Logger().write(LogVerbosity.All, "Get lights in group") group = self.api(self.gateway.get_group(group)) members = group.member_ids result = [self.api(self.gateway.get_device(x)) for x in members] return [d for d in [self.parse_device(x) for x in result] if d is not None] def set_group_name(self, group, name): if not self.check_state(): return Logger().write(LogVerbosity.All, "Set group name") group = self.api(self.gateway.get_group(group)) self.api(group.set_name(name)) def set_group_state(self, group, state): if not self.check_state(): return Logger().write(LogVerbosity.All, "Set group state") group = self.api(self.gateway.get_group(group)) self.api(group.set_state(state)) def set_group_dimmer(self, group, dimmer): if not self.check_state(): return Logger().write(LogVerbosity.All, "Set group dimmer") group = self.api(self.gateway.get_group(group)) self.api(group.set_dimmer(dimmer)) def check_state(self): if not self.enabled: return False # not available if not self.initialized: self.init() if not self.initialized: return False # init failed return True @staticmethod def parse_device(data): if data.has_light_control: lights = [] for light in data.light_control.lights: lights.append(LightDevice( light.state, light.dimmer, light.color_temp, light.hex_color)) return LightControl(data.id, data.name, data.application_type, data.last_seen.timestamp(), data.reachable, data.light_control.can_set_dimmer, data.light_control.can_set_temp, data.light_control.can_set_color, lights) elif data.has_socket_control: sockets = [] for socket in data.socket_control.sockets: sockets.append(SocketDevice(socket.state)) return SocketControl(data.id, data.name, data.application_type, data.last_seen.timestamp(), data.reachable, sockets)