Binary template sensor "Unavailable", but Text Sensor equivalent works

My project is hooked to my furnace to keep track of what is running. My furnace fan, stages, reversing valve, and aux heat are all fed into a Wemos D1 through an optocoupler on each pin. I know the GPIO’s work.

My GPIO binary sensors read On and Off in HA, so far so good. However my template binary sensors that use a simple if statement always show as “Unavailable” in my dashboard. If I convert them into Text sensors they work just fine so I’m not sure what gives. There has to be a typo or something in them but for the life of me I can’t figure out where. Thoughts?

binary_sensor:
  - platform: gpio
    name: "Furnace Fan"
    id: furnace_fan 
    pin:
      number: GPIO1
      inverted: true
      mode:
        input: true

  - platform: gpio
    name: "Thermostat Stage 1"
    id: furnace_stage1
    internal: true     
    pin:
      number: GPIO3
      inverted: true
      mode:
        input: true

  - platform: gpio
    name: "Thermostat Stage 2"
    id: furnace_stage2
    internal: true
    pin:
      number: GPIO12
      inverted: true
      mode:
        input: true
        
  - platform: gpio
    name: "Thermostat Reversing Valve"
    id: furnace_reversing_valve
    internal: true
    pin:
      number: GPIO13
      inverted: true
      mode:
        input: true


  - platform: gpio
    name: "Thermostat Aux Heat"
    id: furnace_gas_heat
    #internal: true
    pin:
      number: GPIO14
      inverted: true
      mode:
        input: true

  - platform: template
    name: "Cool Stage 1"
    id: cool_stage_1
    publish_initial_state: true
    lambda: |-
      if ((id(furnace_reversing_valve).state == false) && (id(furnace_stage1).state == true)) {
        return true;
        } else {
        return false;
        }

  - platform: template
    name: "Cool Stage 2"
    id: cool_stage_2
    publish_initial_state: true
    lambda: |-
      if ((id(furnace_reversing_valve).state == false) && (id(furnace_stage2).state == true)) {
        return true;
        }  else {
        return false;
        }

  - platform: template
    name: "Heat Stage 1"
    id: heat_stage_1
    publish_initial_state: true
    lambda: |-
      if ((id(furnace_reversing_valve).state == true) && (id(furnace_stage1).state == true)) {
        return true;
        } else {
        return false;
        }

  - platform: template
    name: "Heat Stage 2"
    id: heat_stage_2
    publish_initial_state: true
    lambda: |-
      if ((id(furnace_reversing_valve).state == true) && (id(furnace_stage2).state == true)) {
        return true;
        }  else {
        return false;
        }

Try this:

binary_sensor:
  - platform: gpio
    name: "Furnace Fan"
    id: furnace_fan 
    pin:
      number: GPIO1
      inverted: true
      mode:
        input: true

  - platform: gpio
    name: "Thermostat Stage 1"
    id: furnace_stage1
    internal: true     
    pin:
      number: GPIO3
      inverted: true
      mode:
        input: true
    on_state:
      - then:
          - component.update:
              - cool_stage_1
              - cool_stage_2
              - heat_stage_1
              - heat_stage_2

  - platform: gpio
    name: "Thermostat Stage 2"
    id: furnace_stage2
    internal: true
    pin:
      number: GPIO12
      inverted: true
      mode:
        input: true
    on_state:
      - then:
          - component.update:
              - cool_stage_1
              - cool_stage_2
              - heat_stage_1
              - heat_stage_2

  - platform: gpio
    name: "Thermostat Reversing Valve"
    id: furnace_reversing_valve
    internal: true
    pin:
      number: GPIO13
      inverted: true
      mode:
        input: true
    on_state:
      - then:
          - component.update:
              - cool_stage_1
              - cool_stage_2
              - heat_stage_1
              - heat_stage_2

  - platform: gpio
    name: "Thermostat Aux Heat"
    id: furnace_gas_heat
    #internal: true
    pin:
      number: GPIO14
      inverted: true
      mode:
        input: true

  - platform: template
    name: "Cool Stage 1"
    id: cool_stage_1
    publish_initial_state: true
    lambda: |-
      return !id(furnace_reversing_valve).state && id(furnace_stage1).state;

  - platform: template
    name: "Cool Stage 2"
    id: cool_stage_2
    publish_initial_state: true
    lambda: |-
      return !id(furnace_reversing_valve).state && id(furnace_stage2).state;

  - platform: template
    name: "Heat Stage 1"
    id: heat_stage_1
    publish_initial_state: true
    lambda: |-
      return id(furnace_reversing_valve).state && id(furnace_stage1).state;

  - platform: template
    name: "Heat Stage 2"
    id: heat_stage_2
    publish_initial_state: true
    lambda: |-
      return furnace_reversing_valve).state && id(furnace_stage2).state;

Thank you for the help, the lambda logic is a lot cleaner!

Unfortunately, the component.update does not seem to be able to take a list. The error thrown on the on_state component.update of multiple id’s is “string value cannot be dictionary or list.”

Ugh. Really?

You’ll have to do this then:

      - then:
          - component.update: cool_stage_1
          - component.update: cool_stage_2
          - component.update: heat_stage_1
          - component.update: heat_stage_2

Well… Now I get this error on those lines:

ID ‘cool_stage_1’ of type template_::TemplateBinarySensor doesn’t inherit from PollingComponent. Please double check your ID is pointing to the correct value.

The last template is malformed:

Should be:

 - platform: template
    name: "Heat Stage 2"
    id: heat_stage_2
    publish_initial_state: true
    lambda: |-
      return id(furnace_reversing_valve).state && id(furnace_stage2).state;

Gotcha. I caught that one when trying to compile one of my other attempts.

A little progress was made. When I make the template sensors just plain sensors, the code compiles and the values received are 0 and 1. Aside from not being binary, things work.

However, when I make them binary sensors, the component.update action of on_state throws this error:

Ok apparently that is not needed:

https://esphome.io/components/binary_sensor/template
Screenshot 2024-07-08 at 13-52-51 Template Binary Sensor — ESPHome

Delete all the

    on_state:
      - then:
          - component.update: ...

OK, so after copying the ESPhome YAML to a text file, deleting it from HA, removing all entities, rebooting HA, and pasting the ESPhome YAML back in, everything works. Thanks Tom!

Here’s the working code for future reference if anyone runs into this issue:

esphome:
  name: furnace-monitor
  friendly_name: Furnace Monitor

esp8266:
  board: d1_mini

# Enable logging
logger:

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

ota:
  - platform: esphome
    password: ""

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

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

captive_portal:

binary_sensor:
  - platform: gpio #Fan GPIO
    name: "Furnace Fan"
    id: furnace_fan 
    pin:
      number: GPIO3
      inverted: true
      mode:
        input: true

  - platform: gpio #Stage 1 GPIO
    name: "Thermostat Stage 1"
    id: furnace_stage1
    #internal: true
    publish_initial_state: true     
    pin:
      number: GPIO1
      inverted: true
      mode:
        input: true

  - platform: gpio #Stage 2 GPIO
    name: "Thermostat Stage 2"
    id: furnace_stage2
    #internal: true
    publish_initial_state: true
    pin:
      number: GPIO12
      inverted: true
      mode:
        input: true
        
  - platform: gpio #Reversing Valve GPIO
    name: "Thermostat Reversing Valve"
    id: furnace_reversing_valve
    #internal: true
    pin:
      number: GPIO13
      inverted: true
      mode:
        input: true

  - platform: gpio #Aux Heat GPIO
    name: "Thermostat Aux Heat"
    id: furnace_gas_heat
    #internal: true
    pin:
      number: GPIO14
      inverted: true
      mode:
        input: true

  - platform: template #Cooling Stage 1 Template Sensor
    name: "Cool Stage 1"
    id: cool_stage_1
    lambda: |-
      return !id(furnace_reversing_valve).state && id(furnace_stage1).state;

  - platform: template #Cooling Stage 2 Template Sensor
    name: "Cool Stage 2"
    id: cool_stage_2
    lambda: |-
      return !id(furnace_reversing_valve).state && id(furnace_stage2).state;

  - platform: template #Heating Stage 1 Template Sensor
    name: "Heat Stage 1"
    id: heat_stage_1
    lambda: |-
      return id(furnace_reversing_valve).state && id(furnace_stage1).state;

  - platform: template #Heating Stage 2 Template Sensor
    name: "Heat Stage 2"
    id: heat_stage_2
    lambda: |-
      return id(furnace_reversing_valve).state && id(furnace_stage2).state;