mirror of
https://github.com/esphome/esphome.git
synced 2024-11-12 18:27:46 +01:00
Add uFire ISE sensor (#3789)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
parent
f4b0917239
commit
6236db1a27
8 changed files with 390 additions and 0 deletions
|
@ -246,6 +246,7 @@ esphome/components/tuya/switch/* @jesserockz
|
|||
esphome/components/tuya/text_sensor/* @dentra
|
||||
esphome/components/uart/* @esphome/core
|
||||
esphome/components/ufire_ec/* @pvizeli
|
||||
esphome/components/ufire_ise/* @pvizeli
|
||||
esphome/components/ultrasonic/* @OttoWinter
|
||||
esphome/components/version/* @esphome/core
|
||||
esphome/components/wake_on_lan/* @willwill2will54
|
||||
|
|
1
esphome/components/ufire_ise/__init__.py
Normal file
1
esphome/components/ufire_ise/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
CODEOWNERS = ["@pvizeli"]
|
127
esphome/components/ufire_ise/sensor.py
Normal file
127
esphome/components/ufire_ise/sensor.py
Normal file
|
@ -0,0 +1,127 @@
|
|||
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_PH,
|
||||
CONF_TEMPERATURE,
|
||||
DEVICE_CLASS_EMPTY,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
ICON_EMPTY,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_CELSIUS,
|
||||
UNIT_PH,
|
||||
)
|
||||
|
||||
DEPENDENCIES = ["i2c"]
|
||||
|
||||
CONF_SOLUTION = "solution"
|
||||
CONF_TEMPERATURE_SENSOR = "temperature_sensor"
|
||||
|
||||
ufire_ise_ns = cg.esphome_ns.namespace("ufire_ise")
|
||||
UFireISEComponent = ufire_ise_ns.class_(
|
||||
"UFireISEComponent", cg.PollingComponent, i2c.I2CDevice
|
||||
)
|
||||
|
||||
# Actions
|
||||
UFireISECalibrateProbeLowAction = ufire_ise_ns.class_(
|
||||
"UFireISECalibrateProbeLowAction", automation.Action
|
||||
)
|
||||
UFireISECalibrateProbeHighAction = ufire_ise_ns.class_(
|
||||
"UFireISECalibrateProbeHighAction", automation.Action
|
||||
)
|
||||
UFireISEResetAction = ufire_ise_ns.class_("UFireISEResetAction", automation.Action)
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(UFireISEComponent),
|
||||
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_PH): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_PH,
|
||||
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
|
||||
),
|
||||
}
|
||||
)
|
||||
.extend(cv.polling_component_schema("60s"))
|
||||
.extend(i2c.i2c_device_schema(0x3F))
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
|
||||
if CONF_TEMPERATURE in config:
|
||||
sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
|
||||
cg.add(var.set_temperature_sensor(sens))
|
||||
|
||||
if CONF_PH in config:
|
||||
sens = await sensor.new_sensor(config[CONF_PH])
|
||||
cg.add(var.set_ph_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_ISE_CALIBRATE_PROBE_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(UFireISEComponent),
|
||||
cv.Required(CONF_SOLUTION): cv.templatable(float),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"ufire_ise.calibrate_probe_low",
|
||||
UFireISECalibrateProbeLowAction,
|
||||
UFIRE_ISE_CALIBRATE_PROBE_SCHEMA,
|
||||
)
|
||||
async def ufire_ise_calibrate_probe_low_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)
|
||||
template_ = await cg.templatable(config[CONF_SOLUTION], args, float)
|
||||
cg.add(var.set_solution(template_))
|
||||
return var
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"ufire_ise.calibrate_probe_high",
|
||||
UFireISECalibrateProbeHighAction,
|
||||
UFIRE_ISE_CALIBRATE_PROBE_SCHEMA,
|
||||
)
|
||||
async def ufire_ise_calibrate_probe_high_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)
|
||||
template_ = await cg.templatable(config[CONF_SOLUTION], args, float)
|
||||
cg.add(var.set_solution(template_))
|
||||
return var
|
||||
|
||||
|
||||
UFIRE_ISE_RESET_SCHEMA = cv.Schema({cv.GenerateID(): cv.use_id(UFireISEComponent)})
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"ufire_ise.reset",
|
||||
UFireISEResetAction,
|
||||
UFIRE_ISE_RESET_SCHEMA,
|
||||
)
|
||||
async def ufire_ise_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
|
153
esphome/components/ufire_ise/ufire_ise.cpp
Normal file
153
esphome/components/ufire_ise/ufire_ise.cpp
Normal file
|
@ -0,0 +1,153 @@
|
|||
#include "esphome/core/log.h"
|
||||
#include "ufire_ise.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace esphome {
|
||||
namespace ufire_ise {
|
||||
|
||||
static const char *const TAG = "ufire_ise";
|
||||
|
||||
void UFireISEComponent::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up uFire_ise...");
|
||||
|
||||
uint8_t version;
|
||||
if (!this->read_byte(REGISTER_VERSION, &version) && version != 0xFF) {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
ESP_LOGI(TAG, "Found uFire_ise 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);
|
||||
}
|
||||
|
||||
void UFireISEComponent::update() {
|
||||
int wait = 0;
|
||||
if (this->temperature_sensor_ != nullptr) {
|
||||
this->write_byte(REGISTER_TASK, COMMAND_MEASURE_TEMP);
|
||||
wait += 750;
|
||||
}
|
||||
if (this->ph_sensor_ != nullptr) {
|
||||
this->write_byte(REGISTER_TASK, COMMAND_MEASURE_MV);
|
||||
wait += 750;
|
||||
}
|
||||
|
||||
// Wait until measurement are taken
|
||||
this->set_timeout("data", wait, [this]() { this->update_internal_(); });
|
||||
}
|
||||
|
||||
void UFireISEComponent::update_internal_() {
|
||||
float temperature = 0;
|
||||
|
||||
// Read temperature internal and populate it
|
||||
if (this->temperature_sensor_ != nullptr) {
|
||||
temperature = this->measure_temperature_();
|
||||
this->temperature_sensor_->publish_state(temperature);
|
||||
}
|
||||
// Get temperature from external only for adjustments
|
||||
else if (this->temperature_sensor_external_ != nullptr) {
|
||||
temperature = this->temperature_sensor_external_->state;
|
||||
}
|
||||
|
||||
if (this->ph_sensor_ != nullptr) {
|
||||
this->ph_sensor_->publish_state(this->measure_ph_(temperature));
|
||||
}
|
||||
}
|
||||
|
||||
float UFireISEComponent::measure_temperature_() { return this->read_data_(REGISTER_TEMP); }
|
||||
|
||||
float UFireISEComponent::measure_mv_() { return this->read_data_(REGISTER_MV); }
|
||||
|
||||
float UFireISEComponent::measure_ph_(float temperature) {
|
||||
float mv, ph;
|
||||
|
||||
mv = this->measure_mv_();
|
||||
if (mv == -1)
|
||||
return -1;
|
||||
|
||||
ph = fabs(7.0 - (mv / PROBE_MV_TO_PH));
|
||||
|
||||
// Determine the temperature correction
|
||||
float distance_from_7 = std::abs(7 - roundf(ph));
|
||||
float distance_from_25 = std::floor(std::abs(25 - roundf(temperature)) / 10);
|
||||
float temp_multiplier = (distance_from_25 * distance_from_7) * PROBE_TMP_CORRECTION;
|
||||
if ((ph >= 8.0) && (temperature >= 35))
|
||||
temp_multiplier *= -1;
|
||||
if ((ph <= 6.0) && (temperature <= 15))
|
||||
temp_multiplier *= -1;
|
||||
|
||||
ph += temp_multiplier;
|
||||
if ((ph <= 0.0) || (ph > 14.0))
|
||||
ph = -1;
|
||||
if (std::isinf(ph))
|
||||
ph = -1;
|
||||
if (std::isnan(ph))
|
||||
ph = -1;
|
||||
|
||||
return ph;
|
||||
}
|
||||
|
||||
void UFireISEComponent::set_solution_(float solution) {
|
||||
solution = (7 - solution) * PROBE_MV_TO_PH;
|
||||
this->write_data_(REGISTER_SOLUTION, solution);
|
||||
}
|
||||
|
||||
void UFireISEComponent::calibrate_probe_low(float solution) {
|
||||
this->set_solution_(solution);
|
||||
this->write_byte(REGISTER_TASK, COMMAND_CALIBRATE_LOW);
|
||||
}
|
||||
|
||||
void UFireISEComponent::calibrate_probe_high(float solution) {
|
||||
this->set_solution_(solution);
|
||||
this->write_byte(REGISTER_TASK, COMMAND_CALIBRATE_HIGH);
|
||||
}
|
||||
|
||||
void UFireISEComponent::reset_board() {
|
||||
this->write_data_(REGISTER_REFHIGH, NAN);
|
||||
this->write_data_(REGISTER_REFLOW, NAN);
|
||||
this->write_data_(REGISTER_READHIGH, NAN);
|
||||
this->write_data_(REGISTER_READLOW, NAN);
|
||||
}
|
||||
|
||||
float UFireISEComponent::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 UFireISEComponent::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 UFireISEComponent::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "uFire-ISE");
|
||||
LOG_I2C_DEVICE(this)
|
||||
LOG_UPDATE_INTERVAL(this)
|
||||
LOG_SENSOR(" ", "PH Sensor", this->ph_sensor_)
|
||||
LOG_SENSOR(" ", "Temperature Sensor", this->temperature_sensor_)
|
||||
LOG_SENSOR(" ", "Temperature Sensor external", this->temperature_sensor_external_)
|
||||
}
|
||||
|
||||
} // namespace ufire_ise
|
||||
} // namespace esphome
|
95
esphome/components/ufire_ise/ufire_ise.h
Normal file
95
esphome/components/ufire_ise/ufire_ise.h
Normal file
|
@ -0,0 +1,95 @@
|
|||
#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_ise {
|
||||
|
||||
static const float PROBE_MV_TO_PH = 59.2;
|
||||
static const float PROBE_TMP_CORRECTION = 0.03;
|
||||
|
||||
static const uint8_t CONFIG_TEMP_COMPENSATION = 0x02;
|
||||
|
||||
static const uint8_t REGISTER_VERSION = 0;
|
||||
static const uint8_t REGISTER_MV = 1;
|
||||
static const uint8_t REGISTER_TEMP = 5;
|
||||
static const uint8_t REGISTER_REFHIGH = 13;
|
||||
static const uint8_t REGISTER_REFLOW = 17;
|
||||
static const uint8_t REGISTER_READHIGH = 21;
|
||||
static const uint8_t REGISTER_READLOW = 25;
|
||||
static const uint8_t REGISTER_SOLUTION = 29;
|
||||
static const uint8_t REGISTER_CONFIG = 38;
|
||||
static const uint8_t REGISTER_TASK = 39;
|
||||
|
||||
static const uint8_t COMMAND_CALIBRATE_HIGH = 8;
|
||||
static const uint8_t COMMAND_CALIBRATE_LOW = 10;
|
||||
static const uint8_t COMMAND_MEASURE_TEMP = 40;
|
||||
static const uint8_t COMMAND_MEASURE_MV = 80;
|
||||
|
||||
class UFireISEComponent : 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_ph_sensor(sensor::Sensor *ph_sensor) { this->ph_sensor_ = ph_sensor; }
|
||||
void calibrate_probe_low(float solution);
|
||||
void calibrate_probe_high(float solution);
|
||||
void reset_board();
|
||||
|
||||
protected:
|
||||
float measure_temperature_();
|
||||
float measure_mv_();
|
||||
float measure_ph_(float temperature);
|
||||
void set_solution_(float solution);
|
||||
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 *ph_sensor_{nullptr};
|
||||
};
|
||||
|
||||
template<typename... Ts> class UFireISECalibrateProbeLowAction : public Action<Ts...> {
|
||||
public:
|
||||
UFireISECalibrateProbeLowAction(UFireISEComponent *parent) : parent_(parent) {}
|
||||
TEMPLATABLE_VALUE(float, solution)
|
||||
|
||||
void play(Ts... x) override { this->parent_->calibrate_probe_low(this->solution_.value(x...)); }
|
||||
|
||||
protected:
|
||||
UFireISEComponent *parent_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class UFireISECalibrateProbeHighAction : public Action<Ts...> {
|
||||
public:
|
||||
UFireISECalibrateProbeHighAction(UFireISEComponent *parent) : parent_(parent) {}
|
||||
TEMPLATABLE_VALUE(float, solution)
|
||||
|
||||
void play(Ts... x) override { this->parent_->calibrate_probe_high(this->solution_.value(x...)); }
|
||||
|
||||
protected:
|
||||
UFireISEComponent *parent_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class UFireISEResetAction : public Action<Ts...> {
|
||||
public:
|
||||
UFireISEResetAction(UFireISEComponent *parent) : parent_(parent) {}
|
||||
|
||||
void play(Ts... x) override { this->parent_->reset_board(); }
|
||||
|
||||
protected:
|
||||
UFireISEComponent *parent_;
|
||||
};
|
||||
|
||||
} // namespace ufire_ise
|
||||
} // namespace esphome
|
|
@ -493,6 +493,7 @@ CONF_PAYLOAD = "payload"
|
|||
CONF_PAYLOAD_AVAILABLE = "payload_available"
|
||||
CONF_PAYLOAD_NOT_AVAILABLE = "payload_not_available"
|
||||
CONF_PERIOD = "period"
|
||||
CONF_PH = "ph"
|
||||
CONF_PHASE_ANGLE = "phase_angle"
|
||||
CONF_PHASE_BALANCER = "phase_balancer"
|
||||
CONF_PIN = "pin"
|
||||
|
@ -881,6 +882,7 @@ UNIT_PARTS_PER_BILLION = "ppb"
|
|||
UNIT_PARTS_PER_MILLION = "ppm"
|
||||
UNIT_PASCAL = "Pa"
|
||||
UNIT_PERCENT = "%"
|
||||
UNIT_PH = "pH"
|
||||
UNIT_PULSES = "pulses"
|
||||
UNIT_PULSES_PER_MINUTE = "pulses/min"
|
||||
UNIT_SECOND = "s"
|
||||
|
|
|
@ -376,6 +376,11 @@ sensor:
|
|||
temperature_sensor: ha_hello_world_temperature
|
||||
temperature_compensation: 20.0
|
||||
temperature_coefficient: 0.019
|
||||
- platform: ufire_ise
|
||||
id: ufire_ise_board
|
||||
temperature_sensor: ha_hello_world_temperature
|
||||
ph:
|
||||
name: Ufire pH
|
||||
|
||||
time:
|
||||
- platform: homeassistant
|
||||
|
|
|
@ -251,6 +251,12 @@ sensor:
|
|||
name: Ufire EC
|
||||
temperature_compensation: 20.0
|
||||
temperature_coefficient: 0.019
|
||||
- platform: ufire_ise
|
||||
id: ufire_ise_board
|
||||
temperature:
|
||||
name: Ufire Temperature
|
||||
ph:
|
||||
name: Ufire pH
|
||||
|
||||
#
|
||||
# platform sensor.apds9960 requires component apds9960
|
||||
|
|
Loading…
Reference in a new issue