mirror of
https://github.com/esphome/esphome.git
synced 2024-11-25 16:38:16 +01:00
u-fire EC sensor (#3774)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
parent
f77118a90c
commit
e3f2562047
8 changed files with 351 additions and 0 deletions
|
@ -241,6 +241,7 @@ esphome/components/tuya/sensor/* @jesserockz
|
||||||
esphome/components/tuya/switch/* @jesserockz
|
esphome/components/tuya/switch/* @jesserockz
|
||||||
esphome/components/tuya/text_sensor/* @dentra
|
esphome/components/tuya/text_sensor/* @dentra
|
||||||
esphome/components/uart/* @esphome/core
|
esphome/components/uart/* @esphome/core
|
||||||
|
esphome/components/ufire_ec/* @pvizeli
|
||||||
esphome/components/ultrasonic/* @OttoWinter
|
esphome/components/ultrasonic/* @OttoWinter
|
||||||
esphome/components/version/* @esphome/core
|
esphome/components/version/* @esphome/core
|
||||||
esphome/components/wake_on_lan/* @willwill2will54
|
esphome/components/wake_on_lan/* @willwill2will54
|
||||||
|
|
1
esphome/components/ufire_ec/__init__.py
Normal file
1
esphome/components/ufire_ec/__init__.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
CODEOWNERS = ["@pvizeli"]
|
126
esphome/components/ufire_ec/sensor.py
Normal file
126
esphome/components/ufire_ec/sensor.py
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
import esphome.codegen as cg
|
||||||
|
from esphome import automation
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.components import i2c, sensor
|
||||||
|
from esphome.const import (
|
||||||
|
CONF_ID,
|
||||||
|
CONF_EC,
|
||||||
|
CONF_TEMPERATURE,
|
||||||
|
DEVICE_CLASS_EMPTY,
|
||||||
|
DEVICE_CLASS_TEMPERATURE,
|
||||||
|
ICON_EMPTY,
|
||||||
|
STATE_CLASS_MEASUREMENT,
|
||||||
|
UNIT_CELSIUS,
|
||||||
|
UNIT_MILLISIEMENS_PER_CENTIMETER,
|
||||||
|
)
|
||||||
|
|
||||||
|
DEPENDENCIES = ["i2c"]
|
||||||
|
|
||||||
|
CONF_SOLUTION = "solution"
|
||||||
|
CONF_TEMPERATURE_SENSOR = "temperature_sensor"
|
||||||
|
CONF_TEMPERATURE_COMPENSATION = "temperature_compensation"
|
||||||
|
CONF_TEMPERATURE_COEFFICIENT = "temperature_coefficient"
|
||||||
|
|
||||||
|
ufire_ec_ns = cg.esphome_ns.namespace("ufire_ec")
|
||||||
|
UFireECComponent = ufire_ec_ns.class_(
|
||||||
|
"UFireECComponent", cg.PollingComponent, i2c.I2CDevice
|
||||||
|
)
|
||||||
|
|
||||||
|
# Actions
|
||||||
|
UFireECCalibrateProbeAction = ufire_ec_ns.class_(
|
||||||
|
"UFireECCalibrateProbeAction", automation.Action
|
||||||
|
)
|
||||||
|
UFireECResetAction = ufire_ec_ns.class_("UFireECResetAction", automation.Action)
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = (
|
||||||
|
cv.Schema(
|
||||||
|
{
|
||||||
|
cv.GenerateID(): cv.declare_id(UFireECComponent),
|
||||||
|
cv.Exclusive(CONF_TEMPERATURE, "temperature"): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_CELSIUS,
|
||||||
|
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
accuracy_decimals=1,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_EC): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_MILLISIEMENS_PER_CENTIMETER,
|
||||||
|
icon=ICON_EMPTY,
|
||||||
|
device_class=DEVICE_CLASS_EMPTY,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
accuracy_decimals=1,
|
||||||
|
),
|
||||||
|
cv.Exclusive(CONF_TEMPERATURE_SENSOR, "temperature"): cv.use_id(
|
||||||
|
sensor.Sensor
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_TEMPERATURE_COMPENSATION, default=21.0): cv.temperature,
|
||||||
|
cv.Optional(CONF_TEMPERATURE_COEFFICIENT, default=0.019): cv.float_range(
|
||||||
|
min=0.01, max=0.04
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.extend(cv.polling_component_schema("60s"))
|
||||||
|
.extend(i2c.i2c_device_schema(0x3C))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def to_code(config):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
await cg.register_component(var, config)
|
||||||
|
cg.add(var.set_temperature_compensation(config[CONF_TEMPERATURE_COMPENSATION]))
|
||||||
|
cg.add(var.set_temperature_coefficient(config[CONF_TEMPERATURE_COEFFICIENT]))
|
||||||
|
|
||||||
|
if CONF_TEMPERATURE in config:
|
||||||
|
sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
|
||||||
|
cg.add(var.set_temperature_sensor(sens))
|
||||||
|
|
||||||
|
if CONF_EC in config:
|
||||||
|
sens = await sensor.new_sensor(config[CONF_EC])
|
||||||
|
cg.add(var.set_ec_sensor(sens))
|
||||||
|
|
||||||
|
if CONF_TEMPERATURE_SENSOR in config:
|
||||||
|
sens = await cg.get_variable(config[CONF_TEMPERATURE_SENSOR])
|
||||||
|
cg.add(var.set_temperature_sensor_external(sens))
|
||||||
|
|
||||||
|
await i2c.register_i2c_device(var, config)
|
||||||
|
|
||||||
|
|
||||||
|
UFIRE_EC_CALIBRATE_PROBE_SCHEMA = cv.Schema(
|
||||||
|
{
|
||||||
|
cv.GenerateID(): cv.use_id(UFireECComponent),
|
||||||
|
cv.Required(CONF_SOLUTION): cv.templatable(float),
|
||||||
|
cv.Required(CONF_TEMPERATURE): cv.templatable(cv.temperature),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@automation.register_action(
|
||||||
|
"ufire_ec.calibrate_probe",
|
||||||
|
UFireECCalibrateProbeAction,
|
||||||
|
UFIRE_EC_CALIBRATE_PROBE_SCHEMA,
|
||||||
|
)
|
||||||
|
async def ufire_ec_calibrate_probe_to_code(config, action_id, template_arg, args):
|
||||||
|
paren = await cg.get_variable(config[CONF_ID])
|
||||||
|
var = cg.new_Pvariable(action_id, template_arg, paren)
|
||||||
|
solution_ = await cg.templatable(config[CONF_SOLUTION], args, float)
|
||||||
|
temperature_ = await cg.templatable(config[CONF_TEMPERATURE], args, float)
|
||||||
|
cg.add(var.set_solution(solution_))
|
||||||
|
cg.add(var.set_temperature(temperature_))
|
||||||
|
return var
|
||||||
|
|
||||||
|
|
||||||
|
UFIRE_EC_RESET_SCHEMA = cv.Schema(
|
||||||
|
{
|
||||||
|
cv.GenerateID(): cv.use_id(UFireECComponent),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@automation.register_action(
|
||||||
|
"ufire_ec.reset",
|
||||||
|
UFireECResetAction,
|
||||||
|
UFIRE_EC_RESET_SCHEMA,
|
||||||
|
)
|
||||||
|
async def ufire_ec_reset_to_code(config, action_id, template_arg, args):
|
||||||
|
paren = await cg.get_variable(config[CONF_ID])
|
||||||
|
var = cg.new_Pvariable(action_id, template_arg, paren)
|
||||||
|
return var
|
119
esphome/components/ufire_ec/ufire_ec.cpp
Normal file
119
esphome/components/ufire_ec/ufire_ec.cpp
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
#include "ufire_ec.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace ufire_ec {
|
||||||
|
|
||||||
|
static const char *const TAG = "ufire_ec";
|
||||||
|
|
||||||
|
void UFireECComponent::setup() {
|
||||||
|
ESP_LOGCONFIG(TAG, "Setting up uFire_ec...");
|
||||||
|
|
||||||
|
uint8_t version;
|
||||||
|
if (!this->read_byte(REGISTER_VERSION, &version) && version != 0xFF) {
|
||||||
|
this->mark_failed();
|
||||||
|
this->status_set_error();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG, "Found ufire_ec board version 0x%02X", version);
|
||||||
|
|
||||||
|
// Write option for temperature adjustments
|
||||||
|
uint8_t config;
|
||||||
|
this->read_byte(REGISTER_CONFIG, &config);
|
||||||
|
if (this->temperature_sensor_ == nullptr && this->temperature_sensor_external_ == nullptr) {
|
||||||
|
config &= ~CONFIG_TEMP_COMPENSATION;
|
||||||
|
} else {
|
||||||
|
config |= CONFIG_TEMP_COMPENSATION;
|
||||||
|
}
|
||||||
|
this->write_byte(REGISTER_CONFIG, config);
|
||||||
|
|
||||||
|
// Update temperature compensation
|
||||||
|
this->set_compensation_(this->temperature_compensation_);
|
||||||
|
this->set_coefficient_(this->temperature_coefficient_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UFireECComponent::update() {
|
||||||
|
int wait = 0;
|
||||||
|
|
||||||
|
if (this->temperature_sensor_ != nullptr) {
|
||||||
|
this->write_byte(REGISTER_TASK, COMMAND_MEASURE_TEMP);
|
||||||
|
wait += 750;
|
||||||
|
} else if (this->temperature_sensor_external_ != nullptr) {
|
||||||
|
this->set_temperature_(this->temperature_sensor_external_->state);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->ec_sensor_ != nullptr) {
|
||||||
|
this->write_byte(REGISTER_TASK, COMMAND_MEASURE_EC);
|
||||||
|
wait += 750;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wait > 0) {
|
||||||
|
this->set_timeout("data", wait, [this]() { this->update_internal_(); });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UFireECComponent::update_internal_() {
|
||||||
|
if (this->temperature_sensor_ != nullptr)
|
||||||
|
this->temperature_sensor_->publish_state(this->measure_temperature_());
|
||||||
|
if (this->ec_sensor_ != nullptr)
|
||||||
|
this->ec_sensor_->publish_state(this->measure_ms_());
|
||||||
|
}
|
||||||
|
|
||||||
|
float UFireECComponent::measure_temperature_() { return this->read_data_(REGISTER_TEMP); }
|
||||||
|
|
||||||
|
float UFireECComponent::measure_ms_() { return this->read_data_(REGISTER_MS); }
|
||||||
|
|
||||||
|
void UFireECComponent::set_solution_(float solution, float temperature) {
|
||||||
|
solution /= (1 - (this->temperature_coefficient_ * (temperature - 25)));
|
||||||
|
this->write_data_(REGISTER_SOLUTION, solution);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UFireECComponent::set_compensation_(float temperature) { this->write_data_(REGISTER_COMPENSATION, temperature); }
|
||||||
|
|
||||||
|
void UFireECComponent::set_coefficient_(float coefficient) { this->write_data_(REGISTER_COEFFICENT, coefficient); }
|
||||||
|
|
||||||
|
void UFireECComponent::set_temperature_(float temperature) { this->write_data_(REGISTER_TEMP, temperature); }
|
||||||
|
|
||||||
|
void UFireECComponent::calibrate_probe(float solution, float temperature) {
|
||||||
|
this->set_solution_(solution, temperature);
|
||||||
|
this->write_byte(REGISTER_TASK, COMMAND_CALIBRATE_PROBE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UFireECComponent::reset_board() { this->write_data_(REGISTER_CALIBRATE_OFFSET, NAN); }
|
||||||
|
|
||||||
|
float UFireECComponent::read_data_(uint8_t reg) {
|
||||||
|
float f;
|
||||||
|
uint8_t temp[4];
|
||||||
|
|
||||||
|
this->write(®, 1);
|
||||||
|
delay(10);
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < 4; i++) {
|
||||||
|
this->read_bytes_raw(temp + i, 1);
|
||||||
|
}
|
||||||
|
memcpy(&f, temp, sizeof(f));
|
||||||
|
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UFireECComponent::write_data_(uint8_t reg, float data) {
|
||||||
|
uint8_t temp[4];
|
||||||
|
|
||||||
|
memcpy(temp, &data, sizeof(data));
|
||||||
|
this->write_bytes(reg, temp, 4);
|
||||||
|
delay(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UFireECComponent::dump_config() {
|
||||||
|
ESP_LOGCONFIG(TAG, "uFire-EC");
|
||||||
|
LOG_I2C_DEVICE(this)
|
||||||
|
LOG_UPDATE_INTERVAL(this)
|
||||||
|
LOG_SENSOR(" ", "EC Sensor", this->ec_sensor_)
|
||||||
|
LOG_SENSOR(" ", "Temperature Sensor", this->temperature_sensor_)
|
||||||
|
LOG_SENSOR(" ", "Temperature Sensor external", this->temperature_sensor_external_)
|
||||||
|
ESP_LOGCONFIG(TAG, " Temperature Compensation: %f", this->temperature_compensation_);
|
||||||
|
ESP_LOGCONFIG(TAG, " Temperature Coefficient: %f", this->temperature_coefficient_);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ufire_ec
|
||||||
|
} // namespace esphome
|
87
esphome/components/ufire_ec/ufire_ec.h
Normal file
87
esphome/components/ufire_ec/ufire_ec.h
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/automation.h"
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/core/hal.h"
|
||||||
|
#include "esphome/components/sensor/sensor.h"
|
||||||
|
#include "esphome/components/i2c/i2c.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace ufire_ec {
|
||||||
|
|
||||||
|
static const uint8_t CONFIG_TEMP_COMPENSATION = 0x02;
|
||||||
|
|
||||||
|
static const uint8_t REGISTER_VERSION = 0;
|
||||||
|
static const uint8_t REGISTER_MS = 1;
|
||||||
|
static const uint8_t REGISTER_TEMP = 5;
|
||||||
|
static const uint8_t REGISTER_SOLUTION = 9;
|
||||||
|
static const uint8_t REGISTER_COEFFICENT = 13;
|
||||||
|
static const uint8_t REGISTER_CALIBRATE_OFFSET = 33;
|
||||||
|
static const uint8_t REGISTER_COMPENSATION = 45;
|
||||||
|
static const uint8_t REGISTER_CONFIG = 54;
|
||||||
|
static const uint8_t REGISTER_TASK = 55;
|
||||||
|
|
||||||
|
static const uint8_t COMMAND_CALIBRATE_PROBE = 20;
|
||||||
|
static const uint8_t COMMAND_MEASURE_TEMP = 40;
|
||||||
|
static const uint8_t COMMAND_MEASURE_EC = 80;
|
||||||
|
|
||||||
|
class UFireECComponent : public PollingComponent, public i2c::I2CDevice {
|
||||||
|
public:
|
||||||
|
void setup() override;
|
||||||
|
void update() override;
|
||||||
|
void dump_config() override;
|
||||||
|
|
||||||
|
void set_temperature_sensor(sensor::Sensor *temperature_sensor) { this->temperature_sensor_ = temperature_sensor; }
|
||||||
|
void set_temperature_sensor_external(sensor::Sensor *temperature_sensor) {
|
||||||
|
this->temperature_sensor_external_ = temperature_sensor;
|
||||||
|
}
|
||||||
|
void set_ec_sensor(sensor::Sensor *ec_sensor) { this->ec_sensor_ = ec_sensor; }
|
||||||
|
void set_temperature_compensation(float compensation) { this->temperature_compensation_ = compensation; }
|
||||||
|
void set_temperature_coefficient(float coefficient) { this->temperature_coefficient_ = coefficient; }
|
||||||
|
void calibrate_probe(float solution, float temperature);
|
||||||
|
void reset_board();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
float measure_temperature_();
|
||||||
|
float measure_ms_();
|
||||||
|
void set_solution_(float solution, float temperature);
|
||||||
|
void set_compensation_(float temperature);
|
||||||
|
void set_coefficient_(float coefficient);
|
||||||
|
void set_temperature_(float temperature);
|
||||||
|
float read_data_(uint8_t reg);
|
||||||
|
void write_data_(uint8_t reg, float data);
|
||||||
|
void update_internal_();
|
||||||
|
|
||||||
|
sensor::Sensor *temperature_sensor_{nullptr};
|
||||||
|
sensor::Sensor *temperature_sensor_external_{nullptr};
|
||||||
|
sensor::Sensor *ec_sensor_{nullptr};
|
||||||
|
float temperature_compensation_{0.0};
|
||||||
|
float temperature_coefficient_{0.0};
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename... Ts> class UFireECCalibrateProbeAction : public Action<Ts...> {
|
||||||
|
public:
|
||||||
|
UFireECCalibrateProbeAction(UFireECComponent *parent) : parent_(parent) {}
|
||||||
|
TEMPLATABLE_VALUE(float, solution)
|
||||||
|
TEMPLATABLE_VALUE(float, temperature)
|
||||||
|
|
||||||
|
void play(Ts... x) override {
|
||||||
|
this->parent_->calibrate_probe(this->solution_.value(x...), this->temperature_.value(x...));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
UFireECComponent *parent_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename... Ts> class UFireECResetAction : public Action<Ts...> {
|
||||||
|
public:
|
||||||
|
UFireECResetAction(UFireECComponent *parent) : parent_(parent) {}
|
||||||
|
|
||||||
|
void play(Ts... x) override { this->parent_->reset_board(); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
UFireECComponent *parent_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ufire_ec
|
||||||
|
} // namespace esphome
|
|
@ -192,6 +192,7 @@ CONF_DUMMY_RECEIVER_ID = "dummy_receiver_id"
|
||||||
CONF_DUMP = "dump"
|
CONF_DUMP = "dump"
|
||||||
CONF_DURATION = "duration"
|
CONF_DURATION = "duration"
|
||||||
CONF_EAP = "eap"
|
CONF_EAP = "eap"
|
||||||
|
CONF_EC = "ec"
|
||||||
CONF_ECHO_PIN = "echo_pin"
|
CONF_ECHO_PIN = "echo_pin"
|
||||||
CONF_ECO2 = "eco2"
|
CONF_ECO2 = "eco2"
|
||||||
CONF_EFFECT = "effect"
|
CONF_EFFECT = "effect"
|
||||||
|
@ -871,6 +872,7 @@ UNIT_MICROSIEMENS_PER_CENTIMETER = "µS/cm"
|
||||||
UNIT_MICROTESLA = "µT"
|
UNIT_MICROTESLA = "µT"
|
||||||
UNIT_MILLIGRAMS_PER_CUBIC_METER = "mg/m³"
|
UNIT_MILLIGRAMS_PER_CUBIC_METER = "mg/m³"
|
||||||
UNIT_MILLISECOND = "ms"
|
UNIT_MILLISECOND = "ms"
|
||||||
|
UNIT_MILLISIEMENS_PER_CENTIMETER = "mS/cm"
|
||||||
UNIT_MINUTE = "min"
|
UNIT_MINUTE = "min"
|
||||||
UNIT_OHM = "Ω"
|
UNIT_OHM = "Ω"
|
||||||
UNIT_PARTS_PER_BILLION = "ppb"
|
UNIT_PARTS_PER_BILLION = "ppb"
|
||||||
|
|
|
@ -369,6 +369,13 @@ sensor:
|
||||||
name: Propane test distance
|
name: Propane test distance
|
||||||
battery_level:
|
battery_level:
|
||||||
name: Propane test battery level
|
name: Propane test battery level
|
||||||
|
- platform: ufire_ec
|
||||||
|
id: ufire_ec_board
|
||||||
|
ec:
|
||||||
|
name: Ufire EC
|
||||||
|
temperature_sensor: ha_hello_world_temperature
|
||||||
|
temperature_compensation: 20.0
|
||||||
|
temperature_coefficient: 0.019
|
||||||
|
|
||||||
time:
|
time:
|
||||||
- platform: homeassistant
|
- platform: homeassistant
|
||||||
|
|
|
@ -243,6 +243,14 @@ sensor:
|
||||||
- platform: copy
|
- platform: copy
|
||||||
source_id: mcp_sensor
|
source_id: mcp_sensor
|
||||||
name: MCP binary sensor copy
|
name: MCP binary sensor copy
|
||||||
|
- platform: ufire_ec
|
||||||
|
id: ufire_ec_board
|
||||||
|
temperature:
|
||||||
|
name: Ufire Temperature
|
||||||
|
ec:
|
||||||
|
name: Ufire EC
|
||||||
|
temperature_compensation: 20.0
|
||||||
|
temperature_coefficient: 0.019
|
||||||
|
|
||||||
#
|
#
|
||||||
# platform sensor.apds9960 requires component apds9960
|
# platform sensor.apds9960 requires component apds9960
|
||||||
|
|
Loading…
Reference in a new issue