Problems with text_sensor mqtt_subscribe (pool-monitor PH tds, temp)

Hello,
I’m still leraning esp home and just starting to try things.

Therefore, please bear with me if the question below is simple.

About the problem:
I have built a pool monitor to monitor PH, TDS and temp. This runs via battery and solar.
Initially I did everything via the Homeassitant Api (data exchange) now I have switched to MQTT.
Basically, the sensors work and deliver their values nicely but I have also created one in Homeassitant that tells the monitor the deep-sleep times.

The problem is that the text sensor “workmode” does not contain a value after the deepsleep or boot. But if I change this in Homeassitant while the monitor is online, the monitor gets the change and has the value until the next deepsleep or reboot.

Config from HA (configuration.yaml)

mqtt:
  select:
    command_topic: pool-monitor/workmood
    name: "workmode"
    options:
      - "ota"
      - "15 min"
      - "60 min"
      - "120 min"

This is the ESP-home configuration:

esphome:
  name: pool-monitor
  friendly_name: pool-monitor
  on_boot:
    - priority: 601
      then:
        - output.turn_on: gpio_temp #enable temp sensor
        - output.turn_off: gpio_ph
        - output.turn_off: gpio_tds
    - priority: -300
      then:
        - script.execute: check_stuff

  on_shutdown:
    priority: 710
    then:
      - output.turn_off: gpio_temp 

esp32:
  board: esp32dev
  framework:
    type: arduino
deep_sleep:
  id: deep_sleep_control
  sleep_duration: 15min

globals:
  - id: delay_int
    type: int
    restore_value: no
    initial_value: '40'

  - id: loopcount
    type: int
    restore_value: no
    initial_value: '0'

  - id: loops
    type: int
    restore_value: no
    initial_value: '0'

  - id: phflex_done
    type: int
    restore_value: no
    initial_value: '0'

  - id: tds_done
    type: int
    restore_value: no
    initial_value: '0'

logger:
#  baud_rate: 0
#  level: NONE

#api:
#  encryption:
#    key: "removed"

ota:  password: "removed"

mqtt:
  id: mqtt_cli
  broker: !secret mqtthost
  port: 1883
  username: !secret mqttuser
  password: !secret mqttpassword
  birth_message:
  will_message:

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  power_save_mode: LIGHT
  fast_connect: true
  manual_ip:
    static_ip: removed
    gateway: removed
    subnet: removed
    dns1: removed

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Pool-Monitor Fallback Hotspot"
    password: "nToC36JN3K8G"

output:
  - platform: gpio
    pin: GPIO17
    id: gpio_temp
  - platform: gpio
    pin: GPIO16
    id: gpio_ph
  - platform: gpio
    pin: GPIO18
    id: gpio_tds

dallas:
  - pin: GPIO5
    update_interval: never
    id: dallashub

text_sensor:
  - platform: mqtt_subscribe
    name: "current workmode"
    id: workmode
    topic: pool-monitor/workmood

  - platform: template
    name: "Last refresh"
    id: "refresher"
    lambda: |-
      char str[17];
      time_t currTime = id(homeassistant_time).now().timestamp;
      strftime(str, sizeof(str), "%Y-%m-%d %H:%M", localtime(&currTime));
      return  { str };
    update_interval: never

sensor:
  - platform: adc
    id: batt_voltage
    pin: GPIO34
    attenuation: auto
    name: Battery Voltage
    update_interval: 15s
    accuracy_decimals: 2
    filters:
      - median:
          window_size: 10
          send_every: 4
          send_first_at: 3
      - delta: 0.1

#Convert the Voltage to a battery  level (%)
  - platform: copy
    source_id: batt_voltage
    id: solar_plant_batt_level
    icon: "mdi:battery"
    name:  Battery Level
    unit_of_measurement: '%'
    accuracy_decimals: 2
    filters:
      - calibrate_linear:
          - 1.8 -> 0
          - 3.1 -> 100
      - lambda: |
          if (x < 0) return 0;
          else if (x > 100) return 100;
          else return (x);

  - platform: dallas
    address: 0x203c3ce3818e3628
    id: pool_temperature
    name: "Pool Temperature"
    device_class: temperature

