Add ADE7953 Support (#593)

* Add ADE795 support

* Lint

* Fix

* Fix, add test
This commit is contained in:
Otto Winter 2019-10-16 13:19:41 +02:00 committed by GitHub
parent 9c30f4cc68
commit cdb9c59662
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 206 additions and 14 deletions

View file

View file

@ -0,0 +1,51 @@
#include "ade7953.h"
#include "esphome/core/log.h"
namespace esphome {
namespace ade7953 {
static const char *TAG = "ade7953";
void ADE7953::dump_config() {
ESP_LOGCONFIG(TAG, "ADE7953:");
LOG_I2C_DEVICE(this);
LOG_UPDATE_INTERVAL(this);
LOG_SENSOR(" ", "Voltage Sensor", this->voltage_sensor_);
LOG_SENSOR(" ", "Current A Sensor", this->current_a_sensor_);
LOG_SENSOR(" ", "Current B Sensor", this->current_b_sensor_);
LOG_SENSOR(" ", "Active Power A Sensor", this->active_power_a_sensor_);
LOG_SENSOR(" ", "Active Power B Sensor", this->active_power_b_sensor_);
}
#define ADE_PUBLISH_(name, factor) \
if (name) { \
float value = *name / factor; \
this->name##_sensor_->publish_state(value); \
}
#define ADE_PUBLISH(name, factor) ADE_PUBLISH_(name, factor)
void ADE7953::update() {
if (!this->is_setup_)
return;
auto active_power_a = this->ade_read_<int32_t>(0x0312);
ADE_PUBLISH(active_power_a, 154.0f);
auto active_power_b = this->ade_read_<int32_t>(0x0313);
ADE_PUBLISH(active_power_b, 154.0f);
auto current_a = this->ade_read_<uint32_t>(0x031A);
ADE_PUBLISH(current_a, 100000.0f);
auto current_b = this->ade_read_<uint32_t>(0x031B);
ADE_PUBLISH(current_b, 100000.0f);
auto voltage = this->ade_read_<uint32_t>(0x031C);
ADE_PUBLISH(voltage, 26000.0f);
// auto apparent_power_a = this->ade_read_<int32_t>(0x0310);
// auto apparent_power_b = this->ade_read_<int32_t>(0x0311);
// auto reactive_power_a = this->ade_read_<int32_t>(0x0314);
// auto reactive_power_b = this->ade_read_<int32_t>(0x0315);
// auto power_factor_a = this->ade_read_<int16_t>(0x010A);
// auto power_factor_b = this->ade_read_<int16_t>(0x010B);
}
} // namespace ade7953
} // namespace esphome

View file

@ -0,0 +1,67 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/i2c/i2c.h"
#include "esphome/components/sensor/sensor.h"
namespace esphome {
namespace ade7953 {
class ADE7953 : public i2c::I2CDevice, public PollingComponent {
public:
void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; }
void set_current_a_sensor(sensor::Sensor *current_a_sensor) { current_a_sensor_ = current_a_sensor; }
void set_current_b_sensor(sensor::Sensor *current_b_sensor) { current_b_sensor_ = current_b_sensor; }
void set_active_power_a_sensor(sensor::Sensor *active_power_a_sensor) {
active_power_a_sensor_ = active_power_a_sensor;
}
void set_active_power_b_sensor(sensor::Sensor *active_power_b_sensor) {
active_power_b_sensor_ = active_power_b_sensor;
}
void setup() override {
this->set_timeout(100, [this]() {
this->ade_write_<uint8_t>(0x0010, 0x04);
this->ade_write_<uint8_t>(0x00FE, 0xAD);
this->ade_write_<uint16_t>(0x0120, 0x0030);
this->is_setup_ = true;
});
}
void dump_config() override;
void update() override;
protected:
template<typename T> bool ade_write_(uint16_t reg, T value) {
std::vector<uint8_t> data;
data.push_back(reg >> 8);
data.push_back(reg >> 0);
for (int i = sizeof(T) - 1; i >= 0; i--)
data.push_back(value >> (i * 8));
return this->write_bytes_raw(data);
}
template<typename T> optional<T> ade_read_(uint16_t reg) {
uint8_t hi = reg >> 8;
uint8_t lo = reg >> 0;
if (!this->write_bytes_raw({hi, lo}))
return {};
auto ret = this->read_bytes_raw<sizeof(T)>();
if (!ret.has_value())
return {};
T result = 0;
for (int i = 0, j = sizeof(T) - 1; i < sizeof(T); i++, j--)
result |= T((*ret)[i]) << (j * 8);
return result;
}
bool is_setup_{false};
sensor::Sensor *voltage_sensor_{nullptr};
sensor::Sensor *current_a_sensor_{nullptr};
sensor::Sensor *current_b_sensor_{nullptr};
sensor::Sensor *active_power_a_sensor_{nullptr};
sensor::Sensor *active_power_b_sensor_{nullptr};
};
} // namespace ade7953
} // namespace esphome

View file

@ -0,0 +1,39 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor, i2c
from esphome.const import CONF_ID, CONF_VOLTAGE, \
UNIT_VOLT, ICON_FLASH, UNIT_AMPERE, UNIT_WATT
DEPENDENCIES = ['i2c']
ace7953_ns = cg.esphome_ns.namespace('ade7953')
ADE7953 = ace7953_ns.class_('ADE7953', cg.PollingComponent, i2c.I2CDevice)
CONF_CURRENT_A = 'current_a'
CONF_CURRENT_B = 'current_b'
CONF_ACTIVE_POWER_A = 'active_power_a'
CONF_ACTIVE_POWER_B = 'active_power_b'
CONFIG_SCHEMA = cv.Schema({
cv.GenerateID(): cv.declare_id(ADE7953),
cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 1),
cv.Optional(CONF_CURRENT_A): sensor.sensor_schema(UNIT_AMPERE, ICON_FLASH, 2),
cv.Optional(CONF_CURRENT_B): sensor.sensor_schema(UNIT_AMPERE, ICON_FLASH, 2),
cv.Optional(CONF_ACTIVE_POWER_A): sensor.sensor_schema(UNIT_WATT, ICON_FLASH, 1),
cv.Optional(CONF_ACTIVE_POWER_B): sensor.sensor_schema(UNIT_WATT, ICON_FLASH, 1),
}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x38))
def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
yield cg.register_component(var, config)
yield i2c.register_i2c_device(var, config)
for key in [CONF_VOLTAGE, CONF_CURRENT_A, CONF_CURRENT_B, CONF_ACTIVE_POWER_A,
CONF_ACTIVE_POWER_B]:
if key not in config:
continue
conf = config[key]
sens = yield sensor.new_sensor(conf)
cg.add(getattr(var, 'set_{}_sensor'.format(key))(sens))

View file

@ -135,6 +135,9 @@ bool I2CComponent::read_bytes(uint8_t address, uint8_t a_register, uint8_t *data
delay(conversion); delay(conversion);
return this->raw_receive(address, data, len); return this->raw_receive(address, data, len);
} }
bool I2CComponent::read_bytes_raw(uint8_t address, uint8_t *data, uint8_t len) {
return this->raw_receive(address, data, len);
}
bool I2CComponent::read_bytes_16(uint8_t address, uint8_t a_register, uint16_t *data, uint8_t len, bool I2CComponent::read_bytes_16(uint8_t address, uint8_t a_register, uint16_t *data, uint8_t len,
uint32_t conversion) { uint32_t conversion) {
if (!this->write_bytes(address, a_register, nullptr, 0)) if (!this->write_bytes(address, a_register, nullptr, 0))
@ -156,6 +159,11 @@ bool I2CComponent::write_bytes(uint8_t address, uint8_t a_register, const uint8_
this->raw_write(address, data, len); this->raw_write(address, data, len);
return this->raw_end_transmission(address); return this->raw_end_transmission(address);
} }
bool I2CComponent::write_bytes_raw(uint8_t address, const uint8_t *data, uint8_t len) {
this->raw_begin_transmission(address);
this->raw_write(address, data, len);
return this->raw_end_transmission(address);
}
bool I2CComponent::write_bytes_16(uint8_t address, uint8_t a_register, const uint16_t *data, uint8_t len) { bool I2CComponent::write_bytes_16(uint8_t address, uint8_t a_register, const uint16_t *data, uint8_t len) {
this->raw_begin_transmission(address); this->raw_begin_transmission(address);
this->raw_write(address, &a_register, 1); this->raw_write(address, &a_register, 1);

View file

@ -42,6 +42,7 @@ class I2CComponent : public Component {
* @return If the operation was successful. * @return If the operation was successful.
*/ */
bool read_bytes(uint8_t address, uint8_t a_register, uint8_t *data, uint8_t len, uint32_t conversion = 0); bool read_bytes(uint8_t address, uint8_t a_register, uint8_t *data, uint8_t len, uint32_t conversion = 0);
bool read_bytes_raw(uint8_t address, uint8_t *data, uint8_t len);
/** Read len amount of 16-bit words (MSB first) from a register into data. /** Read len amount of 16-bit words (MSB first) from a register into data.
* *
@ -69,6 +70,7 @@ class I2CComponent : public Component {
* @return If the operation was successful. * @return If the operation was successful.
*/ */
bool write_bytes(uint8_t address, uint8_t a_register, const uint8_t *data, uint8_t len); bool write_bytes(uint8_t address, uint8_t a_register, const uint8_t *data, uint8_t len);
bool write_bytes_raw(uint8_t address, const uint8_t *data, uint8_t len);
/** Write len amount of 16-bit words (MSB first) to the specified register for address. /** Write len amount of 16-bit words (MSB first) to the specified register for address.
* *
@ -151,7 +153,6 @@ class I2CDevice {
/// Manually set the parent i2c bus for this device. /// Manually set the parent i2c bus for this device.
void set_i2c_parent(I2CComponent *parent); void set_i2c_parent(I2CComponent *parent);
protected:
/** Read len amount of bytes from a register into data. Optionally with a conversion time after /** Read len amount of bytes from a register into data. Optionally with a conversion time after
* writing the register value to the bus. * writing the register value to the bus.
* *
@ -161,15 +162,23 @@ class I2CDevice {
* @param conversion The time in ms between writing the register value and reading out the value. * @param conversion The time in ms between writing the register value and reading out the value.
* @return If the operation was successful. * @return If the operation was successful.
*/ */
bool read_bytes(uint8_t a_register, uint8_t *data, uint8_t len, uint32_t conversion = 0); // NOLINT bool read_bytes(uint8_t a_register, uint8_t *data, uint8_t len, uint32_t conversion = 0);
bool read_bytes_raw(uint8_t *data, uint8_t len) { return this->parent_->read_bytes_raw(this->address_, data, len); }
template<size_t N> optional<std::array<uint8_t, N>> read_bytes(uint8_t a_register) { // NOLINT template<size_t N> optional<std::array<uint8_t, N>> read_bytes(uint8_t a_register) {
std::array<uint8_t, N> res; std::array<uint8_t, N> res;
if (!this->read_bytes(a_register, res.data(), N)) { if (!this->read_bytes(a_register, res.data(), N)) {
return {}; return {};
} }
return res; return res;
} }
template<size_t N> optional<std::array<uint8_t, N>> read_bytes_raw() {
std::array<uint8_t, N> res;
if (!this->read_bytes_raw(res.data(), N)) {
return {};
}
return res;
}
/** Read len amount of 16-bit words (MSB first) from a register into data. /** Read len amount of 16-bit words (MSB first) from a register into data.
* *
@ -179,12 +188,12 @@ class I2CDevice {
* @param conversion The time in ms between writing the register value and reading out the value. * @param conversion The time in ms between writing the register value and reading out the value.
* @return If the operation was successful. * @return If the operation was successful.
*/ */
bool read_bytes_16(uint8_t a_register, uint16_t *data, uint8_t len, uint32_t conversion = 0); // NOLINT bool read_bytes_16(uint8_t a_register, uint16_t *data, uint8_t len, uint32_t conversion = 0);
/// Read a single byte from a register into the data variable. Return true if successful. /// Read a single byte from a register into the data variable. Return true if successful.
bool read_byte(uint8_t a_register, uint8_t *data, uint32_t conversion = 0); // NOLINT bool read_byte(uint8_t a_register, uint8_t *data, uint32_t conversion = 0);
optional<uint8_t> read_byte(uint8_t a_register) { // NOLINT optional<uint8_t> read_byte(uint8_t a_register) {
uint8_t data; uint8_t data;
if (!this->read_byte(a_register, &data)) if (!this->read_byte(a_register, &data))
return {}; return {};
@ -192,7 +201,7 @@ class I2CDevice {
} }
/// Read a single 16-bit words (MSB first) from a register into the data variable. Return true if successful. /// Read a single 16-bit words (MSB first) from a register into the data variable. Return true if successful.
bool read_byte_16(uint8_t a_register, uint16_t *data, uint32_t conversion = 0); // NOLINT bool read_byte_16(uint8_t a_register, uint16_t *data, uint32_t conversion = 0);
/** Write len amount of 8-bit bytes to the specified register. /** Write len amount of 8-bit bytes to the specified register.
* *
@ -201,7 +210,10 @@ class I2CDevice {
* @param len The amount of bytes to write to the bus. * @param len The amount of bytes to write to the bus.
* @return If the operation was successful. * @return If the operation was successful.
*/ */
bool write_bytes(uint8_t a_register, const uint8_t *data, uint8_t len); // NOLINT bool write_bytes(uint8_t a_register, const uint8_t *data, uint8_t len);
bool write_bytes_raw(const uint8_t *data, uint8_t len) {
return this->parent_->write_bytes_raw(this->address_, data, len);
}
/** Write a vector of data to a register. /** Write a vector of data to a register.
* *
@ -209,13 +221,17 @@ class I2CDevice {
* @param data The data to write. * @param data The data to write.
* @return If the operation was successful. * @return If the operation was successful.
*/ */
bool write_bytes(uint8_t a_register, const std::vector<uint8_t> &data) { // NOLINT bool write_bytes(uint8_t a_register, const std::vector<uint8_t> &data) {
return this->write_bytes(a_register, data.data(), data.size()); return this->write_bytes(a_register, data.data(), data.size());
} }
bool write_bytes_raw(const std::vector<uint8_t> &data) { return this->write_bytes_raw(data.data(), data.size()); }
template<size_t N> bool write_bytes(uint8_t a_register, const std::array<uint8_t, N> &data) { // NOLINT template<size_t N> bool write_bytes(uint8_t a_register, const std::array<uint8_t, N> &data) {
return this->write_bytes(a_register, data.data(), data.size()); return this->write_bytes(a_register, data.data(), data.size());
} }
template<size_t N> bool write_bytes_raw(const std::array<uint8_t, N> &data) {
return this->write_bytes_raw(data.data(), data.size());
}
/** Write len amount of 16-bit words (MSB first) to the specified register. /** Write len amount of 16-bit words (MSB first) to the specified register.
* *
@ -224,14 +240,15 @@ class I2CDevice {
* @param len The amount of bytes to write to the bus. * @param len The amount of bytes to write to the bus.
* @return If the operation was successful. * @return If the operation was successful.
*/ */
bool write_bytes_16(uint8_t a_register, const uint16_t *data, uint8_t len); // NOLINT bool write_bytes_16(uint8_t a_register, const uint16_t *data, uint8_t len);
/// Write a single byte of data into the specified register. Return true if successful. /// Write a single byte of data into the specified register. Return true if successful.
bool write_byte(uint8_t a_register, uint8_t data); // NOLINT bool write_byte(uint8_t a_register, uint8_t data);
/// Write a single 16-bit word of data into the specified register. Return true if successful. /// Write a single 16-bit word of data into the specified register. Return true if successful.
bool write_byte_16(uint8_t a_register, uint16_t data); // NOLINT bool write_byte_16(uint8_t a_register, uint16_t data);
protected:
uint8_t address_{0x00}; uint8_t address_{0x00};
I2CComponent *parent_{nullptr}; I2CComponent *parent_{nullptr};
}; };

View file

@ -54,7 +54,6 @@ def run_tidy(args, tmpdir, queue, lock, failed_files):
if rc != 0: if rc != 0:
print() print()
print("\033[0;32m************* File \033[1;32m{}\033[0m".format(path)) print("\033[0;32m************* File \033[1;32m{}\033[0m".format(path))
print(invocation_s)
print(output) print(output)
print() print()
failed_files.append(path) failed_files.append(path)

View file

@ -203,6 +203,17 @@ sensor:
value: 15.0 value: 15.0
- binary_sensor: bin3 - binary_sensor: bin3
value: 100.0 value: 100.0
- platform: ade7953
voltage:
name: ADE7953 Voltage
current_a:
name: ADE7953 Current A
current_b:
name: ADE7953 Current B
active_power_a:
name: ADE7953 Active Power A
active_power_b:
name: ADE7953 Active Power B
time: time:
- platform: homeassistant - platform: homeassistant