Simple script to update Philips Hue status

From Domoticz
Jump to navigation Jump to search

Purpose

You may have noticed that after you switch your Hue lights outside Domoticz the status of your Hue lights can be incorrect. This may cause scripts or Blockly events to malfunction. Or more accurately said: you scripts work just fine, but they are not getting the correct inputs from Domoticz. The purpose of this script is to eliminate these problems.
The concept of the script is that it will loop through your Hue lights (1, 2, 3, etc) until no more lights are found. It gets both the name and status of each light and corrects Domoticz when needed. All the script does is correct the on/off status. It does nothing with (for instance) dim-level or colors. If you need more advanced functions look here[1].

Dependencies

If you scroll down to the bottom, a version without dependencies is available. To use the older versions of the script you'll need to install socket library [2] and dkjson [3]. Both need to end up in /usr/local/lib/lua/5.2 (path is for Synology NAS, it may be different on RPI).
You need to be able to access your bridge using the HUE api. More information on Philips HUE api and how to get/change your username can be found here [4].

Domoticz setup

Another thing is that the name of each light must be the same in your bridge and Domoticz. The script is very simple: it gets the name of each light from your bridge and then sends the status to Domoticz using the exact same name. So if the names differ between the two then the script will not work. You can check the names of your lights using the Hue app.
I saved it in my scripts folder as "script_time_checkLights.lua". Time based scripts are executed every minute. More information on the event system can be found here [5].
To configure the script all you have to do is enter the IP of your HUE bridge and your HUE username.

The script

-- script to check Philips Hue light status (on/off)
-- m.hagenaars
-- feb. 2016

-- you'll need to install socket library & dkjson, more info can be found here:
-- http://www.domoticz.com/wiki/Upload_energy_data_to_PVoutput#Install_socket_library
-- http://dkolf.de/src/dkjson-lua.fsl/home
-- both need to end up in /usr/local/lib/lua/5.2

-- this time based script wil use the HUE api to check the current status of your lights as reported by the HUE bridge
-- the script starts with light #1 and checks if the status reported by your bridge corresponds with the status in Domoticz
-- if HUE state is not equal to Domoticz state then the script will correct the status in Domoticz
-- the script will keep looping until there are no more lights to check and will then stop
-- in order for the script to work it is important that the names of each light are exactly equal in both your bridge & domoticz
-- I suppose you know the names of the HUE switches in Domoticz, you can check the names in your bridge with the HUE app

-- more info on Philips HUE api and how to get/change your username can be found here: 
-- http://www.developers.meethue.com/documentation/getting-started

-- configure Hue Bridge
local hueBridgeIP = 'enter_HUE_ip_here'
local hueBridgeAPI = 'enter_HUE_username_here'


-- do not change beyond this line
-- this part will get the Hue status
function getHueLight(id)
   local http = require('socket.http')
   local ltn12 = require('ltn12')
   local json = require('dkjson')

   t = {}
   local url = string.format("http://%s/api/%s/lights/%s", hueBridgeIP, hueBridgeAPI, id)
   b, c, h = http.request{url=url, sink = ltn12.sink.table(t), method='GET'}
   huestring = tostring(table.concat(t))
   local hue, pos, err = json.decode(huestring, 1, nil)
   if (hue.name) then 
      hue_name = (hue.name)
      hue_state = (hue.state.on)
   else
      stop = true
   end 
   return hue_name, hue_state, stop
end


-- now check Hue state & correct Domoticz if needed
commandArray = {}

i = 1
repeat
   local hue = getHueLight(i)
   if hue_state == true and otherdevices[hue_name] == 'Off' then
      commandArray[hue_name] = 'On'
      print(string.format("Status %s corrigeren", hue_name))
    elseif hue_state == false and otherdevices[hue_name] ~= 'Off' then
      commandArray[hue_name] = 'Off'
      print(string.format("Status %s corrigeren", hue_name))
    end
    i = i + 1
until(stop)

return commandArray

Update Script Fix if you remove a broken bulb this script will stop there... not updating the lamps after the broken bulb. Change until(stop) into until(i > 25) where 25 is the highest number of lamps found in your Philips HUE app, this will quick fix it...

Update: dzVents version based on the version of m. hagenaars this version uses dzvents and also takes unreachable lights into account (assuming those are off). The script will wait until the light has a unreachable state for 5 minutes before switching them off since I would ran into issues doing this directly.


-- Check the wiki at
-- http://www.domoticz.com/wiki/%27dzVents%27:_next_generation_LUA_scripting
return {
active = true,
        on = {
            timer = {
                'every 1 minutes'
            }
        },
        data = {
            previousData = { 
                            history = true, 
                            maxMinutes  = 5
                            }
        },
        execute = function(domoticz, device)

        
	local hueBridgeIP = 'IP of bridge'
        local hueBridgeAPI = ''
        
        function getHueLights()
           local http = require('socket.http')
           local ltn12 = require('ltn12')
           local json = require('dkjson')
         
           t = {}
           local url = string.format("http://%s/api/%s/lights", hueBridgeIP, hueBridgeAPI)
           b, c, h = http.request{url=url, sink = ltn12.sink.table(t), method='GET'}
           huestring = tostring(table.concat(t))
           local hue, pos, err = json.decode(huestring, 1, nil)

           return hue
        end
        
        local lights = getHueLights()
        local previousData = domoticz.data.previousData
        
           for i, light in pairs(lights) do
               local device = domoticz.devices( light.name )
                if( light.state.on == true and device.state == 'Off') then
                    print(string.format("Correcting Status: %s", light.name))
                elseif( light.state.on == false and device.state == 'On') then
                    print(string.format("Correcting Status: %s", light.name))
                elseif( light.state.reachable == false and device.state == 'On') then
                    print(string.format("Correcting status (unreachable): %s", light.name))
   
                     previousData.add( light.name )
                     

                     local olderItems = previousData.subsetSince('00:05:00')
                     local count = olderItems.reduce(function(acc, item)
                                        if (item.data == light.name) then
                                            acc = acc + 1
                                        end
                                        return acc
                                    end, 0)


                    if( count > 5 ) then -- last 5 polls were unreachable, assuming light is off
                       device.switchOff()  
                    end

                                
                     
                end

            end
	end
}

Update: dzVents version Updated version based on the above scripts without the need for adding the libraries (part of dzVents; forum link: [[6]]) to update on/off status. Script for lights as well as groups available below:

Lights:

return {
	on = {
	    timer = {'every minute'},
        httpResponses = { 'huetrigger' }
	},
    data = {
    previousData = { 
                    history = true, 
                    maxMinutes  = 5
                    }
    },
    execute = function(domoticz,item)
        if (item.isTimer) then
            domoticz.log('timerworks')
            local hueBridgeIP = '192.168.2.XXX'
            local hueBridgeAPI = 'XXXXXXXXXXXXXXXXX'
            
            local url = string.format("http://%s/api/%s/lights", hueBridgeIP, hueBridgeAPI)
            domoticz.log(url)
            
            domoticz.openURL({
                url = url,
                method = 'GET',
                callback = 'huetrigger'
            })
        end
        if (item.isHTTPResponse) then
            if (item.isJSON) then
                local lights = domoticz.utils.fromJSON(item.data)
                local previousData = domoticz.data.previousData
        
               for i, light in pairs(lights) do
                    if (domoticz.devices( light.name ) ~= nil) then
                        local device = domoticz.devices( light.name )
                        if( light.state.on == true and device.state == 'Off') then
                            device.switchOn().checkFirst()
                        elseif( light.state.on == false and device.state ~= 'Off') then
                            device.switchOff().checkFirst()
                        elseif( light.state.reachable == false and device.state ~= 'Off') then
                            previousData.add( light.name )
                             
                            local olderItems = previousData.subsetSince('00:05:00')
                            local count = olderItems.reduce(function(acc, item)
                                if (item.data == light.name) then
                                    acc = acc + 1
                                end
                                return acc
                            end, 0)
        
                            if( count > 5 ) then -- last 5 polls were unreachable, assuming light is off
                               device.switchOff().checkFirst()
                            end
                        end
                    end
                end
            else
                domoticz.log('noJSON',item.data)
            end
        end
    end
}

Groups:

return {
	on = {
	    timer = {'every minute'},
        httpResponses = { 'huetrigger' }
	},
    data = {
    previousData = { 
                    history = true, 
                    maxMinutes  = 5
                    }
    },
    execute = function(domoticz,item)
        if (item.isTimer) then
            domoticz.log('timerworks')
            local hueBridgeIP = '192.168.2.XXX'
            local hueBridgeAPI = 'XXXXXXXXXX'
             
            local url = string.format("http://%s/api/%s/groups/", hueBridgeIP, hueBridgeAPI)
            
            domoticz.openURL({
                url = url,
                method = 'GET',
                callback = 'huetrigger'
            })
        end
        if (item.isHTTPResponse) then
            if (item.isJSON) then
                local huegroups = domoticz.utils.fromJSON(item.data)
                local previousData = domoticz.data.previousData
        
                for i, huegroup in pairs(huegroups) do
                    if ( huegroup.type == 'Room' ) then
                        if (domoticz.devices( "Group "..huegroup.name ) ~= nil) then
                            local device = domoticz.devices( "Group "..huegroup.name )
                            if( huegroup.state.any_on == true and device.state == 'Off') then
                                device.switchOn().checkFirst()
                            elseif( huegroup.state.any_on == false and device.state == 'On') then
                                device.switchOff().checkFirst()
                            end
                        end
                    end
                end
            else
                domoticz.log('noJSON',item.data)
            end
        end
    end
}

Expanded Script

En example of an expanded script can be found Here It add's virtual power and energy meters for HUE and expand/fix the update script.