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.

Philips Hue api v2
#1
Has anyone already made any code for the Philips Hue api V2?

Why the v2 api? because i want to trigger dynamic scenes and thats not possible in the v1 api.

Please correct my code is you see improvements.

Code:
--[[
https://developers.meethue.com/develop/hue-api-v2/api-reference/
https://developers.meethue.com/develop/hue-api-v2/getting-started/
--]]

-- Load modules
https = require('ssl.https')
ltn12 = require('ltn12')
json = require('json')

local bridge = 'ip of the bridge'
local usr = 'same user as api v1'

-- bridge communication base
function hueExec(iMethod, iCommand, iBody)
  resp = {}
  body = json.encode(iBody)
  res, code, headers = https.request({
    url = 'https://'..bridge..'/clip/v2/resource'..iCommand,
    method = iMethod,
    source = ltn12.source.string(body),
    sink = ltn12.sink.table(resp),
    headers = { ['hue-application-key'] = usr,
                ['Content-Type'] = 'application/json',
                ['Accept'] = 'application/json',
                ['Content-Length'] = #body   
              }
  })
  -- handle feedback
  if not res or code ~= 200 then
    return
  else
    resp = table.concat(resp)
    resp = json.pdecode(resp)
    return resp
  end
end


-- get all lights
function getLights()
    return hueExec('GET', '/light')
end
--log(getLights())


-- get light
function getLight(iLight)
    return hueExec('GET', '/light/'..iLight)
end
--log(getLight('e4a834c9-9ca3-4f2d-98be-669c55a90b5d'))


-- activate scene (active or dynamic_palette)
function runScene(iScene, iAction)
  vAction = iAction or 'active'
    hueExec('PUT', '/scene/'..iScene, {recall = {action = vAction}})
end
--runScene('185d4226-213e-4f01-913b-91c22d85bfac') -- living soho scene
--runScene('185d4226-213e-4f01-913b-91c22d85bfac', 'dynamic_palette') -- living soho dynamic
Reply
#2
Can anyone help me to setup an evenstream connection?

Migration Guide to the new Hue API - Philips Hue Developer Program (meethue.com)

The V2 API supports proactive notifications on changes through Server-Sent Events (SSE) under the /eventstream endpoint:

curl --insecure -N -H 'hue-application-key: <appkey>' -H 'Accept: text/event-stream' https://<ipaddress>/eventstream/clip/v2

Events have an id, timestamp, type (‘update’, ‘add’, ‘delete’, ‘error’), and data field which contains the changed properties of the resource in the same format as a GET response on the same resource type. The following is an example event stream that would result from turning a light on and off:

id: 1617322504:0
data: [{"creationtime":"2021-04-02T00:15:04Z","data":[{"id":"4413c8fd-6643-48b5-ad02-59453edf8a61","id_v1":"/lights/1","on":{"on":true},"type":"light"}],"id":"19845c30-2e4c-4205-a7b4-8bd496f3407d","type":"update"}]

id: 1617322505:0
data: [{"creationtime":"2021-04-02T00:15:05Z","data":[{"id":"4413c8fd-6643-48b5-ad02-59453edf8a61","id_v1":"/lights/1","on":{"on":false},"type":"light"}],"id":"bea68344-a36c-4bfd-a658-97830e4e2b1a","type":"update"}]

On HTTP1.1, you will need a separate connection for the SSE request and regular requests, but we recommend using HTTP2 to multiplex them over a single connection which is more resource efficient.

Currently there is a 1 second rate limit on the amount of event containers the Bridge will send. If the same property has changed twice within that timeframe, you only get the last state. If multiple resources have changed within that timeframe, you will get multiple events grouped in a single container.
Reply
#3
You can adapt this code for your use case: https://forum.logicmachine.net/showthrea...1#pid15941
Add headers to initial request lines and change APPKEY to the actual application key:
Code:
sock:send(
  'GET ' .. path .. ' HTTP/1.1\r\n' ..
  'Host: ' .. host .. '\r\n' ..
  'Accept: text/event-stream\r\n' ..
  'hue-application-key: APPKEY\r\n' ..
  '\r\n'
)
Reply
#4
(02.02.2022, 15:53)admin Wrote: You can adapt this code for your use case: https://forum.logicmachine.net/showthrea...1#pid15941
Add headers to initial request lines and change APPKEY to the actual application key:
Code:
sock:send(
  'GET ' .. path .. ' HTTP/1.1\r\n' ..
  'Host: ' .. host .. '\r\n' ..
  'Accept: text/event-stream\r\n' ..
  'hue-application-key: APPKEY\r\n' ..
  '\r\n'
)

i think i do something wrong because the output of the lines is the same as the bridge html source what i see in the browser

Code:
if not sock then
  host = 'my bridge ip'
  port = 80
  path = '/eventstream/clip/v2'
  appkey = 'mysecretappkey'

  sock = require('socket').tcp()
  sock:settimeout(10)
  res, err = sock:connect(host, port)

  if res then
    sock:send(
      'GET ' .. path .. ' HTTP/1.1\r\n' ..
      'Host: ' .. host .. '\r\n' ..
      'Accept: text/event-stream\r\n' ..
      'hue-application-key: ' .. appkey .. '\r\n' ..
      '\r\n'
    )
  else
    log('connect failed: ' .. tostring(err))
    sock:close()
  end
end

line, err = sock:receive()

if line then
  log('line: ' .. line)
else
  log('receive failed: ' .. tostring(err))
  sock:close()
  sock = nil
end
Reply
#5
Maybe some extra headers are needed. What do you get in the output?
Reply
#6
(03.02.2022, 07:21)admin Wrote: Maybe some extra headers are needed. What do you get in the output?

same url in the browser

Attached Files Thumbnail(s)
       
Reply
#7
It can return 404 if the appkey is incorrect.
Reply
#8
(03.02.2022, 09:22)admin Wrote: It can return 404 if the appkey is incorrect.

but its the same appkey as i use for the normal connection from the other example. And there it works.

with curl the code is:
curl --insecure -N -H 'hue-application-key: <appkey>' -H 'Accept: text/event-stream' https://<ipaddress>/eventstream/clip/v2

I see https.. Do the example also setup a https connection? and is this to port 80 or 443?
Reply
#9
Does it work for you with curl? HTTPS is 443 but some extra code is needed for the raw socket to use encryption: https://forum.logicmachine.net/showthrea...903#pid903
Reply
#10
(03.02.2022, 09:50)admin Wrote: Does it work for you with curl? HTTPS is 443 but some extra code is needed for the raw socket to use encryption: https://forum.logicmachine.net/showthrea...903#pid903

yep, curl for windows code works:
curl --insecure -N -H "hue-application-key: sameapiuserasbefore" -H "Accept: text/event-stream" https://myipaddress/eventstream/clip/v2

and thx, the raw socket example works also.

working code for evenstream

Code:
if not sock then
  host = 'fillyourip'
  port = 443
  proto = 'tlsv12'
  path = '/eventstream/clip/v2'
  appkey = 'fillyouruser'

  require('ssl')
  sock = require('socket').tcp()
  sock:settimeout(10)
  res, err = sock:connect(host, port)
  if res then
    sock = ssl.wrap(sock, proto)
      res, err = sock:dohandshake()
    if res then
      sock:send(
        'GET ' .. path .. ' HTTP/1.1\r\n' ..
        'Host: ' .. host .. '\r\n' ..
        'Accept: text/event-stream\r\n' ..
        'hue-application-key: ' .. appkey .. '\r\n' ..
        '\r\n'
      )
        else
        log('handshake failed: ' .. tostring(err))
      end
  else
    log('connect failed: ' .. tostring(err))
    sock:close()
  end
end

line, err = sock:receive()

if line then
  --log('line: ' .. line)
  if line:find(': hi') then
    log('connection ok: ', line)
  elseif line:find('data:') then
    --status = line:split(':')[2]
    log('data', line)
  end
else
  log('receive failed: ' .. tostring(err))
  sock:close()
  sock = nil
end
Reply
#11
parsing the output is another thing:

line output: data: [{"creationtime":"2022-02-03T13:35:32Z","data":[{"id":"f2ef58b3-818e-45ea-a291-325e8eeb97bc","id_v1":"/lights/10","on":{"on":false},"owner":{"rid":"0120cec8-b58e-4206-a409-2bd99055af55","rtype":"device"},"type":"light"}],"id":"49142b93-3da7-49dc-9ad6-10809df8fe9d","type":"update"}]
Code:
line = string.sub(line,8,-2) -- remove "data: [" and the last ] character
line = json.pdecode(line)
This works

But the second output combines more lines, how to decode this line?
line output: 
data: [{"creationtime":"2022-02-03T13:35:32Z","data":[{"id":"728be63c-dc61-42b1-9a84-54daeff76046","id_v1":"/groups/0","on":{"on":false},"type":"grouped_light"}],"id":"b201d09a-0ab2-4a60-b72f-3a099841ebbc","type":"update"},{"creationtime":"2022-02-03T13:35:32Z","data":[{"id":"33b4f136-c59e-4270-83af-814c4f5ba856","id_v1":"/groups/18","on":{"on":false},"type":"grouped_light"}],"id":"e2860e1b-ebd0-40b0-81ed-f1c0b27163a3","type":"update"},{"creationtime":"2022-02-03T13:35:32Z","data":[{"id":"7a8f900c-c312-403a-bf73-9d2fcb005489","id_v1":"/groups/19","on":{"on":false},"type":"grouped_light"}],"id":"1cc814e9-9210-4e75-92c0-29a543c32dcb","type":"update"}]
Reply
#12
I would like to contribute on this and started with the Hue discovey by changing the script i found from https://forum.logicmachine.net/showthrea...4#pid24214
Below the modified version that gives all the Hue Bridge information with most important the IP address.

Code:
hue = io.readproc('avahi-browse _hue._tcp')
lines = hue:split('\n')

for _, line in ipairs(lines) do
  ip, port, name, descr = unpack(line:split('\t'))
  if descr then
    log(ip, port, name, descr)
  end
end
Reply
#13
Hi @gjniewenhuijse,

Have you been able to make any progress on using api V2? like you, I need to use dynamic scenes and motion sensors.

thanks you
Reply
#14
(04.11.2024, 14:17)Fcs Wrote: Hi @gjniewenhuijse,

Have you been able to make any progress on using api V2? like you, I need to use dynamic scenes and motion sensors.

thanks you

I made some code to use the api v2

Code:
-- hue v2 api begin --------------------------------------------------------------------------------------------------

--[[
https://developers.meethue.com/develop/hue-api-v2/api-reference/
https://developers.meethue.com/develop/hue-api-v2/getting-started/
--]]

-- bridge communication base
function hueExec(iMethod, iCommand, iBody)
    -- load modules
  https = require('ssl.https')
  ltn12 = require('ltn12')
  json = require('json')
  resp = {}
  body = json.encode(iBody)
  res, code, headers = https.request({
    url = 'https://'..bridge..'/clip/v2/resource'..iCommand,
    method = iMethod,
    source = ltn12.source.string(body),
    sink = ltn12.sink.table(resp),
    headers = { ['hue-application-key'] = usr,
                ['Content-Type'] = 'application/json',
                ['Accept'] = 'application/json',
                ['Content-Length'] = #body   
              }
  })
  -- handle feedback
  if not res or code ~= 200 then
    return res,code
  else
    return json.pdecode(table.concat(resp))
  end
end


-- get all lights
function getLights()
    return hueExec('GET', '/light')
end
--log(getLights().data)


-- get light
function getLight(iLight)
    return hueExec('GET', '/light/'..iLight)
end
--log(getLight('e4a916c9-9ca3-4f3d-98be-779c55a90b5d'))


-- (api v2) activate scene (active or dynamic_palette)
function runScene(iScene, iAction)
  vAction = iAction or 'active'
    hueExec('PUT', '/scene/'..iScene, {recall = {action = vAction}})
end
--runScene('185d4336-273e-4f09-913c-91c76d85bfac') -- woonkamer soho
--runScene('185d4336-273e-4f09-913c-91c76d85bfac', 'dynamic_palette') -- woonkamer soho dynamic

-- hue v2 api end --------------------------------------------------------------------------------------------------

and tested with the event handler
Code:
--[[
https://developers.meethue.com/develop/hue-api-v2/migration-guide-to-the-new-hue-api/#Event%20Stream
--]]

-- load modules
json = require('json')

if not sock then
  host = 'x.x.x.x'
  port = 443
  proto = 'tlsv12'
  path = '/eventstream/clip/v2'
  appkey = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'

  require('ssl')
  sock = require('socket').tcp()
  sock:settimeout(10)
  res, err = sock:connect(host, port)
  if res then
    sock = ssl.wrap(sock, proto)
      res, err = sock:dohandshake()
    if res then
      sock:send(
        'GET ' .. path .. ' HTTP/1.1\r\n' ..
        'Host: ' .. host .. '\r\n' ..
        'Accept: text/event-stream\r\n' ..
        'hue-application-key: ' .. appkey .. '\r\n' ..
        '\r\n'
      )
        else
        log('handshake failed: ' .. tostring(err))
      end
  else
    log('connect failed: ' .. tostring(err))
    sock:close()
  end
end

line, err = sock:receive()
if line then
  log('line: ' .. line)
  if line:find(': hi') then
    log('connection ok: ', line)
  elseif line:find('data:') then
    line = '{"data": '..string.sub(line,7)..'}'
        log('textline',line)
    line = json.pdecode(line)
    log(line.data)
    for _l,l_items in pairs(line.data) do
      log(l_items)
      log(l_items.type, l_items.data[1].id_v1, l_items.data[1].on.on)
    end
  end
else
  log('receive failed: ' .. tostring(err))
  sock:close()
  sock = nil
end
Reply


Forum Jump: