Setting Up Hijacked RF Remote as Template Fan -- So close, and yet...!

Hi all. I couldn’t get any version of any number of RF sniffers to read the RF remote that came with my ceiling fan (it’s 315 mHz and I have the proper antennas, but alas.), so I went with the nuclear option and wired up a bunch of transistors and a nodeMCU to the buttons on a spare remote, which is set up with MQTT via custom Espurna firmware. So far, so good. The light is set up as a latching relay and works perfectly, I’m super happy with it. The fan control, however, is almost there except for the HA fan control.

I have the raw buttons as hidden mqtt switches (low, medium, high, and off are my options), and then to track the observed state of the fan, I have an input_boolean for binary fan state, and an input_select for storing the fan speed. My ultimate goal is just to have these hidden, since they simply act as a crude HA variable for persistent state storage.

In order to tie these all together, I have scripts for each action that I want – each one “pushes” the corresponding “button” and updates the state variables defined above. script.fan_off pushes the off button and changes the state boolean to false, script.fan_low pushes the low and updates the input_select, etc. This way I can store the binary state, as well as the user-defined speed. With this in place, I also have script.fan_on_combined which uses those two variables to turn the fan back on to whatever it was before. These all work great!!!

Unfortunately, it doesn’t quite all come together properly as a template_fan and I know I’m close which is insanely frustrating! Setting script.fan_off and script.fan_on_combined for the boolean fan on/off parts works perfectly. The speeds are all set as the corresponding scripts, and unfortunately it’s not simply triggering that script when I select a speed? I’m not entirely sure what’s it’s doing actually.

It works as intended if I turn the fan off, manually set the input_select, then turn it back on. BUT, if it’s already on, or I try to use the dropdown speed select from the template fan, it pushes two buttons at once: the desired selection, and whatever it was previously. This results in… nothing happening. I’m so confused as to what the template_fan is doing. How do I make it just… fire the script attached to the speed selector?? I don’t get it, and it’s a major usability issue for anyone else that lives with me. Here’s the code as I have configured:

Raw MQTT input from remote:

  - name: fan_low_button
    platform: mqtt
    state_topic: ceilingfan/relay/0
    command_topic: ceilingfan/relay/0/set
    payload_on: 1
    payload_off: 0
    availability_topic: ceilingfan/status
    payload_available: 1
    payload_not_available: 0

  - name: fan_medium_button
    platform: mqtt
    state_topic: ceilingfan/relay/1
    command_topic: ceilingfan/relay/1/set
    payload_on: 1
    payload_off: 0
    availability_topic: ceilingfan/status
    payload_available: 1
    payload_not_available: 0

  - name: fan_high_button
    platform: mqtt
    state_topic: ceilingfan/relay/2
    command_topic: ceilingfan/relay/2/set
    payload_on: 1
    payload_off: 0
    availability_topic: ceilingfan/status
    payload_available: 1
    payload_not_available: 0

  - name: fan_off_button
    platform: mqtt
    state_topic: ceilingfan/relay/3
    command_topic: ceilingfan/relay/3/set
    payload_on: 1
    payload_off: 0
    availability_topic: ceilingfan/status
    payload_available: 1
    payload_not_available: 0

The input_select and input_boolean acting as state storage (hopefully soon-to-be-hidden):

input_select:
  fan_speed:
    name: Bedroom Fan Speed
    options:
      - "Low"
      - "Medium"
      - "High"
    icon: mdi:fan

input_boolean:
  fan_state:
    initial: 'on'

The scripts for changing state and updating state “variables”, and restoring state based on the same:

fan_low:
  sequence:
    - alias: Update Status
      service: input_select.select_option
      data:
        entity_id: input_select.fan_speed
        option: Low
    - alias: Update State
      service: input_boolean.turn_on
      entity_id: input_boolean.fan_state
    - alias: Trigger Fan Control L
      service: switch.turn_on
      entity_id: switch.fan_low_button

fan_medium:
  sequence:
    - alias: Update Status
      service: input_select.select_option
      data:
        entity_id: input_select.fan_speed
        option: Medium
    - alias: Update State
      service: input_boolean.turn_on
      entity_id: input_boolean.fan_state
    - alias: Trigger Fan Control M
      service: switch.turn_on
      entity_id: switch.fan_medium_button

fan_high:
  sequence:
    - alias: Update Speed
      service: input_select.select_option
      data:
        entity_id: input_select.fan_speed
        option: High
    - alias: Update State
      service: input_boolean.turn_on
      entity_id: input_boolean.fan_state
    - alias: Trigger Fan Control H
      service: switch.turn_on
      entity_id: switch.fan_high_button

fan_off:
  sequence:
    - alias: Update State
      service: input_boolean.turn_off
      entity_id: input_boolean.fan_state
    - alias: Trigger Fan Control Off
      service: switch.turn_on
      entity_id: switch.fan_off_button

fan_on_combined:
  sequence:
    - alias: Fan On
      service: script.turn_on
      data_template:
        entity_id: >
          {% if is_state("input_select.fan_speed", "Low") %}
            script.fan_low
          {% elif is_state("input_select.fan_speed", "Medium") %}
            script.fan_medium
          {% elif is_state("input_select.fan_speed", "High") %}
            script.fan_high
          {% endif %}

And finally, all together in the template_fan:

fan:
  - platform: template
    fans:
      bedroom_fan:
        friendly_name: "Bedroom Ceiling Fan"
        value_template: "{{ states('input_boolean.fan_state') }}"
        speed_template: "{{ states('input_select.fan_speed') }}"
        turn_on:
          service: script.fan_on_combined
        turn_off:
          service: script.fan_off
        set_speed:
          service: script.turn_on
          data_template:
            entity_id: >
              {% if speed == 'High' %}
                script.fan_high
              {% elif speed == 'Medium' %}
                script.fan_medium
              {% elif speed == 'Low' %}
                script.fan_low
              {% else %}
                switch.dummy
              {% endif %}
        speeds:
          - 'High'
          - 'Medium'
          - 'Low'

Any help whatsoever would be highly appreciated, I really thought I had this one down, guys.

Thanks so much.

Don’t know if you got there in the end but i’ve just tripped over your post because i was trying do do something similar

You could change your scripts / add automations that trigger when the input select is changed then make the set_speed on the fan just change the input_select option