# Raw TDS Reading
  - platform: adc
    pin: GPIO32
    name: "TDS 01 Raw"
    attenuation: 6db
    id: tds01_raw
    update_interval: never
    unit_of_measurement: "v"
    accuracy_decimals: 3
    internal: true

# Temperature Compensated Voltage
  - platform: template
    name: "TDS 01 TCV"
    id: temp01_comp_v
    unit_of_measurement: 'v'
    accuracy_decimals: 3
    lambda: 'return ((id(tds01_raw).state) / (1 + (0.02 * ((id(pool_temperature).state) - 25.0))));'
    update_interval: never
    internal: true

# Temperature Compensated TDS
  - platform: template
    name: "TDS-01"
    id: tds01_55g
    icon: "hass:water-opacity"
    unit_of_measurement: 'PPM'
    accuracy_decimals: 0
    update_interval: never  
    lambda: return (133.42*(id(temp01_comp_v).state)*(id(temp01_comp_v).state)*(id(temp01_comp_v).state) - 255.86*(id(temp01_comp_v).state)*(id(temp01_comp_v).state) + 857.39*(id(temp01_comp_v).state))*0.5;
    on_value:
      lambda: |-
        id(tds_done)++;

  - platform: copy
    source_id: phflex
    id: ph_sensor
    icon: "mdi:flask"
    name:  pH Sensor
    unit_of_measurement: 'pH'
    accuracy_decimals: 2
    filters:
      - calibrate_linear:
          - 2.595 -> 4
          - 2.358 -> 6.86
      - delta: 0.1      

  - platform: adc
    pin: GPIO35
    name: "Flex ph"
    id: phflex
    attenuation: auto
    update_interval: never
    accuracy_decimals: 3
    filters:
      - median:
          window_size: 10
          send_every: 4
          send_first_at: 3
    on_value:
      lambda: |-
        id(phflex_done)++;

time:
  - platform: sntp
    id: homeassistant_time
    timezone: Europe/Amsterdam

script:
  - id: check_stuff
    then:
      - lambda: |-
          id(loopcount) = 0;
          if(id(workmode).state.empty()) { #Workaround!!
              id(delay_int) = 900;  
          }

      - while:
          condition:
            lambda: return (strcmp("ota" , id(workmode).state.c_str() ) == 0) || (id(loops) >= id(loopcount));
          then:
          - logger.log:
              format: 'Workomde is set to: %s'
              args:
                - id(workmode).state.c_str()

          - logger.log: 'Updating Sensors'
          - lambda: |-          
              id(dallashub).update();
          - lambda: |-  #reseting counters
              id(phflex_done) = 0;
              id(tds_done) = 0;
          - output.turn_on: gpio_ph #enablining the ph sensor
          - delay: 70s #sensor needs time to stabilize
          - while:
              condition:
                lambda: return (id(phflex_done) <= 3);
              then:
              - delay: 1s
              - lambda: |-
                  id(phflex).update();
          - output.turn_off: gpio_ph #disabling the ph sensor

          - output.turn_on: gpio_tds #enablining the tds sensor
          - delay: 5s #sensor needs time to stabilize
          - while:
              condition:
                lambda: return (id(tds_done) <= 2);
              then:
              - delay: 1s
              - lambda: |-
                  id(tds01_raw).update();
                  id(temp01_comp_v).update();
                  id(tds01_55g).update();

          - output.turn_off: gpio_tds #disabling the tds sensor
          - logger.log: 'Updating Sensors done'
          - lambda: |-
              id(refresher).update();
          - lambda: |-          
              id(loopcount)++;
      - if:
          # 15 min
          condition:
            lambda: return  strcmp( id(workmode).state.c_str(), "15 min") == 0;
          then:
          - lambda: |
                                              id(delay_int) = 900;
          - logger.log: 'Delay set to 15 min'
      - if:
          # 60 min
          condition:
            lambda: return  strcmp( id(workmode).state.c_str(), "60 min") == 0;
          then:
          - lambda: |
                                              id(delay_int) = 3600;
          - logger.log: 'Delay set to 60 min'
      - if:
          # 120 min
          condition:
            lambda: return  strcmp( id(workmode).state.c_str(), "120 min") == 0;
          then:
          - lambda: |
                                              id(delay_int) = 7200;
          - logger.log: 'Delay set to 120 min'
      - lambda: |
          auto now = id(homeassistant_time).now().timestamp;
          auto timefuture = now + id(delay_int);
          timefuture -= (timefuture % 60);
          ESP_LOGI("UGH", "Next target runtime: %d", timefuture);
          id(delay_int) = timefuture - now ;                      
      - logger.log: 'Entering sleep'
      - deep_sleep.enter:
          id: deep_sleep_control
          sleep_duration: !lambda return id(delay_int) * 1000;   #lambdas should return value as ms
captive_portal:

Log-Output after boot / wakeup:

[12:21:05][D][main:250]: Workomde is set to: 
[12:21:05][D][main:253]: Updating Sensors

Logoutput when changeing the Workmode while the monitor is online:

[12:21:22][D][text_sensor:064]: 'current workmode': Sending state '15 min'
[12:21:24][D][text_sensor:064]: 'current workmode': Sending state 'ota'
[12:22:18][D][sensor:094]: 'Flex ph': Sending state 2.30028 V with 3 decimals of accuracy
[12:22:18][D][sensor:094]: 'pH Sensor': Sending state 7.55659 pH with 2 decimals of accuracy
[12:22:22][D][sensor:094]: 'Flex ph': Sending state 2.30827 V with 3 decimals of accuracy
[12:22:26][D][sensor:094]: 'Flex ph': Sending state 2.31077 V with 3 decimals of accuracy
[12:22:26][D][sensor:094]: 'pH Sensor': Sending state 7.43001 pH with 2 decimals of accuracy
[12:22:30][D][sensor:094]: 'Flex ph': Sending state 2.31876 V with 3 decimals of accuracy
[12:22:36][D][sensor:094]: 'TDS 01 Raw': Sending state 0.10700 v with 3 decimals of accuracy
[12:22:36][D][sensor:094]: 'TDS 01 TCV': Sending state 0.10581 v with 3 decimals of accuracy
[12:22:36][D][sensor:094]: 'TDS-01': Sending state 44.00683 PPM with 0 decimals of accuracy
[12:22:37][D][sensor:094]: 'TDS 01 Raw': Sending state 0.10700 v with 3 decimals of accuracy
[12:22:37][D][sensor:094]: 'TDS 01 TCV': Sending state 0.10581 v with 3 decimals of accuracy
[12:22:37][D][sensor:094]: 'TDS-01': Sending state 44.00683 PPM with 0 decimals of accuracy
[12:22:38][D][sensor:094]: 'TDS 01 Raw': Sending state 0.10700 v with 3 decimals of accuracy
[12:22:38][D][sensor:094]: 'TDS 01 TCV': Sending state 0.10581 v with 3 decimals of accuracy
[12:22:38][D][sensor:094]: 'TDS-01': Sending state 44.00683 PPM with 0 decimals of accuracy
[12:22:38][D][main:283]: Updating Sensors done
[12:22:38][D][text_sensor:064]: 'Last refresh': Sending state '2023-08-17 12:22'
[12:22:38][D][main:250]: Workomde is set to: ota
[12:22:38][D][main:253]: Updating Sensors
[12:22:39][D][dallas.sensor:143]: 'Pool Temperature': Got Temperature=25.6°C
[12:22:39][D][sensor:094]: 'Pool Temperature': Sending state 25.56250 °C with 1 decimals of accuracy
[12:23:52][D][sensor:094]: 'Flex ph': Sending state 2.33374 V with 3 decimals of accuracy
[12:23:53][D][sensor:094]: 'pH Sensor': Sending state 7.15272 pH with 2 decimals of accuracy
[12:23:56][D][sensor:094]: 'Flex ph': Sending state 2.33524 V with 3 decimals of accuracy
[12:24:01][D][sensor:094]: 'Flex ph': Sending state 2.34423 V with 3 decimals of accuracy
[12:24:01][D][sensor:094]: 'pH Sensor': Sending state 7.02614 pH with 2 decimals of accuracy
[12:24:05][D][sensor:094]: 'Flex ph': Sending state 2.34423 V with 3 decimals of accuracy
[12:24:11][D][sensor:094]: 'TDS 01 Raw': Sending state 0.10700 v with 3 decimals of accuracy
[12:24:11][D][sensor:094]: 'TDS 01 TCV': Sending state 0.10581 v with 3 decimals of accuracy
[12:24:11][D][sensor:094]: 'TDS-01': Sending state 44.00683 PPM with 0 decimals of accuracy
[12:24:12][D][sensor:094]: 'TDS 01 Raw': Sending state 0.10700 v with 3 decimals of accuracy
[12:24:12][D][sensor:094]: 'TDS 01 TCV': Sending state 0.10581 v with 3 decimals of accuracy
[12:24:12][D][sensor:094]: 'TDS-01': Sending state 44.00683 PPM with 0 decimals of accuracy
[12:24:13][D][sensor:094]: 'TDS 01 Raw': Sending state 0.10700 v with 3 decimals of accuracy
[12:24:13][D][sensor:094]: 'TDS 01 TCV': Sending state 0.10581 v with 3 decimals of accuracy
[12:24:13][D][sensor:094]: 'TDS-01': Sending state 44.00683 PPM with 0 decimals of accuracy

The projekt is still in development so some of the delay lines will be removed and also a clenup will be done.
But can anyone give me a tip on how to solve the workmode problem?
Thanks

Spelling perhaps?

Hi,
Thanks, but was not the problame as it si in both configs (copy/past).
But changed it.

i have solved it by using globals. details follow

As announced, here is the solution to my problem.
It still feels like a workaround but after checking all (found) sources, this seems to be the only solution.

First, there was the obvious mistake of not setting the workmode in HA to retain. By this change the monitor gets the setting even if it was offline at the point where it was changed.

mqtt:

  select:

    command_topic: pool-monitor/workmode

    name: "workmode"

    retain: true

    options:

      - "ota"

      - "15 min"

      - "60 min"

      - "120 min"

Now to the bigger customization, the ESP doesn’t store the MQTT values itself, so I had to make a global in which I store the respective setting, for optimization I set it to a representative int which is handled in the on_message of the mqtt client.

By setting the global to restore_value: yes, the problem was solved.
There are also other customizations in the version (batterlevel etc.)

esphome:

  name: pool-monitor

  friendly_name: pool-monitor

  on_boot:

    - priority: 601

      then:

        - output.turn_on: gpio_temp #enable temp sensor

        - output.turn_off: gpio_ph

        - output.turn_off: gpio_tds

    - priority: -300

      then:

        - script.execute: check_stuff

       

  on_shutdown:

    priority: 710

    then:

      - output.turn_off: gpio_temp

     

esp32:

  board: esp32dev

  framework:

    type: arduino

deep_sleep:

  id: deep_sleep_control

  sleep_duration: 15min #We override this on call

globals:

  - id: delay_int

    type: int

    restore_value: no

    initial_value: '40'

  - id: loopcount

    type: int

    restore_value: no

    initial_value: '0'

  - id: loops

    type: int

    restore_value: no

    initial_value: '0'

  - id: phflex_done

    type: int

    restore_value: no

    initial_value: '0'

  - id: tds_done

    type: int

    restore_value: no

    initial_value: '0'

  - id: temp_done

    type: int

    restore_value: no

    initial_value: '0'

  - id: workmode

    type: int

    restore_value: yes

    initial_value: '0'

    # 0 ==> ota

    # 1 ==> 15 min

    # 2 ==> 60 min

    # 3 ==> 120 min

  - id: tds_offset

    type: float

    restore_value: no

    initial_value: '0'

logger:

#  baud_rate: 0

#  level: NONE

 

# Enable Home Assistant API

#api:

#  encryption:

#    key: "removed"

ota:

  password: "removed"

mqtt:

  id: mqtt_cli

  broker: !secret mqtthost

  port: 1883

  username: !secret mqttuser

  password: !secret mqttpassword

  birth_message:

  will_message:

  on_message:

    - topic: pool-monitor/workmode

      payload: 'ota'

      then:

        - logger.log: 'setting ota Mode'

        - lambda: |-  

            id(workmode) = 0;

    - topic: pool-monitor/workmode

      payload: '15 min'

      then:

        - logger.log: 'setting 15 min Mode'

        - lambda: |-  

            id(workmode) = 1;

    - topic: pool-monitor/workmode

      payload: '60 min'

      then:

        - logger.log: 'setting 60 min Mode'

        - lambda: |-  

            id(workmode) = 2;

    - topic: pool-monitor/workmode

      payload: '120 min'

      then:

        - logger.log: 'setting 120 min Mode'

        - lambda: |-  

            id(workmode) = 3;

wifi:

  ssid: !secret wifi_ssid

  password: !secret wifi_password

  power_save_mode: LIGHT

  fast_connect: true

  manual_ip:

    static_ip: removed

    gateway: removed

    subnet: removed

    dns1: removed

  # Enable fallback hotspot (captive portal) in case wifi connection fails

  ap:

    ssid: "Pool-Monitor Fallback Hotspot"

    password: "removed"

output:

  - platform: gpio

    pin: GPIO18

    id: gpio_temp

  - platform: gpio

    pin: GPIO17

    id: gpio_ph

  - platform: gpio

    pin: GPIO19

    id: gpio_tds

dallas:

  - pin: GPIO5

    update_interval: never

    id: dallashub

text_sensor:

  - platform: mqtt_subscribe

    name: "current workmode"

    id: mqtt_workmode

    topic: pool-monitor/workmode

  - platform: template

    name: "Last refresh"

    id: "refresher"

    lambda: |-

      char str[17];

      time_t currTime = id(homeassistant_time).now().timestamp;

      strftime(str, sizeof(str), "%Y-%m-%d %H:%M", localtime(&currTime));

      return  { str };

    update_interval: never

sensor:

  - platform: adc

    id: batt_voltage

    pin: GPIO33

    attenuation: auto

    name: Battery Voltage

    update_interval: 3s

    accuracy_decimals: 2

    filters:

      - multiply: 2.03

      - median:

          window_size: 10

          send_every: 4

          send_first_at: 3

      - delta: 0.1 #Only send values to HA if they change

#Convert the Voltage to a battery  level (%)

  - platform: copy

    source_id: batt_voltage

    id: solar_plant_batt_level

    icon: "mdi:battery"

    name:  Battery Level

    unit_of_measurement: '%'

    accuracy_decimals: 2

    filters:

      lambda: |-

          float voltage = x;

          float  percentage = 2808.3808 * pow(voltage, 4) - 43560.9157 * pow(voltage, 3) + 252848.5888 * pow(voltage, 2) - 650767.4615 * voltage + 626532.5703;

          if (voltage > 4.19) {

                  percentage = 100;

                } else {

                  if (voltage <= 3.50) percentage = 0;

                }

          return percentage;

  - platform: dallas

    address: 0x203c3ce3818e3628

    id: pool_temperature

    name: "Pool Temperature"

    device_class: temperature

    filters:

      - offset: -1.4

    on_value:

      lambda: |-

        id(temp_done)++;

# Raw TDS Reading

  - platform: adc

    pin: GPIO32

    name: "TDS 01 Raw"

    id: tds01_raw

    #attenuation: 6db

    update_interval: never

    unit_of_measurement: "v"

    accuracy_decimals: 3

    internal: true

# Temperature Compensated Voltage

  - platform: template

    name: "TDS 01 TCV"

    id: temp01_comp_v

    unit_of_measurement: 'v'

    accuracy_decimals: 3

    lambda: |-

      return (((id(tds01_raw).state) - id(tds_offset)) / (1 + (0.02 * ((id(pool_temperature).state) - 25.0))));

    update_interval: never

