Extract new sensors from a string text sensor

I have a sensor that produces this string from ESPHOME, 23 34 54 88 94. I’m looking for help in reporting each of the figures as different sensors using either ESPHOME or Home Assistant. I have no idea how to even start. Thanks for help.

In Home Assistant this can be done using Template sensors.

You will need to supply the ID from your actual sensor and change the names to suit your needs:

template:
  - sensor:
      - name: Sensor 1
        state: "{{ states('sensor.YOUR_SENSORS_ID').rsplit(' ')[0] }}"
      - name: Sensor 2
        state: "{{ states('sensor.YOUR_SENSORS_ID').rsplit(' ')[1] }}"
      - name: Sensor 3
        state: "{{ states('sensor.YOUR_SENSORS_ID').rsplit(' ')[2] }}"
      - name: Sensor 4
        state: "{{ states('sensor.YOUR_SENSORS_ID').rsplit(' ')[3] }}" 
      - name: Sensor 5
        state: "{{ states('sensor.YOUR_SENSORS_ID').rsplit(' ')[4] }}"

This can definitely be done directly in ESPHome instead of HA, but I’ll leave that for someone more familiar with doing so…

1 Like

Thank you for your reply, I will try that. I will be glad if someone can show how to do it in Esphome.

This did not work for me. This is how I got it working.

  - platform: template
    sensors:
      new_sensor:
        friendly_name: "New Sensor
        value_template: '{{ states.sensor.OLD_TEXT_SENSOR.state.split(" ")[2] }}'
        unit_of_measurement: "A"

This worked perfectly. However, I will still prefer to do this in esphome if anyone will show me how to do it.

In ESPHome you can use a custom lambda to extract the value for each sensor. Nicest solution would be a c++ function in a custom header file which you can then include in your yaml. See Includes.

But if you don’t mind having (near) duplicate code in your yaml, the following would also work.

text_sensor:
  - platform: template
    id: new_sensor_0
    name: New Sensor 0
  - platform: template
    id: new_sensor_1
    name: New Sensor 1
  - platform: template
    id: OLD_TEXT_SENSOR
    on_value:
      then:
        - text_sensor.template.publish:
            id: new_sensor_0
            state: !lambda |-
                int32_t nIndex = 0, nCur = 0;
                std::string::size_type curPos = 0, charPos = 0;
                while((charPos = x.find(' ', curPos)) != std::string::npos && nIndex > nCur++) curPos = ++charPos;
                return x.substr(curPos, charPos-curPos);
        - text_sensor.template.publish:
            id: new_sensor_1
            state: !lambda |-
                int32_t nIndex = 1, nCur = 0;
                std::string::size_type curPos = 0, charPos = 0;
                while((charPos = x.find(' ', curPos)) != std::string::npos && nIndex > nCur++) curPos = ++charPos;
                return x.substr(curPos, charPos-curPos);
        - text_sensor.template.publish:
            id: new_sensor_xxx
            ...

The nIndex is the zero-based index of the value that you want. In your example with “23 34 54 88 94”, number 23 would be index 0, 34 index 1, etc.

For 5 values you’ll need to duplicate the code until you get 5 sensors. Don’t forget to change each nIndex.

Disclaimer: I did not test this with an actual ESP board.

Thank you very much. This code works. However, instead of 34, I got 0034 and instead of 0.3, I got 00.30. Is there a way I can format the number normally?

Please how do I assign units to these values? Thanks

Mmm, the script uses pure text sensors to hold the values so if value was 34 it’ll hold “34”, won’t add any leading zeroes so definitely not done by this code. Is it something you see in the ESP’s log or in Home Assistant?

Regarding units, unfortunately, text sensors don’t have that “unit_of_measurement” attribute. When you’re certain that you only receive numeric (floating point?) values, you can change it to sensor type and in the lambda convert the string to a floating value.

sensor:
  - platform: template
    id: new_sensor_0
    name: New Sensor 0
    unit_of_measurement: A

text_sensor:
  - platform: template
    id: OLD_TEXT_SENSOR
    on_value:
      then:
        - sensor.template.publish:
            id: new_sensor_0
            state: !lambda |-
                int32_t nIndex = 0, nCur = 0;
                std::string::size_type curPos = 0, charPos = 0;
                while((charPos = x.find(' ', curPos)) != std::string::npos && nIndex > nCur++) curPos = ++charPos;
                return std::stof(x.substr(curPos));