Lambda time for condition

Hi. I have the code below that I want to add a time-based condition in. My aim is to have a minimum amount of time, say 5 sec, for the condition to be evaluated as true:

  - platform: template
    id: print_status
    lambda:
      if (id(ble_mqtt_num).state < 2.5 && id(ble_mqtt).state != "nan") {
        return {"Present"};
      } else {
        return {"Away"};
      }
    update_interval: 1s

This code works fine. However, the state of ble_mqtt switches to “nan” momentarily every 15s (I can’t figure out why) so I want the code to put a minimum limit of 5s to be in the state “nan” for it to actually publish the state Away so it doesn’t switch back and forth all the time.

I’ve also tried the code below but the ESP32 gets completely overloaded because it updates continuously. (I remove the lambda in the block above when I run the code below):

on_value:
  then:
  - if:
      condition:
        for:
          time: 5s
          condition:
           lambda: 'return id(ble_mqtt_num).state < 2.5 && id(ble_mqtt).state != "nan";'
      then:
        - lambda:
            id(print_status).publish_state("Present");
      else:
        - lambda:
            id(print_status).publish_state("Away");

How can I add a time-based for condition in the first block in lambda, similar to what I’ve done with the regular code in the second block?

Not clear how the whole yaml is setup.

But in the on_value code.
you can probably use wait_until the ble_mqtt_num is some expected value range.

- wait_until:

Second thing, if the on_value is set for the id ble_mqtt_num, you can get the value from the trigger (on_value) in lambda with x instead of id(ble_mqtt_num).state.
if you want to get the state of som other sensor then use id(sensor_id).state

Another thing, check if using filters can sanitize the expected values.
There is a filter called filter-out, might work to remove “nan” ?

Thanks for the suggestions.

Let me clarify what I’m trying to do:
I have a Bluetooth sensor (epresense) that monitors the presence of my phone.

I want it to report “Present” when I’m within roughly 2.5 m, and “Away” when I either leave or turn off the BT. When leaving or turning off the BT the sensor the sensor reports the value “nan”, i.e. “nan” is useful for switching to “Away”.

ble_mqtt_num extracts the distance in meters (MQTT sensor inside HA):

sensor:
  - platform: mqtt_subscribe
    name: "BLE_mqtt"
    id: ble_mqtt_num
    topic:homeassistant/statestream/sensor/ble_office_mqtt/state

ble_mqtt extracts the state as a text sensor so that I can use “nan” In the lambda condition:

text_sensor:
- platform: mqtt_subscribe
    name: "BLE_mqtt"
    id: ble_mqtt
    topic: homeassistant/statestream/sensor/ble_mqtt/state

Ok,
My idea was to filter out the values, as my understanding is that the filters is executed first then it will trigger the on_value with the result.

But now, I understand that you have the need of “nan” as thats the value you expect when the phone is away.

Maybe if you add two if instead of using else?

on_value:
      then:
        - if:
            condition:
              for:
                time: 5s
                condition:
                  lambda: 'return id(ble_mqtt_num).state < 2.5;'
            then:
              - lambda:
                  id(print_status).publish_state("Present");
        - if:
            condition:
              for:
                time: 5s
                condition:
                  lambda: 'return id(ble_mqtt).state == "nan";'
            then:
              - lambda:
                  id(print_status).publish_state("Away");

edit fixed the yaml

Thanks. It kind of fixed the problem. However, now the state is instead stuck on “Present” irrespective of how long “nan” state is sent. I tried removing the time condition from your code and then it switches but suffers from the same initial problem I had.

That might be for the id(ble_mqtt).state never gets the value of “nan”?
We don’t have any if statement returning true, so no publish_state calls are made.

But double check that the text value of ble_mqtt is exactly “nan” case sensitive.

Yup, checked the logs and it is correct. It works without the time conditions so if it was a typo it likely wouldn’t have worked then either.

Hmm.

can you test to add one more if statement with a condition lambda to validate above 2.5
and publish away. Put the if statement as the last one of the three, then test if you move the phone away more than 2.5 if it will change?

lambda: 'return id(ble_mqtt_num).state >= 2.5;'

I ended up solving it in a slightly different way but I greatly appreciate your help anyway. Since I use the companion app and statestream from HA I can extract the transmission status from my phone that the companion app exposes to HA as an separate entity:

sensor.sm_ble_transmitter
SM-BLE Transmitter

The entity status is being published with “Transmitting” or “Bluetooth is turned off”. I merely set == “Transmitting” in the lambda and voilá.

The final code then is:

  - platform: mqtt_subscribe
    name: "BLE_mqtt"
    id: ble_mqtt_num
    topic: ***
  - platform: mqtt_subscribe
    name: "ble_mqtt_transmit"
    id: ble_mqtt_transmit
    topic: ***

  - platform: template
    id: print_status
    update_interval: 3s
    lambda:
      if (id(ble_mqtt_num).state < 2.5 && id(ble_mqtt_transmit).state == "Transmitting") {
        return {"Present"};
      } else {
        return {"Away"};
      }

That being said, I still want to figure out what the initial problem was.

Well to do that, the complete yaml is needed, and some logs too.

Maybe it’s something small, like grouping the return in condition lambda with ( ) like so:

lambda: 'return ( id(ble_mqtt_num).state < 2.5 && id(ble_mqtt).state != "nan" );'

doing so would force it to validate the parentheses first, and that should return true / false

Maybe you could add another if in the lambda inside the then and else, where you check if id(print_status).state is “Present” before publishing “Away” and vise versa.
That way you would not spam it all the time…

The code I gave that got stuck on “Present” feels like id(ble_mqtt_num).state was under 2.5 even when id(ble_mqtt).state == “nan”… that could explain why it never set to away.

Swaping the order of the if statements might help there.

I went to test your suggestions and Espresense now no longer reports the value “nan” but instead gets stuck on the last published value. While my workaround sort of works, it probably won’t work for all solutions. If I stop transmitting while within range, then move far away enough that espresense doesn’t pick up my BLE signal, I think it might trigger when I turn on BLE and the status changes to “Transmitting”.