diff --git a/requirements.txt b/requirements.txt index dfae0669..db6011cd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,4 +4,5 @@ polib pillow Kodistubs~=21.0.0 urllib3 +kodi-addon-checker diff --git a/script.service.hue/addon.xml b/script.service.hue/addon.xml index f02ccae1..566d0280 100644 --- a/script.service.hue/addon.xml +++ b/script.service.hue/addon.xml @@ -1,9 +1,9 @@ - + - + - + diff --git a/script.service.hue/resources/lib/__init__.py b/script.service.hue/resources/lib/__init__.py index abaa9b61..2c34b9a2 100644 --- a/script.service.hue/resources/lib/__init__.py +++ b/script.service.hue/resources/lib/__init__.py @@ -13,6 +13,7 @@ import xbmcvfs STRDEBUG = False # Show string ID in UI +FORCEDEBUGLOG = True # Force output of debug logs regardless of Kodi logging setting #TODO: Change to false before shipping TIMEOUT = 1 # requests default timeout MAX_RETRIES = 7 NOTIFICATION_THRESHOLD = 2 diff --git a/script.service.hue/resources/lib/ambigroup.py b/script.service.hue/resources/lib/ambigroup.py index 23a5c103..7d1cccab 100644 --- a/script.service.hue/resources/lib/ambigroup.py +++ b/script.service.hue/resources/lib/ambigroup.py @@ -13,7 +13,7 @@ from . import ADDON, MINIMUM_COLOR_DISTANCE, imageprocess, lightgroup from . import PROCESS_TIMES, reporting, AMBI_RUNNING -from .kodiutils import notification +from .kodiutils import notification, log from .language import get_string as _ from .lightgroup import STATE_STOPPED, STATE_PAUSED, STATE_PLAYING, VIDEO from .rgbxy import Converter, ColorHelper # https://github.com/benknight/hue-python-rgb-converter @@ -55,7 +55,7 @@ def __init__(self, light_group_id, settings_monitor, bridge): light = {L: {'gamut': gamut, 'prev_xy': (0, 0), "index": index}} self.ambi_lights.update(light) index = index + 1 - xbmc.log(f"[SCRIPT.SERVICE.HUE] AmbiGroup[{self.light_group_id}] Lights: {self.ambi_lights}") + log(f"[SCRIPT.SERVICE.HUE] AmbiGroup[{self.light_group_id}] Lights: {self.ambi_lights}") # convert MS to seconds def onAVStarted(self): @@ -63,23 +63,23 @@ def onAVStarted(self): self.last_media_type = self._playback_type() enabled = getattr(self.settings_monitor, f"group{self.light_group_id}_enabled", False) - xbmc.log(f"[SCRIPT.SERVICE.HUE] AmbiGroup[{self.light_group_id}] onPlaybackStarted. Group enabled: {enabled}, Bridge connected: {self.bridge.connected}, mediaType: {self.media_type}") + 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 - xbmc.log(f"[SCRIPT.SERVICE.HUE] AmbiGroup[{self.light_group_id}] onPlaybackStarted. media_type: {self.media_type} == playback_type: {self._playback_type()}") + 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() except (AttributeError, TypeError) as x: - xbmc.log(f"[SCRIPT.SERVICE.HUE] AmbiGroup{self.light_group_id}: OnAV Started: Can't read infoTag") + 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 if self.activation_check.validate(): - xbmc.log(f"[SCRIPT.SERVICE.HUE] AmbiGroup[{self.light_group_id}] Running Play action") + log(f"[SCRIPT.SERVICE.HUE] AmbiGroup[{self.light_group_id}] Running Play action") # Start the Ambi loop ambi_loop_thread = Thread(target=self._ambi_loop, name="_ambi_loop", daemon=True) @@ -87,13 +87,13 @@ def onAVStarted(self): def onPlayBackStopped(self): # always stop ambilight even if group is disabled or it'll run forever - xbmc.log(f"[SCRIPT.SERVICE.HUE] In ambiGroup[{self.light_group_id}], onPlaybackStopped()") + log(f"[SCRIPT.SERVICE.HUE] In ambiGroup[{self.light_group_id}], onPlaybackStopped()") self.state = STATE_STOPPED AMBI_RUNNING.clear() def onPlayBackPaused(self): # always stop ambilight even if group is disabled or it'll run forever - xbmc.log(f"[SCRIPT.SERVICE.HUE] In ambiGroup[{self.light_group_id}], onPlaybackPaused()") + log(f"[SCRIPT.SERVICE.HUE] In ambiGroup[{self.light_group_id}], onPlaybackPaused()") self.state = STATE_PAUSED AMBI_RUNNING.clear() @@ -102,7 +102,7 @@ def _ambi_loop(self): executor = ThreadPoolExecutor(max_workers=len(self.ambi_lights) * 2) cap = xbmc.RenderCapture() cap_image = bytes - xbmc.log("[SCRIPT.SERVICE.HUE] _ambiLoop started") + log("[SCRIPT.SERVICE.HUE] _ambiLoop started") aspect_ratio = cap.getAspectRatio() # These settings require restarting ambilight video to update: @@ -116,7 +116,7 @@ def _ambi_loop(self): capture_size_y = int(capture_size_x / aspect_ratio) expected_capture_size = capture_size_x * capture_size_y * 4 # size * 4 bytes - RGBA - xbmc.log(f"[SCRIPT.SERVICE.HUE] aspect_ratio: {aspect_ratio}, Capture Size: ({capture_size_x}, {capture_size_y}), expected_capture_size: {expected_capture_size}") + log(f"[SCRIPT.SERVICE.HUE] aspect_ratio: {aspect_ratio}, Capture Size: ({capture_size_x}, {capture_size_y}), expected_capture_size: {expected_capture_size}") cap.capture(capture_size_x, capture_size_y) # start the capture process https://github.com/xbmc/xbmc/pull/8613#issuecomment-165699101 @@ -129,14 +129,14 @@ def _ambi_loop(self): 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)) + log("[SCRIPT.SERVICE.HUE] capImage is none or < expected. captured: {}, expected: {}".format(len(cap_image), expected_capture_size)) self.settings_monitor.waitForAbort(0.25) # pause before trying again continue # no image captured, try again next iteration image = Image.frombytes("RGBA", (capture_size_x, 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") + log(f"[SCRIPT.SERVICE.HUE] capImage: {len(cap_image)}") + log("[SCRIPT.SERVICE.HUE] Value Error") self.settings_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. @@ -150,9 +150,9 @@ def _ambi_loop(self): if not self.settings_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}") + log(f"[SCRIPT.SERVICE.HUE] Average process time: {average_process_time}") ADDON.setSettingString("average_process_time", str(average_process_time)) - xbmc.log("[SCRIPT.SERVICE.HUE] _ambiLoop stopped") + log("[SCRIPT.SERVICE.HUE] _ambiLoop stopped") def _update_hue_rgb(self, r, g, b, light, bri, transition_time): gamut = self.ambi_lights[light].get('gamut') @@ -192,22 +192,22 @@ def _update_hue_rgb(self, r, g, b, light, bri, transition_time): if response is not None: self.ambi_lights[light].update(prev_xy=xy) elif response == 429 or response == 500: - xbmc.log(f"[SCRIPT.SERVICE.HUE] AmbiGroup[{self.light_group_id}] _update_hue_rgb: {response}: Too Many Requests. Aborting request.") + log(f"[SCRIPT.SERVICE.HUE] AmbiGroup[{self.light_group_id}] _update_hue_rgb: {response}: Too Many Requests. Aborting request.") self.bridge_capacity_error() notification(_("Hue Service"), _("Bridge overloaded, stopping ambilight"), icon=xbmcgui.NOTIFICATION_ERROR) elif response == 404: - xbmc.log(f"[SCRIPT.SERVICE.HUE] AmbiGroup[{self.light_group_id}] Not Found") + log(f"[SCRIPT.SERVICE.HUE] AmbiGroup[{self.light_group_id}] Not Found") AMBI_RUNNING.clear() notification(header=_("Hue Service"), message=_(f"ERROR: Light not found, it may have been deleted"), icon=xbmcgui.NOTIFICATION_ERROR) AMBI_RUNNING.clear() # shut it down else: - xbmc.log(f"[SCRIPT.SERVICE.HUE] AmbiGroup[{self.light_group_id}] RequestException Hue call fail") + log(f"[SCRIPT.SERVICE.HUE] AmbiGroup[{self.light_group_id}] RequestException Hue call fail") AMBI_RUNNING.clear() # shut it down reporting.process_exception(response) def bridge_capacity_error(self): self.capacity_error_count = self.capacity_error_count + 1 # increment counter - xbmc.log(f"[SCRIPT.SERVICE.HUE] AmbiGroup[{self.light_group_id}] Bridge capacity error count: {self.capacity_error_count}") + log(f"[SCRIPT.SERVICE.HUE] AmbiGroup[{self.light_group_id}] Bridge capacity error count: {self.capacity_error_count}") if self.capacity_error_count > 50 and self.settings_monitor.show500errors: 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")) @@ -220,7 +220,7 @@ def _get_light_gamut(bridge, light): gamut = "C" # default light_data = bridge.make_api_request("GET", f"light/{light}") if light_data == 404: - xbmc.log(f"[SCRIPT.SERVICE.HUE] _get_light_gamut: Light[{light}] not found or ID invalid") + log(f"[SCRIPT.SERVICE.HUE] _get_light_gamut: Light[{light}] not found or ID invalid") return 404 elif light_data is not None and 'data' in light_data: for item in light_data['data']: @@ -258,7 +258,7 @@ def _get_and_save_light_states(self): } return states else: - xbmc.log(f"[SCRIPT.SERVICE.HUE] Failed to get light states.") + log(f"[SCRIPT.SERVICE.HUE] Failed to get light states.") return None def _resume_all_light_states(self, states): @@ -278,6 +278,6 @@ def _resume_all_light_states(self, states): data["color_temperature"] = {"mirek": state['color_temperature']} response = self.bridge.make_api_request('PUT', f'lights/{light_id}', json=data) if response is not None: - xbmc.log(f"[SCRIPT.SERVICE.HUE] Light[{light_id}] state resumed successfully.") + log(f"[SCRIPT.SERVICE.HUE] Light[{light_id}] state resumed successfully.") else: - xbmc.log(f"[SCRIPT.SERVICE.HUE] Failed to resume Light[{light_id}] state.") + log(f"[SCRIPT.SERVICE.HUE] Failed to resume Light[{light_id}] state.") diff --git a/script.service.hue/resources/lib/core.py b/script.service.hue/resources/lib/core.py index 6fd3a6ed..ecd0b7ed 100644 --- a/script.service.hue/resources/lib/core.py +++ b/script.service.hue/resources/lib/core.py @@ -13,7 +13,7 @@ from . import ADDON, AMBI_RUNNING, BRIDGE_SETTINGS_CHANGED from . import lightgroup, ambigroup, settings from .hue import Hue -from .kodiutils import notification, cache_set, cache_get +from .kodiutils import notification, cache_set, cache_get, log from .language import get_string as _ @@ -41,32 +41,32 @@ def __init__(self, settings_monitor): } def handle_command(self, command, *args): - xbmc.log(f"[SCRIPT.SERVICE.HUE] Started with {command}, Python: {sys.version}") + log(f"[SCRIPT.SERVICE.HUE] Started with {command}, Python: {sys.version}") command_func = self.commands.get(command) if command_func: command_func(*args) else: - xbmc.log(f"[SCRIPT.SERVICE.HUE] Unknown command: {command}") + log(f"[SCRIPT.SERVICE.HUE] Unknown command: {command}") raise RuntimeError(f"Unknown Command: {command}") def discover(self): bridge = Hue(self.settings_monitor, discover=True) if bridge.connected: - xbmc.log("[SCRIPT.SERVICE.HUE] Found bridge. Opening settings.") + log("[SCRIPT.SERVICE.HUE] Found bridge. Opening settings.") ADDON.openSettings() else: - xbmc.log("[SCRIPT.SERVICE.HUE] No bridge found. Opening settings.") + log("[SCRIPT.SERVICE.HUE] No bridge found. Opening settings.") ADDON.openSettings() def scene_select(self, light_group, action): - xbmc.log(f"[SCRIPT.SERVICE.HUE] sceneSelect: light_group: {light_group}, action: {action}") + log(f"[SCRIPT.SERVICE.HUE] sceneSelect: light_group: {light_group}, action: {action}") bridge = Hue(self.settings_monitor) if bridge.connected: bridge.configure_scene(light_group, action) else: - xbmc.log("[SCRIPT.SERVICE.HUE] No bridge found. sceneSelect cancelled.") + log("[SCRIPT.SERVICE.HUE] No bridge found. sceneSelect cancelled.") notification(_("Hue Service"), _("Check Hue Bridge configuration")) def ambi_light_select(self, light_group): @@ -74,7 +74,7 @@ def ambi_light_select(self, light_group): if bridge.connected: bridge.configure_ambilights(light_group) else: - xbmc.log("[SCRIPT.SERVICE.HUE] No bridge found. Select ambi lights cancelled.") + log("[SCRIPT.SERVICE.HUE] No bridge found. Select ambi lights cancelled.") notification(_("Hue Service"), _("Check Hue Bridge configuration")) @@ -88,7 +88,7 @@ def __init__(self, settings_monitor): cache_set("service_enabled", True) def run(self): - xbmc.log(f"[SCRIPT.SERVICE.HUE] Starting Hue Service, Python: {sys.version}") + log(f"[SCRIPT.SERVICE.HUE] Starting Hue Service, Python: {sys.version}") self.light_groups = self.initialize_light_groups() self.timers = Timers(self.settings_monitor, self.bridge, self) @@ -131,7 +131,7 @@ def run(self): if self.settings_monitor.waitForAbort(1): break - xbmc.log("[SCRIPT.SERVICE.HUE] Abort requested...") + log("[SCRIPT.SERVICE.HUE] Abort requested...") def initialize_light_groups(self): # Initialize light groups @@ -143,7 +143,7 @@ def initialize_light_groups(self): def activate(self): # 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") + log(f"[SCRIPT.SERVICE.HUE] Activating scenes") for g in self.light_groups: if ADDON.getSettingBool(f"group{g.light_group_id}_enabled"): @@ -157,7 +157,7 @@ def _process_action(self): if action: action_action = action[0] action_light_group_id = int(action[1]) - 1 - xbmc.log(f"[SCRIPT.SERVICE.HUE] Action command: {action}, action_action: {action_action}, action_light_group_id: {action_light_group_id}") + log(f"[SCRIPT.SERVICE.HUE] Action command: {action}, action_action: {action_action}, action_light_group_id: {action_light_group_id}") # Run the action self.light_groups[action_light_group_id].run_action(action_action) @@ -186,10 +186,10 @@ def stop(self): def _run_morning(self): cache_set("daytime", True) self.bridge.update_sunset() - xbmc.log(f"[SCRIPT.SERVICE.HUE] run_morning(): new sunset: {self.bridge.sunset}") + log(f"[SCRIPT.SERVICE.HUE] run_morning(): new sunset: {self.bridge.sunset}") def _run_sunset(self): - xbmc.log(f"[SCRIPT.SERVICE.HUE] in run_sunset(): Sunset. ") + log(f"[SCRIPT.SERVICE.HUE] in run_sunset(): Sunset. ") cache_set("daytime", False) if self.settings_monitor.force_on_sunset: self.hue_service.activate() @@ -197,7 +197,7 @@ def _run_sunset(self): def _set_daytime(self): now = datetime.now() - xbmc.log(f"[SCRIPT.SERVICE.HUE] _set_daytime(): Morning Time: {self.morning_time}, Now: {now.time()}, bridge.sunset: {self.bridge.sunset}, Sunset offset: {self.settings_monitor.sunset_offset}") + log(f"[SCRIPT.SERVICE.HUE] _set_daytime(): Morning Time: {self.morning_time}, Now: {now.time()}, bridge.sunset: {self.bridge.sunset}, Sunset offset: {self.settings_monitor.sunset_offset}") # Convert self.bridge.sunset to a datetime object by combining it with today's date sunset_datetime = datetime.combine(datetime.today(), self.bridge.sunset) @@ -211,7 +211,7 @@ def _set_daytime(self): else: daytime = False cache_set("daytime", daytime) - xbmc.log(f"[SCRIPT.SERVICE.HUE] in _set_daytime(): Sunset with offset: {sunset_with_offset}, Daytime: {daytime} ") + log(f"[SCRIPT.SERVICE.HUE] in _set_daytime(): Sunset with offset: {sunset_with_offset}, Daytime: {daytime} ") def _task_loop(self): @@ -229,18 +229,18 @@ def _task_loop(self): if time_to_sunset <= 0 or time_to_sunset > time_to_morning: # Morning is next - xbmc.log(f"[SCRIPT.SERVICE.HUE] Timers: Morning is next. wait_time: {time_to_morning}") + log(f"[SCRIPT.SERVICE.HUE] Timers: Morning is next. wait_time: {time_to_morning}") if self.settings_monitor.waitForAbort(time_to_morning): break self._run_morning() else: # Sunset is next - xbmc.log(f"[SCRIPT.SERVICE.HUE] Timers: Sunset is next. wait_time: {time_to_sunset}") + log(f"[SCRIPT.SERVICE.HUE] Timers: Sunset is next. wait_time: {time_to_sunset}") if self.settings_monitor.waitForAbort(time_to_sunset): break self._run_sunset() - xbmc.log("[SCRIPT.SERVICE.HUE] Timers stopped") + log("[SCRIPT.SERVICE.HUE] Timers stopped") @staticmethod def _time_until(current, target): diff --git a/script.service.hue/resources/lib/hue.py b/script.service.hue/resources/lib/hue.py index ac45183d..9c327c93 100644 --- a/script.service.hue/resources/lib/hue.py +++ b/script.service.hue/resources/lib/hue.py @@ -2,18 +2,18 @@ # This file is part of script.service.hue # SPDX-License-Identifier: MIT # See LICENSE.TXT for more information. + import json from socket import getfqdn from urllib.parse import urljoin + import requests -from requests.exceptions import HTTPError, ConnectionError, Timeout import urllib3 - -import xbmc import xbmcgui +from requests.exceptions import HTTPError, ConnectionError, Timeout from . import ADDON, TIMEOUT, NOTIFICATION_THRESHOLD, MAX_RETRIES, reporting -from .kodiutils import notification, convert_time +from .kodiutils import notification, convert_time, log from .language import get_string as _ @@ -35,13 +35,13 @@ def __init__(self, settings_monitor, discover=False): self.sunset = None self.settings_monitor = settings_monitor - xbmc.log(f"[SCRIPT.SERVICE.HUE] v2 init: ip: {self.settings_monitor.ip}, key: {self.settings_monitor.key}") + log(f"[SCRIPT.SERVICE.HUE] v2 init: ip: {self.settings_monitor.ip}, key: {self.settings_monitor.key}") if discover: self.discover() elif self.settings_monitor.ip != "" or self.settings_monitor.key != "": self.connected = self.connect() else: - xbmc.log("[SCRIPT.SERVICE.HUE] No bridge IP or user key provided. Bridge not configured.") + log("[SCRIPT.SERVICE.HUE] No bridge IP or user key provided. Bridge not configured.") notification(_("Hue Service"), _("Bridge not configured"), icon=xbmcgui.NOTIFICATION_ERROR) def __exit__(self, exc_type, exc_val, exc_tb): @@ -50,13 +50,13 @@ def __exit__(self, exc_type, exc_val, exc_tb): def make_api_request(self, method, resource, discovery=False, **kwargs): # Discovery and account creation not yet supported on API V2. This flag uses a V1 URL and supports new IPs. if discovery: - xbmc.log(f"[SCRIPT.SERVICE.HUE] v2 make_request: discovery mode") + log(f"[SCRIPT.SERVICE.HUE] v2 make_request: discovery mode") for attempt in range(MAX_RETRIES): # Prepare the URL for the request - #xbmc.log(f"[SCRIPT.SERVICE.HUE] v2 ip: {self.settings_monitor.ip}, key: {self.settings_monitor.key}") + #log(f"[SCRIPT.SERVICE.HUE] v2 ip: {self.settings_monitor.ip}, key: {self.settings_monitor.key}") base_url = self.base_url if not discovery else f"http://{self.settings_monitor.ip}/api/" url = urljoin(base_url, resource) - #xbmc.log(f"[SCRIPT.SERVICE.HUE] v2 make_request: base_url: {base_url}, url: {url}, method: {method}, kwargs: {kwargs}") + #log(f"[SCRIPT.SERVICE.HUE] v2 make_request: base_url: {base_url}, url: {url}, method: {method}, kwargs: {kwargs}") try: # Make the request response = self.session.request(method, url, timeout=TIMEOUT, **kwargs) @@ -64,74 +64,74 @@ def make_api_request(self, method, resource, discovery=False, **kwargs): return response.json() except ConnectionError as x: # If a ConnectionError occurs, try to handle a new IP, except in discovery mode - xbmc.log(f"[SCRIPT.SERVICE.HUE] v2 make_request: ConnectionError: {x}") + log(f"[SCRIPT.SERVICE.HUE] v2 make_request: ConnectionError: {x}") if self._discover_new_ip() and not discovery: # If handling a new IP is successful, retry the request - xbmc.log(f"[SCRIPT.SERVICE.HUE] v2 make_request: New IP handled successfully. Retrying request.") + log(f"[SCRIPT.SERVICE.HUE] v2 make_request: New IP handled successfully. Retrying request.") continue else: # If handling a new IP fails, abort the request - xbmc.log(f"[SCRIPT.SERVICE.HUE] v2 make_request: Failed to handle new IP. Aborting request.") + log(f"[SCRIPT.SERVICE.HUE] v2 make_request: Failed to handle new IP. Aborting request.") return None except HTTPError as x: # Handle HTTP errors if x.response.status_code == 429: # If a 429 status code is received, abort and log an error - xbmc.log(f"[SCRIPT.SERVICE.HUE] v2 make_request: Too Many Requests: {x} \nResponse: {x.response.text}") + log(f"[SCRIPT.SERVICE.HUE] v2 make_request: Too Many Requests: {x} \nResponse: {x.response.text}") return 429 elif x.response.status_code in [401, 403]: - xbmc.log(f"[SCRIPT.SERVICE.HUE] v2 make_request: Unauthorized: {x}\nResponse: {x.response.text}") + log(f"[SCRIPT.SERVICE.HUE] v2 make_request: Unauthorized: {x}\nResponse: {x.response.text}") notification(_("Hue Service"), _("Bridge unauthorized, please reconfigure."), icon=xbmcgui.NOTIFICATION_ERROR) ADDON.setSettingString("bridgeUser", "") return 401 elif x.response.status_code == 404: - xbmc.log(f"[SCRIPT.SERVICE.HUE] v2 make_request: Not Found: {x}\nResponse: {x.response.text}") + log(f"[SCRIPT.SERVICE.HUE] v2 make_request: Not Found: {x}\nResponse: {x.response.text}") return 404 elif x.response.status_code == 500: - xbmc.log(f"[SCRIPT.SERVICE.HUE] v2 make_request: Internal Bridge Error: {x}\nResponse: {x.response.text}") + log(f"[SCRIPT.SERVICE.HUE] v2 make_request: Internal Bridge Error: {x}\nResponse: {x.response.text}") return 500 else: - xbmc.log(f"[SCRIPT.SERVICE.HUE] v2 make_request: HTTPError: {x}\nResponse: {x.response.text}") + log(f"[SCRIPT.SERVICE.HUE] v2 make_request: HTTPError: {x}\nResponse: {x.response.text}") reporting.process_exception(f"Response: {x.response.text}, Exception: {x}", logging=True) return x.response.status_code except (Timeout, json.JSONDecodeError) as x: - xbmc.log(f"[SCRIPT.SERVICE.HUE] v2 make_request: Timeout/JSONDecodeError: Response: {x.response}\n{x}") + log(f"[SCRIPT.SERVICE.HUE] v2 make_request: Timeout/JSONDecodeError: Response: {x.response}\n{x}") except requests.RequestException as x: # Report other kinds of RequestExceptions - xbmc.log(f"[SCRIPT.SERVICE.HUE] v2 make_request: RequestException: {x}") + log(f"[SCRIPT.SERVICE.HUE] v2 make_request: RequestException: {x}") reporting.process_exception(x) # Calculate the retry time and log the retry attempt retry_time = 2 ** attempt if retry_time >= 7 and attempt >= NOTIFICATION_THRESHOLD: notification(_("Hue Service"), _("Connection failed, retrying..."), icon=xbmcgui.NOTIFICATION_WARNING) - xbmc.log(f"[SCRIPT.SERVICE.HUE] v2 make_request: Retry in {retry_time} seconds, retry {attempt + 1}/{MAX_RETRIES}...") + log(f"[SCRIPT.SERVICE.HUE] v2 make_request: Retry in {retry_time} seconds, retry {attempt + 1}/{MAX_RETRIES}...") if self.settings_monitor.waitForAbort(retry_time): break # If all attempts fail, log the failure and set connected to False - xbmc.log(f"[SCRIPT.SERVICE.HUE] v2 make_request: All attempts failed after {MAX_RETRIES} retries. Setting connected to False") + log(f"[SCRIPT.SERVICE.HUE] v2 make_request: All attempts failed after {MAX_RETRIES} retries. Setting connected to False") self.connected = False return None def _discover_new_ip(self): if self._discover_nupnp(): - xbmc.log(f"[SCRIPT.SERVICE.HUE] v2 _discover_and_handle_new_ip: discover_nupnp SUCCESS, bridge IP: {self.settings_monitor.ip}") + log(f"[SCRIPT.SERVICE.HUE] v2 _discover_and_handle_new_ip: discover_nupnp SUCCESS, bridge IP: {self.settings_monitor.ip}") # TODO: add new discovery methods here ADDON.setSettingString("bridgeIP", self.settings_monitor.ip) if self.connect(): - xbmc.log(f"[SCRIPT.SERVICE.HUE] v2 _discover_and_handle_new_ip: connect SUCCESS") + log(f"[SCRIPT.SERVICE.HUE] v2 _discover_and_handle_new_ip: connect SUCCESS") return True - xbmc.log(f"[SCRIPT.SERVICE.HUE] v2 _discover_and_handle_new_ip: discover_nupnp FAIL, bridge IP: {self.settings_monitor.ip}") + log(f"[SCRIPT.SERVICE.HUE] v2 _discover_and_handle_new_ip: discover_nupnp FAIL, bridge IP: {self.settings_monitor.ip}") return False def connect(self): - xbmc.log(f"[SCRIPT.SERVICE.HUE] v2 connect: ip: {self.settings_monitor.ip}, key: {self.settings_monitor.key}") + log(f"[SCRIPT.SERVICE.HUE] v2 connect: ip: {self.settings_monitor.ip}, key: {self.settings_monitor.key}") self.base_url = f"https://{self.settings_monitor.ip}/clip/v2/resource/" self.session.headers.update({'hue-application-key': self.settings_monitor.key}) self.devices = self.make_api_request("GET", "device") if self.devices is None: - xbmc.log(f"[SCRIPT.SERVICE.HUE] v2 connect: Connection attempts failed. Setting connected to False") + log(f"[SCRIPT.SERVICE.HUE] v2 connect: Connection attempts failed. Setting connected to False") self.connected = False return False @@ -141,15 +141,15 @@ def connect(self): if self._check_version(): self.connected = True self.update_sunset() - xbmc.log(f"[SCRIPT.SERVICE.HUE] v2 connect: Connection successful") + log(f"[SCRIPT.SERVICE.HUE] v2 connect: Connection successful") return True - xbmc.log(f"[SCRIPT.SERVICE.HUE] v2 connect: Connection attempts failed. Setting connected to False") + log(f"[SCRIPT.SERVICE.HUE] v2 connect: Connection attempts failed. Setting connected to False") self.connected = False return False def discover(self): - xbmc.log("[SCRIPT.SERVICE.HUE] v2 Start discover") + log("[SCRIPT.SERVICE.HUE] v2 Start discover") # Reset settings self.settings_monitor.ip = "" self.settings_monitor.key = "" @@ -171,22 +171,22 @@ def discover(self): if not ip_discovered and not progress_bar.iscanceled(): # If the bridge was not found, ask the user to enter the IP manually - xbmc.log("[SCRIPT.SERVICE.HUE] v2 discover: Bridge not found automatically") + log("[SCRIPT.SERVICE.HUE] v2 discover: Bridge not found automatically") progress_bar.update(percent=10, message=_("Bridge not found")) 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.settings_monitor.ip = xbmcgui.Dialog().numeric(3, _("Bridge IP")) - xbmc.log(f"[SCRIPT.SERVICE.HUE] v2 discover: Manual entry: {self.settings_monitor.ip}") + log(f"[SCRIPT.SERVICE.HUE] v2 discover: Manual entry: {self.settings_monitor.ip}") if self.settings_monitor.ip: progress_bar.update(percent=50, message=_("Connecting...")) # Set the base URL for the API self.base_url = f"https://{self.settings_monitor.ip}/clip/v2/resource/" # Try to connect to the bridge - xbmc.log(f"[SCRIPT.SERVICE.HUE] v2 discover: Attempt connection") + log(f"[SCRIPT.SERVICE.HUE] v2 discover: Attempt connection") config = self.make_api_request("GET", "0/config", discovery=True) # bypass some checks in discovery mode, and use Hue API V1 until Philipps provides a V2 method - xbmc.log(f"[SCRIPT.SERVICE.HUE] v2 discover: config: {config}") + log(f"[SCRIPT.SERVICE.HUE] v2 discover: config: {config}") if config is not None and isinstance(config, dict) and not progress_bar.iscanceled(): progress_bar.update(percent=100, message=_("Found bridge: ") + self.settings_monitor.ip) self.settings_monitor.waitForAbort(1) @@ -195,7 +195,7 @@ def discover(self): bridge_user_created = self._create_user(progress_bar) if bridge_user_created: - xbmc.log(f"[SCRIPT.SERVICE.HUE] v2 discover: User created: {self.settings_monitor.key}") + log(f"[SCRIPT.SERVICE.HUE] v2 discover: User created: {self.settings_monitor.key}") progress_bar.update(percent=90, message=_("User Found![CR]Saving settings...")) # Save the IP and user key to the settings @@ -205,45 +205,45 @@ def discover(self): progress_bar.update(percent=100, message=_("Complete!")) self.settings_monitor.waitForAbort(5) progress_bar.close() - xbmc.log("[SCRIPT.SERVICE.HUE] v2 discover: Bridge discovery complete") + log("[SCRIPT.SERVICE.HUE] v2 discover: Bridge discovery complete") self.connect() return elif progress_bar.iscanceled(): - xbmc.log("[SCRIPT.SERVICE.HUE] v2 discover: Discovery cancelled by user") + log("[SCRIPT.SERVICE.HUE] v2 discover: Discovery cancelled by user") progress_bar.update(percent=100, message=_("Cancelled")) progress_bar.close() else: - xbmc.log(f"[SCRIPT.SERVICE.HUE] v2 discover: User not created, received: {self.settings_monitor.key}") + log(f"[SCRIPT.SERVICE.HUE] v2 discover: User not created, received: {self.settings_monitor.key}") progress_bar.update(percent=100, message=_("User not found[CR]Check your bridge and network.")) self.settings_monitor.waitForAbort(5) progress_bar.close() return elif progress_bar.iscanceled(): - xbmc.log("[SCRIPT.SERVICE.HUE] v2 discover: Discovery cancelled by user") + log("[SCRIPT.SERVICE.HUE] v2 discover: Discovery cancelled by user") 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] v2 discover: Bridge not found, check your bridge and network") + log("[SCRIPT.SERVICE.HUE] v2 discover: Bridge not found, check your bridge and network") self.settings_monitor.waitForAbort(5) progress_bar.close() - xbmc.log("[SCRIPT.SERVICE.HUE] v2 discover: Discovery process complete") + log("[SCRIPT.SERVICE.HUE] v2 discover: Discovery process complete") complete = True progress_bar.update(percent=100, message=_("Cancelled")) progress_bar.close() if progress_bar.iscanceled(): - xbmc.log("[SCRIPT.SERVICE.HUE] v2 discover: Bridge discovery cancelled by user") + log("[SCRIPT.SERVICE.HUE] v2 discover: Bridge discovery cancelled by user") progress_bar.update(percent=100, message=_("Cancelled")) progress_bar.close() def _create_user(self, progress_bar): # Log start of user creation - xbmc.log("[SCRIPT.SERVICE.HUE] v2 _create_user: In createUser") + log("[SCRIPT.SERVICE.HUE] v2 _create_user: In createUser") # Prepare data for POST request data = '{{"devicetype": "kodi#{}"}}'.format(getfqdn()) @@ -263,7 +263,7 @@ def _create_user(self, progress_bar): last_progress = progress response = self.make_api_request("POST", "", discovery=True, data=data) - xbmc.log(f"[SCRIPT.SERVICE.HUE] v2 _create_user: response at iteration {time}: {response}") + log(f"[SCRIPT.SERVICE.HUE] v2 _create_user: response at iteration {time}: {response}") # Break loop if link button has been pressed if response and response[0].get('error', {}).get('type') != 101: @@ -279,10 +279,10 @@ def _create_user(self, progress_bar): # Extract and save username from response username = response[0]['success']['username'] self.settings_monitor.key = username - xbmc.log(f"[SCRIPT.SERVICE.HUE] v2 _create_user: User created: {username}") + log(f"[SCRIPT.SERVICE.HUE] v2 _create_user: User created: {username}") return True except (KeyError, TypeError) as exc: - xbmc.log(f"[SCRIPT.SERVICE.HUE] v2 _create_user: Username not found: {exc}") + log(f"[SCRIPT.SERVICE.HUE] v2 _create_user: Username not found: {exc}") return False def _check_version(self): @@ -291,36 +291,36 @@ def _check_version(self): api_split = software_version.split(".") except KeyError as error: notification(_("Hue Service"), _("Bridge outdated. Please update your bridge."), icon=xbmcgui.NOTIFICATION_ERROR) - xbmc.log(f"[SCRIPT.SERVICE.HUE] v2 _version_check(): Connected! Bridge too old: {software_version}, error: {error}") + log(f"[SCRIPT.SERVICE.HUE] v2 _version_check(): Connected! Bridge too old: {software_version}, error: {error}") return False except Exception as exc: reporting.process_exception(exc) return False if int(api_split[0]) >= 1 and int(api_split[1]) >= 60: # minimum bridge version 1.60 - xbmc.log(f"[SCRIPT.SERVICE.HUE] v2 connect() software version: {software_version}") + log(f"[SCRIPT.SERVICE.HUE] v2 connect() software version: {software_version}") return True notification(_("Hue Service"), _("Bridge outdated. Please update your bridge."), icon=xbmcgui.NOTIFICATION_ERROR) - xbmc.log(f"[SCRIPT.SERVICE.HUE] v2 connect(): Connected! Bridge API too old: {software_version}") + log(f"[SCRIPT.SERVICE.HUE] v2 connect(): Connected! Bridge API too old: {software_version}") return False def update_sunset(self): geolocation = self.make_api_request("GET", "geolocation") - xbmc.log(f"[SCRIPT.SERVICE.HUE] v2 update_sunset(): geolocation: {geolocation}") + log(f"[SCRIPT.SERVICE.HUE] v2 update_sunset(): geolocation: {geolocation}") sunset_str = self.search_dict(geolocation, "sunset_time") if sunset_str is None: - xbmc.log(f"[SCRIPT.SERVICE.HUE] Sunset not found; configure Hue geolocalisation") + 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") return self.sunset = convert_time(sunset_str) - xbmc.log(f"[SCRIPT.SERVICE.HUE] v2 update_sunset(): sunset: {self.sunset}") + log(f"[SCRIPT.SERVICE.HUE] v2 update_sunset(): sunset: {self.sunset}") def recall_scene(self, scene_id, duration=400): # 400 is the default used by Hue, defaulting here for consistency - xbmc.log(f"[SCRIPT.SERVICE.HUE] v2 recall_scene(): scene_id: {scene_id}, transition_time: {duration}") + log(f"[SCRIPT.SERVICE.HUE] v2 recall_scene(): scene_id: {scene_id}, transition_time: {duration}") json_data = { "recall": { @@ -330,12 +330,12 @@ def recall_scene(self, scene_id, duration=400): # 400 is the default used by Hu } response = self.make_api_request("PUT", f"scene/{scene_id}", json=json_data) - xbmc.log(f"[SCRIPT.SERVICE.HUE] v2 recall_scene(): response: {response}") + log(f"[SCRIPT.SERVICE.HUE] v2 recall_scene(): response: {response}") return response def configure_scene(self, group_id, action): scene = self.select_hue_scene() - xbmc.log(f"[SCRIPT.SERVICE.HUE] v2 selected scene: {scene}") + log(f"[SCRIPT.SERVICE.HUE] v2 selected scene: {scene}") if scene is not None: # setting ID format example: group0_playSceneID ADDON.setSettingString(f"group{group_id}_{action}SceneID", scene[0]) @@ -353,7 +353,7 @@ def get_scenes_and_areas(self): # Merge rooms and zones into areas areas_dict = {**rooms_dict, **zones_dict} - xbmc.log(f"[SCRIPT.SERVICE.HUE] v2 get_scenes(): areas_dict: {areas_dict}") + log(f"[SCRIPT.SERVICE.HUE] v2 get_scenes(): areas_dict: {areas_dict}") # Create a dictionary for scenes scenes_dict = {} for scene in scenes_data['data']: @@ -364,19 +364,19 @@ def get_scenes_and_areas(self): scenes_dict[scene_id] = {'scene_name': scene_name, 'area_id': area_id} # dict_items = "\n".join([f"{key}: {value}" for key, value in scenes_dict.items()]) - # xbmc.log(f"[SCRIPT.SERVICE.HUE] v2 get_scenes(): scenes_dict:\n{dict_items}") + # log(f"[SCRIPT.SERVICE.HUE] v2 get_scenes(): scenes_dict:\n{dict_items}") return scenes_dict, areas_dict def select_hue_scene(self): dialog_progress = xbmcgui.DialogProgress() dialog_progress.create("Hue Service", "Searching for scenes...") - xbmc.log("[SCRIPT.SERVICE.HUE] V2 selectHueScene{}") + log("[SCRIPT.SERVICE.HUE] V2 selectHueScene{}") hue_scenes, hue_areas = self.get_scenes_and_areas() area_items = [xbmcgui.ListItem(label=name) for _, name in hue_areas.items()] - xbmc.log(f"[SCRIPT.SERVICE.HUE] V2 selectHueScene: area_items: {area_items}") + log(f"[SCRIPT.SERVICE.HUE] V2 selectHueScene: area_items: {area_items}") selected_area_index = xbmcgui.Dialog().select("Select Hue area...", area_items) if selected_area_index > -1: @@ -391,10 +391,10 @@ def select_hue_scene(self): selected_scene_name = selected_scene_item.getLabel() selected_area_name = area_items[selected_area_index].getLabel() selected_name = f"{selected_scene_name} - {selected_area_name}" - xbmc.log(f"[SCRIPT.SERVICE.HUE] V2 selectHueScene: selected: {selected_id}, name: {selected_name}") + log(f"[SCRIPT.SERVICE.HUE] V2 selectHueScene: selected: {selected_id}, name: {selected_name}") dialog_progress.close() return selected_id, selected_name - xbmc.log("[SCRIPT.SERVICE.HUE] V2 selectHueScene: cancelled") + log("[SCRIPT.SERVICE.HUE] V2 selectHueScene: cancelled") dialog_progress.close() return None @@ -419,10 +419,10 @@ def _select_hue_lights(self): return None def _discover_nupnp(self): - xbmc.log("[SCRIPT.SERVICE.HUE] v2 _discover_nupnp:") + log("[SCRIPT.SERVICE.HUE] v2 _discover_nupnp:") result: dict = self.make_api_request('GET', 'https://discovery.meethue.com/') if result is None or isinstance(result, int): - xbmc.log(f"[SCRIPT.SERVICE.HUE] v2 _discover_nupnp: make_request failed, result: {result}") + log(f"[SCRIPT.SERVICE.HUE] v2 _discover_nupnp: make_request failed, result: {result}") return None bridge_ip = None @@ -430,7 +430,7 @@ def _discover_nupnp(self): try: bridge_ip = result[0]["internalipaddress"] except KeyError: - xbmc.log("[SCRIPT.SERVICE.HUE] v2 _discover_nupnp: No IP found in response") + log("[SCRIPT.SERVICE.HUE] v2 _discover_nupnp: No IP found in response") return None self.settings_monitor.ip = bridge_ip return True diff --git a/script.service.hue/resources/lib/kodiutils.py b/script.service.hue/resources/lib/kodiutils.py index 500fc4db..397ee990 100644 --- a/script.service.hue/resources/lib/kodiutils.py +++ b/script.service.hue/resources/lib/kodiutils.py @@ -8,8 +8,9 @@ from json import JSONDecodeError import xbmcgui +import xbmc -from . import ADDON, ADDONID +from . import ADDON, ADDONID, FORCEDEBUGLOG cache_window = xbmcgui.Window(10000) @@ -26,7 +27,6 @@ def convert_time(time_string: str) -> datetime.time: return datetime.time(parts[0], parts[1], parts[2]) - def cache_get(key: str): data_str = cache_window.getProperty(f"{ADDONID}.{key}]") try: @@ -40,3 +40,10 @@ def cache_set(key: str, data): data_str = json.dumps(data) cache_window.setProperty(f"{ADDONID}.{key}]", data_str) return + + +def log(message, level=xbmc.LOGDEBUG): + if FORCEDEBUGLOG: + xbmc.log(message, xbmc.LOGWARNING) + else: + xbmc.log(message, level) diff --git a/script.service.hue/resources/lib/language.py b/script.service.hue/resources/lib/language.py index 3c02761a..c6ef78bd 100644 --- a/script.service.hue/resources/lib/language.py +++ b/script.service.hue/resources/lib/language.py @@ -8,7 +8,8 @@ # generated by language_gen.py -from . import STRDEBUG, ADDON, xbmc +from . import STRDEBUG, ADDON +from .kodiutils import log _strings = {} @@ -16,7 +17,7 @@ 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()}'") + log(f"[SCRIPT.SERVICE.HUE] LANGUAGE: missing translation for '{t.lower()}'") return t if STRDEBUG: diff --git a/script.service.hue/resources/lib/lightgroup.py b/script.service.hue/resources/lib/lightgroup.py index 8922289b..839f6977 100644 --- a/script.service.hue/resources/lib/lightgroup.py +++ b/script.service.hue/resources/lib/lightgroup.py @@ -9,7 +9,7 @@ import xbmcgui from . import ADDON, reporting -from .kodiutils import notification, cache_get +from .kodiutils import notification, cache_get, log from .language import get_string as _ STATE_STOPPED = 0 @@ -32,7 +32,7 @@ def __init__(self, light_group_id, media_type, settings_monitor, bridge=None): self.activation_check = ActivationChecker(self) self.bridge = bridge - xbmc.log(f"[SCRIPT.SERVICE.HUE] LightGroup[{self.light_group_id}] Initialized {self}") + log(f"[SCRIPT.SERVICE.HUE] LightGroup[{self.light_group_id}] Initialized {self}") super().__init__() @@ -44,28 +44,28 @@ def onAVStarted(self): play_enabled = getattr(self.settings_monitor, f"group{self.light_group_id}_play_enabled") play_scene = getattr(self.settings_monitor, f"group{self.light_group_id}_play_scene") - xbmc.log(f"[SCRIPT.SERVICE.HUE] LightGroup[{self.light_group_id}] onPlaybackStarted. Group enabled: {enabled}, Bridge connected: {self.bridge.connected}, mediaType: {self.media_type}") + log(f"[SCRIPT.SERVICE.HUE] LightGroup[{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 - xbmc.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()}") + 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() except (AttributeError, TypeError) as x: - xbmc.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 infoTag") reporting.process_exception(x) else: self.video_info_tag = None if self.activation_check.validate(play_scene): contents = inspect.getmembers(self.video_info_tag) - xbmc.log(f"[SCRIPT.SERVICE.HUE] Start InfoTag: {contents}") + log(f"[SCRIPT.SERVICE.HUE] Start InfoTag: {contents}") - xbmc.log(f"[SCRIPT.SERVICE.HUE] InfoTag: {self.video_info_tag}, {self.video_info_tag.getDuration()}") - xbmc.log(f"[SCRIPT.SERVICE.HUE] LightGroup[{self.light_group_id}] Running Play action") + log(f"[SCRIPT.SERVICE.HUE] InfoTag: {self.video_info_tag}, {self.video_info_tag.getDuration()}") + log(f"[SCRIPT.SERVICE.HUE] LightGroup[{self.light_group_id}] Running Play action") self.run_action("play") def onPlayBackPaused(self): @@ -74,14 +74,14 @@ def onPlayBackPaused(self): pause_enabled = getattr(self.settings_monitor, f"group{self.light_group_id}_pause_enabled") pause_scene = getattr(self.settings_monitor, f"group{self.light_group_id}_pause_scene") - xbmc.log(f"[SCRIPT.SERVICE.HUE] LightGroup[{self.light_group_id}] onPlaybackPaused. Group enabled: {enabled}, Bridge connected: {self.bridge.connected}") + log(f"[SCRIPT.SERVICE.HUE] LightGroup[{self.light_group_id}] onPlaybackPaused. Group enabled: {enabled}, Bridge connected: {self.bridge.connected}") if not enabled or not self.bridge.connected: return if pause_enabled and self.media_type == self._playback_type(): if self.activation_check.validate(pause_scene): - xbmc.log(f"[SCRIPT.SERVICE.HUE] LightGroup[{self.light_group_id}] Running Pause action") + log(f"[SCRIPT.SERVICE.HUE] LightGroup[{self.light_group_id}] Running Pause action") self.run_action("pause") def onPlayBackStopped(self): @@ -90,7 +90,7 @@ def onPlayBackStopped(self): stop_enabled = getattr(self.settings_monitor, f"group{self.light_group_id}_stop_enabled") stop_scene = getattr(self.settings_monitor, f"group{self.light_group_id}_stop_scene") - xbmc.log(f"[SCRIPT.SERVICE.HUE] LightGroup[{self.light_group_id}] onPlaybackStopped. Group enabled: {enabled}, Bridge connected: {self.bridge.connected}") + 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: @@ -100,30 +100,30 @@ def onPlayBackStopped(self): ########### TODO: Remove debug block #xbmc.sleep(5000) contents = inspect.getmembers(self.video_info_tag) - xbmc.log(f"[SCRIPT.SERVICE.HUE] Stop[{self.light_group_id}] InfoTag Inspect Contents: {contents}") + log(f"[SCRIPT.SERVICE.HUE] Stop[{self.light_group_id}] InfoTag Inspect Contents: {contents}") duration = self.video_info_tag.getDuration() - xbmc.log(f"[SCRIPT.SERVICE.HUE] Stop[{self.light_group_id}]: {self.video_info_tag}, {duration}") + log(f"[SCRIPT.SERVICE.HUE] Stop[{self.light_group_id}]: {self.video_info_tag}, {duration}") ############ if self.activation_check.validate(stop_scene): - xbmc.log(f"[SCRIPT.SERVICE.HUE] LightGroup[{self.light_group_id}] Running Stop action") + log(f"[SCRIPT.SERVICE.HUE] LightGroup[{self.light_group_id}] Running Stop action") self.run_action("stop") def onPlayBackResumed(self): - # xbmc.log("[SCRIPT.SERVICE.HUE] In LightGroup[{}], onPlaybackResumed()".format(self.light_group_id)) + # log("[SCRIPT.SERVICE.HUE] In LightGroup[{}], onPlaybackResumed()".format(self.light_group_id)) self.onAVStarted() def onPlayBackError(self): - # xbmc.log("[SCRIPT.SERVICE.HUE] In LightGroup[{}], onPlaybackError()".format(self.light_group_id)) + # log("[SCRIPT.SERVICE.HUE] In LightGroup[{}], onPlaybackError()".format(self.light_group_id)) self.onPlayBackStopped() def onPlayBackEnded(self): - # xbmc.log("[SCRIPT.SERVICE.HUE] In LightGroup[{}], onPlaybackEnded()".format(self.light_group_id)) + # log("[SCRIPT.SERVICE.HUE] In LightGroup[{}], onPlaybackEnded()".format(self.light_group_id)) self.onPlayBackStopped() def run_action(self, action): - xbmc.log(f"[SCRIPT.SERVICE.HUE] LightGroup[{self.light_group_id}], run_action({action})") + log(f"[SCRIPT.SERVICE.HUE] LightGroup[{self.light_group_id}], run_action({action})") service_enabled = cache_get("service_enabled") if service_enabled and self.bridge.connected: @@ -140,33 +140,33 @@ def run_action(self, action): duration = getattr(self.settings_monitor, f"group{self.light_group_id}_stop_transition") else: - xbmc.log(f"[SCRIPT.SERVICE.HUE] Unknown action type: {action}") + log(f"[SCRIPT.SERVICE.HUE] Unknown action type: {action}") raise RuntimeError try: if self.bridge.recall_scene(scene, duration) == 404: # scene not found, clear settings and display error message ADDON.setSettingBool(f"group{self.light_group_id}_{action}Behavior", False) ADDON.setSettingString(f"group{self.light_group_id}_{action}SceneName", "Not Selected") ADDON.setSettingString(f"group{self.light_group_id}_{action}SceneID", "-1") - xbmc.log(f"[SCRIPT.SERVICE.HUE] Scene {scene} not found - group{self.light_group_id}_{action}Behavior ") + log(f"[SCRIPT.SERVICE.HUE] Scene {scene} not found - group{self.light_group_id}_{action}Behavior ") notification(header=_("Hue Service"), message=_("ERROR: Scene not found, it may have been deleted"), icon=xbmcgui.NOTIFICATION_ERROR) else: - xbmc.log(f"[SCRIPT.SERVICE.HUE] Scene {scene} recalled") + log(f"[SCRIPT.SERVICE.HUE] Scene {scene} recalled") except Exception as exc: reporting.process_exception(exc) - xbmc.log(f"[SCRIPT.SERVICE.HUE] LightGroup[{self.light_group_id}] run_action({action}), service_enabled: {service_enabled}, bridge_connected: {self.bridge.connected}") + log(f"[SCRIPT.SERVICE.HUE] LightGroup[{self.light_group_id}] run_action({action}), service_enabled: {service_enabled}, bridge_connected: {self.bridge.connected}") def activate(self): - xbmc.log(f"[SCRIPT.SERVICE.HUE] Activate group [{self.light_group_id}]. State: {self.state}") + log(f"[SCRIPT.SERVICE.HUE] Activate group [{self.light_group_id}]. State: {self.state}") if self.state == STATE_PAUSED: self.onPlayBackPaused() elif self.state == STATE_PLAYING: self.onAVStarted() else: # if not playing and activate is called, probably should do nothing. eg. Don't turn lights on when stopped - xbmc.log(f"[SCRIPT.SERVICE.HUE] Activate group [{self.light_group_id}]. playback stopped, doing nothing. ") + log(f"[SCRIPT.SERVICE.HUE] Activate group [{self.light_group_id}]. playback stopped, doing nothing. ") def _playback_type(self): if self.isPlayingVideo(): @@ -208,8 +208,8 @@ def _video_activation_rules(self): is_pvr = file_name[0:3] == "pvr" # Log settings and values - xbmc.log(f"[SCRIPT.SERVICE.HUE] _video_activation_rules settings: minimum_duration: {minimum_duration}, movie_setting: {movie_setting}, episode_setting: {episode_setting}, music_video_setting: {music_video_setting}, pvr_setting: {pvr_setting}, other_setting: {other_setting}") - xbmc.log(f"[SCRIPT.SERVICE.HUE] _video_activation_rules values: duration: {duration}, is_pvr: {is_pvr}, media_type: {media_type}, file_name: {file_name}") + log(f"[SCRIPT.SERVICE.HUE] _video_activation_rules settings: minimum_duration: {minimum_duration}, movie_setting: {movie_setting}, episode_setting: {episode_setting}, music_video_setting: {music_video_setting}, pvr_setting: {pvr_setting}, other_setting: {other_setting}") + log(f"[SCRIPT.SERVICE.HUE] _video_activation_rules values: duration: {duration}, is_pvr: {is_pvr}, media_type: {media_type}, file_name: {file_name}") # Check if media type matches settings media_type_match = ((movie_setting and media_type == "movie") or @@ -219,10 +219,10 @@ def _video_activation_rules(self): (other_setting and media_type not in ["movie", "episode", "MusicVideo"] and not is_pvr)) if duration >= minimum_duration and media_type_match: - xbmc.log("[SCRIPT.SERVICE.HUE] _video_activation_rules activation: True") + log("[SCRIPT.SERVICE.HUE] _video_activation_rules activation: True") return True - xbmc.log("[SCRIPT.SERVICE.HUE] _video_activation_rules activation: False") + log("[SCRIPT.SERVICE.HUE] _video_activation_rules activation: False") return False def _is_within_schedule(self): @@ -232,7 +232,7 @@ def _is_within_schedule(self): daytime = cache_get("daytime") # Check if it's daytime if daytime: - xbmc.log("[SCRIPT.SERVICE.HUE] Disabled by daytime") + log("[SCRIPT.SERVICE.HUE] Disabled by daytime") return False schedule_enabled = self.settings_monitor.schedule_enabled @@ -241,29 +241,29 @@ def _is_within_schedule(self): # Check if schedule setting is enabled if schedule_enabled: - xbmc.log(f"[SCRIPT.SERVICE.HUE] Schedule enabled: {schedule_enabled}, start: {schedule_start}, end: {schedule_end}") - xbmc.log(f"[SCRIPT.SERVICE.HUE] Schedule enabled: {schedule_enabled}, start: {schedule_start}, end: {schedule_end}") + log(f"[SCRIPT.SERVICE.HUE] Schedule enabled: {schedule_enabled}, start: {schedule_start}, end: {schedule_end}") + log(f"[SCRIPT.SERVICE.HUE] Schedule enabled: {schedule_enabled}, start: {schedule_start}, end: {schedule_end}") # Check if current time is within start and end times if schedule_start < datetime.now().time() < schedule_end: - xbmc.log("[SCRIPT.SERVICE.HUE] _is_within_schedule: True, Enabled by schedule") + log("[SCRIPT.SERVICE.HUE] _is_within_schedule: True, Enabled by schedule") return True else: - xbmc.log("[SCRIPT.SERVICE.HUE] _is_within_schedule. False, Not within schedule") + log("[SCRIPT.SERVICE.HUE] _is_within_schedule. False, Not within schedule") return False # If schedule is not enabled, always return True - xbmc.log("[SCRIPT.SERVICE.HUE] _is_within_schedule: True, Schedule not enabled") + 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: - xbmc.log("[SCRIPT.SERVICE.HUE] _is_scene_already_active: Not enabled") + log("[SCRIPT.SERVICE.HUE] _is_scene_already_active: Not enabled") return False # 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: - xbmc.log("[SCRIPT.SERVICE.HUE] _is_scene_already_active: Current scene not found in scene data") + log("[SCRIPT.SERVICE.HUE] _is_scene_already_active: Current scene not found in scene data") return False # Check if any light in the current scene is on @@ -271,17 +271,17 @@ def skip_time_check_if_light_on(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']: - xbmc.log(f"[SCRIPT.SERVICE.HUE] _is_scene_already_active: Light {light_id} in the scene is on") + log(f"[SCRIPT.SERVICE.HUE] _is_scene_already_active: Light {light_id} in the scene is on") return True - xbmc.log("[SCRIPT.SERVICE.HUE] _is_scene_already_active: No lights in the scene are on") + 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): # 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: - xbmc.log("[SCRIPT.SERVICE.HUE] _is_any_light_off: Current scene not found in scene data") + log("[SCRIPT.SERVICE.HUE] _is_any_light_off: Current scene not found in scene data") return False # Check if any light in the current scene is on @@ -289,7 +289,7 @@ 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']: - xbmc.log(f"[SCRIPT.SERVICE.HUE] _is_any_light_off: Light {light_id} in the scene is on") + log(f"[SCRIPT.SERVICE.HUE] _is_any_light_off: Light {light_id} in the scene is on") return True return False @@ -300,44 +300,44 @@ 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 - xbmc.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] 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}") all_light_states = None if scene and (skip_time_check_if_light_on or skip_scene_if_all_off): # Fetch all light states all_light_states = self.light_group.bridge.make_api_request("GET", "light") - # xbmc.log(f"[SCRIPT.SERVICE.HUE] validate: all_light_states {all_light_states}") + # 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): - xbmc.log("[SCRIPT.SERVICE.HUE] validate: All lights are off, not activating scene") + 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()): - xbmc.log("[SCRIPT.SERVICE.HUE] validate: Not within schedule or video activation rules not met, not activating scene") + log("[SCRIPT.SERVICE.HUE] validate: Not within schedule or video activation rules not met, not activating scene") return False - xbmc.log("[SCRIPT.SERVICE.HUE] validate: Activating scene for VIDEO") + log("[SCRIPT.SERVICE.HUE] validate: Activating scene for VIDEO") 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()): - xbmc.log("[SCRIPT.SERVICE.HUE] validate: Not within schedule or video activation rules not met, not activating scene") + log("[SCRIPT.SERVICE.HUE] validate: Not within schedule or video activation rules not met, not activating scene") return False - xbmc.log("[SCRIPT.SERVICE.HUE] validate: Activating scene for VIDEO") + log("[SCRIPT.SERVICE.HUE] validate: Activating scene for VIDEO") return True elif self.light_group.media_type == AUDIO and scene: if skip_scene_if_all_off and not skip_scene_if_all_off(scene, all_light_states): - xbmc.log("[SCRIPT.SERVICE.HUE] validate: All lights are off, not activating scene") + log("[SCRIPT.SERVICE.HUE] validate: All lights are off, not activating scene") return False if not self._is_within_schedule(): - xbmc.log("[SCRIPT.SERVICE.HUE] validate: Not within schedule, not activating scene") + log("[SCRIPT.SERVICE.HUE] validate: Not within schedule, not activating scene") return False - xbmc.log("[SCRIPT.SERVICE.HUE] validate: Activating scene for AUDIO media type") + log("[SCRIPT.SERVICE.HUE] validate: Activating scene for AUDIO media type") return True elif self.light_group.media_type == AUDIO: if not self._is_within_schedule(): - xbmc.log("[SCRIPT.SERVICE.HUE] validate: Not within schedule, not activating scene") + log("[SCRIPT.SERVICE.HUE] validate: Not within schedule, not activating scene") return False - xbmc.log("[SCRIPT.SERVICE.HUE] validate: Activating scene for AUDIO") + log("[SCRIPT.SERVICE.HUE] validate: Activating scene for AUDIO") return True diff --git a/script.service.hue/resources/lib/menu.py b/script.service.hue/resources/lib/menu.py index fc18b5e6..e97d9e76 100644 --- a/script.service.hue/resources/lib/menu.py +++ b/script.service.hue/resources/lib/menu.py @@ -12,7 +12,7 @@ from xbmcgui import ListItem from . import ADDON, ADDONID, ADDONPATH -from .kodiutils import cache_set, cache_get +from .kodiutils import cache_set, cache_get, log from .language import get_string as _ @@ -22,14 +22,14 @@ def menu(): base_url = sys.argv[0] command = sys.argv[2][1:] parsed = parse_qs(command) - xbmc.log(f"[SCRIPT.SERVICE.HUE] menu: {route}, {addon_handle}, {base_url}, {command}, {parsed}") + log(f"[SCRIPT.SERVICE.HUE] menu: {route}, {addon_handle}, {base_url}, {command}, {parsed}") if route == f"plugin://{ADDONID}/": handle_route(base_url, addon_handle, command) elif route == f"plugin://{ADDONID}/actions": handle_actions_route(parsed, base_url, addon_handle) else: - xbmc.log(f"[SCRIPT.SERVICE.HUE] Unknown command. Handle: {addon_handle}, route: {route}, Arguments: {sys.argv}") + log(f"[SCRIPT.SERVICE.HUE] Unknown command. Handle: {addon_handle}, route: {route}, Arguments: {sys.argv}") def handle_route(base_url, addon_handle, command): @@ -44,20 +44,20 @@ def handle_route(base_url, addon_handle, command): def handle_toggle_command(): if cache_get("service_enabled") and _get_status() != "Disabled by daytime": - xbmc.log("[SCRIPT.SERVICE.HUE] Disable service") + log("[SCRIPT.SERVICE.HUE] Disable service") cache_set("service_enabled", False) elif _get_status() != "Disabled by daytime": - xbmc.log("[SCRIPT.SERVICE.HUE] Enable service") + log("[SCRIPT.SERVICE.HUE] Enable service") cache_set("service_enabled", True) else: - xbmc.log("[SCRIPT.SERVICE.HUE] Disabled by daytime, ignoring") + log("[SCRIPT.SERVICE.HUE] Disabled by daytime, ignoring") xbmc.executebuiltin('Container.Refresh') def handle_actions_route(parsed, base_url, addon_handle): 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}") + 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"))) @@ -68,12 +68,12 @@ def handle_actions_route(parsed, base_url, addon_handle): def build_menu(base_url, addon_handle): - xbmc.log(f"[SCRIPT.SERVICE.HUE] build_menu: status: {_get_status()}") + log(f"[SCRIPT.SERVICE.HUE] build_menu: status: {_get_status()}") status_item = ListItem(_("Hue Status: ") + _get_status()) status_icon = _get_status_icon() if status_icon: status_item.setArt({"icon": status_icon}) - xbmc.log(f"[SCRIPT.SERVICE.HUE] status_icon: {status_icon}") + log(f"[SCRIPT.SERVICE.HUE] status_icon: {status_icon}") settings_item = ListItem(_("Settings")) settings_item.setArt({"icon": xbmcvfs.makeLegalFilename(ADDONPATH + "resources/icons/settings.png")}) add_directory_items(base_url, addon_handle, status_item, settings_item) @@ -91,7 +91,7 @@ def _get_status(): enabled = cache_get("service_enabled") daytime = cache_get("daytime") daytime_disable = ADDON.getSettingBool("daylightDisable") # Legacy setting name, it's daytime everywhere now - xbmc.log(f"[SCRIPT.SERVICE.HUE] _get_status enabled: {enabled} - {type(enabled)}, daytime: {daytime}, daytime_disable: {daytime_disable}") + log(f"[SCRIPT.SERVICE.HUE] _get_status enabled: {enabled} - {type(enabled)}, daytime: {daytime}, daytime_disable: {daytime_disable}") if daytime and daytime_disable: return "Disabled by daytime" elif enabled: @@ -104,7 +104,7 @@ def _get_status_icon(): enabled = cache_get("service_enabled") daytime = cache_get("daytime") daytime_disable = ADDON.getSettingBool("daylightDisable") - # xbmc.log("[SCRIPT.SERVICE.HUE] Current status: {}".format(daytime_disable)) + # log("[SCRIPT.SERVICE.HUE] Current status: {}".format(daytime_disable)) if daytime and daytime_disable: return xbmcvfs.makeLegalFilename(ADDONPATH + "resources/icons/daylight.png") # Disabled by daytime, legacy icon name elif enabled: diff --git a/script.service.hue/resources/lib/reporting.py b/script.service.hue/resources/lib/reporting.py index a99e2899..66ac2b5c 100644 --- a/script.service.hue/resources/lib/reporting.py +++ b/script.service.hue/resources/lib/reporting.py @@ -8,22 +8,22 @@ import traceback import rollbar -import xbmc import xbmcgui from . import ADDONVERSION, ROLLBAR_API_KEY, KODIVERSION, ADDONPATH, ADDON from .language import get_string as _ +from .kodiutils import log def process_exception(exc, level="critical", error="", logging=False): - xbmc.log(f"[SCRIPT.SERVICE.HUE] *** EXCEPTION ***: Type: {type(exc)},\n Exception: {exc},\n Error: {error},\n Traceback: {traceback.format_exc()}") + log(f"[SCRIPT.SERVICE.HUE] *** EXCEPTION ***: Type: {type(exc)},\n Exception: {exc},\n Error: {error},\n Traceback: {traceback.format_exc()}") if ADDON.getSettingBool("error_reporting"): if _error_report_dialog(exc): _report_error(level, error, exc, logging) ''' if exc is RequestException: - xbmc.log("[SCRIPT.SERVICE.HUE] RequestException, not reporting to rollbar") + log("[SCRIPT.SERVICE.HUE] RequestException, not reporting to rollbar") notification(_("Hue Service"), _("Connection Error"), icon=xbmcgui.NOTIFICATION_ERROR) else: ''' @@ -32,7 +32,7 @@ def process_exception(exc, level="critical", error="", logging=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") + log("[SCRIPT.SERVICE.HUE] Error Reporting disabled") ADDON.setSettingBool("error_reporting", False) return False return response diff --git a/script.service.hue/resources/lib/settings.py b/script.service.hue/resources/lib/settings.py index 6e7fe567..516100bb 100644 --- a/script.service.hue/resources/lib/settings.py +++ b/script.service.hue/resources/lib/settings.py @@ -9,7 +9,7 @@ from . import ADDON, BRIDGE_SETTINGS_CHANGED from .language import get_string as _ -from .kodiutils import convert_time, notification +from .kodiutils import convert_time, notification, log class SettingsMonitor(xbmc.Monitor): @@ -25,7 +25,7 @@ def onSettingsChanged(self): self.reload_settings() def reload_settings(self): - xbmc.log("[SCRIPT.SERVICE.HUE] Reloading settings...") + log("[SCRIPT.SERVICE.HUE] Reloading settings...") old_ip = self.ip old_key = self.key @@ -35,7 +35,7 @@ def reload_settings(self): # If IP or key has changed, set flag so core loop knows to try reconnecting if (old_ip != self.ip or old_key != self.key) and self.ip and self.key: - xbmc.log(f"[SCRIPT.SERVICE.HUE] SettingsMonitor: Bridge settings changed: {self.ip} and {self.key}") + log(f"[SCRIPT.SERVICE.HUE] SettingsMonitor: Bridge settings changed: {self.ip} and {self.key}") BRIDGE_SETTINGS_CHANGED.set() self.show500error = ADDON.getSettingBool("show500Error") @@ -108,23 +108,23 @@ def reload_settings(self): self.group3_lights = self.group3_lights.split(",") #split lights on comma - xbmc.log("[SCRIPT.SERVICE.HUE] SettingsMonitor: Settings loaded, validating") + log("[SCRIPT.SERVICE.HUE] SettingsMonitor: Settings loaded, validating") self._validate_schedule() self._validate_ambilight() def _validate_ambilight(self): - xbmc.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: {self.group3_lights}") if self.group3_enabled: if self.group3_lights == '-1': ADDON.setSettingBool('group3_enabled', False) - xbmc.log('[SCRIPT.SERVICE.HUE] _validate_ambilights: No ambilights selected') + log('[SCRIPT.SERVICE.HUE] _validate_ambilights: No ambilights selected') notification(_('Hue Service'), _('No lights selected for Ambilight.'), icon=xbmcgui.NOTIFICATION_ERROR) def _validate_schedule(self): - xbmc.log(f"[SCRIPT.SERVICE.HUE] Validate schedule. Schedule Enabled: {self.schedule_enabled}, Start time: {self.schedule_start}, End time: {self.schedule_end}") + log(f"[SCRIPT.SERVICE.HUE] Validate schedule. Schedule Enabled: {self.schedule_enabled}, Start time: {self.schedule_start}, End time: {self.schedule_end}") if self.schedule_enabled: if self.schedule_start > self.schedule_end: # checking if start time is after the end time ADDON.setSettingBool('EnableSchedule', False) - xbmc.log('[SCRIPT.SERVICE.HUE] _validate_schedule: Start time is after end time, schedule disabled') + log('[SCRIPT.SERVICE.HUE] _validate_schedule: Start time is after end time, schedule disabled') notification(_('Hue Service'), _('Invalid start or end time, schedule disabled'), icon=xbmcgui.NOTIFICATION_ERROR)