Page 1 of 1

Update device state in 'while loop'

PostPosted: Wed Apr 03, 2024 9:10 am
by CliveS
I am trying to break out of a while loop when a contact sensor changes but the script obviously only knows the state at the start. Is there a way of updating it inside the loop?

Code: Select all

# Import the time module for time-related functions
import time

# Initialize the loop counter
count_loop = 0

# Get the device by its ID
dev = indigo.devices[683155862]  # "Z Aqara P1 door & window contact"

# Log the current state of the onOffState
indigo.server.log(f"Current onOffState: {dev.states['onOffState']}")

# Pause execution for 2 seconds
time.sleep(2)

# Retrieve the initial state of the onOffState
doorStatus = dev.states["onOffState"]

# Loop until the doorStatus becomes True or count_loop reaches 10
while not doorStatus and count_loop < 10: 
    # Log the current iteration number and the onOffState
    indigo.server.log(f"Loop iteration {count_loop}, onOffState: {doorStatus}")
   
    # Increment the loop counter
    count_loop += 1
   
    # Pause execution for 2 seconds
    time.sleep(2)
   
    # Update the doorStatus with the current state
    doorStatus = dev.states["onOffState"]

# Log a message indicating the end of the loop and the final value of count_loop
indigo.server.log(f"Loop finished, count_loop is {count_loop}")



Re: Update device state in 'while loop'

PostPosted: Wed Apr 03, 2024 11:10 am
by FlyingDiver
You have to re-fetch the dev object inside the loop, then compare the current state to the saved state.

Re: Update device state in 'while loop'

PostPosted: Wed Apr 03, 2024 11:42 am
by CliveS
Joe, can you give a pointer to the way of doing that in the Indigo documentation please.

Re: Update device state in 'while loop'

PostPosted: Wed Apr 03, 2024 11:46 am
by FlyingDiver
You're fetching a copy of the device object when you do "dev = indigo.devices[683155862]", which is the only Indigo specific thing here.

Code: Select all
# Initialize the loop counter
count_loop = 0

# Get the device by its ID
dev = indigo.devices[683155862]  # "Z Aqara P1 door & window contact"

# Retrieve the initial state of the onOffState
startDoorStatus = dev.states["onOffState"]

# Log the current state of the onOffState
indigo.server.log(f"Current onOffState:  {startDoorStatus}")

# Loop until the doorStatus changes or count_loop reaches 10
while count_loop < 10:
   
    # Pause execution for 2 seconds
    time.sleep(2)

    dev = indigo.devices[683155862]  # "Z Aqara P1 door & window contact"

    # Log the current iteration number and the onOffState
    indigo.server.log(f"Loop iteration {count_loop}, onOffState: { dev.states["onOffState"]}")

    # Check the doorStatus
    if startDoorStatus != dev.states["onOffState"]:
       break
   
    # Increment the loop counter
    count_loop += 1

# Log a message indicating the end of the loop and the final value of count_loop
indigo.server.log(f"Loop finished, count_loop is {count_loop}")

Re: Update device state in 'while loop'

PostPosted: Wed Apr 03, 2024 12:14 pm
by CliveS
That works a treat, I see what I was doing wrong now and another one for my 'python howto file'
Thank you very much.

Re: Update device state in 'while loop'

PostPosted: Wed Apr 03, 2024 1:26 pm
by matt (support)
A slightly more efficient way to update the device instance is with this API (compared to fetching an entirely new instance from the server):

Code: Select all
dev.refreshFromServer()

Re: Update device state in 'while loop'

PostPosted: Wed Apr 03, 2024 2:40 pm
by CliveS
Thanks Matt, I will try that as well, a good learning curve, and another line in the ‘python howto file’.

Re: Update device state in 'while loop'

PostPosted: Wed Apr 03, 2024 2:49 pm
by FlyingDiver
matt (support) wrote:
A slightly more efficient way to update the device instance is with this API (compared to fetching an entirely new instance from the server):

Code: Select all
dev.refreshFromServer()


I should have remembered that one.

Re: Update device state in 'while loop'

PostPosted: Sat Apr 06, 2024 2:29 am
by kw123
Q: how does this work? Does indigo remember the last fetch? And then gets the new data only?


Sent from my iPhone using Tapatalk

Re: Update device state in 'while loop'

PostPosted: Sun Apr 07, 2024 2:13 pm
by matt (support)
It fetches all the current device states and properties, replacing those attributes in the instance. I don't recall what happens if the device class changes (say from a dimmer to a thermostat), so no promises that will be handled correctly or gracefully.

Re: Update device state in 'while loop'

