Hi there.
This entire thing is someone else’s brain child, although I set this up so long ago, I can’t recall who I need to give credit to
The really relevant parts are as follows:
esp8266:
board: d1_mini
esphome:
name: "mains-voltage"
includes:
- custom_sensor.h
external_components:
- source:
type: git
url: https://github.com/robertklep/esphome-custom-component
components: [ custom, custom_component ]
logger:
level: INFO
# The rest of your code for the node
sensor:
# Mains Voltage Sensor
- platform: custom
lambda: |-
auto my_sensor = new ZMPT101BSensor();
App.register_component(my_sensor);
return {my_sensor};
sensors:
id: ${sen_id}
name: "${sen_name}"
unit_of_measurement: V
state_class: "measurement"
accuracy_decimals: 2
icon: "mdi:current-ac"
filters:
- lambda: |
if (x < 120) return 0;
else return (x);
- sliding_window_moving_average:
window_size: 5
send_every: 5
on_value:
then:
- if:
condition:
- lambda: 'return id(${sen_id}).state > 240;'
then:
- switch.turn_on: ${sen_id}_high
else:
- switch.turn_off: ${sen_id}_high
- if:
condition:
and:
- lambda: 'return id(${sen_id}).state <= 240;'
- lambda: 'return id(${sen_id}).state > 210;'
then:
- switch.turn_on: ${sen_id}_ok
else:
- switch.turn_off: ${sen_id}_ok
- if:
condition:
and:
- lambda: 'return id(${sen_id}).state <= 210;'
- lambda: 'return id(${sen_id}).state > 180;'
then:
- switch.turn_on: ${sen_id}_low
else:
- switch.turn_off: ${sen_id}_low
- if:
condition:
- lambda: 'return id(${sen_id}).state <= 180;'
then:
- switch.turn_on: ${sen_id}_no_power
- light.turn_on:
id: led_${sen_id}_status
brightness: 50%
effect: pulse
else:
- switch.turn_off: ${sen_id}_no_power
- light.turn_off: led_${sen_id}_status
binary_sensor:
- platform: template
id: mains_status
name: "Mains Status"
lambda: |-
return id(${sen_id}).state > 140.0;
I didn’t know if you’d want to use the “High”, “Ok”, “Low” etc, so I just left that in, and I also added the binary sensor for the sake of having a definitive On/Off state.
And a manually created file named custom_sensor.h, which needs to go in the root of your ESPHome directory with the contents being:
#include "esphome.h"
#include <Arduino.h>
#define VMAX 250
#define FREQUENCY 50
#define CALIBRATE_READ 130
#define CALIBRATE_ACTUAL 225.25
#define ZERO_VAC 796
class ZMPT101BSensor : public PollingComponent, public Sensor {
public:
// constructor
ZMPT101BSensor() : PollingComponent(1000) {}
float get_setup_priority() const override { return esphome::setup_priority::HARDWARE; }
void setup() override {
// This will be called by App.setup()
}
void update() override {
// This will be called every "update_interval" milliseconds.
uint32_t period = 1000000 / FREQUENCY;
uint32_t t_start = micros();
uint32_t Vsum = 0, measurements_count = 0;
int32_t Vnow;
while (micros() - t_start < period) {
Vnow = analogRead(A0) - ZERO_VAC;
Vsum += Vnow*Vnow;
measurements_count++;
}
float Vrms = sqrt(Vsum / measurements_count) / CALIBRATE_READ * CALIBRATE_ACTUAL;
publish_state(int(Vrms));
}
};
For setting up the ZMPT101B you can just watch these 3 videos:
to get a better understanding of the calibration, otherwise, if you do a search for ZMPT101B Calibration, there are a couple of YouTube videos that show you how to calibrate this sensor with Arduino code, which is the way I did it, and then just re-flashed the node in ESPHome afterwards.
As all the videos recommend, Be Very mindful of the fact that you are messing with Mains Voltage! (Thought I’d better include that )
There will be some final adjustments you would need to make to the custom_sensor.h in the end, as the measurements at your DB and mine would be considerably different depending if you are being supplied 110v, 220v, 250v etc in this section:
#define VMAX 250
#define FREQUENCY 50
#define CALIBRATE_READ 130
#define CALIBRATE_ACTUAL 225.25
#define ZERO_VAC 796
This article also gives a fair amount of setup and advice:
Hope this helps!