How to use ESPHome sensor.on_value_range reliably?

Hi all,

I have modified an Ikea Vindriktning PM2.5 sensor with a Wemos D1 Mini. I removed the original pcb and IC to gain full control over a RGB led and the existing fan. It works fine.

What is not working reliably is switching the led color based on the sensor value with sensor.on_value_range. I already know it will only switch if the sensor value transitions from outside into a given range. However, the switch works like 50% of the time. So the RGB led indicator is basically useless. Is the sensor.on_value_range good for ā€œrealā€ use? The whole design seems a bit error prone to me and I understand itā€™s a tradeoff to not spam HA with status updates?

Hereā€™s the yaml code:

esphome:
  name: esphome-web-b52406
  friendly_name: ikea-vindriktning-2
  on_boot:
    priority: -100
    then:
      - fan.turn_on: pm_sensor_fan

esp8266:
  board: d1_mini

logger:

# Enable Home Assistant API
api:
  encryption:
    key: "..."

ota:
  password: "..."

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Ikea-Vindriktning-2"
    password: "4EuZaNbTeG7q"

captive_portal:

uart:
  rx_pin: 13
  tx_pin: 4
  baud_rate: 9600

sensor:
  - platform: pm1006
    pm_2_5:
      name: "Feinstaub 2.5Āµm"
      accuracy_decimals: 1
      on_value_range:
        - above: 0
          below: 15
          then:
            - light.turn_off:
                id: rgb_indicator
        - above: 15
          below: 30
          then:
            - light.turn_on:
                id: rgb_indicator
                brightness: 100%
                red: 100%
                green: 50%
                blue: 0%
        - above: 30
          below: 60
          then:
            - light.turn_on:
                id: rgb_indicator
                brightness: 100%
                red: 100%
                green: 0%
                blue: 0%
        - above: 60
          then:
            - light.turn_on:
                id: rgb_indicator
                brightness: 100%
                red: 100%
                green: 20%
                blue: 50%
      filters:
        - sliding_window_moving_average:
            window_size: 3
            send_every: 3
            send_first_at: 1
    update_interval: 10s

light:
  - platform: rgb
    id: rgb_indicator
    name: "pm2.5 rgb indicator"
    red: red
    green: green
    blue: blue
    internal: true

fan:
  - platform: binary
    output: pm_sensor_fan_pin
    id: pm_sensor_fan
    name: pm_sensor_fan

output:
  - platform: gpio
    id: pm_sensor_fan_pin
    pin: 0
  - platform: esp8266_pwm
    id: red
    pin: 12
  - platform: esp8266_pwm
    id: green
    pin: 5
  - platform: esp8266_pwm
    id: blue
    pin: 14

Here is an example from the log. A value dropping below 15 Āµg/mĀ³ will turn the light off. Rising to 15.0 will turn on again, but dropping from this value to 14.33 will not turn off again, as the value is not transitioning from outside of the range.

So as long as the value stays below 15 Āµg/mĀ³, the orange warning led stays on giving a false alarm:

