Skip to content

Commit

Permalink
ambilight part 2
Browse files Browse the repository at this point in the history
- support hue gamuts
- rgb blacks & whites cut off support
  • Loading branch information
zim514 committed Jul 14, 2019
1 parent 7b47a65 commit 8946283
Show file tree
Hide file tree
Showing 5 changed files with 195 additions and 44 deletions.
2 changes: 1 addition & 1 deletion script.service.hue/addon.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<addon id="script.service.hue" name="Hue Service"
provider-name="zim514" version="0.5.0~alpha13">
provider-name="zim514" version="0.5.0~alpha14">

<requires>
<import addon="xbmc.python" version="2.26.0" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,8 @@ msgid "[B][I]Warning: Experimental[/B][/I]"
msgstr "[B][I]Warning: Experimental[/B][/I]"

msgctxt "#30522"
msgid "These settings impact performance"
msgstr "These settings impact performance"
msgid "These settings impact system & Hue performance"
msgstr "These settings impact system & Hue performance"

msgctxt "#30523"
msgid "Ambilight"
Expand Down Expand Up @@ -398,4 +398,24 @@ msgctxt "#30057"
msgid "Force on"
msgstr "Force on"

msgctxt "#30058"
msgid "Light Names:"
msgstr "Light Names:"

msgctxt "#30059"
msgid "Light Gamut:"
msgstr "Light Gamut:"

msgctxt "#30060"
msgid "RGB Blacks filter"
msgstr "RGB Blacks filter"

msgctxt "#30061"
msgid "RGB Whites filter"
msgstr "RGB Whites filter"

msgctxt "#30062"
msgid "Colour Filters"
msgstr "Colour filters"


186 changes: 147 additions & 39 deletions script.service.hue/resources/lib/AmbiGroup.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@


from PIL import Image
import colorgram #https://github.com/obskyr/colorgram.py
from . import colorgram #https://github.com/obskyr/colorgram.py
from .rgbxy import Converter# https://github.com/benknight/hue-python-rgb-converter
from .rgbxy import ColorHelper
from .rgbxy import XYPoint
Expand All @@ -20,19 +20,20 @@
import xbmc

from resources.lib.KodiGroup import KodiGroup
from resources.lib.KodiGroup import VIDEO,ALLMEDIA,AUDIO,STATE_IDLE,STATE_PAUSED,STATE_PLAYING
from resources.lib.KodiGroup import VIDEO,AUDIO,ALLMEDIA,STATE_IDLE,STATE_PAUSED,STATE_PLAYING


from . import kodiutils
#from .kodiutils import get_setting,get_setting_as_bool,get_setting_as_int,get_setting_as_float,convertTime
from .qhue import QhueException

import globals
from globals import logger
from . import globals
from .globals import logger
from .language import get_string as _




class AmbiGroup(KodiGroup):
def onAVStarted(self):
logger.info("Ambilight AV Started. Group enabled: {} , isPlayingVideo: {}, isPlayingAudio: {}, self.mediaType: {},self.playbackType(): {}".format(self.kgroupID, self.enabled,self.isPlayingVideo(),self.isPlayingAudio(),self.mediaType,self.playbackType()))
Expand Down Expand Up @@ -81,9 +82,23 @@ def readSettings(self):
self.forceOn=kodiutils.get_setting_as_bool("group{}_forceOn".format(self.kgroupID))
self.setBrightness=kodiutils.get_setting_as_bool("group{}_setBrightness".format(self.kgroupID))
self.brightness=kodiutils.get_setting_as_int("group{}_Brightness".format(self.kgroupID))*255/100#convert percentage to value 1-254
self.blackFilter=kodiutils.get_setting_as_int("group{}_BlackFilter".format(self.kgroupID))
self.whiteFilter=kodiutils.get_setting_as_int("group{}_WhiteFilter".format(self.kgroupID))



#self.lights=kodiutils.get_setting("group{}_Interval".format(self.kgroupID))
self.ambiLights=list(map(int,kodiutils.get_setting("group{}_Lights".format(self.kgroupID)).split(",")))
self.ambiLights={}
lightIDs=kodiutils.get_setting("group{}_Lights".format(self.kgroupID)).split(",")
for L in lightIDs:

gamut=self._getLightGamut(self.bridge,L)
light={L:{'gamut': gamut,'prevxy': (0,0)}}
self.ambiLights.update(light)


#read gamuts, make this a dict
logger.debug("ambilights obj: {}".format(self.ambiLights))


def setup(self, monitor,bridge, kgroupID, flash=False, mediaType=VIDEO):
Expand All @@ -101,66 +116,159 @@ def _getColor(self):
pass

def _ambiLoop(self):
converter=Converter(GamutC)
helper=ColorHelper(GamutC)

cap = xbmc.RenderCapture()


distance=0.0
xy=0.51,0.41
prevxy=0.51,0.41
self.xy=0.5266,0.4133
self.prevxy=0.5266,0.4133
logger.debug("AmbiGroup started!")

while not self.monitor.abortRequested() and self.state == STATE_PLAYING:
startTime = time.time()
cap.capture(250, 250) #async capture request to underlying OS
capImage = cap.getImage() #timeout to wait for OS in ms, default 1000

image = Image.frombuffer("RGBA", (250, 250), buffer(capImage), "raw", "BGRA")

try:
cap.capture(250, 250) #async capture request to underlying OS
capImage = cap.getImage() #timeout to wait for OS in ms, default 1000
image = Image.frombuffer("RGBA", (250, 250), buffer(capImage), "raw", "BGRA")
except Exception:
return #avoid fails in system shutdown



colors = colorgram.extract(image,self.numColors)
#TODO: RGB min and max configurable.
if colors[0].rgb.r < 10 and colors[0].rgb.g < 10 and colors[0].rgb.b <10:
xy=0.5266,0.4133 #max warmth for Gamut C, converted from CT to XY
#xy=converter.rgb_to_xy(1,1,1)
if (colors[0].rgb.r < self.blackFilter and colors[0].rgb.g < self.blackFilter and colors[0].rgb.b <self.blackFilter) or \
(colors[0].rgb.r > self.whiteFilter and colors[0].rgb.g > self.whiteFilter and colors[0].rgb.b > self.whiteFilter):
logger.debug("rgb filter: r,g,b: {},{},{}".format(colors[0].rgb.r,colors[0].rgb.g,colors[0].rgb.b))

xy=0.5266,0.4133 #default

for L in self.ambiLights:
x = Thread(target=self._updateHueXY,name="updateHue", args=(xy,L,self.transitionTime))
x.daemon = True
x.start()

else:
xy=converter.rgb_to_xy(colors[0].rgb.r,colors[0].rgb.g,colors[0].rgb.b)
xy=(round(xy[0],4),round(xy[1],4)) #Hue has a max precision of 4 decimal points.
#self._updateHue(xy,2)
distance=helper.get_distance_between_two_points(XYPoint(xy[0],xy[1]),XYPoint(prevxy[0],prevxy[1])) #only update hue if XY actually changed
if distance > 0:
for L in self.ambiLights:
x = Thread(target=self._updateHue,name="updateHue", args=(xy,L,self.transitionTime))
x = Thread(target=self._updateHueRGB,name="updateHue", args=(colors[0].rgb.r,colors[0].rgb.g,colors[0].rgb.b,L,self.transitionTime))
x.daemon = True
x.start()

endTime= time.time()


if distance > 0:
#logger.debug("time: {},Colors: {}, xy: {},prevxy:{}, distance: {}".format(endTime-startTime,colors,xy,prevxy,distance))
#logger.debug("***** xy: {},prevxy:{}, distance: {}".format(xy,prevxy,distance))
pass
else:
#logger.debug("xy: {},prevxy:{}, distance: {}".format(xy,prevxy,distance))
pass

prevxy=xy
self.monitor.waitForAbort(self.updateInterval) #seconds

logger.debug("AmbiGroup stopped!")




def _updateHue(self,xy,light,transitionTime):
#startTime = time.time()
def _updateHueRGB(self,r,g,b,light,transitionTime):
startTime = time.time()

gamut=self.ambiLights[light].get('gamut')
prevxy=self.ambiLights[light].get('prevxy')

if gamut == "A":
converter=Converter(GamutA)
helper=ColorHelper(GamutA)
elif gamut == "B":
converter=Converter(GamutB)
helper=ColorHelper(GamutB)
elif gamut == "C":
converter=Converter(GamutC)
helper=ColorHelper(GamutC)

xy=converter.rgb_to_xy(r,g,b)
xy=(round(xy[0],4),round(xy[1],4)) #Hue has a max precision of 4 decimal points.

distance=round(helper.get_distance_between_two_points(XYPoint(xy[0],xy[1]),XYPoint(prevxy[0],prevxy[1])) ,4)#only update hue if XY actually changed
if distance > 0:
try:
self.bridge.lights[light].state(xy=xy,transitiontime=transitionTime)
except QhueException as e:
logger.error("Ambi: Hue call fail: {}".format(e))

#=======================================================================
endTime=time.time()
#
#

#logger.debug("_updateHue time: {}".format(int((endTime-startTime)*1000)))

logger.debug("time: {},distance: {}".format(int((endTime-startTime)*1000),distance))
# if distance > 0:
# #logger.debug("time: {},Colors: {}, xy: {},prevxy:{}, distance: {}".format(endTime-startTime,colors,xy,prevxy,distance))
# #logger.debug("***** xy: {},prevxy:{}, distance: {}".format(xy,prevxy,distance))
# pass
# else:
# #logger.debug("* xy: {},prevxy:{}, distance: {}".format(xy,prevxy,distance))
# pass
#
#
#
#=======================================================================
self.ambiLights[light].update(prevxy=xy)


def _updateHueXY(self,xy,light,transitionTime):
startTime = time.time()

gamut=self.ambiLights[light].get('gamut')
prevxy=self.ambiLights[light].get('prevxy')

if gamut == "A":
helper=ColorHelper(GamutA)
elif gamut == "B":
helper=ColorHelper(GamutB)
elif gamut == "C":
helper=ColorHelper(GamutC)


xy=(round(xy[0],4),round(xy[1],4)) #Hue has a max precision of 4 decimal points.

distance=round(helper.get_distance_between_two_points(XYPoint(xy[0],xy[1]),XYPoint(prevxy[0],prevxy[1])) ,4)#only update hue if XY actually changed
if distance > 0:
try:
self.bridge.lights[light].state(xy=xy,transitiontime=transitionTime)
except QhueException as e:
logger.error("Ambi: Hue call fail: {}".format(e))

#=======================================================================
endTime=time.time()
#
#

#logger.debug("_updateHue time: {}".format(int((endTime-startTime)*1000)))

logger.debug("time: {},distance: {}".format(int((endTime-startTime)*1000),distance))
# if distance > 0:
# #logger.debug("time: {},Colors: {}, xy: {},prevxy:{}, distance: {}".format(endTime-startTime,colors,xy,prevxy,distance))
# #logger.debug("***** xy: {},prevxy:{}, distance: {}".format(xy,prevxy,distance))
# pass
# else:
# #logger.debug("* xy: {},prevxy:{}, distance: {}".format(xy,prevxy,distance))
# pass
#
#
#
#=======================================================================
self.ambiLights[light].update(prevxy=xy)




def _getLightGamut(self,bridge,L):
try:
self.bridge.lights[light].state(xy=xy,transitiontime=transitionTime)
except QhueException as e:
logger.error("Ambi: Hue call fail: {}".format(e))
gamut = bridge.lights()[L]['capabilities']['control']['colorgamuttype']
logger.debug("Light: {}, gamut: {}".format(L,gamut))
except Exception:
return None
if gamut == "A" or gamut == "B" or gamut == "C":
return gamut
return None

#endTime=time.time()
#logger.debug("_updateHue time: {}".format(endTime-startTime))


19 changes: 18 additions & 1 deletion script.service.hue/resources/lib/kodiHue.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ def discoverBridgeIP(monitor):




def createUser(monitor, bridgeIP, progressBar=False):
#device = 'kodi#'+getfqdn()
data = '{{"devicetype": "kodi#{}"}}'.format(getfqdn()) #Create a devicetype named kodi#localhostname. Eg: kodi#LibreELEC
Expand Down Expand Up @@ -280,12 +281,28 @@ def configureScene(bridge,kGroupID,action):

def configureAmbiLights(bridge,kGroupID):
lights=selectHueLights(bridge)
lightNames = []
lightGamuts = []
if lights is not None:
kodiutils.set_setting("group{}_Lights".format(kGroupID),','.join(lights))

for L in lights:
lightNames.append(_getLightName(bridge,L))

kodiutils.set_setting("group{}_Lights".format(kGroupID),','.join(lights))
kodiutils.set_setting("group{}_LightNames".format(kGroupID),','.join(lightNames))
globals.ADDON.openSettings()


def _getLightName(bridge,L):
try:
name = bridge.lights()[L]['name']
except Exception:
return None

if name is None:
return None
return name



def selectHueLights(bridge):
Expand Down
8 changes: 7 additions & 1 deletion script.service.hue/resources/settings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,10 @@
<!-- Group playback start setting -->

<setting id="group3_SelectLights" type="action" label="30015" action="RunScript(script.service.hue,ambiLightSelect,3)" option="close" />
<setting id="group3_LightNames" type="text" label="30058" default="-1" enable="false" visible="true" />
<setting id="group3_Lights" type="text" label="30035" default="-1" enable="false" visible="true" />



<setting id="group3_forceOn" type="bool" label="30057" default="false"/>
<setting id="group3_setBrightness" type="bool" label="30056" default="false"/>
Expand All @@ -87,7 +90,10 @@
<setting type="lsep" label="30522" />
<setting id="group3_NumColors" type="slider" label="Number of Colours" default="1" range="1,1,5" option="int" />
<setting id="group3_Interval" type="slider" label="Minimum frame grab interval (ms)" default="100" range="0,100,1000" option="int" />
<setting id="group3_TransitionTime" type="slider" label="Hue Transition time (ms)" default="0" range="0,100,500" option="int" />
<setting id="group3_TransitionTime" type="slider" label="Hue Transition time (ms)" default="100" range="0,100,500" option="int" />
<setting type="lsep" label="30062" />
<setting id="group3_BlackFilter" type="slider" label="30060" default="10" range="1,1,50" option="int" />
<setting id="group3_Whitefilter" type="slider" label="30061" default="255" range="205,1,255" option="int" />

</category>
<!-- /GROUP SECTION 0-->
Expand Down

0 comments on commit 8946283

Please sign in to comment.