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.

[solved] Most efficient way to get sum of all light states?
#1
Hi,

at moment I search for a way to get a total sum of all Lights in my house with state on. The same I have for opened windows and it works for me, but I think my way is not much elegenat.

I have create one KNX Object for the sum count total. Then I created on each windows object an event which gets the value from the total sum and add or substract the total value (+1 or -1).
That works because are only few windows (19) and the action on that is very slow. But if I try to do it for my downlights on the same way it doesn't works.

I have 110 Downlights in my house and each of them has its own status object. I have done that, because I use many different scenes and wants to have in all ways the exact visualisiation. In my living room I have 30 downlights and If I do the calculation in that way I described before, It calculates every times wrong.

In next step I reverted that and put the calculation in the event script of the event trigger (switch/button) and do the calculation for the whole group (+3 or +24 and so on). But it doesn't work also. In some cases it works, in other combination not... 

My idea is now to create a functionality to get the status of all tagged objects (maybe tag: "downlight status") on every 2 minutes. but the disadvantage is, that I have much more traffic and CPU/Bus load.

What is the best way to do such functionality?

Thank you forwards

BR
Habib
Reply
#2
Hi Habib,

this is a script for central status calculations from forum with a little update of sum function.

output - how many lamps are switched on from tagged
counter - how many lamps are tagged for sum

This should be Resident script 0s.

Code:
if not client then
 -- each group has 3 or 4 fields:
 -- tag - status object tag
 -- output - status output object
 -- mode - calculate mode (and/or/avg/sum)
 -- counter - calculate number of light which are tagged(only sum function)
 groups = {
--samples
--    { tag = 'my_group_and', output = '4/0/0', mode = 'and' },
--    { tag = 'my_group_or', output = '4/0/1', mode = 'or' },
--    { tag = 'my_group_avg', output = '4/0/2', mode = 'avg' },
--    { tag = 'my_group_sum', output = '4/0/3', mode = 'sum', counter = '4/0/4' },

--mean temperature
   { tag = 'Lamp', output = '8/2/37', mode = 'sum', counter = '8/2/25' },

--number of lights which are switched on
   { tag = 'Kitchen_L_sum', output = '0/0/64', mode = 'sum', counter = '0/0/65' },
   { tag = 'Livingroom_L_sum', output = '0/0/64', mode = 'sum', counter = '0/0/65' },
   { tag = 'Garderobe_L_sum', output = '0/0/64', mode = 'sum', counter = '0/0/65' },
   { tag = 'Hall_L_sum', output = '0/0/64', mode = 'sum', counter = '0/0/65' },
   { tag = 'Floor1_L_sum', output = '0/0/64', mode = 'sum', counter = '0/0/65' },

 }

 -- time to wait between last telegram from any status in group and update
 updatedelay = 0.5

 timeout = updatedelay / 2

 -- object value cache
 values = {}

 -- object datatype cache
 datatypes = {}

 -- send update only when value changes
 grp.checkupdate = function(alias, value)
   if values[ alias ] ~= value then
     grp.update(alias, value)
   end
 end

 calc = {}

 -- AVERAGE value
 calc['avg'] = function(group)
   local result, count, value = 0, 0

   for _, address in ipairs(group.objects) do
     value = values[ address ]

     -- number must be in [0..100] range
     if type(value) == 'number' then
       result = result + value
       count = count + 1
     -- boolean true is 100%, false is 0%
     elseif type(value) == 'boolean' then
       if toboolean(value) then
         result = result + 100
       end

       count = count + 1
     end
   end

   if count > 0 then
     result = math.floor(result / count + 0.5)
     grp.checkupdate(group.output, result)
   end
 end

   -- SUM value
 calc['sum'] = function(group)
   local result, count, value = 0, 0

   for _, address in ipairs(group.objects) do
     value = values[ address ]

     -- number must be in [0..100] range
     
       if value then
         result = result + 1
         end
     count = count + 1
   end
        
   if count > 0 then
     grp.checkupdate(group.output, result)
     grp.checkupdate(group.counter, count)
   end
 end
   
 
 
 -- AND gate
 calc['and'] = function(group)
   local result = true

   for _, address in ipairs(group.objects) do
     result = result and toboolean(values[ address ])
   end

   grp.checkupdate(group.output, result)
 end

 -- OR gate
 calc['or'] = function(group)
   local result = false

   for _, address in ipairs(group.objects) do
     result = result or toboolean(values[ address ])
   end

   grp.checkupdate(group.output, result)
 end

 -- prepare each group
 for _, group in ipairs(groups) do
   object = grp.find(group.output)

   -- cache output status object value and datatype
   values[ object.address ] = object.data
   datatypes[ object.address ] = object.datatype
   group.output = object.address

   -- group input status object list
   group.objects = {}

   -- find all status objects and cache values and datatypes
   objects = grp.tag(group.tag)
   for _, object in ipairs(objects) do
     values[ object.address ] = object.data
     datatypes[ object.address ] = object.datatype
     table.insert(group.objects, object.address)
   end

   -- force update on first run
   group.timer = 0

   -- calc function reference
   group.fn = calc[ group.mode ]
 end

 -- handle group writes
 function eventhandler(event)
   local dst, datatype

   dst = event.dst
   datatype = datatypes[ event.dst ]
   -- unknown object, stop
   if not datatype then
     return
   end

   values[ dst ] = dpt.decode(event.datahex, datatype)

   -- check if any group needs to be updated
   for _, group in ipairs(groups) do
     for _, address in ipairs(group.objects) do
       if address == dst then
         group.timer = updatedelay
       end
     end
   end
 end

 require('genohm-scada.eibdgm')
 client = eibdgm:new({ timeout = 0.1 })
 client:sethandler('groupwrite', eventhandler)
end

delta = 0
repeat
 tsec, tusec = os.microtime()
 client:step()
 tdelta = os.udifftime(tsec, tusec)

 -- clock jump
 if tdelta < 0 then
   delta = timeout
 else
   delta = delta + tdelta
 end
until delta >= timeout

-- check if any group has an active timer
for _, group in ipairs(groups) do
 timer = group.timer

 if timer then
   timer = timer - delta

   -- timer expired, run calc function
   if timer <= 0 then
     group.fn(group)
     timer = nil
   end

   group.timer = timer
 end
end
Reply
#3
Hi buuuudzik,

wow, thx. A ready solution? I will try it at weekend.
A resident script with 0s. Produce it not to much CPU load?
Reply
#4
No, because there is a variable updatedelay=0,5s(you can change this for more) so the effect is that this script has interval more than 0,5s.

But be careful with number of group addresses which are calculated by this script. I think the sum of 100-200 shouldn't be a problem but more and more can produce some weard beahaviour(in your case bad number of lamps which are switched on and number of whole lamps).
Reply
#5
Hi
In my project I have > 500 bulbs. My experience is the better way is to use two layers in these calculations. First layer (aka kitchen, bedroom) where every room contains less than 10 lights is calculated in per event based scripts. The second layer is calculated just from the first layer's results as a resident task.

I use DALI gateways. I experimented with DALI cumulative feedback but the result was unreliable. The reason was DALI sends not one but few other cumulative feedback according to DALI program and DALI bus state. And from time to time it happened on KNX bus the first telegram outran the second telegram.
LM5Lp, firmware: 2018.08.22 and 2021.12.15, FlashSYS v2, ARMv7 Processor rev 5 (v7l), kernel 4.4.151 and 4.4.259
Reply
#6
Hi,

I do a similar thing, individual lights are tagged to rooms, the rooms are tagged to a floor or areas, the floors\areas are tagged to the whole house \ apartment. That works great. That way you get a status at all of the levels.

Thanks

Roger
Reply
#7
Hi,

@buuuudzik
it works great for me. What I don't understand is, why the "OR" logic don't works...
AND and AVERAGE works, so I made a simple quick&dirty mod and added a forth object "COUNT" and modified for that the function avarage to get the amount of lights which are in state on.

I understand that script in that way, that I can use one script for each room, or sum of windows and so on, for each value I will count. Is that correct?
For example I count with Tag "Spots" all my Downlights which I tagged... If I want to count open windows, I take another copy of that script and Tag all my windows status objects with "windows", and I will get a value for opened windows. ...
Reply
#8
Hi,

I have also applied this with temperatures too,  the following will average Celsius temps and then convert it to Fahrenheit.


 
Code:
-- Temperature AVERAGE value
 calc['tempavg'] = function(group)
   local result, count, value = 0, 0
   for _, address in ipairs(group.objects) do
     value = values[ address ]
     -- number must be in [0..100] range
     if type(value) == 'number' then
       result = result + value
       count = count + 1
     end
   end
   if count > 0 then
     result = result / count
     local fahren= math.floor(((result * 9 / 5 + 32) * 10^2) + 0.5) / (10^2)
     grp.checkupdate(group.output, fahren)
   end
 end  


Thanks,


Roger
Reply
#9
But event scripts can be not a good solution when there is some change of whole room or much more when there is whole floor change. Then there are a lot of unnecessary serial calculations of every event. With this resident script you can also prepare some layers and I use also such solutions.

More on this forum about this task(more about OR calculations but also universal):
http://forum.logicmachine.net/showthread...l+statuses

http://forum.logicmachine.net/showthread...l+statuses
Reply
#10
@buuuudzik
did you mean me with your answer? If yes, you missunderstood me. I use multiple resident scripts to count for different groups. I agree with you, taht event based scripts for an global stat is not a good idea. That was the reason for this thread.

Thank you very much :-)
Reply
#11
(07.01.2017, 11:32)Habib Wrote: @buuuudzik
did you mean me with your answer? If yes, you missunderstood me. I use multiple resident scripts to count for different groups. I agree with you, taht event based scripts for an global stat is not a good idea. That was the reason for this thread.

Thank you very much :-)

This was only an exchange of some experience with this complex subject that everybody should have whole background of this subject(Preparing the elegant solution for calculating central statuses).

But for this script recommended is to using only 1 with whole groups(Kitchen, Livingroom, 1Floor etc.).  I updated this script a few minute ago because I saw that there was no sum function Big Grin  Now there is. Please check.
Reply
#12
that's great :-)
I have adopted your new version. I think the functionality should be part of the original installation.
Reply
#13
Wink 
First version was prepared by Edgars so he can everything. I agree with you but I think it should be not a script but a special app with simple editor. Another think is that this version is simpler to change for suit it to specific solutions.
Reply


Forum Jump: