From 5e35c2694362146a1ed94dc849c6bea668f45ea6 Mon Sep 17 00:00:00 2001 From: zim514 Date: Sat, 5 Oct 2024 22:14:00 +0000 Subject: [PATCH] [script.service.hue] 2.0.12 --- script.service.hue/addon.xml | 13 +-- script.service.hue/resources/lib/ambigroup.py | 46 +++++---- script.service.hue/resources/lib/core.py | 7 +- script.service.hue/resources/lib/hue.py | 6 +- .../resources/lib/lightgroup.py | 96 ++++++++++--------- script.service.hue/resources/lib/settings.py | 4 +- 6 files changed, 90 insertions(+), 82 deletions(-) diff --git a/script.service.hue/addon.xml b/script.service.hue/addon.xml index b4c8bbd3f..3d54b2ec3 100644 --- a/script.service.hue/addon.xml +++ b/script.service.hue/addon.xml @@ -1,4 +1,4 @@ - + @@ -20,14 +20,11 @@ https://github.com/zim514/script.service.hue https://forum.kodi.tv/showthread.php?tid=344886 - v2.0.0 -- Hue API V2 support (requires reconfiguration of scenes and ambilight) -- Now uses standard scenes -- Sunrise is now manually configured (Default 8AM) -- Removed some unnecessary settings -- Refactoring and code improvements -- Many bug fixes + v2.0.12 +- Fix schedule / activation checks +- Fix Music support - Localisation updates from Weblate +- Fix Ambilight support Automatitza les llums Hue amb la reproducció de Kodi diff --git a/script.service.hue/resources/lib/ambigroup.py b/script.service.hue/resources/lib/ambigroup.py index 7d1cccab6..b7d808a00 100644 --- a/script.service.hue/resources/lib/ambigroup.py +++ b/script.service.hue/resources/lib/ambigroup.py @@ -39,23 +39,9 @@ def __init__(self, light_group_id, settings_monitor, bridge): self.converterC = Converter(GamutC) self.helper = ColorHelper(GamutC) # Gamut doesn't matter for this usage - if getattr(self.settings_monitor, f"group{self.light_group_id}_enabled", False) and self.bridge.connected: - index = 0 - lights = getattr(self.settings_monitor, f"group{self.light_group_id}_lights") - if len(lights) > 0: - for L in lights: - gamut = self._get_light_gamut(self.bridge, L) - if gamut == 404: - notification(header=_("Hue Service"), message=_(f"ERROR: Light not found, it may have been deleted"), icon=xbmcgui.NOTIFICATION_ERROR) - AMBI_RUNNING.clear() - ADDON.setSettingString(f"group{self.light_group_id}_Lights", "-1") - ADDON.setSettingString(f"group{self.light_group_id}_LightNames", _("Not selected")) - else: - light = {L: {'gamut': gamut, 'prev_xy': (0, 0), "index": index}} - self.ambi_lights.update(light) - index = index + 1 - log(f"[SCRIPT.SERVICE.HUE] AmbiGroup[{self.light_group_id}] Lights: {self.ambi_lights}") + + # convert MS to seconds def onAVStarted(self): @@ -63,20 +49,23 @@ def onAVStarted(self): self.last_media_type = self._playback_type() enabled = getattr(self.settings_monitor, f"group{self.light_group_id}_enabled", False) + if getattr(self.settings_monitor, f"group{self.light_group_id}_enabled", False) and self.bridge.connected: + self._get_lights() + else: + return + log(f"[SCRIPT.SERVICE.HUE] AmbiGroup[{self.light_group_id}] onPlaybackStarted. Group enabled: {enabled}, Bridge connected: {self.bridge.connected}, mediaType: {self.media_type}") - if not enabled or not self.bridge.connected: - return log(f"[SCRIPT.SERVICE.HUE] AmbiGroup[{self.light_group_id}] onPlaybackStarted. media_type: {self.media_type} == playback_type: {self._playback_type()}") if self.media_type == self._playback_type() and self._playback_type() == VIDEO: try: - self.video_info_tag = self.getVideoInfoTag() + self.info_tag = self.getVideoInfoTag() except (AttributeError, TypeError) as x: log(f"[SCRIPT.SERVICE.HUE] AmbiGroup{self.light_group_id}: OnAV Started: Can't read infoTag") reporting.process_exception(x) else: - self.video_info_tag = None + self.info_tag = None if self.activation_check.validate(): log(f"[SCRIPT.SERVICE.HUE] AmbiGroup[{self.light_group_id}] Running Play action") @@ -281,3 +270,20 @@ def _resume_all_light_states(self, states): log(f"[SCRIPT.SERVICE.HUE] Light[{light_id}] state resumed successfully.") else: log(f"[SCRIPT.SERVICE.HUE] Failed to resume Light[{light_id}] state.") + + def _get_lights(self): + index = 0 + lights = getattr(self.settings_monitor, f"group{self.light_group_id}_lights") + if len(lights) > 0: + for L in lights: + gamut = self._get_light_gamut(self.bridge, L) + if gamut == 404: + notification(header=_("Hue Service"), message=_(f"ERROR: Light not found, it may have been deleted"), icon=xbmcgui.NOTIFICATION_ERROR) + AMBI_RUNNING.clear() + ADDON.setSettingString(f"group{self.light_group_id}_Lights", "-1") + ADDON.setSettingString(f"group{self.light_group_id}_LightNames", _("Not selected")) + else: + light = {L: {'gamut': gamut, 'prev_xy': (0, 0), "index": index}} + self.ambi_lights.update(light) + index = index + 1 + log(f"[SCRIPT.SERVICE.HUE] AmbiGroup[{self.light_group_id}] Lights: {self.ambi_lights}") \ No newline at end of file diff --git a/script.service.hue/resources/lib/core.py b/script.service.hue/resources/lib/core.py index 6a435a118..c97d814eb 100644 --- a/script.service.hue/resources/lib/core.py +++ b/script.service.hue/resources/lib/core.py @@ -8,9 +8,8 @@ import threading from datetime import datetime, timedelta, date -import xbmc -from . import ADDON, AMBI_RUNNING, BRIDGE_SETTINGS_CHANGED +from . import ADDON, AMBI_RUNNING, BRIDGE_SETTINGS_CHANGED, KODIVERSION, ADDONVERSION from . import lightgroup, ambigroup, settings from .hue import Hue from .kodiutils import notification, cache_set, cache_get, log @@ -41,7 +40,7 @@ def __init__(self, settings_monitor): } def handle_command(self, command, *args): - log(f"[SCRIPT.SERVICE.HUE] Started with {command}, Python: {sys.version}") + log(f"[SCRIPT.SERVICE.HUE] Started with {command}, Kodi: {KODIVERSION}, Addon: {ADDONVERSION}, Python: {sys.version}") command_func = self.commands.get(command) if command_func: @@ -88,7 +87,7 @@ def __init__(self, settings_monitor): cache_set("service_enabled", True) def run(self): - log(f"[SCRIPT.SERVICE.HUE] Starting Hue Service, Python: {sys.version}") + log(f"[SCRIPT.SERVICE.HUE] Starting Hue Service, Kodi: {KODIVERSION}, Addon: {ADDONVERSION}, Python: {sys.version}") self.light_groups = self.initialize_light_groups() self.timers = Timers(self.settings_monitor, self.bridge, self) diff --git a/script.service.hue/resources/lib/hue.py b/script.service.hue/resources/lib/hue.py index bc9521e4d..455707dc3 100644 --- a/script.service.hue/resources/lib/hue.py +++ b/script.service.hue/resources/lib/hue.py @@ -131,8 +131,8 @@ def connect(self): self.session.headers.update({'hue-application-key': self.settings_monitor.key}) self.devices = self.make_api_request("GET", "device") - if self.devices is None: - log(f"[SCRIPT.SERVICE.HUE] v2 connect: Connection attempts failed. Setting connected to False") + if not isinstance(self.devices, dict): + log(f"[SCRIPT.SERVICE.HUE] v2 connect: Connection error. Setting connected to False. {type(self.devices)} : {self.devices}") self.connected = False return False @@ -313,7 +313,7 @@ def update_sunset(self): geolocation = self.make_api_request("GET", "geolocation") log(f"[SCRIPT.SERVICE.HUE] v2 update_sunset(): geolocation: {geolocation}") sunset_str = self.search_dict(geolocation, "sunset_time") - if sunset_str is None: + if sunset_str is None or sunset_str == "": log(f"[SCRIPT.SERVICE.HUE] Sunset not found; configure Hue geolocalisation") notification(_("Hue Service"), _("Configure Hue Home location to use Sunset time, defaulting to 19:00"), icon=xbmcgui.NOTIFICATION_ERROR) self.sunset = convert_time("19:00") diff --git a/script.service.hue/resources/lib/lightgroup.py b/script.service.hue/resources/lib/lightgroup.py index 839f6977b..98e284a08 100644 --- a/script.service.hue/resources/lib/lightgroup.py +++ b/script.service.hue/resources/lib/lightgroup.py @@ -25,7 +25,7 @@ def __init__(self, light_group_id, media_type, settings_monitor, bridge=None): self.light_group_id = light_group_id self.state = STATE_STOPPED self.media_type = media_type - self.video_info_tag = xbmc.InfoTagVideo + self.info_tag = None self.last_media_type = self.media_type self.settings_monitor = settings_monitor @@ -51,20 +51,23 @@ def onAVStarted(self): log(f"[SCRIPT.SERVICE.HUE] LightGroup[{self.light_group_id}] onPlaybackStarted. play_behavior: {play_enabled}, media_type: {self.media_type} == playback_type: {self._playback_type()}") if play_enabled and self.media_type == self._playback_type() and self._playback_type() == VIDEO: - try: - self.video_info_tag = self.getVideoInfoTag() + self.info_tag = self.getVideoInfoTag() except (AttributeError, TypeError) as x: - log(f"[SCRIPT.SERVICE.HUE] LightGroup{self.light_group_id}: OnAV Started: Can't read infoTag") + log(f"[SCRIPT.SERVICE.HUE] LightGroup{self.light_group_id}: OnAV Started: Can't read VideoInfoTag") + reporting.process_exception(x) + elif play_enabled and self.media_type == self._playback_type() and self._playback_type() == AUDIO: + try: + self.info_tag = self.getMusicInfoTag() + except (AttributeError, TypeError) as x: + log(f"[SCRIPT.SERVICE.HUE] LightGroup{self.light_group_id}: OnAV Started: Can't read AudioInfoTag") reporting.process_exception(x) - else: - self.video_info_tag = None if self.activation_check.validate(play_scene): - contents = inspect.getmembers(self.video_info_tag) + contents = inspect.getmembers(self.info_tag) log(f"[SCRIPT.SERVICE.HUE] Start InfoTag: {contents}") - log(f"[SCRIPT.SERVICE.HUE] InfoTag: {self.video_info_tag}, {self.video_info_tag.getDuration()}") + #log(f"[SCRIPT.SERVICE.HUE] InfoTag: {self.info_tag}, {self.info_tag.getDuration()}") log(f"[SCRIPT.SERVICE.HUE] LightGroup[{self.light_group_id}] Running Play action") self.run_action("play") @@ -92,18 +95,17 @@ def onPlayBackStopped(self): log(f"[SCRIPT.SERVICE.HUE] LightGroup[{self.light_group_id}] onPlaybackStopped. Group enabled: {enabled}, Bridge connected: {self.bridge.connected}") - if not enabled or not self.bridge.connected: return if stop_enabled and (self.media_type == self.last_media_type or self.media_type == self._playback_type()): ########### TODO: Remove debug block #xbmc.sleep(5000) - contents = inspect.getmembers(self.video_info_tag) + contents = inspect.getmembers(self.info_tag) log(f"[SCRIPT.SERVICE.HUE] Stop[{self.light_group_id}] InfoTag Inspect Contents: {contents}") - duration = self.video_info_tag.getDuration() - log(f"[SCRIPT.SERVICE.HUE] Stop[{self.light_group_id}]: {self.video_info_tag}, {duration}") + duration = self.info_tag.getDuration() + log(f"[SCRIPT.SERVICE.HUE] Stop[{self.light_group_id}]: {self.info_tag}, {duration}") ############ if self.activation_check.validate(stop_scene): @@ -195,7 +197,7 @@ def _video_activation_rules(self): other_setting = self.settings_monitor.other_setting # Fetch video info tag - info_tag = self.light_group.video_info_tag + info_tag = self.light_group.info_tag # Get duration in minutes duration = info_tag.getDuration() / 60 # Get media type and file name @@ -255,10 +257,8 @@ def _is_within_schedule(self): log("[SCRIPT.SERVICE.HUE] _is_within_schedule: True, Schedule not enabled") return True - def skip_time_check_if_light_on(self, scene_id, all_light_states): - if not self.settings_monitor.enable_if_already_active: - log("[SCRIPT.SERVICE.HUE] _is_scene_already_active: Not enabled") - return False + def _check_any_lights_on(self, scene_id, all_light_states): + """ Checks if ANY light in the current scene is on""" # Find the current scene from the scene data current_scene = next((scene for scene in self.light_group.bridge.scene_data['data'] if scene['id'] == scene_id), None) @@ -277,7 +277,8 @@ def skip_time_check_if_light_on(self, scene_id, all_light_states): log("[SCRIPT.SERVICE.HUE] _is_scene_already_active: No lights in the scene are on") return False - def skip_scene_if_all_off(self, scene_id, all_light_states): + def _check_all_lights_off(self, scene_id, all_light_states): + """ Checks if ALL the lights in the given scene are off""" # Find the current scene from the scene data current_scene = next((scene for scene in self.light_group.bridge.scene_data['data'] if scene['id'] == scene_id), None) if not current_scene: @@ -289,9 +290,9 @@ def skip_scene_if_all_off(self, scene_id, all_light_states): light_id = action['target']['rid'] light_state = next((state for state in all_light_states['data'] if state['id'] == light_id), None) if light_state and 'on' in light_state and light_state['on']['on']: - log(f"[SCRIPT.SERVICE.HUE] _is_any_light_off: Light {light_id} in the scene is on") + log(f"[SCRIPT.SERVICE.HUE] _check_all_lights_off: Light {light_id} in the scene is on") return True - + log(f"[SCRIPT.SERVICE.HUE] _check_all_lights_off: All in scene {scene_id} are off") return False def validate(self, scene=None): @@ -300,7 +301,7 @@ def validate(self, scene=None): skip_time_check_if_light_on = self.settings_monitor.skip_time_check_if_light_on skip_scene_if_all_off = self.settings_monitor.skip_scene_if_all_off - log(f"[SCRIPT.SERVICE.HUE] LightGroup[{self.light_group_id}] ActivationChecker.validate(): scene: {scene}, media_type: {self.light_group.media_type}, skip_time_check_if_light_on: {skip_time_check_if_light_on}, skip_scene_if_all_off: {skip_scene_if_all_off}") + log(f"[SCRIPT.SERVICE.HUE] Validate Activation LightGroup[{self.light_group_id}] Scene: {scene}, media_type: {self.light_group.media_type}, skip_time_check_if_light_on: {skip_time_check_if_light_on}, skip_scene_if_all_off: {skip_scene_if_all_off}") all_light_states = None if scene and (skip_time_check_if_light_on or skip_scene_if_all_off): @@ -309,35 +310,40 @@ def validate(self, scene=None): # log(f"[SCRIPT.SERVICE.HUE] validate: all_light_states {all_light_states}") if self.light_group.media_type == VIDEO and scene: - if skip_scene_if_all_off and not skip_scene_if_all_off(scene, all_light_states): - log("[SCRIPT.SERVICE.HUE] validate: All lights are off, not activating scene") - return False - if not (self._is_within_schedule() and self._video_activation_rules()): - log("[SCRIPT.SERVICE.HUE] validate: Not within schedule or video activation rules not met, not activating scene") + # Check video activation rules with a Scene + if skip_scene_if_all_off and not self._check_all_lights_off(scene, all_light_states): + log("[SCRIPT.SERVICE.HUE] Validate video: All lights are off, not activating scene") return False - log("[SCRIPT.SERVICE.HUE] validate: Activating scene for VIDEO") - return True + elif (skip_time_check_if_light_on and self._check_any_lights_on(scene, all_light_states)) and self._video_activation_rules(): + log("[SCRIPT.SERVICE.HUE] Validate video: Some lights are on, skipping schedule check") + return True + elif self._is_within_schedule() and self._video_activation_rules(): + log("[SCRIPT.SERVICE.HUE] Validate video: Scene selected, within schedule and video activation rules, activate") + return True - elif self.light_group.media_type == VIDEO: # if no scene is set, use the default activation. This is the case for ambilight. - if not (self._is_within_schedule() and self._video_activation_rules()): - log("[SCRIPT.SERVICE.HUE] validate: Not within schedule or video activation rules not met, not activating scene") + log("[SCRIPT.SERVICE.HUE] Validate Video: No valid checks passed, not activating scene") + return False + + elif self.light_group.media_type == VIDEO: + # if no scene is set, use the default activation. This is the case for ambilight. + if self._is_within_schedule() and self._video_activation_rules(): + log("[SCRIPT.SERVICE.HUE] Validate Video: No scene selected, within schedule and video activation rules: activate") + return True + else: + log("[SCRIPT.SERVICE.HUE] Validate Video: No scene selected, not within schedule or activation rules, ignoring") return False - log("[SCRIPT.SERVICE.HUE] validate: Activating scene for VIDEO") - return True elif self.light_group.media_type == AUDIO and scene: + # Check audio activation rules if skip_scene_if_all_off and not skip_scene_if_all_off(scene, all_light_states): - log("[SCRIPT.SERVICE.HUE] validate: All lights are off, not activating scene") - return False - if not self._is_within_schedule(): - log("[SCRIPT.SERVICE.HUE] validate: Not within schedule, not activating scene") + log("[SCRIPT.SERVICE.HUE] Validate Audio: All lights are off, not activating scene") return False - log("[SCRIPT.SERVICE.HUE] validate: Activating scene for AUDIO media type") - return True + elif (skip_time_check_if_light_on and self._check_any_lights_on(scene, all_light_states)): + log("[SCRIPT.SERVICE.HUE] Validate Audio: A light in the scene is on, activating scene") + return True + elif self._is_within_schedule(): + log("[SCRIPT.SERVICE.HUE] Validate Audio: Within schedule, activating scene") + return True + log("[SCRIPT.SERVICE.HUE] Validate Audio: Checks not passed, not activating") + return False - elif self.light_group.media_type == AUDIO: - if not self._is_within_schedule(): - log("[SCRIPT.SERVICE.HUE] validate: Not within schedule, not activating scene") - return False - log("[SCRIPT.SERVICE.HUE] validate: Activating scene for AUDIO") - return True diff --git a/script.service.hue/resources/lib/settings.py b/script.service.hue/resources/lib/settings.py index 516100bb0..42f5c38d8 100644 --- a/script.service.hue/resources/lib/settings.py +++ b/script.service.hue/resources/lib/settings.py @@ -114,9 +114,9 @@ def reload_settings(self): self._validate_ambilight() def _validate_ambilight(self): - log(f"[SCRIPT.SERVICE.HUE] Validate ambilight config. Enabled: {self.group3_enabled}, Lights: {self.group3_lights}") + log(f"[SCRIPT.SERVICE.HUE] Validate ambilight config. Enabled: {self.group3_enabled}, Lights: {type(self.group3_lights)} : {self.group3_lights}") if self.group3_enabled: - if self.group3_lights == '-1': + if self.group3_lights == ["-1"]: ADDON.setSettingBool('group3_enabled', False) log('[SCRIPT.SERVICE.HUE] _validate_ambilights: No ambilights selected') notification(_('Hue Service'), _('No lights selected for Ambilight.'), icon=xbmcgui.NOTIFICATION_ERROR)