Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[script.service.hue@matrix] 2.0.15 #2682

Merged
merged 1 commit into from
Dec 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions script.service.hue/addon.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<addon id="script.service.hue" name="Hue Service" provider-name="zim514" version="2.0.12">
<addon id="script.service.hue" name="Hue Service" provider-name="zim514" version="2.0.15">
<requires>
<import addon="xbmc.python" version="3.0.0"/>
<import addon="script.module.requests" version="2.31.0"/>
Expand All @@ -20,11 +20,10 @@
</assets>
<source>https://github.com/zim514/script.service.hue</source>
<forum>https://forum.kodi.tv/showthread.php?tid=344886</forum>
<news>v2.0.12
- Fix schedule / activation checks
- Fix Music support
- Localisation updates from Weblate
- Fix Ambilight support
<news>v2.0.15
- Hide disabled menu action when service is disabled
- Fix inconsistent activation code
- Crash fix

</news>
<summary lang="ca_ES">Automatitza les llums Hue amb la reproducció de Kodi</summary>
Expand Down
2 changes: 1 addition & 1 deletion script.service.hue/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@
from resources.lib import menu, reporting

try:
menu.menu()
menu.Menu()
except Exception as exc:
reporting.process_exception(exc)
2 changes: 1 addition & 1 deletion script.service.hue/resources/lib/ambigroup.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ def _ambi_loop(self):

self.settings_monitor.waitForAbort(update_interval) # seconds

executor.shutdown(wait=False)
executor.shutdown(wait=False) #stop _update_hue_rgb thread(s)

if not self.settings_monitor.abortRequested(): # ignore writing average process time if Kodi is shutting down
average_process_time = self._perf_average(PROCESS_TIMES)
Expand Down
2 changes: 1 addition & 1 deletion script.service.hue/resources/lib/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from .language import get_string as _


def core():
def core_dispatcher():
settings_monitor = settings.SettingsMonitor()

if len(sys.argv) > 1:
Expand Down
99 changes: 56 additions & 43 deletions script.service.hue/resources/lib/lightgroup.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,30 +46,37 @@ def onAVStarted(self):

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:
if not enabled:
log(f"[SCRIPT.SERVICE.HUE] LightGroup[{self.light_group_id}] not enabled, doing nothing")
return

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.info_tag = self.getVideoInfoTag()
except (AttributeError, TypeError) as x:
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)

if self.activation_check.validate(play_scene):
contents = inspect.getmembers(self.info_tag)
log(f"[SCRIPT.SERVICE.HUE] Start InfoTag: {contents}")

#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")
elif not play_enabled:
log(f"[SCRIPT.SERVICE.HUE] LightGroup[{self.light_group_id}] play action not enabled")
return
elif not self.bridge.connected:
log(f"[SCRIPT.SERVICE.HUE] Bridge not connected")
return
else:
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 self.media_type == self._playback_type() and self._playback_type() == VIDEO:
try:
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 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)

if self.activation_check.validate(play_scene):
contents = inspect.getmembers(self.info_tag)
log(f"[SCRIPT.SERVICE.HUE] Start InfoTag: {contents}")

#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")

def onPlayBackPaused(self):
self.state = STATE_PAUSED
Expand All @@ -79,13 +86,21 @@ def onPlayBackPaused(self):

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:
if not enabled:
log(f"[SCRIPT.SERVICE.HUE] LightGroup[{self.light_group_id}] not enabled, doing nothing")
return
elif not pause_enabled:
log(f"[SCRIPT.SERVICE.HUE] LightGroup[{self.light_group_id}] Pause action not enabled")
return
elif not self.bridge.connected:
log(f"[SCRIPT.SERVICE.HUE] Bridge not connected")
return
else:

if pause_enabled and self.media_type == self._playback_type():
if self.activation_check.validate(pause_scene):
log(f"[SCRIPT.SERVICE.HUE] LightGroup[{self.light_group_id}] Running Pause action")
self.run_action("pause")
if self.media_type == self._playback_type():
if self.activation_check.validate(pause_scene):
log(f"[SCRIPT.SERVICE.HUE] LightGroup[{self.light_group_id}] Running Pause action")
self.run_action("pause")

