def multiselect_lights(bridge_ip, bridge_user, label, exclude, preselect): xbmclog('Kodi Hue: In multiselect_lights(bridge_ip={}, bridge_user={}, ' 'label={}, exclude={}, preselect={})'.format( bridge_ip, bridge_user, label, exclude, preselect) ) lights = bridge.get_lights_by_ids(bridge_ip, bridge_user) actual_lights = [] items = [] preselect_items = [] index = 0 for light_id, light in lights.items(): if str(light_id) not in exclude.split(','): items.append(xbmcgui.ListItem(label=light.name)) actual_lights.append(light) if str(light_id) in preselect.split(','): preselect_items.append(index) index += 1 selected = xbmcgui.Dialog().multiselect(label, items, preselect=preselect_items) if selected: light_ids = [str(actual_lights[idx].light_id) for idx in selected] return ','.join(light_ids) return ''
def update_controllers(self): self.ambilight_controller = AmbilightController( bridge.get_lights_by_ids( self.settings.bridge_ip, self.settings.bridge_user, self.settings.ambilight_group.split(',')), self.settings ) self.theater_controller = TheaterController( bridge.get_lights_by_ids( self.settings.bridge_ip, self.settings.bridge_user, self.settings.theater_group.split(',')), self.settings ) self.static_controller = StaticController( bridge.get_lights_by_ids( self.settings.bridge_ip, self.settings.bridge_user, self.settings.static_group.split(',')), self.settings ) xbmclog( 'Kodi Hue: In Hue.update_controllers() instantiated following ' 'controllers {} {} {}'.format( self.theater_controller, self.ambilight_controller, self.static_controller, ) )
def run(): player = MyPlayer() if player is None: xbmclog('Kodi Hue: In run() could not instantiate player') return while not monitor.abortRequested(): if len(hue.ambilight_controller.lights) and not ev.is_set(): startReadOut = False vals = {} if player.playingvideo: # only if there's actually video try: vals = capture.getImage(200) if len(vals) > 0 and player.playingvideo: startReadOut = True if startReadOut: screen = image.Screenshot( capture.getImage()) hsv_ratios = screen.spectrum_hsv( screen.pixels, hue.settings.ambilight_threshold_value, hue.settings.ambilight_threshold_saturation, hue.settings.color_bias, len(hue.ambilight_controller.lights) ) for i in range(len(hue.ambilight_controller.lights)): algorithm.transition_colorspace( hue, hue.ambilight_controller.lights.values()[i], hsv_ratios[i], ) except ZeroDivisionError: pass if monitor.waitForAbort(0.1): xbmclog('Kodi Hue: In run() deleting player') del player # might help with slow exit.
def on_playback_pause(self): if self.settings.theater_pause_dim_subgroup: xbmclog('Kodi Hue: In TheaterController.on_playback_pause() ' 'undimming theater subgroup') if self.settings.theater_pause_bri_override: self.set_state( bri=self.settings.theater_pause_bri, lights=self.settings.theater_subgroup.split(','), force_on=self.settings.force_light_on, ) else: self.restore_initial_state( lights=self.settings.theater_subgroup.split(','), force_on=self.settings.force_light_on, ) else: xbmclog('Kodi Hue: In TheaterController.on_playback_pause() ' 'undimming theater group') if self.settings.theater_pause_bri_override: self.set_state( bri=self.settings.theater_pause_bri, force_on=self.settings.force_light_on, ) else: self.restore_initial_state( force_on=self.settings.force_light_on, )
def onPlayBackResumed(self): xbmclog('Kodi Hue: In MyPlayer.onPlayBackResume()') state_changed("resumed", self.duration) if self.isPlayingVideo(): self.playingvideo = True if self.duration == 0: self.duration = self.getTotalTime()
def onPlayBackEnded(self): xbmclog('Kodi Hue: In MyPlayer.onPlayBackEnded()') # If there are upcoming plays, ignore if self.playlistpos < self.playlistlen-1: return self.playingvideo = False state_changed("stopped", self.duration)
def onPlayBackStarted(self): xbmclog('Kodi Hue: In MyPlayer.onPlayBackStarted()') playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO) self.playlistlen = playlist.size() self.playlistpos = playlist.getposition() self.playingvideo = True self.duration = self.getTotalTime() state_changed("started", self.duration)
def save_state_as_initial(self, lights=None): xbmclog( 'Kodi Hue: In {}.save_state_as_initial(lights={})' .format(self.__class__.__name__, lights) ) for light in self._calculate_subgroup(lights): light.save_state_as_initial()
def on_playback_start(self): if self.settings.ambilight_start_dim_enable: self.save_state_as_initial() xbmclog('Kodi Hue: In AmbilightController.on_playback_start() ' 'dimming ambilight group') self.set_state( bri=self.settings.ambilight_start_dim, force_on=self.settings.force_light_on, )
def on_playback_stop(self): xbmclog('Kodi Hue: In TheaterController.on_playback_stop() ' 'undimming theater group') if self.settings.theater_stop_bri_override: self.set_state( bri=self.settings.theater_stop_bri, force_on=self.settings.force_light_on, ) else: self.restore_initial_state( force_on=self.settings.force_light_on, )
def _calculate_subgroup(self, lights=None): if lights is None: ret = self.lights.values() else: ret = [light for light in self.lights.values() if light.light_id in lights] xbmclog( 'Kodi Hue: In {}._calculate_subgroup' '(lights={}) returning {}'.format( self.__class__.__name__, lights, ret) ) return ret
def flash_lights(self): xbmclog( 'Kodi Hue: In {} flash_lights())' .format(self.__class__.__name__) ) self.set_state( on=False, force_on=self.settings.force_light_on, ) self.restore_initial_state( force_on=self.settings.force_light_on, )
def on_playback_pause(self): if self.settings.ambilight_start_dim_enable: xbmclog('Kodi Hue: In AmbilightController.on_playback_pause() ' 'undimming ambilight group') if self.settings.ambilight_pause_bri_override: bri = self.settings.ambilight_pause_bri self.set_state( bri=bri, force_on=self.settings.force_light_on, ) else: self.restore_initial_state( force_on=self.settings.force_light_on, )
def on_playback_start(self): # Let's keep only the last user-set state # BUT! Avoid theater subgroup if enabled subgroup = None if self.settings.theater_pause_dim_subgroup: subgroup = self.settings.theater_subgroup.split(',') self.save_state_as_initial(subgroup) # Theater dimming xbmclog('Kodi Hue: In TheaterController.on_playback_start() ' 'dimming theater group') self.set_state( bri=self.settings.theater_start_bri, force_on=self.settings.force_light_on, )
def onNotification(self, sender, method, data): xbmclog('Kodi Hue: In onNotification(sender={}, method={}, data={})' .format(sender, method, data)) if sender == __addon__.getAddonInfo('id'): if method == 'Other.start_setup_theater_lights': ret = ui.multiselect_lights( self.settings.bridge_ip, self.settings.bridge_user, 'Select Theater Lights', ','.join([self.settings.ambilight_group, self.settings.static_group]), self.settings.theater_group ) self.settings.update(theater_group=ret) hue.update_controllers() if method == 'Other.start_setup_theater_subgroup': ret = ui.multiselect_lights( self.settings.bridge_ip, self.settings.bridge_user, 'Select Theater Subgroup', ','.join([self.settings.ambilight_group, self.settings.static_group]), self.settings.theater_subgroup ) self.settings.update(theater_subgroup=ret) hue.update_controllers() if method == 'Other.start_setup_ambilight_lights': ret = ui.multiselect_lights( self.settings.bridge_ip, self.settings.bridge_user, 'Select Ambilight Lights', ','.join([self.settings.theater_group, self.settings.static_group]), self.settings.ambilight_group ) self.settings.update(ambilight_group=ret) hue.update_controllers() if method == 'Other.start_setup_static_lights': ret = ui.multiselect_lights( self.settings.bridge_ip, self.settings.bridge_user, 'Select Static Lights', ','.join([self.settings.theater_group, self.settings.ambilight_group]), self.settings.static_group ) self.settings.update(static_group=ret) hue.update_controllers()
def restore_initial_state(self, lights=None, force_on=True): xbmclog( 'Kodi Hue: In {}.restore_initial_state(lights={})' .format(self.__class__.__name__, lights) ) for light in self._calculate_subgroup(lights): if not force_on and not light.init_on: continue transition_time = self.settings.dim_time if self.settings.proportional_dim_time: transition_time = self._transition_time(light, light.init_bri) light.restore_initial_state( transition_time )
def on_playback_start(self): xbmclog('Kodi Hue: In StaticController.on_playback_start() ' 'turning on static group') hue = None if self.settings.static_start_hue_override: hue = self.settings.static_start_hue sat = None if self.settings.static_start_sat_override: sat = self.settings.static_start_sat if self.settings.static_start_random: hue = random.randint(0, 65535) sat = random.randint(100, 254) self.set_state( hue=hue, sat=sat, bri=self.settings.static_start_bri, on=True, )
def set_state(self, hue=None, sat=None, bri=None, on=None, transition_time=None, lights=None, force_on=True): xbmclog( 'Kodi Hue: In {}.set_state(hue={}, sat={}, bri={}, ' 'on={}, transition_time={}, lights={}, force_on={})'.format( self.__class__.__name__, hue, sat, bri, on, transition_time, lights, force_on ) ) for light in self._calculate_subgroup(lights): if not force_on and not light.init_on: continue if bri: if self.settings.proportional_dim_time: transition_time = self._transition_time(light, bri) else: transition_time = self.settings.dim_time light.set_state( hue=hue, sat=sat, bri=bri, on=on, transition_time=transition_time )
def state_changed(state, duration): xbmclog('Kodi Hue: In state_changed(state={}, duration={})'.format( state, duration)) if (xbmc.getCondVisibility('Window.IsActive(screensaver-atv4.xml)') or xbmc.getCondVisibility('Window.IsActive(screensaver-video-main.xml)')): return if duration < hue.settings.misc_disableshort_threshold and hue.settings.misc_disableshort: return if state == "started": # start capture when playback starts capture_width = 32 # 100 capture_height = capture_width / capture.getAspectRatio() if capture_height == 0: capture_height = capture_width # fix for divide by zero. capture.capture(int(capture_width), int(capture_height)) if state == "started" or state == "resumed": ev.set() hue.theater_controller.on_playback_start() hue.ambilight_controller.on_playback_start() hue.static_controller.on_playback_start() ev.clear() elif state == "paused": ev.set() hue.theater_controller.on_playback_pause() hue.ambilight_controller.on_playback_pause() hue.static_controller.on_playback_pause() elif state == "stopped": ev.set() hue.theater_controller.on_playback_stop() hue.ambilight_controller.on_playback_stop() hue.static_controller.on_playback_stop()
def onPlayBackStopped(self): xbmclog('Kodi Hue: In MyPlayer.onPlayBackStopped()') state_changed("stopped", self.duration) self.playingvideo = False self.playlistlen = 0
def onPlayBackPaused(self): xbmclog('Kodi Hue: In MyPlayer.onPlayBackPaused()') state_changed("paused", self.duration) if self.isPlayingVideo(): self.playingvideo = False
def on_playback_stop(self): xbmclog('Kodi Hue: In StaticController.on_playback_pause() ' 'restoring static group') self.restore_initial_state()
def on_playback_pause(self): xbmclog('In StaticController.on_playback_pause() ' 'turning off static group') self.set_state(on=False, force_on=True)
def __init__(self): xbmclog('Kodi Hue: In MyPlayer.__init__()') xbmc.Player.__init__(self)
def save_state_as_initial(self, lights=None): xbmclog('Kodi Hue: In {}.save_state_as_initial(lights={})'.format( self.__class__.__name__, lights)) for light in self._calculate_subgroup(lights): light.save_state_as_initial()
def on_playback_pause(self): xbmclog('Kodi Hue: In StaticController.on_playback_pause() ' 'turning off static group') self.set_state(on=False, )
def onPlayBackStopped(self): xbmclog('In KodiPlayer.onPlayBackStopped()') self.ga.sendEventData("Playback", "Stopped", "Video") self.hue_service.state_changed("stopped", self.duration) self.playingvideo = False self.playlistlen = 0
def __init__(self): xbmclog('In KodiMonitor.__init__()') xbmc.Monitor.__init__(self)
def setSetting(key, value): # Get or add addon setting global __addon__ if value is not None: __addon__.setSetting(key, value) xbmclog("Setting {}={}".format(key, value))
def on_playback_stop(self): xbmclog('In StaticController.on_playback_pause() ' 'restoring static group') self.restore_initial_state(force_on=True)
def onSettingsChanged(self): hue.settings.readxml() xbmclog('Kodi Hue: In onSettingsChanged() {}'.format(hue.settings)) hue.update_controllers()
def __init__(self): xbmclog('In KodiMonitor.__init__()') xbmc.Monitor.__init__(self) self.ga = GoogleAnalytics()
def formatException(self): stack = traceback.extract_stack() exc_type, exc_obj, exc_tb = sys.exc_info() tb = traceback.extract_tb(exc_tb) full_tb = stack[:-1] + tb #log.error(str(full_tb)) # get last stack frame latestStackFrame = None if (len(tb) > 0): latestStackFrame = tb[-1] #log.error(str(tb)) fileStackTrace = "" try: # get files from stack stackFileList = [] for frame in full_tb: #log.error(str(frame)) frameFile = (os.path.split(frame[0])[1])[:-3] frameLine = frame[1] if (len(stackFileList) == 0 or stackFileList[-1][0] != frameFile): stackFileList.append([frameFile, [str(frameLine)]]) else: file = stackFileList[-1][0] lines = stackFileList[-1][1] lines.append(str(frameLine)) stackFileList[-1] = [file, lines] #log.error(str(stackFileList)) for item in stackFileList: lines = ",".join(item[1]) fileStackTrace += item[0] + "," + lines + ":" #log.error(str(fileStackTrace)) except Exception as e: fileStackTrace = None # log.error(e) xbmclog(e) errorType = "NA" errorFile = "NA" if latestStackFrame is not None: if fileStackTrace is None: fileStackTrace = os.path.split( latestStackFrame[0])[1] + ":" + str(latestStackFrame[1]) codeLine = "NA" if (len(latestStackFrame) > 3 and latestStackFrame[3] != None): codeLine = latestStackFrame[3].strip() errorFile = "%s(%s)(%s)" % (fileStackTrace, exc_obj.message, codeLine) errorFile = errorFile[0:499] errorType = "%s" % (exc_type.__name__) #log.error(errorType + " - " + errorFile) del (exc_type, exc_obj, exc_tb) return errorType, errorFile
def onNotification(self, sender, method, data): xbmclog('Kodi Hue: In onNotification(sender={}, method={}, data={})'. format(sender, method, data))
__resource__ = xbmc.translatePath(os.path.join(__cwd__, 'resources', 'lib')) sys.path.append(__resource__) from settings import Settings from tools import get_version, xbmclog from ambilight_controller import AmbilightController from theater_controller import TheaterController from static_controller import StaticController import bridge import ui import algorithm import image import xbmc xbmclog("Kodi Hue: In .(argv={}) service started, version: {}".format( sys.argv, get_version())) ev = Event() capture = xbmc.RenderCapture() fmt = capture.getImageFormat() # BGRA or RGBA fmtRGBA = fmt == 'RGBA' xbmc_version=xbmc.getInfoLabel("System.BuildVersion") version=float(xbmc_version[:4]) class MyMonitor(xbmc.Monitor): def __init__(self, settings): xbmc.Monitor.__init__(self) self.settings = settings
def update_controllers(self): xbmclog( 'Kodi Hue: In Hue.update_controllers() instantiated following ')
def set_state(self, hue=0, sat=0, bri=0, kel=3500, on=None, transition_time=None): # NOTE: From https://github.com/mclarkk/lifxlan - # rapid is True/False. If True, don't wait for successful confirmation, just send multiple packets and move on # rapid is meant for super-fast light shows with lots of changes. state = { 'hue' : self.hue, 'sat' : self.sat, 'bri' : self.bri, 'kel' : self.kel, 'on' : None, 'transitiontime' : 0, 'rapid' : False } xbmclog('set_state() - light={} - requested_state: HSBK=[{}, {}, {}, {}] on={} transition_time={})'.format(self.name, hue, sat, bri, kel, on, transition_time)) xbmclog("set_state() - light={} - current_state: HSBK=[{}, {}, {}, {}] on={}".format(self.name, self.hue, self.sat, self.bri, self.kel, self.on)) # reset `hue` and `sat` if light doesn't support color if not self.supports_color: hue = self.init_hue sat = self.init_sat # reset `kel` if light doesn't support temperature if not self.supports_temperature: kel = self.init_kel if transition_time is not None: state['transitiontime'] = transition_time # transition_time less than 1 second => set rapid = True state['rapid'] = True if transition_time < 1/100 else False if hue is not None and hue != self.hue: self.hue = hue state['hue'] = hue if sat is not None and sat != self.sat: self.sat = sat state['sat'] = sat # Use initial Kel values if `sat` == 0 and `kel` == None if sat == 0 and kel is None: kel = self.init_kel # Override `kel` to neutral if `sat` > 0 if sat > 0: kel = 3500 if bri is not None and bri != self.bri: self.bri = bri state['bri'] = bri # Turn the light on or off based on `bri` value # No need to turn the power off, just setting the bri=0 should turn the light "off", but keep the power=on # if bri <= 0 and self.on and on is None: # on = False if bri >= 1 and not self.on and on is None: on = True if kel is not None and self.supports_temperature and kel != self.kel: self.kel = kel state['kel'] = kel # if `kel` is not neutral, reset `hue`, `sat` to initial values if kel != 3500: self.hue = self.init_hue self.sat = self.init_sat state['hue'] = self.init_hue state['sat'] = self.init_sat if on is not None and on != self.on: self.on = on state['on'] = on xbmclog('set_state() - light={} - final_state={})'.format(self.name, state)) # Set power state if state['on'] != None: try: self.light.set_power(state['on'], rapid=False) except WorkflowException as error: errStrings = ga.formatException() ga.sendExceptionData(errStrings[0]) ga.sendEventData("Exception", errStrings[0], errStrings[1]) xbmclog("set_state() - set_power({}) - Exception - {}".format(state['on'], str(error))) # Set color state try: # NOTE: # Lifx color is a list of HSBK values: [hue (0-65535), saturation (0-65535), brightness (0-65535), Kelvin (2500-9000)] # 65535/255 = 257 color = [int(state['hue']),int(state['sat']*257),int(state['bri']*257),int(state['kel'])] # xbmclog('set_state() - light={} - color={})'.format(self.name, color)) # color_log = [int(data["hue"]*360/65535),int(data["sat"]*100/255),int(data["bri"]*100/255),int(data["kel"])] # self.logger.debuglog("set_light2: %s: %s (%s ms)" % (self.light.get_label(), color_log, data["transitiontime"]*self.multiplier)) # NOTE: # Lifxlan duration is in miliseconds, for hue it's multiple of 100ms - https://developers.meethue.com/documentation/lights-api#16_set_light_state self.light.set_color(color, state['transitiontime']*100, rapid=state['rapid']) except WorkflowException as error: errStrings = ga.formatException() ga.sendExceptionData(errStrings[0]) ga.sendEventData("Exception", errStrings[0], errStrings[1]) xbmclog("set_color() - light={} - failed to set_color({}) - Exception - {}".format(self.name, color, str(error)))
def on_playback_pause(self): xbmclog('Kodi Hue: In StaticController.on_playback_pause() ' 'turning off static group') self.set_state( on=False, )
def __init__(self): xbmclog('In KodiPlayer.__init__()') xbmc.Player.__init__(self) self.ga = GoogleAnalytics()
def onPlayBackPaused(self): xbmclog('In KodiPlayer.onPlayBackPaused()') if self.isPlayingVideo(): self.ga.sendEventData("Playback", "Paused", "Video") self.hue_service.state_changed("paused", self.duration) self.playingvideo = False
__cwd__ = __addon__.getAddonInfo('path') __resource__ = xbmc.translatePath(os.path.join(__cwd__, 'resources', 'lib')) sys.path.append(__resource__) from settings import Settings from tools import get_version, xbmclog from ambilight_controller import AmbilightController from theater_controller import TheaterController from static_controller import StaticController import bridge import ui import algorithm import image xbmclog("Kodi Hue: In .(argv={}) service started, version: {}".format( sys.argv, get_version())) ev = Event() capture = xbmc.RenderCapture() fmt = capture.getImageFormat() # BGRA or RGBA fmtRGBA = fmt == 'RGBA' class MyMonitor(xbmc.Monitor): def __init__(self, settings): xbmc.Monitor.__init__(self) self.settings = settings def onSettingsChanged(self):