16:19:25][D][sensor:109]: 'Feinstaub 2.5Āµm': Sending state 14.66667 Āµg/mĀ³ with 1 decimals of accuracy
[16:19:25][D][light:035]: 'pm2.5 rgb indicator' Setting:
[16:19:25][D][light:046]:   State: OFF
[16:19:25][D][light:084]:   Transition length: 1.0s
[16:19:35][D][pm1006:091]: Got PM2.5 Concentration: 14 Āµg/mĀ³
[16:19:45][D][pm1006:091]: Got PM2.5 Concentration: 14 Āµg/mĀ³
[16:19:55][D][pm1006:091]: Got PM2.5 Concentration: 15 Āµg/mĀ³
[16:19:55][D][sensor:109]: 'Feinstaub 2.5Āµm': Sending state 14.33333 Āµg/mĀ³ with 1 decimals of accuracy
[16:20:05][D][pm1006:091]: Got PM2.5 Concentration: 15 Āµg/mĀ³
[16:20:15][D][pm1006:091]: Got PM2.5 Concentration: 15 Āµg/mĀ³
[16:20:25][D][pm1006:091]: Got PM2.5 Concentration: 14 Āµg/mĀ³
[16:20:25][D][sensor:109]: 'Feinstaub 2.5Āµm': Sending state 14.66667 Āµg/mĀ³ with 1 decimals of accuracy
[16:20:35][D][pm1006:091]: Got PM2.5 Concentration: 14 Āµg/mĀ³
[16:20:45][D][pm1006:091]: Got PM2.5 Concentration: 15 Āµg/mĀ³
[16:20:55][D][pm1006:091]: Got PM2.5 Concentration: 15 Āµg/mĀ³
[16:20:55][D][sensor:109]: 'Feinstaub 2.5Āµm': Sending state 14.66667 Āµg/mĀ³ with 1 decimals of accuracy
[16:21:05][D][pm1006:091]: Got PM2.5 Concentration: 15 Āµg/mĀ³
[16:21:15][D][pm1006:091]: Got PM2.5 Concentration: 15 Āµg/mĀ³
[16:21:25][D][pm1006:091]: Got PM2.5 Concentration: 15 Āµg/mĀ³
[16:21:25][D][sensor:109]: 'Feinstaub 2.5Āµm': Sending state 15.00000 Āµg/mĀ³ with 1 decimals of accuracy
[16:21:25][D][light:035]: 'pm2.5 rgb indicator' Setting:
[16:21:25][D][light:046]:   State: ON
[16:21:25][D][light:050]:   Brightness: 100%
[16:21:25][D][light:057]:   Red: 100%, Green: 50%, Blue: 0%
[16:21:25][D][light:084]:   Transition length: 1.0s
[16:21:35][D][pm1006:091]: Got PM2.5 Concentration: 15 Āµg/mĀ³
[16:21:45][D][pm1006:091]: Got PM2.5 Concentration: 14 Āµg/mĀ³
[16:21:55][D][pm1006:091]: Got PM2.5 Concentration: 14 Āµg/mĀ³
[16:21:55][D][sensor:109]: 'Feinstaub 2.5Āµm': Sending state 14.33333 Āµg/mĀ³ with 1 decimals of accuracy
[16:22:05][D][pm1006:091]: Got PM2.5 Concentration: 15 Āµg/mĀ³
[16:22:15][D][pm1006:091]: Got PM2.5 Concentration: 14 Āµg/mĀ³
[16:22:25][D][pm1006:091]: Got PM2.5 Concentration: 14 Āµg/mĀ³
[16:22:25][D][sensor:109]: 'Feinstaub 2.5Āµm': Sending state 14.33333 Āµg/mĀ³ with 1 decimals of accuracy
[16:22:35][D][pm1006:091]: Got PM2.5 Concentration: 14 Āµg/mĀ³
[16:22:45][D][pm1006:091]: Got PM2.5 Concentration: 14 Āµg/mĀ³
[16:22:55][D][pm1006:091]: Got PM2.5 Concentration: 14 Āµg/mĀ³

So this is not going to work by design. I was following the example in the documentation, where the below value is equal to the above value of the next range: Sensor Component ā€” ESPHome

Okayā€¦ so it seems the range values take decimals, so this is what I came up with and it works so far. Essentially we need a very small range (e.g. 15.0 - 15.1) where the state is undefined and stays at the previous ranges state.

Itā€™s a bit unfortunate the official documentation gives a non-working example.

 on_value_range:
  - above: 0
    below: 15
    then:
      - light.turn_off:
          id: rgb_indicator
  - above: 15.1
    below: 30
    then:
      - light.turn_on:
          id: rgb_indicator
          brightness: 100%
          red: 100%
          green: 50%
          blue: 0%
  - above: 30.1
    below: 60
    then:
      - light.turn_on:
          id: rgb_indicator
          brightness: 100%
          red: 100%
          green: 0%
          blue: 0%
  - above: 60.1
    then:
      - light.turn_on:
          id: rgb_indicator
          brightness: 100%
          red: 100%
          green: 20%
          blue: 50%

UPDATE: This seems to work. I will just leave this here so others can find this solution.

3 Likes

Was just going to advise precisely what you have done. Donā€™t overlap the range limits.