def onPlayBackStopped(self):
self.state = STATE_STOPPED
Expand All @@ -95,22 +110,21 @@ 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:
if not enabled:
log(f"[SCRIPT.SERVICE.HUE] LightGroup[{self.light_group_id}] not enabled, doing nothing")
return
elif not stop_enabled:
log(f"[SCRIPT.SERVICE.HUE] LightGroup[{self.light_group_id}] Pause action not enabled")
return
elif not self.bridge.connected:
log(f"[SCRIPT.SERVICE.HUE] Bridge not connected")
return
else:
if self.media_type == self.last_media_type or self.media_type == self._playback_type():

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.info_tag)
log(f"[SCRIPT.SERVICE.HUE] Stop[{self.light_group_id}] InfoTag Inspect Contents: {contents}")

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):
log(f"[SCRIPT.SERVICE.HUE] LightGroup[{self.light_group_id}] Running Stop action")
self.run_action("stop")
if self.activation_check.validate(stop_scene):
log(f"[SCRIPT.SERVICE.HUE] LightGroup[{self.light_group_id}] Running Stop action")
self.run_action("stop")

def onPlayBackResumed(self):
# log("[SCRIPT.SERVICE.HUE] In LightGroup[{}], onPlaybackResumed()".format(self.light_group_id))
Expand Down Expand Up @@ -346,4 +360,3 @@ def validate(self, scene=None):
return True
log("[SCRIPT.SERVICE.HUE] Validate Audio: Checks not passed, not activating")
return False

179 changes: 87 additions & 92 deletions script.service.hue/resources/lib/menu.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,97 +16,92 @@
from .language import get_string as _


def menu():
route = sys.argv[0]
addon_handle = int(sys.argv[1])
base_url = sys.argv[0]
command = sys.argv[2][1:]
parsed = parse_qs(command)
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:
log(f"[SCRIPT.SERVICE.HUE] Unknown command. Handle: {addon_handle}, route: {route}, Arguments: {sys.argv}")


def handle_route(base_url, addon_handle, command):
if not command:
build_menu(base_url, addon_handle)
elif command == "settings":
ADDON.openSettings()
class Menu():
def __init__(self):
route = sys.argv[0]
addon_handle = int(sys.argv[1])
base_url = sys.argv[0]
command = sys.argv[2][1:]
parsed = parse_qs(command)
log(f"[SCRIPT.SERVICE.HUE] menu: {route}, {addon_handle}, {base_url}, {command}, {parsed}")

self.enabled = cache_get("service_enabled")
self.daytime = cache_get("daytime")

if route == f"plugin://{ADDONID}/":
self.handle_route(base_url, addon_handle, command)
elif route == f"plugin://{ADDONID}/actions":
self.handle_actions_route(parsed, base_url, addon_handle)
else:
log(f"[SCRIPT.SERVICE.HUE] Unknown command. Handle: {addon_handle}, route: {route}, Arguments: {sys.argv}")

def handle_route(self, base_url, addon_handle, command):
if not command:
self.build_menu(base_url, addon_handle)
elif command == "settings":
ADDON.openSettings()
xbmc.executebuiltin('Container.Refresh')
elif command == "toggle":
self.handle_toggle_command()

def handle_toggle_command(self):
if self.enabled and self._get_status() != "Disabled by daytime":
log("[SCRIPT.SERVICE.HUE] Disable service")
cache_set("service_enabled", False)
elif self._get_status() != "Disabled by daytime":
log("[SCRIPT.SERVICE.HUE] Enable service")
cache_set("service_enabled", True)
else:
log("[SCRIPT.SERVICE.HUE] Disabled by daytime, ignoring")
xbmc.executebuiltin('Container.Refresh')
elif command == "toggle":
handle_toggle_command()


def handle_toggle_command():
if cache_get("service_enabled") and _get_status() != "Disabled by daytime":
log("[SCRIPT.SERVICE.HUE] Disable service")
cache_set("service_enabled", False)
elif _get_status() != "Disabled by daytime":
log("[SCRIPT.SERVICE.HUE] Enable service")
cache_set("service_enabled", True)
else:
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]
log(f"[SCRIPT.SERVICE.HUE] Actions: {action}, light_group_id: {light_group_id}")
if action == "menu":
xbmcplugin.addDirectoryItem(addon_handle, base_url + "?action=play&light_group_id=" + light_group_id, ListItem(_("Play")))
xbmcplugin.addDirectoryItem(addon_handle, base_url + "?action=pause&light_group_id=" + light_group_id, ListItem(_("Pause")))
xbmcplugin.addDirectoryItem(addon_handle, base_url + "?action=stop&light_group_id=" + light_group_id, ListItem(_("Stop")))
xbmcplugin.endOfDirectory(handle=addon_handle, cacheToDisc=True)
else:
cache_set("action", (action, light_group_id))


def build_menu(base_url, addon_handle):
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})
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)
xbmcplugin.endOfDirectory(handle=addon_handle, cacheToDisc=False)


def add_directory_items(base_url, addon_handle, status_item, settings_item):
xbmcplugin.addDirectoryItem(addon_handle, base_url + "/actions?light_group_id=1&action=menu", ListItem(_("Video Scenes")), True)
xbmcplugin.addDirectoryItem(addon_handle, base_url + "/actions?light_group_id=2&action=menu", ListItem(_("Audio Scenes")), True)
xbmcplugin.addDirectoryItem(addon_handle, base_url + "?toggle", status_item)
xbmcplugin.addDirectoryItem(addon_handle, base_url + "?settings", settings_item)


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
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:
return "Enabled"
else:
return "Disabled"


def _get_status_icon():
enabled = cache_get("service_enabled")
daytime = cache_get("daytime")
daytime_disable = ADDON.getSettingBool("daylightDisable")
# 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:
return xbmcvfs.makeLegalFilename(ADDONPATH + "resources/icons/enabled.png") # Enabled
return xbmcvfs.makeLegalFilename(ADDONPATH + "resources/icons/disabled.png") # Disabled
def handle_actions_route(self, parsed, base_url, addon_handle):
action = parsed['action'][0]
light_group_id = parsed['light_group_id'][0]
log(f"[SCRIPT.SERVICE.HUE] Actions: {action}, light_group_id: {light_group_id}")
if action == "menu":
xbmcplugin.addDirectoryItem(addon_handle, base_url + "?action=play&light_group_id=" + light_group_id, ListItem(_("Play")))
xbmcplugin.addDirectoryItem(addon_handle, base_url + "?action=pause&light_group_id=" + light_group_id, ListItem(_("Pause")))
xbmcplugin.addDirectoryItem(addon_handle, base_url + "?action=stop&light_group_id=" + light_group_id, ListItem(_("Stop")))
xbmcplugin.endOfDirectory(handle=addon_handle, cacheToDisc=True)
else:
cache_set("action", (action, light_group_id))

def build_menu(self, base_url, addon_handle):
log(f"[SCRIPT.SERVICE.HUE] build_menu: status: {self._get_status()}")
status_item = ListItem(_("Hue Status: ") + self._get_status())
status_icon = self._get_status_icon()
if status_icon:
status_item.setArt({"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")})
self.add_directory_items(base_url, addon_handle, status_item, settings_item)
xbmcplugin.endOfDirectory(handle=addon_handle, cacheToDisc=False)

def add_directory_items(self, base_url, addon_handle, status_item, settings_item):
xbmcplugin.addDirectoryItem(addon_handle, base_url + "?toggle", status_item)
xbmcplugin.addDirectoryItem(addon_handle, base_url + "?settings", settings_item)
if self.enabled:
xbmcplugin.addDirectoryItem(addon_handle, base_url + "/actions?light_group_id=1&action=menu", ListItem(_("Video Scenes")), True)
xbmcplugin.addDirectoryItem(addon_handle, base_url + "/actions?light_group_id=2&action=menu", ListItem(_("Audio Scenes")), True)

def _get_status(self):
daytime_disable = ADDON.getSettingBool("daylightDisable") # Legacy setting name, it's daytime everywhere now
log(f"[SCRIPT.SERVICE.HUE] _get_status enabled: {self.enabled} - {type(self.enabled)}, daytime: {self.daytime}, daytime_disable: {daytime_disable}")
if self.daytime and daytime_disable:
return "Disabled by daytime"
elif self.enabled:
return "Enabled"
else:
return "Disabled"

def _get_status_icon(self):

daytime_disable = ADDON.getSettingBool("daylightDisable")
# log("[SCRIPT.SERVICE.HUE] Current status: {}".format(daytime_disable))
if self.daytime and daytime_disable:
return xbmcvfs.makeLegalFilename(ADDONPATH + "resources/icons/daylight.png") # Disabled by daytime, legacy icon name
elif self.enabled:
return xbmcvfs.makeLegalFilename(ADDONPATH + "resources/icons/enabled.png") # Enabled
return xbmcvfs.makeLegalFilename(ADDONPATH + "resources/icons/disabled.png") # Disabled
2 changes: 1 addition & 1 deletion script.service.hue/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@
from resources.lib import core, reporting

try:
core.core()
core.core_dispatcher()
except Exception as exc:
reporting.process_exception(exc)