# Temperature Compensated TDS

  - platform: template

    name: "TDS-01"

    id: tds01_55g

    icon: "hass:water-opacity"

    unit_of_measurement: 'PPM'

    accuracy_decimals: 0

    update_interval: never  

    lambda: |-

      float voltage = id(temp01_comp_v).state;

      return (133.42 / voltage * voltage * voltage - 255.86 * voltage * voltage + 857.39* voltage)*0.5;

    on_value:

      lambda: |-

        id(tds_done)++;

  - platform: copy

    source_id: phflex

    id: ph_sensor

    icon: "mdi:flask"

    name:  pH Sensor

    unit_of_measurement: 'pH'

    accuracy_decimals: 2

    filters:

      - calibrate_linear:

          - 2.37 -> 4

          - 2.15 -> 6.86

      - delta: 0.1      

  - platform: adc

    pin: GPIO35

    name: "Flex ph"

    id: phflex

    attenuation: auto

    update_interval: never

    accuracy_decimals: 3

    filters:

      - median:

          window_size: 10

          send_every: 4

          send_first_at: 3

    on_value:

      lambda: |-

        id(phflex_done)++;

time:

  - platform: sntp

    id: homeassistant_time

    timezone: Europe/Amsterdam

script:

  - id: check_stuff

    then:

      - while:

          condition:

            lambda: return (id(workmode) == 0) || (id(loops) >= id(loopcount));

          then:

          - logger.log: 'Updating Sensors'

          - lambda: |-  #reseting counters

              id(phflex_done) = 0;

              id(tds_done) = 0;

              id(temp_done) = 0;

          - while:

                condition:

                  lambda: return (id(temp_done) <= 1);

                then:

                - delay: 1s

                - lambda: |-

                    id(dallashub).update();

          - output.turn_on: gpio_ph #enablining the ph sensor

          - delay: 60s #sensor needs time to stabilize

          - while:

              condition:

                lambda: return (id(phflex_done) <= 3);

              then:

              - delay: 2s

              - lambda: |-

                  id(phflex).update();

          - output.turn_off: gpio_ph #disabling the ph sensor

          - output.turn_on: gpio_tds #enablining the tds sensor

          - delay: 3s #sensor needs time to stabilize

          - while:

              condition:

                lambda: return (id(tds_done) <= 2);

              then:

              - delay: 1s

              - lambda: |-

                  id(tds01_raw).update();

                  id(temp01_comp_v).update();

                  id(tds01_55g).update();

          - output.turn_off: gpio_tds #disabling the tds sensor

          - logger.log: 'Updating Sensors done'

          - lambda: |-

              id(refresher).update();

          - lambda: |-          

              id(loopcount)++;

          - delay: 1s #time to Update the Mqtt topics

      - if:

          # 15 min

          condition:

            lambda: return  id(workmode) == 1;

          then:

          - lambda: |

                                              id(delay_int) = 900;

          - logger.log: 'Delay set to 15 min'

      - if:

          # 60 min

          condition:

            lambda: return  id(workmode) == 2;

          then:

          - lambda: |

                                              id(delay_int) = 3600;

          - logger.log: 'Delay set to 60 min'

      - if:

          # 120 min

          condition:

            lambda: return  id(workmode) == 3;

          then:

          - lambda: |

                                              id(delay_int) = 7200;

          - logger.log: 'Delay set to 120 min'

      - lambda: |

          auto now = id(homeassistant_time).now().timestamp;

          auto timefuture = now + id(delay_int);

          timefuture -= (timefuture % 60);

          ESP_LOGI("UGH", "Next target runtime: %d", timefuture);

          id(delay_int) = timefuture - now ;                      

      - logger.log: 'Entering sleep'

      - deep_sleep.enter:

          id: deep_sleep_control

          sleep_duration: !lambda return id(delay_int) * 1000;   #lambdas should return value as ms

captive_portal: