def check_active_time(): service_enabled = CACHE.get(f"{ADDONID}.service_enabled") daylight = CACHE.get("script.service.hue.daylight") # xbmc.log("[script.service.hue] Schedule: {}, daylightDisable: {}, daylight: {}, startTime: {}, endTime: {}".format(ADDON.getSettingBool("enableSchedule"), ADDON.getSettingBool("daylightDisable"), daylight, # ADDON.getSettingBool("startTime"), ADDON.getSettingBool("endTime"))) if ADDON.getSettingBool("daylightDisable") and daylight: xbmc.log("[script.service.hue] Disabled by daylight") return False if service_enabled: if ADDON.getSettingBool("enableSchedule"): start = convert_time(ADDON.getSettingString("startTime")) end = convert_time(ADDON.getSettingString("endTime")) now = datetime.datetime.now().time() if (now > start) and (now < end): # xbmc.log("[script.service.hue] Enabled by schedule") return True # xbmc.log("[script.service.hue] Disabled by schedule") return False # xbmc.log("[script.service.hue] Schedule not enabled") return True # xbmc.log("[script.service.hue] Service disabled") return False
def _error_report_dialog(exc): response = xbmcgui.Dialog().yesnocustom(heading=_("Hue Service Error"), message=_("The following error occurred:") + f"\n[COLOR=red]{exc}[/COLOR]\n" + _("Automatically report this error?"), customlabel=_("Never report errors")) if response == 2: xbmc.log("[script.service.hue] Error Reporting disabled") ADDON.setSettingBool("error_reporting", False) return False return response
def menu(): route = sys.argv[0] addon_handle = int(sys.argv[1]) base_url = sys.argv[0] command = sys.argv[2][1:] parsed = parse_qs(command) logger.debug( "Menu started. route: {}, handle: {}, command: {}, parsed: {}, Arguments: {}" .format(route, addon_handle, command, parsed, sys.argv)) if route == "plugin://script.service.hue/": if not command: build_menu(base_url, addon_handle) elif command == "settings": logger.debug("Opening settings") ADDON.openSettings() elif command == "toggle": if cache.get("script.service.hue.service_enabled" ) and get_status() != "Disabled by daylight": logger.info("Disable service") cache.set("script.service.hue.service_enabled", False) elif get_status() != "Disabled by daylight": logger.info("Enable service") cache.set("script.service.hue.service_enabled", True) else: logger.info("Disabled by daylight, ignoring") xbmc.executebuiltin('Container.Refresh') elif route == "plugin://script.service.hue/actions": action = parsed['action'][0] kgroupid = parsed['kgroupid'][0] logger.debug("Actions: {}, kgroupid: {}".format(action, kgroupid)) if action == "menu": items = [ (base_url + "?action=play&kgroupid=" + kgroupid, ListItem(_("Play"))), (base_url + "?action=pause&kgroupid=" + kgroupid, ListItem(_("Pause"))), (base_url + "?action=stop&kgroupid=" + kgroupid, ListItem(_("Stop"))), ] xbmcplugin.addDirectoryItems(addon_handle, items, len(items)) xbmcplugin.endOfDirectory(handle=addon_handle, cacheToDisc=True) else: cache.set("script.service.hue.action", (action, kgroupid), expiration=(timedelta(seconds=5))) else: logger.error( "Unknown command. Handle: {}, route: {}, Arguments: {}".format( addon_handle, route, sys.argv))
def _validate_ambilight(): logger.debug("Validate ambilight config. Enabled: {}".format( settings_storage['ambiEnabled'])) if settings_storage['ambiEnabled']: if settings_storage['zones'] == "0": light_ids = ADDON.getSetting("group3_Lights") if light_ids == "-1": logger.error("No ambilights selected") xbmcgui.Dialog().notification( _("Hue Service"), _("No lights selected for Ambilight."), icon=xbmcgui.NOTIFICATION_ERROR) ADDON.setSettingBool("group3_enabled", False) settings_storage['ambiEnabled'] = False if settings_storage['zones'] == "1": light_idsLeft = ADDON.getSetting("group3_LightsLeft") if light_idsLeft == "-1": logger.error("No ambilights selected for left zone") xbmcgui.Dialog().notification( _("Hue Service"), _("No lights selected for Ambilight in left zone."), icon=xbmcgui.NOTIFICATION_ERROR) ADDON.setSettingBool("group3_enabled", False) settings_storage['ambiEnabled'] = False light_idsRight = ADDON.getSetting("group3_LightsRight") if light_idsRight == "-1": logger.error("No ambilights selected for right zone") xbmcgui.Dialog().notification( _("Hue Service"), _("No lights selected for Ambilight in right zone."), icon=xbmcgui.NOTIFICATION_ERROR) ADDON.setSettingBool("group3_enabled", False) settings_storage['ambiEnabled'] = False
def __init__(self, monitor, silent=True, discover=False): self.bridge = None self.bridge_ip = ADDON.getSettingString("bridgeIP") self.bridge_user = ADDON.getSettingString("bridgeUser") self.monitor = monitor self.connected = False if discover: self.discover_bridge() else: self.connect_bridge(silent)
def connect_bridge(self, silent=False): xbmc.log( f"[script.service.hue] in connect_bridge() with settings: bridgeIP: {self.bridge_ip}, bridgeUser: {self.bridge_user}" ) if self.bridge_ip and self.bridge_user: if not self._check_version(): xbmc.log( "[script.service.hue] in connect_bridge(): Bridge not responding to connection test, attempt finding a new bridge IP." ) if self._discover_bridge_ip(): xbmc.log( f"[script.service.hue] in connect_bridge(): New IP found: {self.bridge_ip}. Saving" ) ADDON.setSettingString("bridgeIP", self.bridge_ip) else: xbmc.log("[script.service.hue] Bridge not found") notification(_("Hue Service"), _("Bridge connection failed"), icon=xbmcgui.NOTIFICATION_ERROR) self.connected = False return xbmc.log("[script.service.hue] in Connect(): Checking User") if self._check_user(): bridge = qhue.Bridge(self.bridge_ip, self.bridge_user, timeout=QHUE_TIMEOUT) self.connected = True self.bridge = bridge xbmc.log( f"[script.service.hue] Successfully connected to Hue Bridge: {self.bridge_ip}" ) if not silent: notification(_("Hue Service"), _("Hue connected"), sound=False) return else: xbmc.log("[script.service.hue] Bridge not responding") notification(_("Hue Service"), _("Bridge connection failed"), icon=xbmcgui.NOTIFICATION_ERROR) self.connected = False else: xbmc.log("[script.service.hue] Bridge not configured") notification(_("Hue Service"), _("Bridge not configured"), icon=xbmcgui.NOTIFICATION_ERROR) self.connected = False
def _validate_ambilight(): xbmc.log( f"[script.service.hue] Validate ambilight config. Enabled: {ADDON.getSettingBool('group3_enabled')}" ) if ADDON.getSettingBool("group3_enabled"): light_ids = ADDON.getSetting("group3_Lights") if light_ids == "-1": ADDON.setSettingBool("group3_enabled", False) xbmc.log("[script.service.hue] No ambilights selected") notification(_("Hue Service"), _("No lights selected for Ambilight."), icon=xbmcgui.NOTIFICATION_ERROR)
def _bridge_error500(self): self.bridge_error500 = self.bridge_error500 + 1 # increment counter if self.bridge_error500 > 50 and ADDON.getSettingBool("show500Error"): AMBI_RUNNING.clear() # shut it down stop_showing_error = xbmcgui.Dialog().yesno( _("Hue Bridge over capacity"), _("The Hue Bridge is over capacity. Increase refresh rate or reduce the number of Ambilights." ), yeslabel=_("Do not show again"), nolabel=_("Ok")) if stop_showing_error: ADDON.setSettingBool("show500Error", False) self.bridge_error500 = 0
def _validate_schedule(): xbmc.log( f"[script.service.hue] Validate schedule. Schedule Enabled: {ADDON.getSettingBool('enableSchedule')}" ) if ADDON.getSettingBool("enableSchedule"): try: convert_time(ADDON.getSettingString("startTime")) convert_time(ADDON.getSettingString("endTime")) # xbmc.log("[script.service.hue] Time looks valid") except ValueError as e: ADDON.setSettingBool("EnableSchedule", False) xbmc.log(f"[script.service.hue] Invalid time settings: {e}") notification(_("Hue Service"), _("Invalid start or end time, schedule disabled"), icon=xbmcgui.NOTIFICATION_ERROR)
def check_keep_lights_off_rule(self, scene): if not scene: return True xbmc.log( f"[script.service.hue] Check if lights should stay off, settings: enable {ADDON.getSettingBool('keep_lights_off')}" ) if ADDON.getSettingBool("keep_lights_off"): try: scene_data = self.bridge.scenes[scene]() for light in scene_data["lights"]: l = self.bridge.lights[light]() if l["state"][ "on"] is False: # one light is off, the scene should not be applied xbmc.log( "[script.service.hue] Check if lights should stay off: True" ) return False xbmc.log( "[script.service.hue] Check if lights should stay off: False" ) except QhueException as exc: xbmc.log( f"[script.service.hue] checkKeepLightsOffRule: Hue call fail: {exc.type_id}: {exc.message} {traceback.format_exc()}" ) reporting.process_exception(exc) except requests.RequestException as exc: xbmc.log(f"[script.service.hue] Requests exception: {exc}") notification(header=_("Hue Service"), message=_(f"Connection Error"), icon=xbmcgui.NOTIFICATION_ERROR) return True
def check_already_active(self, scene): if not scene: return False xbmc.log( f"[script.service.hue] Check if scene light already active, settings: enable {ADDON.getSettingBool('enable_if_already_active')}" ) if ADDON.getSettingBool("enable_if_already_active"): try: scene_data = self.bridge.scenes[scene]() for light in scene_data["lights"]: l = self.bridge.lights[light]() if l["state"][ "on"]: # one light is on, the scene can be applied # xbmc.log("[script.service.hue] Check if scene light already active: True") return True # xbmc.log("[script.service.hue] Check if scene light already active: False") except QhueException as exc: if ["7", "3"] in exc.type_id: xbmc.log("[script.service.hue] Scene not found") notification(_("Hue Service"), _("ERROR: Scene not found"), icon=xbmcgui.NOTIFICATION_ERROR) else: xbmc.log( f"[script.service.hue] checkAlreadyActive: Hue call fail: {exc.type_id}: {exc.message} {traceback.format_exc()}" ) reporting.process_exception(exc) except requests.RequestException as exc: xbmc.log(f"[script.service.hue] Requests exception: {exc}") notification(header=_("Hue Service"), message=_(f"Connection Error"), icon=xbmcgui.NOTIFICATION_ERROR) return False
def _validate_schedule(): logger.debug("Validate schedule. Schedule Enabled: {}".format( settings_storage['enableSchedule'])) if settings_storage['enableSchedule']: try: convert_time(settings_storage['startTime']) convert_time(settings_storage['endTime']) logger.debug("Time looks valid") except ValueError as e: logger.error("Invalid time settings: {}".format(e)) xbmcgui.Dialog().notification( _("Hue Service"), _("Invalid start or end time, schedule disabled"), icon=xbmcgui.NOTIFICATION_ERROR) ADDON.setSettingBool("EnableSchedule", False) settings_storage['enableSchedule'] = False
def __init__(self, light_group_id, hue_connection, media_type, initial_state=STATE_STOPPED, video_info_tag=xbmc.InfoTagVideo): self.light_group_id = light_group_id self.bridge = hue_connection.bridge self.enabled = ADDON.getSettingBool( f"group{self.light_group_id}_enabled") self.start_behavior = ADDON.getSettingBool( f"group{self.light_group_id}_startBehavior") self.start_scene = ADDON.getSettingString( f"group{self.light_group_id}_startSceneID") self.pause_behavior = ADDON.getSettingBool( f"group{self.light_group_id}_pauseBehavior") self.pause_scene = ADDON.getSettingString( f"group{self.light_group_id}_pauseSceneID") self.stop_behavior = ADDON.getSettingBool( f"group{self.light_group_id}_stopBehavior") self.stop_scene = ADDON.getSettingString( f"group{self.light_group_id}_stopSceneID") self.state = initial_state self.media_type = media_type self.video_info_tag = video_info_tag self.last_media_type = self.media_type self.lights = self.bridge.lights self.group0 = self.bridge.groups[0] if self.enabled: super().__init__()
def _get_status_icon(): enabled = CACHE.get(f"{ADDONID}.service_enabled") daylight = CACHE.get(f"{ADDONID}.daylight") daylight_disable = ADDON.getSettingBool("daylightDisable") # xbmc.log("[script.service.hue] Current status: {}".format(daylight_disable)) if daylight and daylight_disable: return xbmcvfs.makeLegalFilename(ADDONPATH + "resources/icons/daylight.png") # Disabled by Daylight elif enabled: return xbmcvfs.makeLegalFilename(ADDONPATH + "resources/icons/enabled.png") # Enabled return xbmcvfs.makeLegalFilename(ADDONPATH + "resources/icons/disabled.png") # Disabled
def _get_status(): enabled = CACHE.get(f"{ADDONID}.service_enabled") daylight = CACHE.get(f"{ADDONID}.daylight") daylight_disable = ADDON.getSettingBool("daylightDisable") # xbmc.log("[script.service.hue] Current status: {}".format(daylight_disable)) if daylight and daylight_disable: return _("Disabled by daylight") elif enabled: return _("Enabled") return _("Disabled")
def get_string(t): string_id = _strings.get(t.lower()) if not string_id: xbmc.log(f"[script.service.hue] LANGUAGE: missing translation for '{t.lower()}'") return t if STRDEBUG: return f"STR:{string_id} {ADDON.getLocalizedString(string_id)}" return ADDON.getLocalizedString(string_id)
def menu(): route = sys.argv[0] addon_handle = int(sys.argv[1]) base_url = sys.argv[0] command = sys.argv[2][1:] parsed = parse_qs(command) if route == f"plugin://{ADDONID}/": if not command: build_menu(base_url, addon_handle) elif command == "settings": ADDON.openSettings() elif command == "toggle": if CACHE.get(f"{ADDONID}.service_enabled") and _get_status() != "Disabled by daylight": xbmc.log("[script.service.hue] Disable service") CACHE.set(f"{ADDONID}.service_enabled", False) elif _get_status() != "Disabled by daylight": xbmc.log("[script.service.hue] Enable service") CACHE.set(f"{ADDONID}.service_enabled", True) else: xbmc.log("[script.service.hue] Disabled by daylight, ignoring") xbmc.executebuiltin('Container.Refresh') elif route == f"plugin://{ADDONID}/actions": action = parsed['action'][0] light_group_id = parsed['light_group_id'][0] xbmc.log(f"[script.service.hue] Actions: {action}, light_group_id: {light_group_id}") if action == "menu": xbmcplugin.addDirectoryItem(addon_handle, base_url + "?action=play&light_group_id=" + light_group_id, ListItem(_("Play"))) xbmcplugin.addDirectoryItem(addon_handle, base_url + "?action=pause&light_group_id=" + light_group_id, ListItem(_("Pause"))) xbmcplugin.addDirectoryItem(addon_handle, base_url + "?action=stop&light_group_id=" + light_group_id, ListItem(_("Stop"))) xbmcplugin.endOfDirectory(handle=addon_handle, cacheToDisc=True) else: CACHE.set(f"{ADDONID}.action", (action, light_group_id), expiration=(timedelta(seconds=5))) else: xbmc.log(f"[script.service.hue] Unknown command. Handle: {addon_handle}, route: {route}, Arguments: {sys.argv}")
def configure_ambilights(self, group_id): lights = self.select_hue_lights() light_names = [] color_lights = [] if lights is not None: for L in lights: light_names.append(self._get_light_name(L)) color_lights.append(L) ADDON.setSettingString(f"group{group_id}_Lights", ','.join(color_lights)) ADDON.setSettingString(f"group{group_id}_LightNames", ', '.join(light_names)) ADDON.setSettingBool(f"group{group_id}_enabled", True) ADDON.openSettings()
def activate(light_groups): """ Activates play action as appropriate for all groups. Used at sunset and when service is re-enabled via Actions. """ xbmc.log( f"[script.service.hue] Activating scenes: light_groups: {light_groups}" ) for g in light_groups: xbmc.log( f"[script.service.hue] in activate g: {g}, light_group_id: {g.light_group_id}" ) if ADDON.getSettingBool(f"group{g.light_group_id}_enabled"): g.activate()
def configure_scene(self, group_id, action): scene = self.select_hue_scene() if scene is not None: # group0_startSceneID ADDON.setSettingString(f"group{group_id}_{action}SceneID", scene[0]) ADDON.setSettingString(f"group{group_id}_{action}SceneName", scene[1]) ADDON.openSettings()
def check_video_activation(self, info_tag): try: duration = info_tag.getDuration( ) / 60 # returns seconds, convert to minutes media_type = info_tag.getMediaType() file_name = info_tag.getFile() if not file_name and self.isPlayingVideo(): file_name = self.getPlayingFile() # # if not fileName and previousFileName: # fileName = previousFileName # elif fileName: # previousFileName = fileName # xbmc.log("[script.service.hue] InfoTag contents: duration: {}, mediaType: {}, file: {}".format(duration, mediaType, fileName)) except (AttributeError, TypeError) as exc: xbmc.log("[script.service.hue] Can't read infoTag {exc}") return False # xbmc.log("Video Activation settings({}): minDuration: {}, Movie: {}, Episode: {}, MusicVideo: {}, PVR : {}, Other: {}".format(self.light_group_id, settings_storage['videoMinimumDuration'], settings_storage['video_enableMovie'], # settings_storage['video_enableEpisode'], settings_storage['video_enableMusicVideo'], settings_storage['video_enablePVR'], settings_storage['video_enableOther'])) # xbmc.log("[script.service.hue] Video Activation ({}): Duration: {}, mediaType: {}, ispvr: {}".format(self.light_group_id, duration, mediaType, fileName[0:3] == "pvr")) if ((duration >= ADDON.getSettingInt("video_MinimumDuration") or file_name[0:3] == "pvr") and ((ADDON.getSettingBool("video_Movie") and media_type == "movie") or (ADDON.getSettingBool("video_Episode") and media_type == "episode") or (ADDON.getSettingBool("video_MusicVideo") and media_type == "MusicVideo") or (ADDON.getSettingBool("video_PVR") and file_name[0:3] == "pvr") or (ADDON.getSettingBool("video_Other") and media_type != "movie" and media_type != "episode" and media_type != "MusicVideo" and file_name[0:3] != "pvr"))): xbmc.log("[script.service.hue] Video activation: True") return True xbmc.log("[script.service.hue] Video activation: False") return False
def __init__(self, light_group_id, hue_connection, initial_state=STATE_STOPPED, video_info_tag=xbmc.InfoTagVideo): self.hue_connection = hue_connection self.light_group_id = light_group_id self.bridge = hue_connection.bridge self.monitor = hue_connection.monitor self.group0 = self.bridge.groups[0] self.bridge_error500 = 0 self.state = initial_state self.enabled = ADDON.getSettingBool( f"group{self.light_group_id}_enabled") self.saved_light_states = {} self.video_info_tag = video_info_tag self.image_process = imageprocess.ImageProcess() self.converterA = Converter(GamutA) self.converterB = Converter(GamutB) self.converterC = Converter(GamutC) self.helper = ColorHelper( GamutC) # Gamut doesn't matter for this usage self.enabled = ADDON.getSettingBool( f"group{self.light_group_id}_enabled") self.transition_time = int( ADDON.getSettingInt(f"group{self.light_group_id}_TransitionTime") / 100 ) # This is given as a multiple of 100ms and defaults to 4 (400ms). transition_time:10 will make the transition last 1 second. self.force_on = ADDON.getSettingBool( f"group{self.light_group_id}_forceOn") self.disable_labs = ADDON.getSettingBool( f"group{self.light_group_id}_disableLabs") self.min_bri = ADDON.getSettingInt( f"group{self.light_group_id}_MinBrightness" ) * 255 / 100 # convert percentage to value 1-254 self.max_bri = ADDON.getSettingInt( f"group{self.light_group_id}_MaxBrightness" ) * 255 / 100 # convert percentage to value 1-254 self.saturation = ADDON.getSettingNumber( f"group{self.light_group_id}_Saturation") self.capture_size_x = ADDON.getSettingInt( f"group{self.light_group_id}_CaptureSize") self.resume_state = ADDON.getSettingBool( f"group{self.light_group_id}_ResumeState") self.resume_transition = ADDON.getSettingInt( f"group{self.light_group_id}_ResumeTransition" ) * 10 # convert seconds to multiple of 100ms self.update_interval = ADDON.getSettingInt( f"group{self.light_group_id}_Interval" ) / 1000 # convert MS to seconds if self.update_interval == 0: self.update_interval = 0.1 if self.enabled: self.ambi_lights = {} light_ids = ADDON.getSetting( f"group{self.light_group_id}_Lights").split(",") index = 0 if len(light_ids) > 0: for L in light_ids: gamut = self._get_light_gamut(self.bridge, L) light = { L: { 'gamut': gamut, 'prev_xy': (0, 0), "index": index } } self.ambi_lights.update(light) index = index + 1 super(xbmc.Player).__init__()
def process_exception(exc, level="critical", error=""): if ADDON.getSettingBool("error_reporting"): if _error_report_dialog(exc): _report_error(level, error, exc)
def read_settings(): settings_storage['disable_connection_message'] = ADDON.getSettingBool( "disableConnectionMessage") settings_storage['reloadFlash'] = ADDON.getSettingBool("reloadFlash") settings_storage['initialFlash'] = ADDON.getSettingBool("initialFlash") settings_storage['forceOnSunset'] = ADDON.getSettingBool("forceOnSunset") settings_storage['daylightDisable'] = ADDON.getSettingBool( "daylightDisable") cache.set("script.service.hue.daylightDisable", ADDON.getSettingBool("daylightDisable")) settings_storage['enableSchedule'] = ADDON.getSettingBool("enableSchedule") settings_storage['startTime'] = ADDON.getSetting( "startTime") # string HH:MM settings_storage['endTime'] = ADDON.getSetting("endTime") # string HH:MM settings_storage['disableConnectionMessage'] = ADDON.getSettingBool( "disableConnectionMessage") settings_storage['videoMinimumDuration'] = ADDON.getSettingInt( "video_MinimumDuration" ) # Setting in Minutes. Kodi library uses seconds, needs to be converted. settings_storage['video_enableMovie'] = ADDON.getSettingBool("video_Movie") settings_storage['video_enableMusicVideo'] = ADDON.getSettingBool( "video_MusicVideo") settings_storage['video_enableEpisode'] = ADDON.getSettingBool( "video_Episode") settings_storage['video_enableOther'] = ADDON.getSettingBool("video_Other") settings_storage['ambiEnabled'] = ADDON.getSettingBool("group3_enabled") settings_storage['show500Error'] = ADDON.getSettingBool("show500Error") _validate_schedule() _validate_ambilight()
def notification(header, message, time=5000, icon=ADDON.getAddonInfo('icon'), sound=False): xbmcgui.Dialog().notification(header, message, icon, time, sound)
def _commands(monitor, command): xbmc.log(f"[script.service.hue] Started with {command}") if command == "discover": hue_connection = hueconnection.HueConnection(monitor, silent=True, discover=True) if hue_connection.connected: xbmc.log("[script.service.hue] Found bridge. Starting service.") ADDON.openSettings() _service(monitor) else: ADDON.openSettings() elif command == "createHueScene": hue_connection = hueconnection.HueConnection( monitor, silent=True, discover=False) # don't rediscover, proceed silently if hue_connection.connected: hue_connection.create_hue_scene() else: xbmc.log( "[script.service.hue] No bridge found. createHueScene cancelled." ) notification(_("Hue Service"), _("Check Hue Bridge configuration")) elif command == "deleteHueScene": hue_connection = hueconnection.HueConnection( monitor, silent=True, discover=False) # don't rediscover, proceed silently if hue_connection.connected: hue_connection.delete_hue_scene() else: xbmc.log( "[script.service.hue] No bridge found. deleteHueScene cancelled." ) notification(_("Hue Service"), _("Check Hue Bridge configuration")) elif command == "sceneSelect": # sceneSelect=light_group,action / sceneSelect=0,play light_group = sys.argv[2] action = sys.argv[3] # xbmc.log(f"[script.service.hue] sceneSelect: light_group: {light_group}, action: {action}") hue_connection = hueconnection.HueConnection( monitor, silent=True, discover=False) # don't rediscover, proceed silently if hue_connection.connected: hue_connection.configure_scene(light_group, action) else: xbmc.log( "[script.service.hue] No bridge found. sceneSelect cancelled.") notification(_("Hue Service"), _("Check Hue Bridge configuration")) elif command == "ambiLightSelect": # ambiLightSelect=light_group_id light_group = sys.argv[2] # xbmc.log(f"[script.service.hue] ambiLightSelect light_group_id: {light_group}") hue_connection = hueconnection.HueConnection( monitor, silent=True, discover=False ) # don't rediscover, proceed silently # don't rediscover, proceed silently if hue_connection.connected: hue_connection.configure_ambilights(light_group) else: xbmc.log( "[script.service.hue] No bridge found. Select ambi lights cancelled." ) notification(_("Hue Service"), _("Check Hue Bridge configuration")) else: xbmc.log(f"[script.service.hue] Unknown command: {command}") raise RuntimeError(f"Unknown Command: {command}")
def _service(monitor): hue_connection = HueConnection( monitor, silent=ADDON.getSettingBool("disableConnectionMessage"), discover=False) service_enabled = CACHE.get(f"{ADDONID}.service_enabled") if hue_connection.connected: light_groups = [ lightgroup.LightGroup(0, hue_connection, lightgroup.VIDEO), lightgroup.LightGroup(1, hue_connection, lightgroup.AUDIO), ambigroup.AmbiGroup(3, hue_connection) ] connection_retries = 0 timer = 60 daylight = hue_connection.get_daylight() new_daylight = daylight CACHE.set(f"{ADDONID}.daylight", daylight) CACHE.set(f"{ADDONID}.service_enabled", True) # xbmc.log("[script.service.hue] Core service starting. Connected: {}".format(CONNECTED)) while hue_connection.connected and not monitor.abortRequested(): # check if service was just re-enabled and if so activate groups prev_service_enabled = service_enabled service_enabled = CACHE.get(f"{ADDONID}.service_enabled") if service_enabled and not prev_service_enabled: activate(light_groups) # if service disabled, stop ambilight._ambi_loop thread if not service_enabled: AMBI_RUNNING.clear() # process cached waiting commands action = CACHE.get(f"{ADDONID}.action") if action: _process_actions(action, light_groups) # reload groups if settings changed, but keep player state if SETTINGS_CHANGED.is_set(): light_groups = [ lightgroup.LightGroup( 0, hue_connection, lightgroup.VIDEO, initial_state=light_groups[0].state, video_info_tag=light_groups[0].video_info_tag), lightgroup.LightGroup( 1, hue_connection, lightgroup.AUDIO, initial_state=light_groups[1].state, video_info_tag=light_groups[1].video_info_tag), ambigroup.AmbiGroup( 3, hue_connection, initial_state=light_groups[2].state, video_info_tag=light_groups[2].video_info_tag) ] SETTINGS_CHANGED.clear() # every minute, check for sunset & connection if timer > 59: timer = 0 # check connection to Hue hue_connection and fetch daylight status try: if connection_retries > 0: hue_connection.connect_bridge(silent=True) if hue_connection.connected: new_daylight = hue_connection.get_daylight() connection_retries = 0 else: new_daylight = hue_connection.get_daylight() except (requests.RequestException, ConnectionError) as error: connection_retries = connection_retries + 1 if connection_retries <= 10: xbmc.log( f"[script.service.hue] Bridge Connection Error. Attempt: {connection_retries}/10 : {error}" ) notification( _("Hue Service"), _("Connection lost. Trying again in 2 minutes")) timer = -60 else: xbmc.log( f"[script.service.hue] Bridge Connection Error. Attempt: {connection_retries}/10. Shutting down : {error}" ) notification( _("Hue Service"), _("Connection lost. Check settings. Shutting down") ) hue_connection.connected = False # check if sunset took place if new_daylight != daylight: xbmc.log( f"[script.service.hue] Daylight change. current: {daylight}, new: {new_daylight}" ) daylight = new_daylight CACHE.set(f"{ADDONID}.daylight", daylight) if not daylight and service_enabled: xbmc.log("[script.service.hue] Sunset activate") activate(light_groups) timer += 1 monitor.waitForAbort(1) xbmc.log("[script.service.hue] Process exiting...") return xbmc.log("[script.service.hue] No connected hue_connection, exiting...") return
def _ambi_loop(self): AMBI_RUNNING.set() cap = xbmc.RenderCapture() xbmc.log("[script.service.hue] _ambiLoop started") aspect_ratio = cap.getAspectRatio() self.capture_size_y = int(self.capture_size_x / aspect_ratio) expected_capture_size = self.capture_size_x * self.capture_size_y * 4 # size * 4 bytes - RGBA xbmc.log( f"[script.service.hue] aspect_ratio: {aspect_ratio}, Capture Size: ({self.capture_size_x},{self.capture_size_y}), expected_capture_size: {expected_capture_size}" ) cap.capture( self.capture_size_x, self.capture_size_y ) # start the capture process https://github.com/xbmc/xbmc/pull/8613#issuecomment-165699101 for L in list(self.ambi_lights): self.ambi_lights[L].update(prev_xy=(0.0001, 0.0001)) while not self.monitor.abortRequested() and AMBI_RUNNING.is_set( ) and self.hue_connection.connected: # loop until kodi tells add-on to stop or video playing flag is unset. try: cap_image = cap.getImage( ) # timeout to wait for OS in ms, default 1000 if cap_image is None or len(cap_image) < expected_capture_size: xbmc.log( "[script.service.hue] capImage is none or < expected. captured: {}, expected: {}" .format(len(cap_image), expected_capture_size)) self.monitor.waitForAbort( 0.25) # pause before trying again continue # no image captured, try again next iteration image = Image.frombytes( "RGBA", (self.capture_size_x, self.capture_size_y), bytes(cap_image), "raw", "BGRA", 0, 1) # Kodi always returns a BGRA image. except ValueError: xbmc.log(f"[script.service.hue] capImage: {len(cap_image)}") xbmc.log("[script.service.hue] Value Error") self.monitor.waitForAbort(0.25) continue # returned capture is smaller than expected, but this happens when player is stopping so fail silently. give up this loop. colors = self.image_process.img_avg(image, self.min_bri, self.max_bri, self.saturation) for L in list(self.ambi_lights): t = Thread(target=self._update_hue_rgb, name="_update_hue_rgb", args=(colors['rgb'][0], colors['rgb'][1], colors['rgb'][2], L, self.transition_time, colors['bri']), daemon=True) t.start() self.monitor.waitForAbort(self.update_interval) # seconds if not self.monitor.abortRequested( ): # ignore writing average process time if Kodi is shutting down average_process_time = self._perf_average(PROCESS_TIMES) xbmc.log( f"[script.service.hue] Average process time: {average_process_time}" ) ADDON.setSetting("average_process_time", str(average_process_time)) xbmc.log("[script.service.hue] _ambiLoop stopped")
def discover_bridge(self): xbmc.log("[script.service.hue] Start bridgeDiscover") # Create new config if none exists. Returns success or fail as bool ADDON.setSettingString("bridgeIP", "") ADDON.setSettingString("bridgeUser", "") self.bridge_ip = "" self.bridge_user = "" self.connected = False progress_bar = xbmcgui.DialogProgress() progress_bar.create(_('Searching for bridge...')) progress_bar.update(5, _("Discovery started")) complete = False while not progress_bar.iscanceled( ) and not complete and not self.monitor.abortRequested(): progress_bar.update(percent=10, message=_("N-UPnP discovery...")) bridge_ip_found = self._discover_nupnp() if not bridge_ip_found and not progress_bar.iscanceled(): manual_entry = xbmcgui.Dialog().yesno( _("Bridge not found"), _("Bridge not found automatically. Please make sure your bridge is up to date and has access to the internet. [CR]Would you like to enter your bridge IP manually?" )) if manual_entry: self.bridge_ip = xbmcgui.Dialog().numeric( 3, _("Bridge IP")) if self.bridge_ip: progress_bar.update(percent=50, message=_("Connecting...")) if self._check_version() and self._check_bridge_model( ) and not progress_bar.iscanceled(): progress_bar.update(percent=100, message=_("Found bridge: ") + self.bridge_ip) self.monitor.waitForAbort(1) bridge_user_created = self._create_user(progress_bar) if bridge_user_created: xbmc.log( f"[script.service.hue] User created: {self.bridge_user}" ) progress_bar.update( percent=90, message=_("User Found![CR]Saving settings...")) ADDON.setSettingString("bridgeIP", self.bridge_ip) ADDON.setSettingString("bridgeUser", self.bridge_user) progress_bar.update(percent=100, message=_("Complete!")) self.monitor.waitForAbort(5) progress_bar.close() xbmc.log( "[script.service.hue] Bridge discovery complete") self.connect_bridge(True) return True elif progress_bar.iscanceled(): xbmc.log("[script.service.hue] Cancelled 2") complete = True progress_bar.update(percent=100, message=_("Cancelled")) progress_bar.close() else: xbmc.log( f"[script.service.hue] User not created, received: {self.bridge_user}" ) progress_bar.update( percent=100, message= _("User not found[CR]Check your bridge and network." )) self.monitor.waitForAbort(5) complete = True progress_bar.close() return elif progress_bar.iscanceled(): xbmc.log("[script.service.hue] Cancelled 3") complete = True progress_bar.update(percent=100, message=_("Cancelled")) progress_bar.close() else: progress_bar.update( percent=100, message=_( "Bridge not found[CR]Check your bridge and network." )) xbmc.log( "[script.service.hue] Bridge not found, check your bridge and network" ) self.monitor.waitForAbort(5) complete = True progress_bar.close() xbmc.log("[script.service.hue] Cancelled 4") complete = True progress_bar.update(percent=100, message=_("Cancelled")) progress_bar.close() if progress_bar.iscanceled(): xbmc.log( "[script.service.hue] Bridge discovery cancelled by user 5") progress_bar.update(percent=100, message=_("Cancelled")) progress_bar.close()