This forum uses cookies
This forum makes use of cookies to store your login information if you are registered, and your last visit if you are not. Cookies are small text documents stored on your computer; the cookies set by this forum can only be used on this website and pose no security risk. Cookies on this forum also track the specific topics you have read and when you last read them. Please confirm that you accept these cookies being set.

Run tunable white (HCL)
#1
I made a script that changes the tunable white based on time of day. Maybe someone else can need it in the future. The data for the tunable white parameters was lifted from Schneiders DALI gateway. 

First run this once to store values in storage object HCL (note theres a lot of data, so if you frequent storage a lot maybe store data somewhere else):

Code:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
hclTable = { [0] = {     m1 = 0,     m2 = 10,     m3 = 30,     m4 = 40,     m1v = 2500,     m2v = 2500,     m3v = 2500,     m4v = 2500 }, [1] = {     m1 = 0,     m2 = 10,     m3 = 30,     m4 = 40,     m1v = 2500,     m2v = 2500,     m3v = 2500,     m4v = 2500 }, [2] = {     m1 = 0,     m2 = 10,     m3 = 30,     m4 = 40,     m1v = 2500,     m2v = 2500,     m3v = 2500,     m4v = 2500 }, [3] = {     m1 = 0,     m2 = 10,     m3 = 30,     m4 = 40,     m1v = 2500,     m2v = 2500,     m3v = 2500,     m4v = 2500 }, [4] = {     m1 = 0,     m2 = 10,     m3 = 30,     m4 = 40,     m1v = 2500,     m2v = 2500,     m3v = 2500,     m4v = 2500 }, [5] = {     m1 = 0,     m2 = 10,     m3 = 30,     m4 = 40,     m1v = 2500,     m2v = 2500,     m3v = 2500,     m4v = 2500 }, [6] = {     m1 = 0,     m2 = 10,     m3 = 30,     m4 = 40,     m1v = 2200,     m2v = 2400,     m3v = 2500,     m4v = 2600 }, [7] = {     m1 = 0,     m2 = 10,     m3 = 30,     m4 = 40,     m1v = 2700,     m2v = 3200,     m3v = 4000,     m4v = 5000 }, [8] = {     m1 = 0,     m2 = 10,     m3 = 30,     m4 = 40,     m1v = 6500,     m2v = 6390,     m3v = 6281,     m4v = 6062 }, [9] = {     m1 = 0,     m2 = 10,     m3 = 30,     m4 = 40,     m1v = 5840,     m2v = 5625,     m3v = 5513,     m4v = 5400 }, [10] = {     m1 = 0,     m2 = 10,     m3 = 30,     m4 = 40,     m1v = 5177,     m2v = 4963,     m3v = 4750,     m4v = 4312 }, [11] = {     m1 = 0,     m2 = 10,     m3 = 30,     m4 = 40,     m1v = 4093,     m2v = 3875,     m3v = 3656,     m4v = 3328 }, [12] = {     m1 = 0,     m2 = 10,     m3 = 30,     m4 = 40,     m1v = 3000,     m2v = 3437,     m3v = 3875,     m4v = 4750 }, [13] = {     m1 = 0,     m2 = 10,     m3 = 30,     m4 = 40,     m1v = 5187,     m2v = 5843,     m3v = 6500,     m4v = 6062 }, [14] = {     m1 = 0,     m2 = 10,     m3 = 30,     m4 = 40,     m1v = 5625,     m2v = 5187,     m3v = 4750,     m4v = 4312 }, [15] = {     m1 = 0,     m2 = 10,     m3 = 30,     m4 = 40,     m1v = 3875,     m2v = 3437,     m3v = 3000,     m4v = 3437 }, [16] = {     m1 = 0,     m2 = 10,     m3 = 30,     m4 = 40,     m1v = 3875,     m2v = 4312,     m3v = 4750,     m4v = 5625 }, [17] = {     m1 = 0,     m2 = 10,     m3 = 30,     m4 = 40,     m1v = 6500,     m2v = 5625,     m3v = 4750,     m4v = 3875 }, [18] = {     m1 = 0,     m2 = 10,     m3 = 30,     m4 = 40,     m1v = 3000,     m2v = 3000,     m3v = 2900,     m4v = 2900 }, [19] = {     m1 = 0,     m2 = 10,     m3 = 30,     m4 = 40,     m1v = 2800,     m2v = 2700,     m3v = 2600,     m4v = 2500 }, [20] = {     m1 = 0,     m2 = 10,     m3 = 30,     m4 = 40,     m1v = 2500,     m2v = 2500,     m3v = 2500,     m4v = 2500 }, [21] = {     m1 = 0,     m2 = 10,     m3 = 30,     m4 = 40,     m1v = 2500,     m2v = 2500,     m3v = 2500,     m4v = 2500 }, [22] = {     m1 = 0,     m2 = 10,     m3 = 30,     m4 = 40,     m1v = 2500,     m2v = 2500,     m3v = 2500,     m4v = 2500 }, [23] = {     m1 = 0,     m2 = 10,     m3 = 30,     m4 = 40,     m1v = 2500,     m2v = 2500,     m3v = 2500,     m4v = 2500 }, } storage.set('HCL', hclTable)

