Control Philips Hue lamps from KNX bus
Example: Control Philips Hue lamps with LogicMachine
Task
How to control Philips Hue lamps from KNX bus?
Below see examples how to control HUE lamps from LogicMachine making it Bacnet to HUE, Modbus to HUE, KNX to HUE, EnOcean to HUE and other protocol to HUE gateway.
There are two version of integration scripts – V1 for old type HUEs without integration into HomeKit, V2 – for new HUE with changed API for HomeKit support.
V1 HUE scripts
Event based script for On/Off a lamp or send predefined HUE value
Add Event-based script for the KNX group address which you want to control the Philips Hue from. In this example it is 12/0/4
- require('socket.http')
- body_on = '{"on":true,"sat":255,"bri":255,"hue":6144}'
- body_off = '{"on":false}'
- response = {}
-
- start = grp.getvalue('12/4/0')
-
- -- Turn Philips hue lamp 4 and 5 on
- if start==true
- then
-
- socket.http.request({
- url = "http://192.168.178.21/api/richardmeiland/lights/4/state",
- method = 'PUT',
- sink = ltn12.sink.table(response),
- headers = {
- ['content-length'] = #body_on,
- ['content-type'] = 'application/json',
- },
- source = ltn12.source.string(body_on),
- })
- log(response)
-
- socket.http.request({
- url = "http://192.168.178.21/api/richardmeiland/lights/5/state",
- method = 'PUT',
- sink = ltn12.sink.table(response),
- headers = {
- ['content-length'] = #body_on,
- ['content-type'] = 'application/json',
- },
- source = ltn12.source.string(body_on),
- })
- log(response)
- end
-
- -- Turn Philips hue lamp 4 and 5 off
-
- if start==false
- then
-
- socket.http.request({
- url = "http://192.168.178.21/api/richardmeiland/lights/4/state",
- method = 'PUT',
- sink = ltn12.sink.table(response),
- headers = {
- ['content-length'] = #body_off,
- ['content-type'] = 'application/json',
- },
- source = ltn12.source.string(body_off),
- })
- log(response)
-
- socket.http.request({
- url = "http://192.168.178.21/api/richardmeiland/lights/5/state",
- method = 'PUT',
- sink = ltn12.sink.table(response),
- headers = {
- ['content-length'] = #body_off,
- ['content-type'] = 'application/json',
- },
- source = ltn12.source.string(body_off),
- })
- log(response)
- end
Created by Richard Meiland
HUE control with RGB wheel
In case of more advanced control scenarios, like control from RGB wheel, you can use the following script. The script calculates the HUE values on the fly from RGB color wheel and in such way you have dynamic HUE control instead of predefined values (like in the example above, where we use bit objects). This and the following examples requires user.rgb user library to be present.
user.rgb library:
- function value_to_rgb (value)
- value = lmcore.inttohex(value, 3)
- redandgreen = string.sub(value, 1, 4)
- red = string.sub(redandgreen, 1, 2)
- green = string.sub(redandgreen, -2)
- blue = string.sub(value, -2)
- valuered = lmcore.hextoint (red)
- valuegreen = lmcore.hextoint (green)
- valueblue = lmcore.hextoint (blue)
- low = math.min(unpack({valuered, valuegreen, valueblue}))
- high = math.max(unpack({valuered, valuegreen, valueblue}))
- saturation = math.floor((100 * ((high - low) / high)) + 0.5)
- if valuered == 0 and valuegreen == 0 and valueblue == 0 then
- white = 0
- else
- white = math.floor(((255 - saturation) / 255 * (valuered + valuegreen + valueblue) / 3)+ 0.5)
- end
- return valuered,valuegreen,valueblue,white
- end
-
- function rgb_to_value (r, g, b)
- red = lmcore.inttohex(r, 1)
- green = lmcore.inttohex(g, 1)
- blue = lmcore.inttohex(b, 1)
- rgb = red .. green .. blue
- value = lmcore.hextoint(rgb,3)
- return value
- end
-
- function hsb_to_rgb(h, s, b)
- local hi = math.floor( h / 60 ) % 6
- local f = h/60 - math.floor(h/60)
- local p = b*(1-s)
- local q = b*(1-f*s)
- local t = b*(1-(1-f)*s)
- if hi == 0 then
- return b, t, p
- elseif hi == 1 then
- return q, b, p
- elseif hi == 2 then
- return p, b, t
- elseif hi == 3 then
- return p, q, b
- elseif hi == 4 then
- return t, p, b
- else
- return b, p, q
- end
- end
-
- function rgb_to_hsb(r, g, b)
- local max, min = math.max(r, g, b), math.min(r, g, b)
- local h, s, v
- if max == min then
- h = 0
- elseif max == r and g >= b then
- h = 60 * (g-b)/(max-min)
- elseif max == r and g < b then
- h = 60 * (g-b)/(max-min) + 360
- elseif max == g then
- h = 60 * (b-r)/(max-min) + 120
- else
- h = 60 * (r-g)/(max-min) + 240
- end
- if max == 0 then
- s = 0
- else
- s = 1 - min/max
- end
- v = max
- return h, s, v
- end
Event-based script:
- require('user.rgb')
-
- value = event.getvalue()
- Red, Green, Blue = value_to_rgb(value)
-
- H,S,B = rgb_to_hsb(Red,Green,Blue)
-
- Value_Hue = math.floor(H + 0.5)
- Value_Saturation = math.floor((S * 100) + 0.5)
- Value_Brightness = math.floor(((B / 256) * 100) + 0.5)
-
- -- log ("Hue = " .. Value_Hue .. "°")
- -- log ("Sat = " .. Value_Saturation .. "%")
- -- log ("Bri = " .. Value_Brightness .. "%")
-
- Phillips_Hue = math.floor((Value_Hue * 182.044) + 0.5) -- Convert degrees to 2 byte value
- Phillips_Saturation = math.floor((Value_Saturation * 2.55) + 0.5) -- Convert % to 1 byte value
- Phillips_Brightness = math.floor((Value_Brightness * 2.55) + 0.5) -- Convert % to 1 byte value
-
- if Phillips_Brightness > 0 or Phillips_Saturation > 0 or Phillips_Hue > 0 then
- body_value = '{"on":true,"sat":' .. Phillips_Saturation .. ',"bri":' .. Phillips_Brightness .. ',"hue":' .. Phillips_Hue .. '}'
- log(body_value)
- else
- body_value = '{"on":false}'
- log(body_value)
- end
-
- require('socket.http')
- response = {}
-
- socket.http.request({
- url = "http://192.168.1.1/api/username/lights/1/state",
- method = 'PUT',
- sink = ltn12.sink.table(response),
- headers = {
- ['content-length'] = #body_value,
- ['content-type'] = 'application/json',
- },
- source = ltn12.source.string(body_value),
- })
- --log(response)
Dim RGB over the brightness value and keep same (color scope) during dimming
This example requires user.rgb user library to be present (see above).
- require('user.rgb')
- valuergb = grp.getvalue('1/1/13') -- RGB Colorwheel object
- Red, Green, Blue = value_to_rgb(valuergb)
- H,S,B = rgb_to_hsb(Red,Green,Blue)
-
- Value_Hue = math.floor(H + 0.5)
- Value_Saturation = math.floor((S * 100) + 0.5)
- Value_Brightness = math.floor(((B / 256) * 100) + 0.5)
-
- B = math.floor((event.getvalue() * 2.55 ) + 0.5) -- Slider to set new RGB brightness (byte object 05.001 (0-100%))
- Red, Green, Blue = hsb_to_rgb(H, S, B)
- value = rgb_to_value (Red, Green, Blue)
- grp.update('1/1/13', value) -- RGB Colorwheel object
V2 HUE scripts with adjusted API for HomeKit
Add HUE user library
Create a user lib called hue (disable automatic load checkbox). In the script change only the IP to your HUE bridge IP and save.
See below scripts how to search for HUE bridges in your network, create user, find new lights, get all light information, control HUE from bit/byte/RGB objects.
- require('json')
- require('socket.http')
- require("ltn12")
-
- --************************************************
- ip_add = 'xxx.xxx.xxx.xxx' -- result from bridgeSearch()
- user = 'xxxxxxxxxxxxx' -- result from bridgeSetup()
- --************************************************
-
- function bridgeSearch()
- require("socket")
- require('socket.http')
- local udp = socket.udp()
- if udp == nil then
- return
- else
- local result, message
- local datagram = "M-SEARCH * HTTP/1.1\r\n"
- .. "HOST: 239.255.255.250:1900\r\n"
- .. "ST: MAN: ssdp:discover MX: 10 ST: ssdp:all\r\n"
- .. "\r\n"
- result, message = udp:sendto(datagram, "239.255.255.250", 1900)
- datagram, message = udp:receivefrom()
- ip = datagram:match("http://(.-):80")
- end
- udp:close()
- return ip
- end
-
- function bridgeSetup()
- local response = {}
- body_bridgeSetup= '{"devicetype":"homelynk#homelynk"}'
- -- Set username for homeLYnk
- socket.http.request({
- url = "http://"..ip_add.."/api",
- method = 'POST',
- sink = ltn12.sink.table(response),
- headers = {
- ['content-length'] = #body_bridgeSetup,
- ['content-type'] = 'application/json',
- },
- source = ltn12.source.string(body_bridgeSetup),
- })
- return response
- end
-
- function checkUser()
- local response = {}
- body_bridgeSetup= '{}'
- -- Set username for homeLYnk
- socket.http.request({
- url = "http://"..ip_add.."/api/"..user,
- method = 'GET',
- sink = ltn12.sink.table(response),
- headers = {
- ['content-length'] = #body_bridgeSetup,
- ['content-type'] = 'application/json',
- },
- source = ltn12.source.string(body_bridgeSetup),
- })
- ret = table.concat(response)
- data = json.pdecode(ret)
- if (data[1].error.type)==1 then
- return false
- else
- return true
- end
- end
-
- function searchHueLights()
- local response = {}
- body_searchHueLights= ''
- -- Set username for homeLYnk
- socket.http.request({
- url = "http://"..ip_add.."/api/"..user.."/lights",
- method = 'POST',
- sink = ltn12.sink.table(response),
- headers = {
- ['content-length'] = #body_searchHueLights,
- ['content-type'] = 'application/json',
- },
- source = ltn12.source.string(body_searchHueLights),
- })
- return response
- end
-
- function getHueLights()
- local response = {}
- body_searchHueLights= ''
- -- Set username for homeLYnk
- socket.http.request({
- url = "http://"..ip_add.."/api/"..user.."/lights",
- method = 'GET',
- sink = ltn12.sink.table(response),
- headers = {
- ['content-length'] = #body_searchHueLights,
- ['content-type'] = 'application/json',
- },
- source = ltn12.source.string(body_searchHueLights),
- })
- return response
- end
-
- function sendToLight(Light_num,body_request)
- local response = {}
- socket.http.request({
- url = "http://"..ip_add.."/api/"..user.."/lights/"..tostring(Light_num).."/state",
- method = 'PUT',
- sink = ltn12.sink.table(response),
- headers = {
- ['content-length'] = #body_request,
- ['content-type'] = 'application/json',
- },
- source = ltn12.source.string(body_request),
- })
- return response
- end
-
- function sendToGroup(Group_num,body_request)
- local response = {}
- socket.http.request({
- url = "http://"..ip_add.."/api/"..user.."/groups/"..tostring(Light_num).."/action",
- method = 'PUT',
- sink = ltn12.sink.table(response),
- headers = {
- ['content-length'] = #body_request,
- ['content-type'] = 'application/json',
- },
- source = ltn12.source.string(body_request),
- })
- return response
- end
-
- function setBrightness(Light_num,brightness)
- if brightness == 0 then
- body_msg = '{"on":false}'
- response = sendToLight(lamp_id,body_msg)
- return response
- else
- brightness = math.floor((brightness * 2.54) + 0.5)
- --HTTP request send
- body_msg = '{"on":true,"bri":'..brightness..'}'
- response = sendToLight(Light_num,body_msg)
- return response
- end
- end
-
- function setRGB(Light_num,RGB_variable)
- --RGB color set in homeLYnk
- Red=bit.rshift(bit.band(RGB_variable,0xff0000),16)
- Green=bit.rshift(bit.band(RGB_variable,0x00ff00),8)
- Blue=bit.band(RGB_variable,0x0000ff)
- -- Norm of RGB values
- Red_f= Red/255
- Green_f=Green/255
- Blue_f= Blue/255
- --Gamma correction
- if Red_f>0.04045 then
- Red_f=((Red_f+0.055)/(1+0.055))^2.4
- else
- Red_f=Red_f/12.92
- end
- if Green_f>0.04045 then
- Green_f=((Green_f+0.055)/(1+0.055))^2.4
- else
- Green_f=Green_f/12.92
- end
- if Blue_f>0.04045 then
- Blue_f=((Blue_f+0.055)/(1+0.055))^2.4
- else
- Blue_f=Red_f/12.92
- end
- --Coversion RGB->xy
- X=Red_f*0.649926+Green_f*0.103455+Blue_f*0.197109
- Y=Red_f*0.234327+Green_f*0.743075+Blue_f*0.022598
- Z=Green_f*0.053077+Blue_f*1.035763
- x=X/(X+Y+Z)
- y=Y/(X+Y+Z)
- --HTTP request send
- body_msg = '{"on":true,"xy":['..x..','..y..']}'
- response = sendToLight(Light_num,body_msg)
- return response
- end
Search for HUE bridges in your network and enter the IP result into user.hue where now ‘xxx.xxx.xxx.xxx’ is entered
- require('user.hue')
- -- get bridge IP
- result = bridgeSearch()
- log(result) -- paste this result into user.hue as IP address
Create a user and enter the user result into user.hue where now ‘xxxxxxxxxxxx’ is entered
- require('user.hue')
- -- get bridge user IMPORTANT! make sure to press link button on HUE bridge before running bridgeSetup()
- result = bridgeSetup()
- log(result) -- paste this result into user.hue as user
Make bridge search for new lights
- require('user.hue')
- -- get bridge to search for new lights
- result = searchHueLights()
- log(result)
Get all light information
- require('user.hue')
- -- get all lights from bridge
- result = getHueLights()
- log(result)
Control HUE from BIT object (on/off) (use as event based script)
- require('user.hue')
- value = event.getvalue()
- lamp_id = 1 -- change this ID to your actual light, see getHueLights() result for lamps or use your app to resolve them
-
- if value == true then
- body_msg = '{"on":true}'
- sendToLight(lamp_id,body_msg)
- else
- body_msg = '{"on":false}'
- sendToLight(lamp_id,body_msg)
- end
Control HUE from byte object (brightness) (use as event based script)
- require('user.hue')
- value = event.getvalue() -- 1 byte unsigned integer scale 0 - 100
- lamp_id = 1 -- change this ID to your actual light, see getHueLights() result for lamps or use your app to resolve them
- setBrightness(lamp_id,value)
Control HUE from RGB object (color) (use as event based script)
- require('user.hue')
- value = event.getvalue()
- lamp_id = 1 -- change this ID to your actual light, see getHueLights() result for lamps or use your app to resolve them
-
- if value == 0 then
- body_msg = '{"on":false}'
- sendToLight(lamp_id,body_msg)
- else
- setRGB(lamp_id,value)
- end
Get feedback values from HUE when you change state with the HUE app (Resident script, 1 to 5 seconds)
- address_state_lamp_1 = '1/1/1'
- address_state_lamp_2 = '1/1/2'
- address_state_lamp_3 = '1/1/3'
- address_state_lamp_4 = '1/1/4'
-
- name_lamp_1 = 'Hue color lamp 1'
- name_lamp_2 = 'Hue color lamp 2'
- name_lamp_3 = 'Hue color lamp 3'
- name_lamp_4 = 'Hue color lamp 4'
-
- require('user.hue')
- require('json')
- reply = getHueLights()
- mylamps = json.decode(reply[1])
- for _, item in pairs(mylamps) do
- --log(item.manufacturername)
- --log(item.swversion)
- --log(item.type)
- --log(item.state.ct)
- --log(item.state.reachable)
- --log(item.state.alert)
- --log(item.state.on)
- --log(item.state.bri)
- --log(item.state.colormode)
- --log(item.state.hue)
- --log(item.state.sat)
- --log(item.state.effect)
- --log(item.state.xy[1])
- --log(item.state.xy[2])
- --log(item.uniqueid)
- --log(item.modelid)
- --log(item.name)
- if item.name == name_lamp_1 then
- if item.state.reachable == true then
- currentvalue = grp.getvalue(address_state_lamp_1)
- if currentvalue ~= item.state.on then
- grp.update(address_state_lamp_1, item.state.on)
- log('lamp ' .. item.name .. ' state is: ' .. tostring(item.state.on))
- end
- end
- end
- if item.name == name_lamp_2 then
- if item.state.reachable == true then
- currentvalue = grp.getvalue(address_state_lamp_2)
- if currentvalue ~= item.state.on then
- grp.update(address_state_lamp_2, item.state.on)
- log('lamp ' .. item.name .. ' state is: ' .. tostring(item.state.on))
- end
- end
- end
- if item.name == name_lamp_3 then
- if item.state.reachable == true then
- currentvalue = grp.getvalue(address_state_lamp_3)
- if currentvalue ~= item.state.on then
- grp.update(address_state_lamp_3, item.state.on)
- log('lamp ' .. item.name .. ' state is: ' .. tostring(item.state.on))
- end
- end
- end
- if item.name == name_lamp_4 then
- if item.state.reachable == true then
- currentvalue = grp.getvalue(address_state_lamp_4)
- if currentvalue ~= item.state.on then
- grp.update(address_state_lamp_4, item.state.on)
- log('lamp ' .. item.name .. ' state is: ' .. tostring(item.state.on))
- end
- end
- end
- end
Created by Erwin van der Zwart