Esphomelib - library to greatly simplify Home Assistant integration with ESP32

I managed to make it work, only trying to add more custom effects now.

I almost forgot, the names of the effects have changed since I flashed my ESP. Check the docs and rename the effects accordingly.

Hi All,

First of all, I’d like to thank Otto and people who work hard on this project, it’s really great.

I need an advice and hope here is the right place to ask.
I have a node (Wemos D1 mini) with a DHT22 sensor attached that configured to send readings every 30 seconds.
My goal is to detect when the humidity goes up quickly (in real life it means a shower is on).
Currently I store previous humidity value/temperature and when a new value arrives, calculate gradient.
It kind of works as by observation I decided on a gradient threshold that indicates a rise that usually happens when a shower is on.
When it’s above the threshold, I use the previous value as a target to decide if the humidity is back to normal.
I also have to ignore fast consecutive readings as it usually happens when the node reconnects to HA.

The thing that bugs me is a sudden significant jitter of humidity that occurs randomly. It’s looks like 75, 84, 74, 75 etc. Only one value, but the resulting gradient is enough to cause false positive.

There is another flaw in my approach - sometimes the rise in humidity is not very sharp but goes in 2 stages. Therefore in the first stage I get a little bit LOWER gradient than I expect to consider it as a shower on event, but the humidity value is pretty high already. And on the next reading I might get gradient above the threshold value, but the previous humidity value is too high already and that decision about normal humidity doesn’t work correctly as I actually need the one that was before that.

I’m thinking of adding two-step gradient, i.e saving 2 previous values/times and calculate gradient between current and -2 value and use -2 humidity as a target if the gradient is above a set threshold.

What do you guys think?
Or maybe there is a better approach to implement?

By the way, currently my esphome node has no fancy functionality defined and all the work is done on HA side. Is there anything that makes sense to implement on the node side in my case?

I believe sensor filters is what you’re looking for. You can then even create custom code that filters/processes the sensor values any way you want.

1 Like

Thanks Otto, I saw those filters in docs but didn’t dig too deep.
And my concern was whether they filter out only occasional spikes or smoother out the output, in which case I’ll lose a distinctive gradient increases.

Here is my esphome sensor description:

sensor:
- platform: dht
  pin: D1
  model: dht22
  update_interval: 10s
  temperature:
    name: "bathroom_shower_temperature"
  humidity:
    name: "bathroom_shower_humidity"
    filters:
      - filter_nan:
      - exponential_moving_average:
          alpha: 0.5
          send_every: 3

So far it works, will keep an eye on it to see if it resolves my problem with spikes.
I have some questions though:

  1. Initially I didn’t use send_every: and it sent humidity state once in 15 readings or something. Then I set it to 30 thinking it’s every 30 seconds, but apparently it means every 30th reading, quite a difference. If I set it to 1, it makes no sense as it just repeats incoming values. Is 3 the right value?
  2. I used to have update_interval: 30s, but because of that send_every: 3 changed it to 10s.
    Therefore currently HA receives humidity values every 30 seconds, but temperature values come every 10 seconds as it’s impossible to set different update_intervals for them. Any way to get both temperature and humidity values in HA every 30?

And still need to find a way to implement that 2-step gradient on esphome level (via lambdas? but I’m not quite sure it is possible as I need access to previous values and times to calculate gradients)

@OttoWinter, I just coded a custom component using your library to solve my fireplace problem, and while it works, the node becomes unavailable while the controller is adjusting the settings. I’m theorizing that it has to do with the use of the delay command in order to get the timing right for the controller. Is there a better way to handle this?

 #include "esphome.h"
using namespace esphome;
static const char *TAG = "debug";
const int BLUE_LED = 13;     // wifi signal led
const int MASTER_RELAY = 12; // relay 1, setup inline with the others as a master disconnect switch
const int CONTACT_1 = 5;     // relay 2
const int CONTACT_2 = 4;     // relay 3
const int CONTACT_3 = 15;    // relay 4
float fire_level = 0.0; 

// for automating a Flare fireplace with a Maxitrol controller
// documentation from the manufacturer can be viewed here: https://i.imgur.com/iPU1glv.png

class FireplaceOutput : public Component, public output::FloatOutput {
public:
void setup() override {
    // This will be called by App.setup()
    pinMode(BLUE_LED, OUTPUT);
    pinMode(MASTER_RELAY, OUTPUT);
    pinMode(CONTACT_1, OUTPUT);
    pinMode(CONTACT_2, OUTPUT);
    pinMode(CONTACT_3, OUTPUT);
    // blue led blink on boot just to let me know it has rebooted/started
    digitalWrite(BLUE_LED, LOW); 
    // make sure the fireplace is off on boot
    fireplace_off();
    digitalWrite(BLUE_LED, HIGH);
}

void connect(){
    digitalWrite(MASTER_RELAY, HIGH);
}

void disconnect() {
    digitalWrite(MASTER_RELAY, LOW);
}

void fireplace_off() {
    connect();
    digitalWrite(CONTACT_1, LOW);
    digitalWrite(CONTACT_2, LOW);
    digitalWrite(CONTACT_3, LOW);
    delay(2000);
    disconnect();
    delay(12000); // takes a while to turn off, and doesn't respond to any other signals during this period
    fire_level = 0.0;

}
void fireplace_on() {
    connect();
    digitalWrite(CONTACT_1, LOW);
    digitalWrite(CONTACT_2, HIGH);
    digitalWrite(CONTACT_3, LOW);
    delay(2000);
    disconnect();
    delay(30000);  // takes a while to turn on, and doesn't respond to any other signals during this period
    fire_level = 1.0;
}

void turn_up_fireplace() {
    digitalWrite(CONTACT_1, LOW);
    digitalWrite(CONTACT_2, HIGH);
    digitalWrite(CONTACT_3, HIGH);
}

void turn_down_fireplace() {
    digitalWrite(CONTACT_1, HIGH);
    digitalWrite(CONTACT_2, HIGH);
    digitalWrite(CONTACT_3, LOW);
}

void set_fireplace(float set_level){
    if (set_level > fire_level) {
        float difference = set_level - fire_level;
        float ramp_time = 12000 * difference;
        connect();
        turn_up_fireplace();
        delay(ramp_time);
        disconnect();
    }
    else if (set_level < fire_level) {
        float difference =  fire_level - set_level;
        float ramp_time = 12000 * difference;
        connect();
        turn_down_fireplace();
        delay(ramp_time);
        disconnect();
    }
    fire_level = set_level;
}

void write_state(float state) override {
    if (state == 0.0) {
        fireplace_off();
    }
    else {
        if (fire_level == 0.0) {
            fireplace_on();
        }
        set_fireplace(state);
    }
}
};

Yes you cannot use delays that last that long as it will suspend the entire esphome core loop.

For such delays please use set_timeout (look for usages in esphome-core code base for examples)

I will try that, thank you @OttoWinter !

How will the library handle concurrent requests? (i.e. another write_state command is called while the previous command is still being executed) Is there any way for me to queue requests if the previous command hasn’t finished executing? (I would rather HA not accidentally leave the fireplace running)

@OttoWinter is it possible to recreate effects for FastLed (ws2812b) using custom component? Something like @Tuckie did in the post above?

I just started tinkering with ESPHome - I have a device that doesn’t behave normally, and am hoping that I can use this to “convert” it to a local only device (currently Tuya based).

Anyway, I am using this in Docker - works great!
I just was wondering why it is so large (500MB+)?
Is the program behind it that large, or just because of the base image?
I’m just wondering - not trying to complain - just thought it would be smaller.

For those interested in “not normally” I have a door sensor that appears to have a MCU that connects to the reed switch. When the reed switch is triggered, the MCU then turns on the ESP8266 to connect to WIFI and send the data. I am hoping that I can put ESPHome on the ESP8266, use logging and find out how it is being fed data (I2c, SPI, or UART). Then setup ESPHome to take that data, and send it to MQTT broker. If anyone has done something similar I would be happy to have points ;-).

Cheers!
DeadEnd

You could probably use tuya-convert (GitHub) to convert the device to use ESPhome as long as you can work out the GPIO mapping

I actually have used Tuya-Convert already to pull the original firmware.
Once I compile the YAML file using ESP Home, where do I find the BIN to push using Tuya-Convert?

Also, the problem is I don’t know what protocol? or pins are being used. I have a TTL Serial Converter that should be delivered tomorrow. I am hoping that I can use the logging (Very-Verbose) to catch the reed switch single coming in. I am hoping through trial and error I will be able to figure it out, and then build the correct code (YAML) to capture the message, and then generate MQTT based on whatever it sends for “open” and “close”.

I hope that all makes sense :confused:

It works like the JS functions with equivalent names, you can specify optional names to the timeouts that will make them unique.

@INTEL Yes… as seen in the post above :wink:

@DeadEnd It is not that large, what I see on docker hub is 190mb. Most of that the is the ESP compiler toolchains which are bundled.

@DeadEnd See FAQ

Using ESPhome, you need to compile the yaml and download the .bin file. See the FAQ.

What are the details of the device? There might be GPIO data available online. Might be best to create a new thread for this discussion rather than filling up the blog thread

@OttoWinter, I was able to get set_timeout working, however, I’m not sure how to handle concurrent requests. (i.e. another write_state command is called while the previous command is still being executed) Is there any way for me to queue requests if the previous command hasn’t finished executing? (I would rather HA not accidentally leave the fireplace running.)

The line of interest is: if (this->currently_changing_level_ == true) at the bottom.

Thank you again!

#include "esphome.h"
using namespace esphome;
static const char *TAG = "debug";
const int BLUE_LED = 13;     // wifi signal led
const int MASTER_RELAY = 12; // relay 1, setup inline with the others as a master disconnect switch
const int CONTACT_1 = 5;     // relay 2
const int CONTACT_2 = 4;     // relay 3
const int CONTACT_3 = 15;    // relay 4


// for automating a Flare fireplace with a Maxitrol controller
// documentation from the manufacturer can be viewed here: https://i.imgur.com/iPU1glv.png

class FireplaceOutput : public Component, public output::FloatOutput {
protected:
  float fire_level_{0.0};
  bool currently_changing_level_{false};
public:
void setup() override {
    // This will be called by App.setup()
    pinMode(BLUE_LED, OUTPUT);
    pinMode(MASTER_RELAY, OUTPUT);
    pinMode(CONTACT_1, OUTPUT);
    pinMode(CONTACT_2, OUTPUT);
    pinMode(CONTACT_3, OUTPUT);
    // blue led blink on boot just to let me know it has rebooted/started
    digitalWrite(BLUE_LED, LOW); 
    // make sure the fireplace is off on boot
    fireplace_off();
    digitalWrite(BLUE_LED, HIGH);
}

void connect(){
    ESP_LOGD(TAG, "Starting Fireplace change...");
    this->currently_changing_level_ = true;
    digitalWrite(MASTER_RELAY, HIGH);
}

void disconnect() {
    ESP_LOGD(TAG, "... Ending Fireplace change");
    digitalWrite(MASTER_RELAY, LOW);
    this->currently_changing_level_ = false;
}

void contacts_off(){
    digitalWrite(CONTACT_1, HIGH);
    digitalWrite(CONTACT_2, HIGH);
    digitalWrite(CONTACT_3, HIGH);
}

void fireplace_off() {
    ESP_LOGD(TAG, "Fireplace off");
    connect();
    digitalWrite(CONTACT_1, LOW);
    digitalWrite(CONTACT_2, LOW);
    digitalWrite(CONTACT_3, LOW);
    this->set_timeout("turning-off", 2000, [this]() { this->contacts_off(); });
    // takes a while to turn off, and doesn't respond to any other signals during this period
    this->set_timeout("powering-down", 14000, [this]() { this->disconnect(); this->fire_level_ = 0.0;});
}

void fireplace_on() {
    ESP_LOGD(TAG, "Fireplace on");
    connect();
    digitalWrite(CONTACT_1, LOW);
    digitalWrite(CONTACT_2, HIGH);
    digitalWrite(CONTACT_3, LOW);
    this->set_timeout("turning-on", 2000, [this]() { this->contacts_off(); });
    // takes a while to turn on, and doesn't respond to any other signals during this period
    this->set_timeout("booting-up", 30000, [this]() { this->disconnect(); this->fire_level_ = 1.0;});
}


void turn_up_fireplace() {
    digitalWrite(CONTACT_1, LOW);
    digitalWrite(CONTACT_2, HIGH);
    digitalWrite(CONTACT_3, HIGH);
}

void turn_down_fireplace() {
    digitalWrite(CONTACT_1, HIGH);
    digitalWrite(CONTACT_2, HIGH);
    digitalWrite(CONTACT_3, LOW);
}

void set_fireplace(float set_level){
    ESP_LOGD(TAG, "Setting fireplace to: %.2f", set_level);
    float difference = 0.0;
    if (set_level == this->fire_level_) {
        return;
    }
    else if (set_level > this->fire_level_) {
        connect();
        difference = set_level - this->fire_level_;
        turn_up_fireplace();
    }
    else if (set_level < this->fire_level_) {
        connect();
        difference = this->fire_level_ - set_level;
        turn_down_fireplace();
    }
    float ramp_time = 12000 * difference;
    this->set_timeout("changing-level", ramp_time, [this, set_level]() { this->disconnect(); this->fire_level_ = set_level;});
}

void write_state(float state) override {
    if (this->currently_changing_level_ == true) {
        //what to do here? Is there any way to report back the previous setting? Queue the requested setting?
    }
    else {
        if (state == 0.0) {
            fireplace_off();
        }
        else {
            if (this->fire_level_ == 0.0) {
                fireplace_on();
                this->set_timeout("waiting-to-set-level", 30100, [this, state] () { this->set_fireplace(state); } );
            }
            else {
                set_fireplace(state);
            }
        }
    }
}
};

I’m having an issue in which one of my devices keeps popping up as a newly discovered device on the integrations page even though I already have it configured in the integration for ESPHome.

When I first flashed it and brought it online the initial “We have discovered new devices for your network” popped up and so I figured i’d just try to click on “submit” in the box that popped up and configure it that way. It failed to configure it successfully for some reason so I just went back to the usual way of clicking on the “ESPHome” under “Set up a new integration” which is the way I’ve normally done it every other time. And from there everything was successfully configured and it all worked.

However, now every few hours or after restarting HA I keep getting a notification that the same device is discovered and it asks me to configure it. Even though it’s already configured and listed in the active integrations. If I click dismiss the notification goes away but the “Discovered” box is still there asking me to configure it. If I click on “configure” and then “submit” then it now says “configuration successful” and the box goes away. Until a while later or I restart and it pops up again with another notification.

Is there anything I can check to see why the “discovery” keeps happening for a device (ext_cord_3) that’s already configured?

I think they fixed it in the latest HA (0.89)/esphome(1.12).
I had to remove and configure form scratch my node to stop it being rediscovered though.

I’m on v89 of HA but I only used v1.10.1 to flash with. However I’m not sure that should be an issue because I have 13 devices flashed with that version of ESPHOME and none of the others have this behavior.

And I just tried removing everything related to ext_cord_3 and re-configuring it manually and it still came back immediately after restart.

EDIT:

Actually I just tried to do it the opposite way in that I removed everything relating to that node and restarted. Then when it came back up I configured it automatically using the discovery notification box and so far everything is working correctly with no new notification. But it didn’t right away all the time so time will tell if it actually sticks this time.

I’d still like to know why it was/is acting differently than every other device that I’ve flashed before it tho using the same procedure.

I’m not sure we’ll ever find it out.
Congratulations, that notification bugged me all the way between 0.88 and 0.89 so I was really happy to upgrade :\