I set the next script as a user library, user.HCL. The function get called by a scheduled script that runs every 3 minutes. It returns the value when called. Via other triggers i disable and enable this scheduled script on demand. Note there is a Philips HUE true or false setting. If you have pure Kelvin values on your tunable whites, please set this to false. Setting it to true will convert to Mired color temperature. Also, testMode set to true will only output to log and not return value. debugMode will output to log and value if set to true.

Code:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
function HCL()     -- Fetches HCL table from storage -- data = storage.get('HCL', 0)     -- Time and minutes right now -- local date = os.date('*t') local hour = date.hour local min = date.min     -- Testmode, debugmode and Philips HUE states -- testMode = false debugMode = false philipsHue = false     -- Kelvin values from HCL table -- value1 = data[hour].m1v value2 = data[hour].m2v value3 = data[hour].m3v value4 = data[hour].m4v     -- Sets color values to Mired Color Temperature if Philips Hue is used -- if philipsHue == true then  value1 = math.floor(1000000 / data[hour].m1v)  value2 = math.floor(1000000 / data[hour].m2v)  value3 = math.floor(1000000 / data[hour].m3v)  value4 = math.floor(1000000 / data[hour].m4v) end     -- Time in minutes is between 40 and 59 -- if min <= 59 and data[hour].m4 <= min then  if testMode == true then    log('Testmode: The time is: '..hour..':'..min..'. Fetching Kelvin value for between 40 to 59 mins. Value: '..value4)  else    value = value4      if debugMode == true then      log('Debug: The time is: '..hour..':'..min..'. Fetching Kelvin value for between 40 to 59 mins. Value: '..value4)      end  end  -- Time in minutes is between 30 and 40 -- elseif min <= data[hour].m4 and data[hour].m3 <= min then  if testMode == true then    log('Testmode: The time is: '..hour..':'..min..'. Fetching Kelvin value for between 30 to 40 mins. Value: '..value3)  else    value = value3    if debugMode == true then      log('Debug: The time is: '..hour..':'..min..'. Fetching Kelvin value for between 30 to 40 mins. Value: '..value3)    end  end  -- Time in minutes is between 10 and 30 -- elseif min <= data[hour].m3 and data[hour].m2 <= min then  if testMode == true then    log('Testmode: The time is: '..hour..':'..min..'. Fetching Kelvin value for between 10 to 30 mins. Value: '..value2)  else    value = value2    if debugMode == true then      log('Debug: The time is: '..hour..':'..min..'. Fetching Kelvin value for between 10 to 30 mins. Value: '..value2)    end  end    -- Time in minutes is between 0 and 10 -- elseif min <= data[hour].m2 and data[hour].m1 <= min then  if testMode == true then    log('Testmode: The time is: '..hour..':'..min..'. Fetching Kelvin value for between 0 to 10 mins. Value: '..value1)  else    value = value1    if debugMode == true then      log('Debug: The time is: '..hour..':'..min..'. Fetching Kelvin value for between 0 to 10 mins. Value: '..value1)    end  end    -- Error, time was not detected properly -- else  log('HCL: ERROR, time value was not detected.') if philipsHue == true then      value = math.floor(1000000 / 2700)    else        value = 2700    end end return value   end
Reply
#2
This can be simplified by having a table with time/color temperature values. Script can also do a transition between values.
Reply
#3
Here's my version with linear transition. Values at 0:00 and 24:00 are mandatory, values in between can be added as needed.

Code:
123456789101112131415161718192021222324252627282930313233343536373839
function tomins(hour, min)   return hour * 60 + min end points = {   { hour = 0, min = 0, value = 2500 },   { hour = 7, min = 30, value = 2500 },   { hour = 8, min = 0, value = 3000 },   { hour = 9, min = 0, value = 5000 },   { hour = 16, min = 0, value = 5000 },   { hour = 17, min = 0, value = 3000 },   { hour = 18, min = 30, value = 2500 },   { hour = 24, min = 0, value = 2500 }, } now = os.date('*t') currmins = tomins(now.hour, now.min) for i, point in ipairs(points) do   nextmins = tomins(point.hour, point.min)   if nextmins >= currmins then     nextpoint = point     prevpoint = points[ i - 1 ] or nextpoint     prevmins = tomins(prevpoint.hour, prevpoint.min)     break   end end deltamins = nextmins - prevmins if deltamins > 0 then   delta = (nextpoint.value - prevpoint.value) * (currmins - prevmins) / deltamins else   delta = 0 end value = math.floor(prevpoint.value + delta) log(now.hour, now.min, value)
Reply
#4
Wow, that's a lot simpler! I´ll try this out tonight. I´ll have to write in the conversion for Philips HUE Wink
Reply
#5
(13.08.2018, 11:24)admin Wrote: Here's my version with linear transition. Values at 0:00 and 24:00 are mandatory, values in between can be added as needed.