PostPosted: Wed Apr 17, 2024 9:30 am
by kw123
Added this code to the beginning of runConcurrentThread(self) ( before the main loop) in one of my plugins to check what the effect of dev.refreshFromServer() vs dev = indigo.devices{} ds:
Code: Select all
      devId = 1667343748
      tStart = time.time()
      for ii in range(1000):
         dev = indigo.devices[devId]
         dev.updateStateOnServer("hardwareVendor",str(time.time())) # dummy state update of an empty unused state
      indigo.server.log( "after 1000 iterations with new dev=indigo.devices[] dt = {:.5f} ".format(time.time() - tStart))
      self.sleep(1)
      
      tStart = time.time()
      dev = indigo.devices[devId]
      for ii in range(1000):
         dev.updateStateOnServer("hardwareVendor",str(time.time()))# dummy state update of an empty unused state
         dev.refreshFromServer()
      indigo.server.log( "after 1000 iterations with dev.refreshFromServer(): dt = {:.5f} ".format(time.time() - tStart))

i got:
Code: Select all
after 1000 iterations with new dev=indigo.devices[] dt = 0.79437
after 1000 iterations with dev.refreshFromServer(): dt = 0.89304   


dev.refreshFromServer() seems to be a tiny bit slower than dev = indigo.devices[devId] ... or am I looking at some caching effect?

and on a side note:
Code: Select all
 
      tStart = time.time()
      dev = indigo.devices[devId]
      for ii in range(1000):
         dev.updateStateOnServer("hardwareVendor",str(time.time()))
      indigo.server.log( "dev.updateStateOnServer after 1000 iterations  = {:.5f} ".format(time.time() - tStart))
gives:
dev.updateStateOnServer after 1000 iterations dt = 0.00636


Karl

Re: Update device state in 'while loop'

PostPosted: Wed Apr 17, 2024 10:45 am
by FlyingDiver
You're only measuring the time spent in the plugin, not the load on the server.

Re: Update device state in 'while loop'

PostPosted: Wed Apr 17, 2024 11:43 am
by kw123
ok got that,
2. test with a larger loop (1k--> 20k) to check the cpu time manually w activity monitor

Code: Select all
      devId = 1667343748
      indigo.server.log( "starting w 1 20000 loop , check server load")
      self.sleep(2)
      tStart = time.time()
      for ii in range(20000):
         dev = indigo.devices[devId]
         dev.updateStateOnServer("hardwareVendor",str(time.time())) # dummy state update of an empty unused state
      indigo.server.log( "after 20000 iterations with new indigo.devices[] dt = {:.5f} ".format(time.time() - tStart))

      time.sleep(50)  ## let the cpu indicator cool down

      indigo.server.log( "starting w 2. 20000 loop , check server load")
      self.sleep(2)
      tStart = time.time()
      dev = indigo.devices[devId]
      for ii in range(20000):
         dev.updateStateOnServer("hardwareVendor",str(time.time())) # dummy state update of an empty unused state
         dev.refreshFromServer()
      indigo.server.log( "after 20000 iterations with new refreshFromServer dt = {:.5f} ".format(time.time() - tStart))


gives
starting w 1 20000 loop , check server load
after 20000 iterations with new indigo.devices[] dt = 16.17746
starting w 2. 20000 loop , check server load
after 20000 iterations with new refreshFromServer dt = 16.31658

in both cases CPU load:
indigo 2023.2 up to 120%
indigo server 50%
the plugin is around 1-4%

it looks as if in both cases the execution takes ~ 16 secs for 20,000 iterations and the CPU load is about the same ( kind of in overload @ 120% for indigo client, should not be the dev.updateStateOnServer as that alone w/o the dev =indigodevcies[] is done in 0.1 secs)

next step would be to measure the total cpu consumed ( within the plugin ) before and after loop for indigo client and server processes

Re: Update device state in 'while loop'

PostPosted: Wed Apr 17, 2024 2:22 pm
by kw123
Using the same loops as before , just adding a call to
/bin/ps -ef | grep 'Contents/MacOS/Indigo 2023.2' | grep -v grep"
and
/bin/ps -ef | grep 'IndigoServer' | grep -v grep
before and after loop

that returns something like:
...... 29:53.49 Applications/Indigo', '2023.2.app/Contents/MacOS/Indigo', '2023.2']
...... 30:11.51 Applications/Indigo', '2023.2.app/Contents/MacOS/Indigo', '2023.2']
and the delta 30:11.51 - 29:53.49 should be the consumed CPU time


gives:
Code: Select all
            indigo.devices[]  vs     refreshFromServer
indigo client:   18.02         vs      21.54 secs
indigo server:    6.87         vs      10.59 secs

loop with
Code: Select all
indigo.devices[]    --> ~24 secs consumed CPU by indigo server and indigo client
refreshFromServer   --> ~31 secs consumed CPU by indigo server and indigo client


in Summary
if this is correct the loop with 20,000 calls
w dev=indigo.devices[] uses 7 secs LESS CPU that the loop with dev.refreshFromServer()

Karl

Re: Update device state in 'while loop'

PostPosted: Thu Apr 18, 2024 11:08 am
by matt (support)
Hi Karl,

Interesting performance numbers. I would have expected the opposite. I just looked at the python API / glue code and I would expect that dev.refreshFromServer would be the same speed or a bit faster than requesting a new device instance from the server.. I agree with your data though, so there must be some overhead I'm not seeing. I'd probably still recommend using dev.refreshFromServer on the chance that some day we add some optimizations to it (and it isn't horribly slower), but that is just a personal preference.