Page 1 of 1

Looking to combine several LUA scripts for performance. Ideas?

Posted: Sunday 27 August 2017 14:30
by simon_rb
As the title says, Looking to get a bit of performance by having less LUA scripts. Quick question about time scripts, does combining them have any performance benefit?

The two scripts for each squeezebox player to get volume status/power etc.. One is for device change and one is time. Please see them below, is it possible to combine all 8 device changes into one LUA script? Many Thanks

Code: Select all

commandArray = {}
if ((devicechanged['Bathroom Radio'] == 'On' or devicechanged['Bathroom Radio'] == 'Playing') and uservariables['BTH'] == 'Off') then
    commandArray[#commandArray+1]={['Variable:LTV1'] = 'On'}
    --commandArray[#commandArray+1]={['Lounge Touch']='Set Volume 30'}
    --os.execute('curl -s "http://192.168.1.26:8080/json.htm?type=command&param=switchlight&idx=265&switchcmd=Set%20Level&level=30" &')
elseif (devicechanged['Bathroom Radio'] == 'Off' or devicechanged['Bathroom Radio'] == 'Stopped' or devicechanged['Bathroom Radio'] == 'Disconnected') then
    if otherdevices['Bathroom Radio V'] ~= 'Off' then
        commandArray[#commandArray+1]={['Bathroom Radio V'] = 'Off'}
    end
    commandArray[#commandArray+1]={['Variable:BTH'] = 'Off'}
end

if devicechanged['Bathroom Radio V'] ~= 'Off' and devicechanged['Bathroom Radio V'] ~= nil then
    if tonumber(otherdevices_svalues['Bathroom Radio V']) > 0 then
        volume = otherdevices_svalues['Bathroom Radio V']
        commandArray['Bathroom Radio']='Set Volume '..volume
    end
end

return commandArray

Code: Select all

commandArray = {}
if otherdevices['Squeezeboxes'] == 'On' and (otherdevices['Bathroom Radio'] ~= 'Disconnected') then

    json = (loadfile '/home/pi/domoticz/scripts/lua/json.lua')()
    file = assert(io.popen('curl \'http://192.168.1.26:9000/jsonrpc.js\' --data-binary \'{"id":1,"method":"slim.request","params":["aa:aa:f8:02:4e:bd",["status","-","1",""]]}\''))  
        
    raw = file:read('*all')
    file:close()   
    deviceinfo = json:decode(raw)
    
    powerstatus = deviceinfo.result.power
    playervolume = deviceinfo.result["mixer volume"] + 1
    switchvolume = otherdevices_svalues['Bathroom Radio V']
    difference = math.abs(switchvolume - playervolume)
       
    if difference > 1 and tonumber(powerstatus) == 1 then
        os.execute('curl -s "http://192.168.1.26:8080/json.htm?type=command&param=switchlight&idx=361&switchcmd=Set%20Level&level='..playervolume..'" &')
    end

    if tonumber(powerstatus) == 1 and otherdevices['Bathroom Radio V'] == 'Off' then
        os.execute('curl -s "http://192.168.1.26:8080/json.htm?type=command&param=switchlight&idx=361&switchcmd=Set%20Level&level='..playervolume..'" &')
    end
end

return commandArray

Re: Looking to combine several LUA scripts for performance. Ideas?

Posted: Sunday 27 August 2017 15:05
by dannybloe
Simple: switch to dzVents.

Re: Looking to combine several LUA scripts for performance. Ideas?

Posted: Sunday 27 August 2017 15:56
by simon_rb
dannybloe wrote: Sunday 27 August 2017 15:05 Simple: switch to dzVents.
Simple for you, not so for me. I'd love to learn, could you post an example of the 2 two scripts converted to dzVents? I did read you only need to add a bit of code to make it a module.. I have 7 of those scripts (14 in total). Do I add them all to the same dvents script? Assume 2 separate time and device scripts still

Many Thanks

Re: Looking to combine several LUA scripts for performance. Ideas?

Posted: Monday 28 August 2017 18:49
by BakSeeDaa
simon_rb wrote: Sunday 27 August 2017 15:56
dannybloe wrote: Sunday 27 August 2017 15:05 Simple: switch to dzVents.
Simple for you, not so for me. I'd love to learn, could you post an example of the 2 two scripts converted to dzVents? I did read you only need to add a bit of code to make it a module.. I have 7 of those scripts (14 in total). Do I add them all to the same dvents script? Assume 2 separate time and device scripts still

Many Thanks
Hi simon_rb

I'm quite sure that you will gain performance by putting all logic in a single script. I suggest that you divide your scripts logically depending on what they do and what devices are involved. Just make your scripts logical and easy to maintain.
Roberto Lerusalimschy wrote: Lua Performance Tips
In Lua, as in any other programming language, we should always follow the two
maxims of program optimization:

Rule #1: Don’t do it.
Rule #2: Don’t do it yet. (for experts only)

Those rules are particularly relevant when programming in Lua. Lua is famous
for its performance, and it deserves its reputation among scripting languages
Source: https://www.lua.org/gems/sample.pdf

But as Danny says, you can switch to dzVents because it's not only efficient but also beautiful, almost like syntactical art.

Why don't you just try to convert one or a few of your scripts into dzVents and try it out first

This script:

Code: Select all

commandArray = {}
if ((devicechanged['Bathroom Radio'] == 'On' or devicechanged['Bathroom Radio'] == 'Playing') and uservariables['BTH'] == 'Off') then
    commandArray[#commandArray+1]={['Variable:LTV1'] = 'On'}
    --commandArray[#commandArray+1]={['Lounge Touch']='Set Volume 30'}
    --os.execute('curl -s "http://192.168.1.26:8080/json.htm?type=command&param=switchlight&idx=265&switchcmd=Set%20Level&level=30" &')
elseif (devicechanged['Bathroom Radio'] == 'Off' or devicechanged['Bathroom Radio'] == 'Stopped' or devicechanged['Bathroom Radio'] == 'Disconnected') then
    if otherdevices['Bathroom Radio V'] ~= 'Off' then
        commandArray[#commandArray+1]={['Bathroom Radio V'] = 'Off'}
    end
    commandArray[#commandArray+1]={['Variable:BTH'] = 'Off'}
end

if devicechanged['Bathroom Radio V'] ~= 'Off' and devicechanged['Bathroom Radio V'] ~= nil then
    if tonumber(otherdevices_svalues['Bathroom Radio V']) > 0 then
        volume = otherdevices_svalues['Bathroom Radio V']
        commandArray['Bathroom Radio']='Set Volume '..volume
    end
end

return commandArray
Could look something similar to this in dzVents: (Untested, but it might be used as a starter...)

Code: Select all

return {
	active = true,
	on = {
		devices = {
			'Bathroom Radio*',
		}
	},
	execute = function(domoticz, device)
		if device.name == 'Bathroom Radio' then
			if (device.state == 'On' or device.state == 'Playing')
			and domoticz.variables('BTH').value == 'Off' then
				domoticz.variables('LTV1').set('On')
			elseif (device.state == 'Off' or device.state == 'Stopped' or device.state == 'Disconnected') then
				if domoticz.devices('Bathroom Radio V').state ~= 'Off' then
					domoticz.devices('Bathroom Radio V').switchOff()
				end
				domoticz.variables('BTH').set('Off')
			end
			
		end

		if device.name == 'Bathroom Radio V' then
			if (device.state and device.state ~= 'Off') then
				if tonumber(domoticz.devices('Bathroom Radio V').text) > 0 then -- NOT SURE ABOUT THIS, is it a text device?
					volume = tonumber(domoticz.devices('Bathroom Radio V').text) -- NOT SURE ABOUT THIS
					domoticz.sendCommand('Bathroom Radio', 'Set Volume ' .. volume)
				end
			end
		end
	end
}
I'm not sure how to work with a Domoticz squeezebox device in dzVents. Maybe there is a device adapter for it. I have to check it out first... must go...

EDIT: I Did not find any device adapter for Squeezebox in dzVents and I'm not sure of what kind of Domoticz device it actually is. That's why I'm not sure about how to control it. Maybe someone else has better knowledge in this subject.

Re: Looking to combine several LUA scripts for performance. Ideas?

Posted: Monday 28 August 2017 23:45
by simon_rb
Thanks for your reply, you obviously took time to compose it. I shall try the script. From memory that script doesn’t actually use the Logitech Controller. I have a virtual dimmer which turns it on/off and changes volume. Unless your talking about how to update it? I could update the device directly instead.

Any other ideas are more than welcome. Still best to keep time and device scripts separate? I guess you have too 👍

Re: Looking to combine several LUA scripts for performance. Ideas?

Posted: Tuesday 29 August 2017 8:45
by BakSeeDaa
simon_rb wrote: Monday 28 August 2017 23:45 Thanks for your reply, you obviously took time to compose it. I shall try the script. From memory that script doesn’t actually use the Logitech Controller. I have a virtual dimmer which turns it on/off and changes volume. Unless your talking about how to update it? I could update the device directly instead.

Any other ideas are more than welcome. Still best to keep time and device scripts separate? I guess you have too 👍
Knowing that you used a dimmer, I changed the script a bit. I also combined both of your script into a single. It's still untested but there shouldn't be to many errors in it I hope.

Code: Select all

return {
	active = true,
	on = {
		devices = {
			'Bathroom Radio*',
		},
		timer = {
			'every 1 minutes'
		}
	},
	execute = function(domoticz, device, triggerInfo)

		if (triggerInfo.type == domoticz.EVENT_TYPE_DEVICE) then

			if device.name == 'Bathroom Radio' then
				if (device.state == 'On' or device.state == 'Playing')
				and domoticz.variables('BTH').value == 'Off' then
					domoticz.variables('LTV1').set('On')
				elseif (device.state == 'Off' or device.state == 'Stopped' or device.state == 'Disconnected') then
					if domoticz.devices('Bathroom Radio V').state ~= 'Off' then
						domoticz.devices('Bathroom Radio V').switchOff()
					end
					domoticz.variables('BTH').set('Off')
				end
				
			end
	
			if device.name == 'Bathroom Radio V' and device.state ~= 'Off' and device.level > 0 then
				domoticz.sendCommand('Bathroom Radio', 'Set Volume ' .. device.level)
			end

		elseif (triggerInfo.type == domoticz.EVENT_TYPE_TIMER) then

			if domoticz.devices('Squeezeboxes').state == 'On'
			and domoticz.devices('Bathroom Radio').state ~= 'Disconnected' then
				local json = (loadfile '/home/pi/domoticz/scripts/lua/json.lua')()
				local x = os.clock() -- Start measuring time
				local file = assert(io.popen('curl \'http://192.168.1.26:9000/jsonrpc.js\' --data-binary \'{"id":1,"method":"slim.request","params":["aa:aa:f8:02:4e:bd",["status","-","1",""]]}\''))  
				domoticz.log(string.format("elapsed time: %.3f\n", os.clock() - x), domoticz.LOG_FORCE) -- Report the time used
				local raw = file:read('*all')
				file:close()   
				local deviceinfo = json:decode(raw)

				local powerstatus = tonumber(deviceinfo.result.power)
				local playervolume = deviceinfo.result["mixer volume"] + 1
				local switchvolume = domoticz.devices('Bathroom Radio V').level
				local difference = math.abs(switchvolume - playervolume)

				if powerstatus == 1 and ((difference > 1)
				or (domoticz.devices('Bathroom Radio V').state ~= 'On')) then
					local url = 'http://192.168.1.26:8080/json.htm?type=command&param=switchlight&idx=361&switchcmd=Set%20Level&level='..playervolume
					domoticz.openURL(url)
				end

			end

		end
	end
}
Please note that the command:

Code: Select all

assert(io.popen('curl \'http://192.168.1.26:9000/jsonrpc.js\' --data-binary \'{"id":1,"method":"slim.request","params":["aa:aa:f8:02:4e:bd",["status","-","1",""]]}\''))
will lock the eventsystem for up to 10 seconds while waiting for 192.168.1.26 to respond. You must be 100% sure that 192.168.1.26 is always on and always responding fast or Domoticz will appear unresponsive. Even 1 second would be a long time to wait for an eventsystem. Anyway, I also added some time measuring especially for that statement. Look in the log.

If you wish to solve that, you can for example make a python script making the calls to 192.168.1.26 (every 30 seconds or so) and save the result to a file that you can read in Domoticz. That can probably also add some performance benefits for your script depending on how long it normally takes for 192.168.1.26 to produce a HTTP response. Reading the file on disk should be much faster than waiting for the HTTP response and doing it that way won't stall your Domoticz evensystem if 192.168.1.26 is unresponsive.

I also made some of your variables locale. It's a good coding practice and it will also help performance. (I think it's a very small gain though)

What do you think about that?

Re: Looking to combine several LUA scripts for performance. Ideas?

Posted: Tuesday 29 August 2017 21:54
by simon_rb
I think thats awesome! I haven't had chance to test them yet. Hopefully at the weekend.Its actually running on the same Raspberry Pi.. maybe 127:0:0:1 would be quicker?

Writing a Python script to grab the data?!?! I understand the principle however how that would look I have not idea. Also it would only need to run when the radio is on. That should save performance.

Thanks again. I can hopefully muddle through it and get the above script to work. I have 6 or 7 of these radio's all with virtual dimmers in domoticz. Could I repeat the contents above into one giant script with all radios inside?

Thanks again!

Re: Looking to combine several LUA scripts for performance. Ideas?

Posted: Wednesday 30 August 2017 11:19
by BakSeeDaa
Hi! You're welcome!

If you call an URL on the same Domoticz host, then your call can be simplified to somethin similar to:

Code: Select all

local url = domoticz.settings['Domoticz url']..'/json.htm?type=command&param=BLAH
Is there a reason why you use the Domoticz URL API to set the virtual dimmer device.

That is, instead of:

Code: Select all

local url = 'http://192.168.1.26:8080/json.htm?type=command&param=switchlight&idx=361&switchcmd=Set%20Level&level='..playervolume
domoticz.openURL(url)
Why don't you just do:

Code: Select all

domoticz.devices(361).dimTo(playervolume)
?