Code:
123456789101112131415161718192021222324252627282930313233343536373839
function tomins(hour, min)   return hour * 60 + min end points = {   { hour = 0, min = 0, value = 2500 },   { hour = 7, min = 30, value = 2500 },   { hour = 8, min = 0, value = 3000 },   { hour = 9, min = 0, value = 5000 },   { hour = 16, min = 0, value = 5000 },   { hour = 17, min = 0, value = 3000 },   { hour = 18, min = 30, value = 2500 },   { hour = 24, min = 0, value = 2500 }, } now = os.date('*t') currmins = tomins(now.hour, now.min) for i, point in ipairs(points) do   nextmins = tomins(point.hour, point.min)   if nextmins >= currmins then     nextpoint = point     prevpoint = points[ i - 1 ] or nextpoint     prevmins = tomins(prevpoint.hour, prevpoint.min)     break   end end deltamins = nextmins - prevmins if deltamins > 0 then   delta = (nextpoint.value - prevpoint.value) * (currmins - prevmins) / deltamins else   delta = 0 end value = math.floor(prevpoint.value + delta) log(now.hour, now.min, value)
Could this be adjusted for use with two objects for warm and cold white?
Reply
#6
Like this:
Code:
123456789101112131415161718192021222324252627282930313233343536373839404142
function tomins(hour, min)   return hour * 60 + min end points = {   { hour = 0, min = 0, v1 = 25, v2 = 50 },   { hour = 7, min = 30, v1 = 25, v2 = 50 },   { hour = 8, min = 0, v1 = 30, v2 = 30 },   { hour = 9, min = 0, v1 = 50, v2 = 25 },   { hour = 16, min = 0, v1 = 40, v2 = 15 },   { hour = 17, min = 0, v1 = 30, v2 = 30 },   { hour = 18, min = 30, v1 = 25, v2 = 50  },   { hour = 24, min = 0, v1 = 25, v2 = 50 }, } now = os.date('*t') currmins = tomins(now.hour, now.min) for i, point in ipairs(points) do   nextmins = tomins(point.hour, point.min)   if nextmins >= currmins then     nextpoint = point     prevpoint = points[ i - 1 ] or nextpoint     prevmins = tomins(prevpoint.hour, prevpoint.min)     break   end end deltamins = nextmins - prevmins if deltamins > 0 then   d1 = (nextpoint.v1 - prevpoint.v1) * (currmins - prevmins) / deltamins   d2 = (nextpoint.v2 - prevpoint.v2) * (currmins - prevmins) / deltamins else   d1 = 0   d2 = 0 end v1 = math.floor(prevpoint.v1 + d1) v2 = math.floor(prevpoint.v2 + d2) log(now.hour, now.min, v1, v2)
Reply
#7
Yeah, I thought it might be something like that. I also want to have manual dimming of the values, with them still having "same" colour value. Thinking I could make an virtual object with dim value and use this value for offset?
Reply
#8
I assume you have to LED lamps - warm and cold white.
This is the simplest formula that probably won't give very accurate results:
Code:
123456
br = 100 -- brightness (0..100%) ct = 50 -- color temperature (0..100%; 0 = warmest, 100 = coldest) ww = (100 - ct) * br / 100 -- warm white value (0..100%) cw = ct * br / 100 -- cold white value (0..100%) log(ww, cw)

More complicated approach is to use a luxometer and create a table with multiple brightness points which can be used a reference to calculate warm and cold white levels for a given temperature.
Reply
#9
(10.10.2019, 08:14)admin Wrote: I assume you have to LED lamps - warm and cold white.
This is the simplest formula that probably won't give very accurate results:
Code:
123456
br = 100 -- brightness (0..100%) ct = 50 -- color temperature (0..100%; 0 = warmest, 100 = coldest) ww = (100 - ct) * br / 100 -- warm white value (0..100%) cw = ct * br / 100 -- cold white value (0..100%) log(ww, cw)

More complicated approach is to use a luxometer and create a table with multiple brightness points which can be used a reference to calculate warm and cold white levels for a given temperature.

Would this also be usable to use a Dali lamp with tunable white?

I have a Dali 2.0 Trilux light with tunable white but as the Logicmachine is Dali 1.0 it is seen as CW and WW seperately
I have as control panel a MDT touch screen that can steer by 3.007 for both brightness and colour and % for brightness and absolute colour temperature or % back for the colour. 

