Page 1 of 1

mirror switches unnecessarily complicated :D

Posted: Monday 16 December 2024 14:59
by bitzy
hello everyone.
i've made myself a little script that will make 2 switches mirror each other, no matter which one i push and in what state it is, the other one will mirror. (not like the slave device for a switch). so let say i have a switch that will control some real device. i want to activate that switch from some script after some conditions are met and i found out that the best way for me is to use another virtual switch that after is activated and the conditions are met, will send the command for the real switch.
the mirroring must work in both ways and in any state of the switches.

i've done this in a little script (and because i can make my life more complicated that already is) i wanted to make the script a general one, to be able to mirror any 2 switches and how many pairs of them i want:

the script is working for what i can see so far:

Code: Select all

local sec = 1 -- second to delay the toggle

return {
    on = {
        devices = {
            'Centrala_ON/OFF', 'Centrala_ON/OFF_(rLe1)', -- Pair 1
            'Pompa_ON/OFF', 'Pompa_On/OFF_(rLe2)', -- Pair 2
            --'Pompa2_ON/OFF', 'Pompa2_On/OFF_(rLe2)', -- Pair 3
            --'Pompa4_ON/OFF', 'Pompa4_On/OFF_(rLe4)'  -- Pair 4
        }
    },
    logging = {
        level = domoticz.LOG_INFO,
        marker = "========= Sync Multiple Dummy and Real Switches ========="
    },

    execute = function(domoticz, device) 
        
        -- Defining the switch pairs
        local switchPairs = {
            {dummy = 'Centrala_ON/OFF', real = 'Centrala_ON/OFF_(rLe1)'},
            {dummy = 'Pompa_ON/OFF', real = 'Pompa_On/OFF_(rLe2)'},
            
            --{dummy = 'Pompa3_ON/OFF', real = 'Pompa3_On/OFF_(rLe3)'},
            --{dummy = 'Pompa4_ON/OFF', real = 'Pompa4_On/OFF_(rLe4)'}
        }

        --getting the switch pairs that was activated
        for _, pair in ipairs(switchPairs) do
            local dummySwitch = domoticz.devices(pair.dummy)  	-- Getting the dummy switch
            local realSwitch = domoticz.devices(pair.real)    		-- Getting the real switch

            -- Synchronizing the dummy with the real switch
            if device.name == pair.dummy then
                if dummySwitch.state == 'On' and realSwitch.state ~= 'On' then
                    realSwitch.switchOn().afterSec(sec) 					
                elseif dummySwitch.state == 'Off' and realSwitch.state ~= 'Off' then
                    realSwitch.switchOff()  
                end
            -- Synchronizing the real with the dummy switch
            elseif device.name == pair.real then
                if realSwitch.state == 'On' and dummySwitch.state ~= 'On' then
                    dummySwitch.switchOn()--.afterSec(sec)  
                elseif realSwitch.state == 'Off' and dummySwitch.state ~= 'Off' then
                    dummySwitch.switchOff()  
                end
            end
        end
    end
now, because i have mirrored a switch that some times need to be kept Off by a script, for security reasons, and because i can manually activate that switch in this time, before the security script activate and turns back Off the switch, i need a script that check if the state of the first one is not already changed before mirroring.
or a way to block the switch that is kept Off by the script to be accessible for me to change state.
i don't know how to better explain the problem since english is not my primary language.

the real application - maybe i manage to explain better:
a switch "real_1" controls a combi boiler that heats a buffer tank. when the buffer tank reach a max temperature, a script will not allow the switch 'real_1' to be activated. the problem is, i can manually push the switch in domoticz that will turn On, the boiler starte then the security script is activated and the 'real_1' is shut Off and the boiler is turned off.

'dummy_1' mirrors real_1, and also real_1 mirrors dummy_1.
i need that the script, when i activate dummy_1, before sending the command to real_1 for the mirror part, to do the check if dummy_1 was not already been shut Off by the script.

the modification that i made to my initial script were like this: i introduced another dummy switch that turns on immediately after the first switch changes state, and check if the initial state of the first switch is still the same then i mirror it with the second switch. something like this:

Code: Select all

local sec = 1 -- second to delay the toggle

return {
    on = {
        devices = {
            'Centrala_ON/OFF', 'Centrala_ON/OFF_(rLe1)', -- Pair 1
            'Pompa_ON/OFF', 'Pompa_On/OFF_(rLe2)', -- Pair 2
            --'Pompa2_ON/OFF', 'Pompa2_On/OFF_(rLe2)', -- Pair 3
            --'Pompa4_ON/OFF', 'Pompa4_On/OFF_(rLe4)'  -- Pair 4
        }
    },
    logging = {
        level = domoticz.LOG_INFO,
        marker = "========= Sync Multiple Dummy and Real Switches ========="
    },

    execute = function(domoticz, device) 
        -- Definim perechile de switch-uri
        local switchPairs = {
            {dummy = 'Centrala_ON/OFF', real = 'Centrala_ON/OFF_(rLe1)'},
            {dummy = 'Pompa_ON/OFF', real = 'Pompa_On/OFF_(rLe2)'},
            --{dummy = 'Pompa3_ON/OFF', real = 'Pompa3_On/OFF_(rLe3)'},
            --{dummy = 'Pompa4_ON/OFF', real = 'Pompa4_On/OFF_(rLe4)'}
        }

            local helpOnOff = domoticz.devices('helpOnOff')
            helpOnOff.switchOff()
         
		--getting the switch pairs that was activated		 
        for _, pair in ipairs(switchPairs) do
            local dummySwitch = domoticz.devices(pair.dummy)  -- getting the dummy switch
            local realSwitch = domoticz.devices(pair.real)    -- getting the real switch

            --  synchronizing the dummy switch (if was pushed) with his correspondent
            if device.name == pair.dummy then
                if dummySwitch.state == 'On' and realSwitch.state ~= 'On' then
                    helpOnOff.switchOn().afterSec(sec)                          -- adding a delay before sincronisation
                    if helpOnOff.state == 'On'and dummySwitch.state == 'On' then
                        realSwitch.switchOn()  
                    end 
                elseif dummySwitch.state == 'Off' and realSwitch.state ~= 'Off' then
                        helpOnOff.switchOn().afterSec(sec)                          -- adding a delay before sincronisation
                    if helpOnOff.state == 'On'and dummySwitch.state == 'Off' then
                        realSwitch.switchOff() 
                    end                                           
                end
            
			--  synchronizing the real switch (if was pushed) with his correspondent
            elseif device.name == pair.real then
                if realSwitch.state == 'On' and dummySwitch.state ~= 'On' then
                    helpOnOff.switchOn().afterSec(sec)                          -- adding a delay before sincronisation
                    if helpOnOff.state == 'On'and realSwitch.state == 'On' then
                        dummySwitch.switchOn()  
                    end 
                elseif realSwitch.state == 'Off' and dummySwitch.state ~= 'Off' then
                    helpOnOff.switchOn().afterSec(sec)                          -- adding a delay before sincronisation
                    if helpOnOff.state == 'On'and realSwitch.state == 'Off' then
                        dummySwitch.switchOff()  
                    end 
                end
            end
        end
    end
}


the problem i have with this :

Code: Select all

 if dummySwitch.state == 'On' and realSwitch.state ~= 'On' then
                    helpOnOff.switchOn().afterSec(sec)                          -- adding a delay before sincronisation
                    if helpOnOff.state == 'On'and dummySwitch.state == 'On' then
                        realSwitch.switchOn()  
                    end 
is that the check "if helpOnOff.state == 'On'and dummySwitch.state == 'On' then" needs to be made after "helpOnOff.switchOn().afterSec(sec) " but for how domoticz works, if i understand correctly, the state of the switch helpOnOff doesn't have time to change in domoticz (even withow afterseSec() ) before the next line is executed so it will always be Off.
can someone help me get around this problem?
or doese someone know a easier way to do this type of synchronization i want to do?

Re: mirror switches unnecessarily complicated :D

Posted: Monday 16 December 2024 17:44
by waltervl
You have to create another script that triggers on device 'helpOnOff' changes or add device helpOnOff to the trigger devices of your current scripts and act on that.
But for me it feels a lot of complicated actions with all your exceptions. If you put also your calculations just on the real (real_1) it would make life a lot easier and you do not need to sync things. If you want to prevent manual action on the switch you can hide it (rename to $real_1) or protect it with a password. https://wiki.domoticz.com/Managing_Devices#Protected

Re: mirror switches unnecessarily complicated :D

Posted: Monday 16 December 2024 18:06
by bitzy
the real switches are hidden on the normal use. i need them on the user that is allowed (me :D) to test on them, work on them, change on them, work on them, change on them, test on them ...
that is why protect them with a password will be useless or a pain in the brain.
can u develop a little on the other script or the trigger idea?

Re: mirror switches unnecessarily complicated :D

Posted: Monday 16 December 2024 19:38
by waltervl
OK
Or you skip the AfterSec() part. Why do you need that delay?

Re: mirror switches unnecessarily complicated :D

Posted: Wednesday 18 December 2024 9:20
by bitzy
sometime i need a little bit of time for the physical contacts of the relays that are controlled from domoticz to move since the physical movement is not so fast as a command on a computer.
or for example i have an actuator that his motor can be moved in both ways. if it moves in 1 way and i give the command to change direction of the movement directly from the switch that controls it , i want the initial movement to close first and then the relay for the new direction to be put in On state and the motor to execute the movement in the opposite direction. this way the motor will not get the commands to move in opposite directions int he same time .

anyway, like i said in the initial post, even if i switchOn widow afterSec, domoticz still not change the state of the switch in his internal guts fast enough.
for what i understand, the script reads the command to switchOn and send it to the switch. the switch change state and the new state is updated in domoticz wherw he keep track of that state. but the script does not wait to this changes to take effect and quickly pass to the next command line. or this is what i understand so far to be the problem. this short time from when the switch get the command until his state is updated in domoticz is what i need to go over and i don't know how.

Re: mirror switches unnecessarily complicated :D

Posted: Wednesday 18 December 2024 9:45
by waltervl
If you add the dummy helpOnOff switch in the script trigger (put it in the on devices array like below) then the command helpOnOff.switchOn().afterSec(sec) will trigger a second run of the script.

Code: Select all

 on = {
        devices = {
           'helpOnOff',
            'Centrala_ON/OFF', 'Centrala_ON/OFF_(rLe1)', -- Pair 1
            'Pompa_ON/OFF', 'Pompa_On/OFF_(rLe2)', -- Pair 2
            --'Pompa2_ON/OFF', 'Pompa2_On/OFF_(rLe2)', -- Pair 3
            --'Pompa4_ON/OFF', 'Pompa4_On/OFF_(rLe4)'  -- Pair 4
        }
    },

You can check in the script what device is triggering the script and then act on that:

Code: Select all

      if dummySwitch.state == 'On' and realSwitch.state ~= 'On' then
                    helpOnOff.switchOn().afterSec(sec)
         end             
      if device.name== 'helpOnOff' and helpOnOff.state == 'On' and dummySwitch.state == 'On' then
                        realSwitch.switchOn()  
         end 
But you probably have to rewrite your whole script because above example will also trigger helpOnOff.switchOn().afterSec(sec) again....
And 'helpOnOff' is always switched Off at the beginning of the script.

Re: mirror switches unnecessarily complicated :D

Posted: Wednesday 18 December 2024 15:12
by bitzy
i can't make your idea work and any attempt of trying to use helpOnOff as a trigger for the scripts ended in failure. the logic is needed to making this work escapes me for the moment.
nothing i've tried doesn't seems to make it go well. at some point i thought i made it work, the action was mirrored, everything seemed to be ok, but when i looked at the log, the script was recalled for many many many many many many many many many (this is how many) times and any attempt of making it better end up in failure. now i don't even know what was the version that behaved this way :D

Re: mirror switches unnecessarily complicated :D

Posted: Wednesday 18 December 2024 17:26
by HvdW
I asked CoPilot this question:

i've made myself a little script that will make 2 switches mirror each other, no matter which one i push and in what state it is, the other one will mirror. (not like the slave device for a switch). so let say i have a switch that will control some real device. i want to activate that switch from some script after some conditions are met and i found out that the best way for me is to use another virtual switch that after is activated and the conditions are met, will send the command for the real switch.
the mirroring must work in both ways and in any state of the switches.


This is what CoPilot says:
It sounds like you've put a lot of thought into your script! Mirroring switches can indeed be a bit tricky, especially when you need them to work seamlessly in both directions and under various conditions.

From what you've described, it seems like you're using a combination of real and virtual switches to control devices, and you want to ensure that the state of one switch is always mirrored by the other. This involves not only the initial synchronization but also handling any changes in state due to scripts or manual interventions.

One approach to ensure that the mirroring works correctly is to use a helper switch (like your `helpOnOff` switch) to introduce a delay and check the state before performing the mirroring action. However, as you've noticed, the timing can be tricky because the state changes might not be reflected immediately in Domoticz.

Here are a few suggestions to improve your script:

1. **Trigger on State Change**: Instead of relying on a delay, you can trigger the script based on the state change of the helper switch. This way, you ensure that the state has been updated before performing the mirroring action.

2. **Use a Separate Script for the Helper Switch**: Create a separate script that triggers when the helper switch changes state. This script can then perform the necessary checks and update the real and dummy switches accordingly.

3. **Protect Critical Switches**: If you have switches that should not be manually changed for safety reasons, consider hiding them or protecting them with a password. This way, you can prevent accidental manual changes that might interfere with your scripts.

Here's an example of how you might modify your script to include a separate trigger for the helper switch:

Code: Select all

local sec = 1 -- second to delay the toggle
return {
    on = {
        devices = {
            'Centrala_ON/OFF',
            'Centrala_ON/OFF_(rLe1)', -- Pair 1
            'Pompa_ON/OFF',
            'Pompa_On/OFF_(rLe2)', -- Pair 2
            'helpOnOff' -- Helper switch
        }
    },
    logging = {
        level = domoticz.LOG_INFO,
        marker = "========= Sync Multiple Dummy and Real Switches ========="
    },
    execute = function(domoticz, device)
        -- Defining the switch pairs
        local switchPairs = {
            {dummy = 'Centrala_ON/OFF', real = 'Centrala_ON/OFF_(rLe1)'},
            {dummy = 'Pompa_ON/OFF', real = 'Pompa_On/OFF_(rLe2)'}
        }
        local helpOnOff = domoticz.devices('helpOnOff')
        
        -- Reset the helper switch
        helpOnOff.switchOff()
        
        -- Check which device triggered the script
        if device.name == 'helpOnOff' then
            -- Perform the mirroring action based on the helper switch state
            for _, pair in ipairs(switchPairs) do
                local dummySwitch = domoticz.devices(pair.dummy)
                local realSwitch = domoticz.devices(pair.real)
                
                if helpOnOff.state == 'On' then
                    if dummySwitch.state == 'On' then
                        realSwitch.switchOn()
                    elseif dummySwitch.state == 'Off' then
                        realSwitch.switchOff()
                    end
                end
            end
        else
            -- Handle the initial state change and trigger the helper switch
            for _, pair in ipairs(switchPairs) do
                local dummySwitch = domoticz.devices(pair.dummy)
                local realSwitch = domoticz.devices(pair.real)
                
                if device.name == pair.dummy then
                    if dummySwitch.state == 'On' and realSwitch.state ~= 'On' then
                        helpOnOff.switchOn().afterSec(sec)
                    elseif dummySwitch.state == 'Off' and realSwitch.state ~= 'Off' then
                        helpOnOff.switchOn().afterSec(sec)
                    end
                elseif device.name == pair.real then
                    if realSwitch.state == 'On' and dummySwitch.state ~= 'On' then
                        helpOnOff.switchOn().afterSec(sec)
                    elseif realSwitch.state == 'Off' and dummySwitch.state ~= 'Off' then
                        helpOnOff.switchOn().afterSec(sec)
                    end
                end
            end
        end
    end
}
This script introduces a helper switch that triggers the mirroring action after a delay, ensuring that the state changes are properly reflected in Domoticz before performing the mirroring. You might need to adjust the logic based on your specific requirements and test it thoroughly to ensure it works as expected.

If you have any specific questions or need further assistance, feel free to ask!

Re: mirror switches unnecessarily complicated :D

Posted: Wednesday 18 December 2024 17:44
by waltervl
This line, will ruin the check on helpOnOff so you should only do that if the triggered device is not helpOnOff

Code: Select all

        -- Reset the helper switch
        helpOnOff.switchOff()

Re: mirror switches unnecessarily complicated :D

Posted: Wednesday 18 December 2024 17:53
by HvdW
Like this?

Code: Select all

local sec = 1 -- second to delay the toggle
return {
    on = {
        devices = {
            'Centrala_ON/OFF',
            'Centrala_ON/OFF_(rLe1)', -- Pair 1
            'Pompa_ON/OFF',
            'Pompa_On/OFF_(rLe2)', -- Pair 2
            'helpOnOff' -- Helper switch
        }
    },
    logging = {
        level = domoticz.LOG_INFO,
        marker = "========= Sync Multiple Dummy and Real Switches ========="
    },
    execute = function(domoticz, device)
        -- Defining the switch pairs
        local switchPairs = {
            {dummy = 'Centrala_ON/OFF', real = 'Centrala_ON/OFF_(rLe1)'},
            {dummy = 'Pompa_ON/OFF', real = 'Pompa_On/OFF_(rLe2)'}
        }
        local helpOnOff = domoticz.devices('helpOnOff')
        
        -- Reset the helper switch only if the triggered device is not helpOnOff
        if device.name ~= 'helpOnOff' then
            helpOnOff.switchOff()
        end
        
        -- Check which device triggered the script
        if device.name == 'helpOnOff' then
            -- Perform the mirroring action based on the helper switch state
            for _, pair in ipairs(switchPairs) do
                local dummySwitch = domoticz.devices(pair.dummy)
                local realSwitch = domoticz.devices(pair.real)
                
                if helpOnOff.state == 'On' then
                    if dummySwitch.state == 'On' then
                        realSwitch.switchOn()
                    elseif dummySwitch.state == 'Off' then
                        realSwitch.switchOff()
                    end
                end
            end
        else
            -- Handle the initial state change and trigger the helper switch
            for _, pair in ipairs(switchPairs) do
                local dummySwitch = domoticz.devices(pair.dummy)
                local realSwitch = domoticz.devices(pair.real)
                
                if device.name == pair.dummy then
                    if dummySwitch.state == 'On' and realSwitch.state ~= 'On' then
                        helpOnOff.switchOn().afterSec(sec)
                    elseif dummySwitch.state == 'Off' and realSwitch.state ~= 'Off' then
                        helpOnOff.switchOn().afterSec(sec)
                    end
                elseif device.name == pair.real then
                    if realSwitch.state == 'On' and dummySwitch.state ~= 'On' then
                        helpOnOff.switchOn().afterSec(sec)
                    elseif realSwitch.state == 'Off' and dummySwitch.state ~= 'Off' then
                        helpOnOff.switchOn().afterSec(sec)
                    end
                end
            end
        end
    end
}