I figured out how to add some of its functionality to Domoticz:
- Reading sensors (light, sound, temperature, humidity)
- Controlling the dimmable light
Reading sensors
- Create a HTTP/HTTPS poller device.
Method: GET
ContentType: application/json
URL: https://YOUR_SOMNEO_HOST/di/v1/products/1/wusrd
Command: somneo_sensors.lua
Refresh: Whatever you like, but don't set it insanely fast. It looks like the Somneo only supports 1 active connection, so a too low value (like 1 second) can interfere with the SleepMapper app behavior. I've just set it to 60 as I'm mostly interested in temperature and humidity, which don't change that rapidly anyway.
. - Create three virtual sensors:
- Temp + Humidity
- Lux
- Sound Level
- Create the domoticz/scripts/lua_parsers/somneo_sensors.lua script.
Of course you'll need to change the device indices to match your setup.Code: Select all
s = request['content']; -- Philips Somneo sensors devSomneoTempHumIdx = 103 -- Temperature + humidity devSomneoLuxIdx = 104 -- Light intensity devSomneoDbIdx = 105 -- Noise local l_Temp = domoticz_applyJsonPath(s, '.mstmp') -- Temperature local l_Hum = domoticz_applyJsonPath(s, '.msrhu') -- Humidity local l_Lux = domoticz_applyJsonPath(s, '.mslux') -- Light local l_Db = domoticz_applyJsonPath(s, '.mssnd') -- Noise local l_HumStat = 0 -- Humidity status: 0 = Normal (30%..40% and 60%..70%) if ((l_Hum >= 40) and (l_Hum <= 60)) then l_HumStat = 1 -- Comfortable elseif (l_Hum < 30) then l_HumStat = 2 -- Dry elseif (l_Hum > 70) then l_HumStat = 3 -- Wet end -- nvalue is always zero. All values are passed as svalue. domoticz_updateDevice(devSomneoTempHumIdx, 0, l_Temp .. ";" .. l_Hum .. ";" .. l_HumStat) domoticz_updateDevice(devSomneoLuxIdx, 0, l_Lux) domoticz_updateDevice(devSomneoDbIdx, 0, l_Db)
Reading and controlling the light
- Create a HTTP/HTTPS poller device.
Method: GET
ContentType: application/json
URL: https://YOUR_SOMNEO_HOST/di/v1/products/1/wulgt
Command: somneo_light.lua
Refresh: See above. This one I've set to 5 seconds so the dimmer device in Domoticz accurately reflects the status.
. - Create a virtual dimmer device.
Warning: do NOT use "Create Virtual Sensors" from the HTTP/HTTPS poller for this. A virtual dimmer created in this way won't trigger the event system when on/off/level is changed by the user (I'll submit a bug for that). Instead create a separate Dummy hardware device -if you don't have one already- and use that to create the virtual dimmer. (Edit: warning no longer applicable since build 15177)
. - Create a user variable (type: integer).
When the somneo_light.lua script reads the lamp status and updates the virtual dimmer, you don't want the dimmer device to send that same status back to your Somneo. That would result in unwanted loop behavior as well as unnecessary network activity every few seconds.
This user variable is used as a flag to prevent that. Yes, using a flag is kind of an ugly hack If anyone knows whether it's possible to update a device from a LUA parser without triggering the event system, I'd be very interested.
. - Create the domoticz/scripts/lua_parsers/somneo_light.lua script.
Of course you'll need to change the device index and the polling flag (user variable) name to match your setup.
Note: This works on Linux, but I guess you can also find a Windows port of the "curl" utility.Now the Somneo's light status will be shown in Domoticz. Change the light setting via the SleepMapper app (or on the Somneo itself) and check that your virtual dimmer device reflects it.Code: Select all
-- Use IP address instead of 'localhost' hostname. -- Otherwise the "local networks (no username/password)" setting doesn't seem to work. jsonBaseUrl = 'http://127.0.0.1:8080/json.htm?' function callJsonUrl(a_UrlSuffix) os.execute("curl '"..jsonBaseUrl..a_UrlSuffix.."'") end -- Philips Somneo light dimmer devDimmerIdx = 106 pollingFlag = 'Wekkerverlichting_pollingUpdate' s = request['content']; -- domoticz_applyJsonPath doesn't support booleans. -- Simple hack: replace by an integer, which we'll need in the end anyway. s = s:gsub("false", "0") s = s:gsub("true", "1") local l_Level = domoticz_applyJsonPath(s, '.ltlvl') local l_IsOn = domoticz_applyJsonPath(s, '.onoff') -- The Somneo's maximum level is 25. Scale this to a percentage. l_Level = l_Level * 4 -- The event system will be triggered by this update. -- It should only process user input, so we'll first set the "polling update" -- flag to 1 so the event system knows to ignore this update (and it will then -- just reset the flag). callJsonUrl('type=command¶m=updateuservariable&vname='..pollingFlag..'&vtype=0&vvalue=1') -- nvalue = on/off status -- svalue = level domoticz_updateDevice(devDimmerIdx, l_IsOn, l_Level)
. - Create the domoticz/scripts/lua/script_device_somneo.lua script.
Again, of course: update the device name, polling flag and hostname to match your setup.Et voilà, you can now also control your Somneo's light from DomoticzCode: Select all
devName = 'Wekkerverlichting' pollingFlag = 'Wekkerverlichting_pollingUpdate' somneoUrl = 'https://YOUR_SOMNEO_HOST/di/v1/products/1/wulgt' commandArray = {} if (devicechanged[devName]) then if (uservariables[pollingFlag] == 1) then -- This event was triggered by an update of the polling script. -- Ignore this and reset the polling flag. commandArray['Variable:'..pollingFlag] = '0' else -- This event was triggered by user input, so process it. local jsonPayload = '' if (devicechanged[devName] == 'On') then -- Switch on the Somneo light. -- The "tempy" setting has something to do with the sunset mode. -- When just switching the light, it must explicitly be set to false. jsonPayload = '{ "onoff": true, "tempy": false }' elseif (devicechanged[devName] == 'Off') then -- Switch off the Somneo light jsonPayload = '{ "onoff": false, "tempy": false }' end -- Check if the event starts with "Set Level". -- We can't simply do a compare because the slider level will also -- be part of the string. We don't bother parsing that though, as -- it's also available through otherdevices_svalues. if (devicechanged[devName]:find('^Set Level') ~= nil) then local dimLevel = otherdevices_svalues[devName] -- Scale to Somneo range 0..25 dimLevel = math.floor((dimLevel / 4) + 0.5) -- Make sure the "onoff" setting corresponds with the dimming level. -- This way it also becomes possible to switch the light on or off -- by just moving the slider. jsonOnOff = (dimLevel > 0) and 'true' or 'false' -- Lua variant of a ternary operator jsonPayload = '{ "ltlvl": '..tostring(dimLevel)..', "onoff": '..jsonOnOff..', "tempy": false }' end if (jsonPayload ~= '') then -- Send our JSON request to the Somneo device. -- The "insecure" option is required because Philips used a self-signed SSL certificate. os.execute("curl --insecure --request PUT --header 'Content-Type: application/json' --data '"..jsonPayload.."' "..somneoUrl) end end end return commandArray
Update for Domoticz build 15028 and later:
Since build 15028, which includes commit 0986bd0, the above script doesn't work anymore because the "Set Level" event will no longer be received.
A simplified version that will work:Code: Select all
devName = 'Wekkerverlichting' pollingFlag = 'Wekkerverlichting_pollingUpdate' somneoUrl = 'https://YOUR_SOMNEO_HOST/di/v1/products/1/wulgt' commandArray = {} if (devicechanged[devName]) then if (uservariables[pollingFlag] == 1) then -- This event was triggered by an update of the polling script. -- Ignore this and reset the polling flag. commandArray['Variable:'..pollingFlag] = '0' else -- This event was triggered by user input, so process it. local jsonPayload = '' if (devicechanged[devName] == 'On') then local dimLevel = otherdevices_svalues[devName] -- Scale to Somneo range 0..25 dimLevel = math.floor((dimLevel / 4) + 0.5) -- Switch on the Somneo light. -- The "tempy" setting has something to do with the sunset mode. -- When just switching the light, it must explicitly be set to false. jsonPayload = '{ "ltlvl": '..tostring(dimLevel)..', "onoff": true, "tempy": false }' elseif (devicechanged[devName] == 'Off') then -- Switch off the Somneo light jsonPayload = '{ "onoff": false, "tempy": false }' end if (jsonPayload ~= '') then -- Send our JSON request to the Somneo device. -- The "insecure" option is required because Philips used a self-signed SSL certificate. os.execute("curl --insecure --request PUT --header 'Content-Type: application/json' --data '"..jsonPayload.."' "..somneoUrl) end end end return commandArray