Data with and without filtering

Hello everyone.
I use an ultrasonic sensor to determine the filling of a barrel.
Using Filter, I converted the depth data to percentages. And I only get data in %.
But how can I make it also transmit standard data in distances in meters from the same sensor?

sensor:

  - platform: ultrasonic
    trigger_pin: GPIO12
    echo_pin: GPIO14
    name: Distance1
    id: distance1
    update_interval: 60s
    timeout: 2.0m

    filters:
      - lambda: |-
          if (x > 1.45) {
            return 0;
          } else if (x < 0.3) {
            return 100;
          } else {
            return (1.45-x) / (1.45-0.3) * 100.0;
          }
    unit_of_measurement: "%"

I hope for help=)

I think if you keep this as is, then add a new template sensor and add the filter to that sensor.

You can create a dumb template sensor and pass the raw value from your ultrasonic to it:

on_raw_value:
      then:
        - sensor.template.publish:
            id: distance
            state: !lambda "return x;"
sensor:
  - platform: template
    name: "Distance Sensor"
    id:distance
    update_interval: never

You should probably use a copy sensor rather than a template sensor these days. It’s the newer way to do things.

1 Like

Nice!
Does it output raw or filtered?

Thanks for the right way, I figured it out with the first example.

  • platform: template
    my code just in case, what happened:
- platform: ultrasonic
    trigger_pin: GPIO12
    echo_pin: GPIO14
    name: Distance1m
    id: distance1m
    update_interval: 60s
    unit_of_measurement: "m"

  - platform: template
    name: "Distance1"
    id: Distance1
    lambda: |-
          if (id(distance1m).state > 1.45) {
            return 0;
          } else if (id(distance1m).state < 0.3) {
            return 100;
          } else {
            return (1.45-id(distance1m).state) / (1.45-0.3) * 100.0;
          }
    unit_of_measurement: "%"
    accuracy_decimals: 2
    update_interval: 60s

It just copies the output of the sensor you point it at. So if the copied sensor already has filters on it it would take the filtered output.

I typically keep a “raw” version and maybe mark it as internal: true when I’ve done testing if I don’t really need it to go to HA.

Sometimes I even chain a few variants (say raw, processed, and one as a %).

You pretty much just use them like you would when “making a copy via a template sensor”. I think they were implemented to make that simpler.

1 Like

The copy version of this would look something like this.

  - platform: ultrasonic
    trigger_pin: ${ultra_sonic_trigger_pin} # Yellow wire
    echo_pin: ${ultra_sonic_echo_pin} # Green wire
    name: "Tank Level"
    id: hcsr04
    entity_category: diagnostic
    update_interval: never 
    internal: false     
    accuracy_decimals: 1
    unit_of_measurement: 'cm'
    timeout: 1m
    pulse_time: 10us
    filters:
      - multiply: 100 # Convert to cm
      - median:       # Moving median to smooth noise. 
          window_size:    10
          send_every:     10
          send_first_at:  10

#Convert the hcsr04 distance to a water tank level (% full)
  - platform: copy
    source_id: hcsr04
    id: water_tank_level
    internal: false
    name:  Water Tank Level
    unit_of_measurement: '%'
    accuracy_decimals: 1
    entity_category: ''
    filters:
      # Map from distance to % full. To calibrate.
      - calibrate_linear:
          - 3 -> 100 
          - 33 -> 0
      # Overide values less than 0% and more than 100%. Round to 5%.
      - lambda: |
          if (x > 100) return 100; 
          else if (x < 0) return 0;
          else return ceil(x / 5) * 5;

Great, we’ll use this advice too. :smiley:


sensor:

  - platform: ultrasonic
    trigger_pin: GPIO12
    echo_pin: GPIO14
    name: Distance1m
    id: distance1m
    update_interval: 60s
    timeout: 2.0m
    unit_of_measurement: "m"
    accuracy_decimals: 3
    filters:
      - sliding_window_moving_average:
          window_size: 20
          send_every: 1

  - platform: template
    name: "Distance1"
    id: Distance1
    lambda: |-
          if (id(distance1m).state > 1.45) {
            return 0;
          } else if (id(distance1m).state < 0.3) {
            return 100;
          } else {
            return (1.45-id(distance1m).state) / (1.45-0.3) * 100.0;
          }
    unit_of_measurement: "%"
    accuracy_decimals: 2
    update_interval: 60s
    filters:
      - sliding_window_moving_average:
          window_size: 20
          send_every: 1

  - platform: copy
    source_id: distance1m
    id: tank1_Liters
    name:  tank1_Liters
    unit_of_measurement: 'L'
    accuracy_decimals: 1
    filters:
      - calibrate_linear:
          - 1.45 -> 0 
          - 0.3 -> 903

Guys! How to turn the variable “distance1m” from positive to negative?

The thing is that the depth of the distance to the surface is visually better taken in the direction of decreasing :slight_smile:

multiply with negative one

I needed the characters to post

1 Like

BTW I find median much better than average (per my example) as a measure of central tendancy as average is sensitive to spikes/outliers whereas median isn’t). It’s a “Robust measure”.

From the web…

You can also use clamp for capping values although I forget to use that myself.

There is also multiply.

As you can see there is typically a native ESPHome way and a lambda way.
Sometimes layering up the filters the ESPHOME way can make things easier to read and debug. Although some prefer lambda and sometimes it can be more succinct.

Thank you my friend. I made the changes and will keep an eye on it :slight_smile:

image

1 Like

Wow that really demonstrates the difference!

As a result, with simple calculations I got the height => the volume of the tank in meters => in liters.

It is interesting now to compare the data now = there were 100 liters,
after 10 minutes, it became 90 liters = the output of the variable: 10 liters consumed in 10 minutes.

How to make the variables compare in a given period of time…

make Home Assistant statistics, state changes ±