Lovelace Fan Control Entity Row

Okay, I’ve done as you’ve suggested.
I’ve changed up the wemos code to eliminate one of the fans to minimize confusion. I reformatted some of it as well to try and make it more legible (I’m still a novice).


/*   
 * RF_Transmitter
 * a Home-Automation-friendly ESP8266-based MQTT Hunter Ceiling Fan RF Controller
 * 2020 essaysee
*/

#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include "config.h"


boolean recordedSignal_fanlow2[] =     {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0};
boolean recordedSignal_fanmed2[] =     {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0};
boolean recordedSignal_fanhigh2[] =    {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0};
boolean recordedSignal_fanoff2[] =   {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0};
boolean recordedSignal_lightoffon2[] = {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0};

int a;

// Mapping NodeMCU Ports to Arduino GPIO Pins
// THIS IS ALL YOU NEED TO CHANGE IN THIS FILE
#define D0 16
#define D2 15
#define D3 0
#define D4 2
#define D5 14
#define D6 12 
#define D7 13
#define D8 15
#define WIFI_SSID "wifi name"
#define WIFI_PASSWORD "wifi password"
#define MQTT_BROKER "HA installation / MQTT"
#define MQTT_CLIENTID "RF_Transmitter"
#define MQTT_USERNAME "rf"
#define MQTT_PASSWORD "rf"
#define BEDROOM2_ALIAS "Guest Bedroom 2"
#define MQTT_ROOM2_FAN_ACTION_TOPIC "bedroom/fan/2/FanSpeed"
#define MQTT_ROOM2_FAN_STATUS_TOPIC "bedroom/fan/2/status"
#define MQTT_ROOM2_LIGHT_ACTION_TOPIC "bedroom/light/2/action"
#define MQTT_ROOM2_LIGHT_STATUS_TOPIC "bedroom/light/2/status"
#define TRANSMITTER_PIN D2
#define RECEIVER_PIN D8

//THAT WAS ALL YOU NEEDED TO CHANGE IN THE FILE



const char* ssid = WIFI_SSID;
const char* password = WIFI_PASSWORD;

//const boolean static_ip = STATIC_IP;
//IPAddress ip(IP);
//IPAddress gateway(GATEWAY);
//IPAddress subnet(SUBNET);

const char* mqtt_broker = MQTT_BROKER;
const char* mqtt_clientId = MQTT_CLIENTID;
const char* mqtt_username = MQTT_USERNAME;
const char* mqtt_password = MQTT_PASSWORD;



const char* bedroom2_alias = BEDROOM2_ALIAS;
const char* mqtt_room2_fan_action_topic = MQTT_ROOM2_FAN_ACTION_TOPIC;
const char* mqtt_room2_fan_status_topic = MQTT_ROOM2_FAN_STATUS_TOPIC;

const char* mqtt_room2_light_action_topic = MQTT_ROOM2_LIGHT_ACTION_TOPIC;
const char* mqtt_room2_light_status_topic = MQTT_ROOM2_LIGHT_STATUS_TOPIC;
const int transmitter_pin = TRANSMITTER_PIN;
const int receiver_pin = RECEIVER_PIN;

String availabilityBase = MQTT_CLIENTID;
String availabilitySuffix = "/availability";
String availabilityTopicStr = availabilityBase + availabilitySuffix;
const char* availabilityTopic = availabilityTopicStr.c_str();
const char* birthMessage = "online";
const char* lwtMessage = "offline";

WiFiClient espClient;
PubSubClient client(espClient);

// Wifi setup function

void setup_wifi() {

  delay(10);
  Serial.println();
  Serial.print("Connecting to ");
  Serial.print(ssid);

  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);

//  if (static_ip) {
//    WiFi.config(ip, gateway, subnet);
//  }

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.print(" WiFi connected - IP address: ");
  Serial.println(WiFi.localIP());
}

// Callback when MQTT message is received; calls triggerBedroomAction(), passing topic and payload as parameters

void callback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
  }
  
  Serial.println();

  String topicToProcess = topic;
  payload[length] = '\0';
  String payloadToProcess = (char*)payload;
  triggerBedroomAction(topicToProcess, payloadToProcess);
}



// Function that publishes birthMessage

void publish_birth_message() {
  // Publish the birthMessage
  Serial.print("Publishing birth message \"");
  Serial.print(birthMessage);
  Serial.print("\" to ");
  Serial.print(availabilityTopic);
  Serial.println("...");
  client.publish(availabilityTopic, birthMessage, true);
}





// Function called by callback() when a message is received 
// Passes the message topic as the "requestedRoom" parameter and the message payload as the "requestedAction" parameter
// THIS JUST PRINTS OUT THE ACTION AND MESSAGE RECEIVED VIA SERIAL CONNECTION
// KEEP AN EYE OUT FOR THE transmitRFsignal ACTION
void triggerBedroomAction(String requestedRoom, String requestedAction) {
   if (requestedRoom == mqtt_room2_fan_action_topic && requestedAction == "1") {
    Serial.print("Setting ");
    Serial.print(bedroom2_alias);
    Serial.println("'s fan speed to low!");
        {
        for (a = 0; a < 8; a++) {
        for (int i = 0; i < sizeof(recordedSignal_fanlow2) - 1; i++) {
        digitalWrite(transmitter_pin, recordedSignal_fanlow2[i]);
        delayMicroseconds(393.9);
        }
        delayMicroseconds(9000);  }
        delay(800);
        }
      client.publish(mqtt_room2_fan_status_topic, "1");
  }
  else if (requestedRoom == mqtt_room2_fan_action_topic && requestedAction == "2") {
    Serial.print("Setting ");
    Serial.print(bedroom2_alias);
    Serial.println("'s fan speed to medium!");
        {
        for (a = 0; a < 8; a++) {
        for (int i = 0; i < sizeof(recordedSignal_fanmed2) - 1; i++) {
        digitalWrite(transmitter_pin, recordedSignal_fanmed2[i]);
        delayMicroseconds(393.9);
        }
        delayMicroseconds(9000);
        }
        delay(800);
        }
      client.publish(mqtt_room2_fan_status_topic, "2");
  }
  else if (requestedRoom == mqtt_room2_fan_action_topic && requestedAction == "3") {
    Serial.print("Setting ");
    Serial.print(bedroom2_alias);
    Serial.println("'s fan speed to high!");
        {
        for (a = 0; a < 8; a++) {
        for (int i = 0; i < sizeof(recordedSignal_fanhigh2) - 1; i++) {
        digitalWrite(transmitter_pin, recordedSignal_fanhigh2[i]);
        delayMicroseconds(393.9);
        }
        delayMicroseconds(9000);
        }
        delay(800);
        }
      client.publish(mqtt_room2_fan_status_topic, "3");
  }
  else if (requestedRoom == mqtt_room2_fan_action_topic && requestedAction == "0") {
    Serial.print("Setting ");
    Serial.print(bedroom2_alias);
    Serial.println("'s fan off!");
        {
        for (a = 0; a < 8; a++) {
        for (int i = 0; i < sizeof(recordedSignal_fanoff2) - 1; i++) {
        digitalWrite(transmitter_pin, recordedSignal_fanoff2[i]);
        delayMicroseconds(393.9);
        }
        delayMicroseconds(9000);
        }
        delay(800);
        }
      client.publish(mqtt_room2_fan_status_topic, "0");
  }
  else if (requestedRoom == mqtt_room2_light_action_topic && requestedAction == "off") {
    Serial.print("Changing ");
    Serial.print(bedroom2_alias);
    Serial.println("'s light status!");
        {
        for (a = 0; a < 8; a++) {
        for (int i = 0; i < sizeof(recordedSignal_lightoffon2) - 1; i++) {
        digitalWrite(transmitter_pin, recordedSignal_lightoffon2[i]);
        delayMicroseconds(393.9);
        }
        delayMicroseconds(9000);
        }
        delay(600);
        }
      client.publish(mqtt_room2_light_status_topic, "off");
  }
  else if (requestedRoom == mqtt_room2_light_action_topic && requestedAction == "on") {
    Serial.print("Changing ");
    Serial.print(bedroom2_alias);
    Serial.println("'s light status!");
        {
        for (a = 0; a < 8; a++) {
        for (int i = 0; i < sizeof(recordedSignal_lightoffon2) - 1; i++) {
        digitalWrite(transmitter_pin, recordedSignal_lightoffon2[i]);
        delayMicroseconds(393.9);
        }
        delayMicroseconds(9000);
        }
        delay(600);
        }
    client.publish(mqtt_room2_light_status_topic, "on");  
  }  
  else { Serial.println("Unrecognized action payload... taking no action!");
 }
}
//ENDS THE ACTION OF WRITING IN SERIAL WHAT WAS RECEIVED AND WHAT IT WAS TOLD TO DO



// Function that runs in loop() to connect/reconnect to the MQTT broker, and publish the current door statuses on connect

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    if (client.connect(mqtt_clientId, mqtt_username, mqtt_password, availabilityTopic, 0, true, lwtMessage)) {
      Serial.println("Connected!");

      // Publish the birth message on connect/reconnect
      publish_birth_message();

      // Subscribe to the action topics to listen for action messages
      Serial.print("Subscribing to ");
      Serial.print(mqtt_room2_fan_action_topic);
      Serial.println("...");
      client.subscribe(mqtt_room2_fan_action_topic);
      Serial.print("Subscribing also to ");
      Serial.print(mqtt_room2_light_action_topic);
      Serial.println("...");
      client.subscribe(mqtt_room2_light_action_topic);    
      Serial.println("I am all subscribed.");


    } 
    else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}



void setup() {
  // Setup the output and input pins used in the sketch
  // Set the lastStatusValue variables to the state of the status pins at setup time

  // Setup Door 1 pins
  pinMode(transmitter_pin, OUTPUT);

  // Setup serial output, connect to wifi, connect to MQTT broker, set MQTT message callback

  Serial.begin(115200);
  delay(2);
  Serial.println("Starting RF transmitter for Hunter Fans...");
  setup_wifi();
  client.setServer(mqtt_broker, 1883);
  client.setCallback(callback);
}

void loop() {
  // Connect/reconnect to the MQTT broker and listen for messages
  if (!client.connected()) {
    reconnect();
  }
  client.loop();
}

fan.yaml :

  - platform: mqtt  
    name: "Guest Bedroom 2 Fan"  
    state_topic: "bedroom/fan/2/FanSpeed"
    speed_state_topic: "bedroom/fan/2/FanSpeed"
    state_value_template: >
      {% if value_json.FanSpeed is defined %}
        {% if value_json.FanSpeed == 0 -%}0{%- elif value_json.FanSpeed > 0 -%}4{%- endif %}
      {% else %}
        {% if states.fan.guest_bedroom_2_fan.state == 'off' -%}0{%- elif states.fan.guest_bedroom_2_fan.state == 'on' -%}4{%- endif %}
      {% endif %}
    speed_value_template: "{{ value_json.FanSpeed }}"
    availability:
      - topic: "RF_Transmitter/availability"
    payload_available: Online
    payload_not_available: Offline
    speed_command_topic: "bedroom/fan/2/FanSpeed"
    payload_low_speed: "1"
    payload_medium_speed: "2"
    payload_high_speed: "3"
    command_topic: "bedroom/fan/2/FanSpeed"
    payload_off: "0"
    payload_on: "4"
    qos: 0
    retain: false
    speeds:
      - off
      - low
      - medium
      - high

I know you’re going to point out that my status topci doesn’t quite align with conventional coding too, I changed this to “…/RESULT” as well in my testing. I’m open to any suggestions, I just wanted to point out I am aware the status topic matches the action topic.

The ‘off’ button does not seem to send any command to the wemos; no payload is sent over MQTT.
Also the buttons still don’t seem to change upon changing fan speed.
Seen here in this image, I’ve selected “M” or payload 2:

And the serial output from hitting button “M” (payload 2):
serialoutput

Payloads are all being received, and I can confirm that the publish is working, as you can see that the light status changes.
In the following image, I have clicked on all buttons, changing the status (including the off button).

I guess one of my questions also is "what is the necessity for payload 4?

EDIT: I found your thread trying to work out your jinja:

How does this implement into the TASMOTA fans? My fan’s remote only has 5 buttons:
Low
Medium
High
Off
Light (off/on)

I changed the speed state topic to match the javascript “FanSpeed” also to try to narrow down the issue/solution.
I’m not real strong in javascript but I imagine the js in the fan.yaml, says that any FanSpeed payload higher than 0 should change the icon color status.
I imagine in your fan-control-entity-row.js it says that if the button is pressed, the color of the button changes too, but that doesn’t seem to work for me.

Should I be publishing payload 4 in addition to any of my fan speeds 1,2,3?
Would that tell the card that the fan is on?

EDIT: I tried publishing payload 4 as well at the same time, this was a novice experimet, but I now believe I understand what you were achieving in your jinja code (I found an old link you were working on in 2018)

oops, I forgot to mention that I do have an ‘else’ statement that should serial print if it receives a payload it is not familiar with.

Any help or instruction greatly appreciated! I’m just trying to get this to work like you have and I think there may be some things still I don’t quite fully understand that are influencing my unexpected results.