How long has entity x been for in state y

Hi everybody,

this is also somewhat being discussed in this thread, but I would like to find an all-appdaemon solution for this (and in general).

Since I am still learning AppDaemon, I don’t quite understand how to do certain things, so it’d be really nice if you could help me write this app.

apps.yaml

entity_timer:
  module: entity_timer
  class: EntityTimer
  # entity to observe
  trigger_entity: vacuum.medusa
  # which state triggers the entity
  trigger_state: cleaning
  # interval in which to update the sensor (in seconds)
  interval: 30
  # output current runtime to Home Assistant sensor
  output: sensor.ad_medusa_times

So what this ought to do is do something when vacuum.medusa changes it’s state to cleaning. It should set the attribute started to time.time() when triggered. Then, every seconds, it should set the attribute running to time.time() - <attribute.started>.

I am trying to improve on my initial approach as I am writing this. I don’t know how to implement certain things, for example

  • how to update the running attribute every seconds
  • how to determine that the entity is done without having to manually define all possible states that it could change to (in this case idle, docked, error, but depending on other trigger entities, there might be much different numbers of states, which one might not want to manually define)¹
  • how to keep the start_time attribute, when we update the entity and write the running_time attribute²

entity_timer/entity_timer.py

import appdaemon.plugins.hass.hassapi as hass
import time
import datetime

class EntityTimer(hass.Hass):
    def initialize(self):
      trigger_entity = self.args['trigger_entity']
      trigger_state = self.args['trigger_state']
      interval = self.args['interval']
      output = self.args['output']

      # trigger when state changes to defined value
      self.listen_state(self.my_action, trigger_entity, state=trigger_state)


    def my_action(attribute, entity, kwargs, new, old, self):
# is there a way to use these variable generated in initialize(self) for this? They will always be the same, anyway
# But when I try using them without defining them here again, they will not be found
      trigger_entity = self.args['trigger_entity']
      trigger_state = self.args['trigger_state']
      interval = self.args['interval']
      output = self.args['output']
      start_time = self.get_state(output, attribute="start_time")
      
      current_state = self.get_state(trigger_entity)
      
      # do this when entity state changes to defined value
      if ( current_state == trigger_state ):
          self.log("Started.")
          _now = time.time() # current time as timestamp
          self.set_state(output, state=trigger_state, attributes={"start_time": _jetzt, "running_time": 0, "stop_time": 0}
      # this needs to be continued to
      # calculate and write "running_time" every <interval> seconds
      else:
        pass

So this should simple write the current timestamp when the entity changes it’s state to whatever we defined in apps.yaml. So far, so good.

As I wrote above, I re-wrote my original approach while typing here, so there might be an error, but I believe it should work like this.
¹ could this be achieved by putting this in place of pass

          self.log("Not running.")
          _now = time.time() # current time as timestamp
          self.set_state(output, state=trigger_state, attributes={"start_time": <what goes here?), "running_time": <what goes here>, "stop_time": _now}

² I believe I have found the answer to this… Instead of just updating, for example, running_time, we always have to update every single attribute when using self.set_state.

I’ll keep working on this and updating the code here. However, since I have not yet found a solution to update the output every seconds, can you please help me out there?

Thank you for your help :slight_smile:

UPDATE 1:

Added start_time = self.get_state(output, attribute="start_time") so that we can calculate the time like this

running_time = (_now - start_time)
running_time = running-time.strftime("%H:%M:%S") # perhaps even just "%M:%S"

So then we can self.set_state(output, state=trigger_state, attributes={"start_time": start_time, "running_time": running_time, "stop_time": 0}. So we should be able to query this in Home Assistant via {{ state_attr("sensor.ad_medusa_times", "running_time") }}.