In my opinion, any device that needs time to complete received command should be handled in a similar way, using a proxy virtual item as you did, but in a NON-BLOCKING fashion. The proxy item should accept and process ANY COMMAND AT ANYTIME and “take care of the magic” to set the device to the correct state when the real device is ready to accept commands.
Your beamer_switch should handle the ON and OFF commands sent at anytime. To achieve this, you can store the “target state” for later processing if the beamer is currently in a state that does not allow receiving/processing commands immediately. Then the beamer_switch state should be updated to transitional states (HEATING/COOLING) if needed and to the final state (ON/OFF) once ready. On my side, I use Number items to handle the transitonal state that Switch items cannot handle. Rollershutter items are a good idea if only 2 transitional states (but for A/V devices with input switching, this is not enough to handle all possible transitions).
I believe that blocking patterns (waiting or debouncing or similar ) are nonsense : life is asynchroneous and we spent years to take computers from sequential processing to asynchroneous mutithreaded processing for good reasons : processing get smooths and user-friendly. If you block or debounce, you slow down processing or even jeopardize the states expected by the triggering items or rules when the logic of processing is complex.
var Timer vp_task = null //timer that will take care of device delays
var Integer vp_on_delay = 20 //delay for switching on the device (or getting to the )
var Integer vp_off_delay = 40 // delay for switching off the device
var Integer vp_state_target = null
rule "Projector command processor (command)"
when
Item AV_mRDC_Salon_vp received command
then
//AV_mRDC_Salon_vp states : 0=OFF, 10=Preheating, 90=Shutting-down, 100=0N
//IR_mRDC_Salon_command is a virtual item that control IR sending and IR queue
if(AV_mRDC_Salon_testmode.state == ON) { // when in test mode: reduce delays
vp_on_delay = 5
vp_off_delay = 2
}
switch(AV_mRDC_Salon_vp.state.toString + ">" + receivedCommand.toString){
//cases when ON command received
case "0>100": {
vp_state_target = 100 // ON
AV_mRDC_Salon_vp.postUpdate(10) //Preheating
IR_mRDC_Salon_command.sendCommand("Jvc_vp,poweron")
vp_task?.cancel()
vp_task = createTimer(now.plusSeconds(vp_on_delay)) [|
AV_mRDC_Salon_vp.postUpdate(100) //ON
vp_task = null
]
}
//case "10>100": case "100>100": { }//Preheating>ON or ON>ON: nothing to do
case "90>100": { //Shutting-down>ON: wait for state to change to OFF before sending the ON command
vp_state_target = 100 // ON
AV_mRDC_Salon_vp.postUpdate(10) //Preheating
}
//cases when OFF command received
//case "0>0": case "90>0": { }//Shutting-down>OFF or OFF>OFF: nothing to do
case "100>0": {
vp_state_target = 0 //OFF
AV_mRDC_Salon_vp.postUpdate(90) //Shutting-down
IR_mRDC_Salon_command.sendCommand("Jvc_vp,poweroff")
vp_task?.cancel()
vp_task = createTimer(now.plusSeconds(vp_off_delay)) [|
AV_mRDC_Salon_vp.postUpdate(0) //OFF
vp_task = null
]
}
case "10>100": { //Preheating>OFF: wait for state to change to ON before sending the OFF command
vp_state_target = 0 //OFF
AV_mRDC_Salon_vp.postUpdate(90) //Shutting-down
}
}
end
rule "Projector command processor (state)"
when
Item AV_mRDC_Salon_vp changed
then
if (AV_mRDC_Salon_vp.state != vp_state_target) { AV_mRDC_Salon_vp.sendCommand(vp_state_target.toString)}
end
For the screen, processing the logic is different. When receiving an UP command while rolling-down, I store the elapsed time for the rolling-down so I can calculate when the screen will actually be in the UP position and call a postUpdate() at the right time. In this case, “the magic” it to send a STOP command to the screen, followed by an UP command (but this depends on behavior of your device).
My examples are DSL-style. I just installed JSR and I am still investigating the benefits of Jython over DSL (except speed which is obvious) to take advantage of it in upcomng reimplementation.