Listen_state in initialize(self) to entities that may not exist when initializing

I’m trying to set up an AppDaemon app that should run the function self.recalculate() whenever one of three entities change state. I would like to run it first and then set up three times listen_state to get this done.

  def initialize(self):
    self.recalculate()
    self.energyPriceEntity = self.get_entity(self.args["energyPrice"])
    self.energySurchargeEntity = self.get_entity(self.args["energySurcharge"])
    self.gridTariffEntity = self.get_entity(self.args["gridTariff"])
    self.energyPriceEntity.listen_state(self.recalculate)
    self.energySurchargeEntity.listen_state(self.recalculate)
    self.gridTariffEntity.listen_state(self.recalculate)

What I’m wondering is:
The three entities may not yet exist when the AppDaemon app initializes.

I can check in self.recalculate() whether they exist, but in initialize() I have only the one go? Or? Is this likely to fail if the entities do not exist at the time of initializing?

Any pointers would be appreciated.

In case anyone else should wonder at a later date: Here’s how I solved it.

  def initialize(self):
    self.listenersSet = False
    self.setup_listeners()


  def setup_listeners(self):
    if self.entity_exists(self.args["energyPrice"]):
      self.log("__function__: {} = {} kr/kWh".format(self.args["energyPrice"], self.get_state(self.args["energyPrice"])), log="main_log", level="INFO")
      if self.entity_exists(self.args["energySurcharge"]):
        self.log("__function__: {} = {} kr/kWh".format(self.args["energySurcharge"], float(self.get_state(self.args["energySurcharge"]))/100.0), log="main_log", level="INFO")
        if self.entity_exists(self.args["gridTariff"]):
          self.log("__function__: {} = {} kr/kWh".format(self.args["gridTariff"], self.get_state(self.args["gridTariff"])), log="main_log", level="INFO")
          self.listen_state(self.on_listen_test, self.args["energyPrice"])
          self.listen_state(self.on_listen_test, self.args["energySurcharge"])
          self.listen_state(self.on_listen_test, self.args["gridTariff"])
          self.listenersSet = True
    if not(self.listenersSet):
      self.run_in(self.setup_listeners, 10)


  def on_listen_test(self, entity, attribute, old, new, kwargs):
    self.log("__function__: {} changed".format(entity), log="main_log", level="INFO")
    self.log("__function__: {} = {} kr/kWh".format(self.args["energyPrice"], self.get_state(self.args["energyPrice"])), log="main_log", level="INFO")
    self.log("__function__: {} = {} kr/kWh".format(self.args["energySurcharge"], float(self.get_state(self.args["energySurcharge"]))/100.0), log="main_log", level="INFO")
    self.log("__function__: {} = {} kr/kWh".format(self.args["gridTariff"], self.get_state(self.args["gridTariff"])), log="main_log", level="INFO")

Next step is to do something useful (other than outputting the values in on_listen_test) :wink:

This is my preference and there are a lot of different ways you can do this but I would probably define the entity_id as well as attributes associated with that inside YAML then use a for loop in initialize() to create the entities. I’d also store a reference to the unique entity name with the entity object in a dictionary to make it easy to access later on (if you want).

user_defined_sensors:
  energy_price:
    entity_id: sensor.energy_price
    attributes:
      friendly_name: Energy Price
      unit: kr/kWh
  energy_surcharge:
    entity_id: sensor.energy_surcharge
    attributes:
      friendly_name: Energy Surcharge
      unit: kr/kWh

then within initialize()

self.user_defined_sensors = self.args["user_defined_sensors"]
self.user_defined_entities = dict()

for entity_name, entity_vals in self.user_defined_sensors.items():
    entity = self.get_entity(entity_vals["entity_id"])
    if not entity.exists():
        # define whatever default state you want or use another method to determine what it should be
        entity.set_state(state="unknown", attributes=entity_vals["attributes"])
    self.user_defined_entities[entity_name] = entity

    # can add the listen_states within this loop or another if desired
    # passing entity_name gives a reference to any data associated with the user defined
    # sensor and can be accessed in the callback, for example
    # entity_name = kwargs["entity_name"]
    # unit = self.user_defined_sensors[entity_name]["attributes"]["unit"]
    entity.listen_state(self.on_listen_test, entity_id, entity_name=entity_name)

That’s brilliant, and a much more elegant solution than the one I came up with. Thanks!