Basically a Dali 2.0 tunable white converter to Dali 1.0
Reply
#10
DALI color temperature control is already supported by CANx-DALI. The only difference with DALI 2 is how the ballast feature detection works. But then the ballast should only have a single DALI address. Maybe it can be configured? We have a Meanwell ballast that can be switched between single and dual address mode via a physical switch.

You can check the ballast type/mode by sending querydevicetype command in CANx-DALI monitor to the ballast short address. What value do you get?
Reply
#11
(21.09.2023, 05:15)admin Wrote: DALI color temperature control is already supported by CANx-DALI. The only difference with DALI 2 is how the ballast feature detection works. But then the ballast should only have a single DALI address. Maybe it can be configured? We have a Meanwell ballast that can be switched between single and dual address mode via a physical switch.

You can check the ballast type/mode by sending querydevicetype command in CANx-DALI monitor to the ballast short address. What value do you get?

72
07:58:38.656
RX
16 78 B9 05 6A 26 CE F5
0.1 DALI gateway (internal)
11 06
ACK; DATA = 06

71
07:58:38.621
TX
16 78 B9 05 6A 26 CE F5
0.1 DALI gateway (internal)
50 FF 99
BCAST; CMD = querydevicetype (153
Reply
#12
06 is DT6 - basic ballast with brightness level control. It should either report 08 (DT8) or FF (DALI2 multiple types supported).
Reply
#13
(21.09.2023, 08:22)admin Wrote: 06 is DT6 - basic ballast with brightness level control. It should either report 08 (DT8) or FF (DALI2 multiple types supported).

So i have to make it with script converting the data from KNX to Dali

Can i use this script for it? 

Code:
123456
br = 100 -- brightness (0..100%) ct = 50 -- color temperature (0..100%; 0 = warmest, 100 = coldest) ww = (100 - ct) * br / 100 -- warm white value (0..100%) cw = ct * br / 100 -- cold white value (0..100%) log(ww, cw)


or should i try to use the light colour value and change it to 2x 0-100% like i did below in excel

Attached Files Thumbnail(s)
   
Reply
#14
The script does not use color temperature in K but simply 0..100% scale from warmest to coldest. It's up to you which approach to use.
If LED parameters are known you can tune the formula to get a more consistent brightness across the whole color temperature range.
Reply
#15
(21.09.2023, 12:48)admin Wrote: The script does not use color temperature in K but simply 0..100% scale from warmest to coldest. It's up to you which approach to use.
If LED parameters are known you can tune the formula to get a more consistent brightness across the whole color temperature range.

ok i made a first try with scripting but i am not sure if it is right :-)

Code:
123456789101112
--Tunable White br = event.getvalue('1/5/10') ct = event.getvalue('1/0/14') br = 100 -- brightness (0..100%) ct = 50 -- color temperature (0..100%; 0 = warmest, 100 = coldest) ww = (100 - ct) * br / 100 -- warm white value (0..100%) cw = ct * br / 100 -- cold white value (0..100%) value = cw grp.write('32/6/1', value) value = ww grp.write('32/6/2', value) end
Reply
#16
Should be something like this:
Code:
12345678
br = grp.getvalue('1/5/10') ct = grp.getvalue('1/0/14') ww = (100 - ct) * br / 100 -- warm white value (0..100%) cw = ct * br / 100 -- cold white value (0..100%) grp.write('32/6/1', cw) grp.write('32/6/2', ww)

You need to attach the same script to both brightness and color temp value objects. grp.getvalue should be used because event.getvalue only returns the value of the object that triggered the event.
Reply
#17
(21.09.2023, 14:45)admin Wrote: Should be something like this:
Code:
12345678
br = grp.getvalue('1/5/10') ct = grp.getvalue('1/0/14') ww = (100 - ct) * br / 100 -- warm white value (0..100%) cw = ct * br / 100 -- cold white value (0..100%) grp.write('32/6/1', cw) grp.write('32/6/2', ww)

You need to attach the same script to both brightness and color temp value objects. grp.getvalue should be used because event.getvalue only returns the value of the object that triggered the event.

or can i run it as a resident script with 0 seconds?

If i would like to turn out the value of dali directly instead if in percent is it right like this (reference is colour temp in K so it also works with homekit)?

CW or WW Light is 1% = 85 and 100% = 254

Code:
12345678
br = grp.getvalue('1/5/10') ct = grp.getvalue('1/0/14') ww = ((100 - (ct - 2700) * 0.000263157894736842) * br / 100) * 169 + 85  -- warm white value (85..254) cw = (((ct - 2700) * 0.000263157894736842) * br / 100) * 169 + 85 -- cold white value (85..254) grp.write('32/6/1', cw) grp.write('32/6/2', ww)
Reply


Forum Jump: