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 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 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 _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 _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 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 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 _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 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 _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 __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__()