How to set 'Not yet set' state for this AD created sensor

using an AD app in the Frontend works great, with one exception: the entity created by the app doesn’t exist before the app is actually triggered to update. I used the CC ‘Variables’ before and could use something like this:

variable:

  last_motion:
    value: No motion detected

which allowed to be available, and this displayed, before an actual value was set.

With my AP app/module I need to wait for the first (in this case) motion, for the entity to be created. Until that time, the entity doesn’t exist and all red cards in the frontend are displayed…

please see my code below. How could this be adapted to create the ‘new_entity’ at startup (preferably recorded by recorder) so it shows immediately?:

apps.yaml:

LastMotionOutside:
  class: MultiBinSensor
  module: last_motion_outside
  new_entity: sensor.last_motion_outside_ad
  max_history: 10
  binary_sensors:
    binary_sensor.backdoor_buiten_sensor_motion: Backdoor
    binary_sensor.driveway_buiten_sensor_motion: Driveway
    binary_sensor.garden_backyard_buiten_sensor_motion: Garden backyard
    etc etc etc
  format_last_changed: '%H:%M:%S'
  friendly_name: Last motion outside

and the module:

# -*- coding: utf-8 -*-

from collections import deque
from typing import Deque, Dict

import appdaemon.plugins.hass.hassapi as hass

_ATTR_NAME = "history_"


class MultiBinSensor(hass.Hass):

    _entity: str
    _date_format: str
    _history: Deque[str]
    _friendly_names: Dict[str, str]
    _entity_attributes: Dict[str, str]

    def initialize(self):
        """AppDaemon required method for app init."""
        self._entity = self.args.get("new_entity")
        icon = self.args.get("icon", "mdi:motion-sensor")
        friendly_name = self.args.get("friendly_name", "Last motion outside")
        self._entity_attributes = {"icon": icon, "friendly_name": friendly_name}
        self._date_format = self.args.get("format_last_changed")

        # Set up state history for attributes
        self._history = deque([], maxlen=int(self.args.get("max_history")))

        # Listen for binary sensor activations and store friendly names for them
        bin_sensors: Dict[str, str] = self.args.get("binary_sensors")
        self._friendly_names = {}
        for sensor, pretty_name in bin_sensors.items():
            self._friendly_names[sensor] = pretty_name
            self.listen_state(self._bin_sensor_activation, sensor, new="on")

        # recover old values, if any
        old_attrs = self.get_state(self._entity, attribute="all")
        if old_attrs:
            state = old_attrs.get("state", "unknown")
            for k, old_value in reversed(old_attrs.get("attributes", {}).items()):
                if k.startswith(_ATTR_NAME) and ": " in old_value:
                    self._history.append(old_value)
            # Re-Publish old state
            self._set_new_sensor_state(state)

    def _set_new_sensor_state(self, state):
        """Publish a new state for the sensor."""
        history_attrs = {
            f"{_ATTR_NAME}{i}": old_state
            for i, old_state in enumerate(reversed(self._history))
            if i > 0
        }
        attributes = {**self._entity_attributes, **history_attrs}
        self.set_state(self._entity, state=state, attributes=attributes)

    def _bin_sensor_activation(self, entity, attribute, old, new, kwargs):
        """Listen to bin sensors turning on, update history and publish a new state."""
        location = self._friendly_names[entity]
        pretty_date_now = self.datetime().strftime(self._date_format)

        # Add to history
        self._history.append(f"{location}: {pretty_date_now}")

        # Publish new state
        self._set_new_sensor_state(location)

Surely just an else clause on the if old_attrs: block to _set_new_sensor_state to 'unknown' or whatever default value you want?

thanks for chiming in! So much appreciated, I am a true Noob at AD… Merely hacked my way through this app which was made my Azogue some while ago.

you mean like:

        else:
            self._set_new_sensor_state("Not yet set")

(edit: above is incorrect, doesnt work, but no error in the AD log…)

Thing is, as I understand it, this sensor is recreated upon each HA start.I would have hoped recording the entity in recorder.yaml would ‘recover old values’, but it doesn’t. Can that not be realized by AD?

Or, another take, should I add the homeassistant_start event in the listener? (not sure how though…)

ha, I had a weird thought, why not use this in the def initialize(self):

self.set_state(self._entity, state="Not yet set")

which seems to do the trick :fireworks:

would this be the correct AD way of doing things?

I store the entity in a separate namespace. Then I check if the entity exists in Home Assistant. self.get_state() returns None if the entity doesn’t exist. If it does not exist I pull the entity from the separate namespace and set it in the Home Assistant namespace.