mirror of
https://github.com/esphome/esphome.git
synced 2024-12-04 20:48:21 +01:00
Merge branch 'dev' into add-graphical-layout-system
This commit is contained in:
commit
d51e54b878
55 changed files with 1298 additions and 252 deletions
|
@ -228,7 +228,7 @@ esphome/components/nextion/binary_sensor/* @senexcrenshaw
|
|||
esphome/components/nextion/sensor/* @senexcrenshaw
|
||||
esphome/components/nextion/switch/* @senexcrenshaw
|
||||
esphome/components/nextion/text_sensor/* @senexcrenshaw
|
||||
esphome/components/nfc/* @jesserockz
|
||||
esphome/components/nfc/* @jesserockz @kbx81
|
||||
esphome/components/noblex/* @AGalfra
|
||||
esphome/components/number/* @esphome/core
|
||||
esphome/components/ota/* @esphome/core
|
||||
|
@ -363,6 +363,7 @@ esphome/components/ufire_ec/* @pvizeli
|
|||
esphome/components/ufire_ise/* @pvizeli
|
||||
esphome/components/ultrasonic/* @OttoWinter
|
||||
esphome/components/vbus/* @ssieb
|
||||
esphome/components/veml3235/* @kbx81
|
||||
esphome/components/version/* @esphome/core
|
||||
esphome/components/voice_assistant/* @jesserockz
|
||||
esphome/components/wake_on_lan/* @willwill2will54
|
||||
|
|
|
@ -18,6 +18,7 @@ from esphome.const import (
|
|||
UNIT_KILOWATT_HOURS,
|
||||
UNIT_VOLT,
|
||||
UNIT_WATT,
|
||||
STATE_CLASS_TOTAL_INCREASING,
|
||||
)
|
||||
|
||||
DEPENDENCIES = ["uart"]
|
||||
|
@ -54,6 +55,7 @@ CONFIG_SCHEMA = (
|
|||
unit_of_measurement=UNIT_KILOWATT_HOURS,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_ENERGY,
|
||||
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||
),
|
||||
cv.Optional(CONF_INTERNAL_TEMPERATURE): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
|
|
|
@ -19,6 +19,7 @@ from esphome.const import (
|
|||
UNIT_VOLT,
|
||||
UNIT_WATT,
|
||||
UNIT_HERTZ,
|
||||
STATE_CLASS_TOTAL_INCREASING,
|
||||
)
|
||||
|
||||
DEPENDENCIES = ["uart"]
|
||||
|
@ -52,6 +53,7 @@ CONFIG_SCHEMA = (
|
|||
unit_of_measurement=UNIT_KILOWATT_HOURS,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_ENERGY,
|
||||
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||
),
|
||||
cv.Optional(CONF_FREQUENCY): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_HERTZ,
|
||||
|
|
|
@ -113,8 +113,9 @@ void CSE7766Component::parse_data_() {
|
|||
bool have_voltage = adj & 0x40;
|
||||
if (have_voltage) {
|
||||
// voltage cycle of serial port outputted is a complete cycle;
|
||||
this->voltage_acc_ += voltage_calib / float(voltage_cycle);
|
||||
this->voltage_counts_ += 1;
|
||||
float voltage = voltage_calib / float(voltage_cycle);
|
||||
if (this->voltage_sensor_ != nullptr)
|
||||
this->voltage_sensor_->publish_state(voltage);
|
||||
}
|
||||
|
||||
bool have_power = adj & 0x10;
|
||||
|
@ -126,8 +127,8 @@ void CSE7766Component::parse_data_() {
|
|||
if (!power_cycle_exceeds_range) {
|
||||
power = power_calib / float(power_cycle);
|
||||
}
|
||||
this->power_acc_ += power;
|
||||
this->power_counts_ += 1;
|
||||
if (this->power_sensor_ != nullptr)
|
||||
this->power_sensor_->publish_state(power);
|
||||
|
||||
uint32_t difference;
|
||||
if (this->cf_pulses_last_ == 0) {
|
||||
|
@ -141,7 +142,10 @@ void CSE7766Component::parse_data_() {
|
|||
}
|
||||
this->cf_pulses_last_ = cf_pulses;
|
||||
this->energy_total_ += difference * float(power_calib) / 1000000.0f / 3600.0f;
|
||||
this->energy_total_counts_ += 1;
|
||||
if (this->energy_sensor_ != nullptr)
|
||||
this->energy_sensor_->publish_state(this->energy_total_);
|
||||
} else if ((this->energy_sensor_ != nullptr) && !this->energy_sensor_->has_state()) {
|
||||
this->energy_sensor_->publish_state(0);
|
||||
}
|
||||
|
||||
if (adj & 0x20) {
|
||||
|
@ -150,42 +154,13 @@ void CSE7766Component::parse_data_() {
|
|||
if (have_voltage && !have_power) {
|
||||
// Testing has shown that when we have voltage and current but not power, that means the power is 0.
|
||||
// We report a power of 0, which in turn means we should report a current of 0.
|
||||
this->power_counts_ += 1;
|
||||
if (this->power_sensor_ != nullptr)
|
||||
this->power_sensor_->publish_state(0);
|
||||
} else if (power != 0.0f) {
|
||||
current = current_calib / float(current_cycle);
|
||||
}
|
||||
this->current_acc_ += current;
|
||||
this->current_counts_ += 1;
|
||||
}
|
||||
}
|
||||
void CSE7766Component::update() {
|
||||
const auto publish_state = [](const char *name, sensor::Sensor *sensor, float &acc, uint32_t &counts) {
|
||||
if (counts != 0) {
|
||||
const auto avg = acc / counts;
|
||||
|
||||
ESP_LOGV(TAG, "Got %s_acc=%.2f %s_counts=%" PRIu32 " %s=%.1f", name, acc, name, counts, name, avg);
|
||||
|
||||
if (sensor != nullptr) {
|
||||
sensor->publish_state(avg);
|
||||
}
|
||||
|
||||
acc = 0.0f;
|
||||
counts = 0;
|
||||
}
|
||||
};
|
||||
|
||||
publish_state("voltage", this->voltage_sensor_, this->voltage_acc_, this->voltage_counts_);
|
||||
publish_state("current", this->current_sensor_, this->current_acc_, this->current_counts_);
|
||||
publish_state("power", this->power_sensor_, this->power_acc_, this->power_counts_);
|
||||
|
||||
if (this->energy_total_counts_ != 0) {
|
||||
ESP_LOGV(TAG, "Got energy_total=%.2f energy_total_counts=%" PRIu32, this->energy_total_,
|
||||
this->energy_total_counts_);
|
||||
|
||||
if (this->energy_sensor_ != nullptr) {
|
||||
this->energy_sensor_->publish_state(this->energy_total_);
|
||||
}
|
||||
this->energy_total_counts_ = 0;
|
||||
if (this->current_sensor_ != nullptr)
|
||||
this->current_sensor_->publish_state(current);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -196,7 +171,6 @@ uint32_t CSE7766Component::get_24_bit_uint_(uint8_t start_index) {
|
|||
|
||||
void CSE7766Component::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "CSE7766:");
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
LOG_SENSOR(" ", "Voltage", this->voltage_sensor_);
|
||||
LOG_SENSOR(" ", "Current", this->current_sensor_);
|
||||
LOG_SENSOR(" ", "Power", this->power_sensor_);
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
namespace esphome {
|
||||
namespace cse7766 {
|
||||
|
||||
class CSE7766Component : public PollingComponent, public uart::UARTDevice {
|
||||
class CSE7766Component : public Component, public uart::UARTDevice {
|
||||
public:
|
||||
void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; }
|
||||
void set_current_sensor(sensor::Sensor *current_sensor) { current_sensor_ = current_sensor; }
|
||||
|
@ -16,7 +16,6 @@ class CSE7766Component : public PollingComponent, public uart::UARTDevice {
|
|||
|
||||
void loop() override;
|
||||
float get_setup_priority() const override;
|
||||
void update() override;
|
||||
void dump_config() override;
|
||||
|
||||
protected:
|
||||
|
@ -31,16 +30,8 @@ class CSE7766Component : public PollingComponent, public uart::UARTDevice {
|
|||
sensor::Sensor *current_sensor_{nullptr};
|
||||
sensor::Sensor *power_sensor_{nullptr};
|
||||
sensor::Sensor *energy_sensor_{nullptr};
|
||||
float voltage_acc_{0.0f};
|
||||
float current_acc_{0.0f};
|
||||
float power_acc_{0.0f};
|
||||
float energy_total_{0.0f};
|
||||
uint32_t cf_pulses_last_{0};
|
||||
uint32_t voltage_counts_{0};
|
||||
uint32_t current_counts_{0};
|
||||
uint32_t power_counts_{0};
|
||||
// Setting this to 1 means it will always publish 0 once at startup
|
||||
uint32_t energy_total_counts_{1};
|
||||
};
|
||||
|
||||
} // namespace cse7766
|
||||
|
|
|
@ -22,43 +22,37 @@ from esphome.const import (
|
|||
DEPENDENCIES = ["uart"]
|
||||
|
||||
cse7766_ns = cg.esphome_ns.namespace("cse7766")
|
||||
CSE7766Component = cse7766_ns.class_(
|
||||
"CSE7766Component", cg.PollingComponent, uart.UARTDevice
|
||||
)
|
||||
CSE7766Component = cse7766_ns.class_("CSE7766Component", cg.Component, uart.UARTDevice)
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(CSE7766Component),
|
||||
cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_VOLT,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_VOLTAGE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_CURRENT): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_AMPERE,
|
||||
accuracy_decimals=2,
|
||||
device_class=DEVICE_CLASS_CURRENT,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_POWER): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_WATT,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_POWER,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_ENERGY): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_WATT_HOURS,
|
||||
accuracy_decimals=3,
|
||||
device_class=DEVICE_CLASS_ENERGY,
|
||||
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||
),
|
||||
}
|
||||
)
|
||||
.extend(cv.polling_component_schema("60s"))
|
||||
.extend(uart.UART_DEVICE_SCHEMA)
|
||||
)
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(CSE7766Component),
|
||||
cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_VOLT,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_VOLTAGE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_CURRENT): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_AMPERE,
|
||||
accuracy_decimals=2,
|
||||
device_class=DEVICE_CLASS_CURRENT,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_POWER): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_WATT,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_POWER,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_ENERGY): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_WATT_HOURS,
|
||||
accuracy_decimals=3,
|
||||
device_class=DEVICE_CLASS_ENERGY,
|
||||
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||
),
|
||||
}
|
||||
).extend(uart.UART_DEVICE_SCHEMA)
|
||||
FINAL_VALIDATE_SCHEMA = uart.final_validate_device_schema(
|
||||
"cse7766", baud_rate=4800, require_rx=True
|
||||
)
|
||||
|
|
|
@ -217,8 +217,12 @@ bool HOT IRAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, bool r
|
|||
uint16_t raw_humidity = (uint16_t(data[0] & 0xFF) << 8) | (data[1] & 0xFF);
|
||||
uint16_t raw_temperature = (uint16_t(data[2] & 0xFF) << 8) | (data[3] & 0xFF);
|
||||
|
||||
if (this->model_ != DHT_MODEL_DHT22_TYPE2 && (raw_temperature & 0x8000) != 0)
|
||||
raw_temperature = ~(raw_temperature & 0x7FFF);
|
||||
if (raw_temperature & 0x8000) {
|
||||
if (!(raw_temperature & 0x4000))
|
||||
raw_temperature = ~(raw_temperature & 0x7FFF);
|
||||
} else if (raw_temperature & 0x800) {
|
||||
raw_temperature |= 0xf000;
|
||||
}
|
||||
|
||||
if (raw_temperature == 1 && raw_humidity == 10) {
|
||||
if (report_errors) {
|
||||
|
|
|
@ -145,7 +145,7 @@ async def display_page_show_to_code(config, action_id, template_arg, args):
|
|||
DisplayPageShowNextAction,
|
||||
maybe_simple_id(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.templatable(cv.use_id(DisplayBuffer)),
|
||||
cv.Required(CONF_ID): cv.templatable(cv.use_id(Display)),
|
||||
}
|
||||
),
|
||||
)
|
||||
|
@ -159,7 +159,7 @@ async def display_page_show_next_to_code(config, action_id, template_arg, args):
|
|||
DisplayPageShowPrevAction,
|
||||
maybe_simple_id(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.templatable(cv.use_id(DisplayBuffer)),
|
||||
cv.Required(CONF_ID): cv.templatable(cv.use_id(Display)),
|
||||
}
|
||||
),
|
||||
)
|
||||
|
@ -173,7 +173,7 @@ async def display_page_show_previous_to_code(config, action_id, template_arg, ar
|
|||
DisplayIsDisplayingPageCondition,
|
||||
cv.maybe_simple_value(
|
||||
{
|
||||
cv.GenerateID(CONF_ID): cv.use_id(DisplayBuffer),
|
||||
cv.GenerateID(CONF_ID): cv.use_id(Display),
|
||||
cv.Required(CONF_PAGE_ID): cv.use_id(DisplayPage),
|
||||
},
|
||||
key=CONF_PAGE_ID,
|
||||
|
|
|
@ -141,6 +141,122 @@ void Display::filled_circle(int center_x, int center_y, int radius, Color color)
|
|||
}
|
||||
} while (dx <= 0);
|
||||
}
|
||||
void HOT Display::triangle(int x1, int y1, int x2, int y2, int x3, int y3, Color color) {
|
||||
this->line(x1, y1, x2, y2);
|
||||
this->line(x1, y1, x3, y3);
|
||||
this->line(x2, y2, x3, y3);
|
||||
}
|
||||
void Display::sort_triangle_points_by_y_(int *x1, int *y1, int *x2, int *y2, int *x3, int *y3) {
|
||||
if (*y1 > *y2) {
|
||||
int x_temp = *x1, y_temp = *y1;
|
||||
*x1 = *x2, *y1 = *y2;
|
||||
*x2 = x_temp, *y2 = y_temp;
|
||||
}
|
||||
if (*y1 > *y3) {
|
||||
int x_temp = *x1, y_temp = *y1;
|
||||
*x1 = *x3, *y1 = *y3;
|
||||
*x3 = x_temp, *y3 = y_temp;
|
||||
}
|
||||
if (*y2 > *y3) {
|
||||
int x_temp = *x2, y_temp = *y2;
|
||||
*x2 = *x3, *y2 = *y3;
|
||||
*x3 = x_temp, *y3 = y_temp;
|
||||
}
|
||||
}
|
||||
void Display::filled_flat_side_triangle_(int x1, int y1, int x2, int y2, int x3, int y3, Color color) {
|
||||
// y2 must be equal to y3 (same horizontal line)
|
||||
|
||||
// Initialize Bresenham's algorithm for side 1
|
||||
int s1_current_x = x1;
|
||||
int s1_current_y = y1;
|
||||
bool s1_axis_swap = false;
|
||||
int s1_dx = abs(x2 - x1);
|
||||
int s1_dy = abs(y2 - y1);
|
||||
int s1_sign_x = ((x2 - x1) >= 0) ? 1 : -1;
|
||||
int s1_sign_y = ((y2 - y1) >= 0) ? 1 : -1;
|
||||
if (s1_dy > s1_dx) { // swap values
|
||||
int tmp = s1_dx;
|
||||
s1_dx = s1_dy;
|
||||
s1_dy = tmp;
|
||||
s1_axis_swap = true;
|
||||
}
|
||||
int s1_error = 2 * s1_dy - s1_dx;
|
||||
|
||||
// Initialize Bresenham's algorithm for side 2
|
||||
int s2_current_x = x1;
|
||||
int s2_current_y = y1;
|
||||
bool s2_axis_swap = false;
|
||||
int s2_dx = abs(x3 - x1);
|
||||
int s2_dy = abs(y3 - y1);
|
||||
int s2_sign_x = ((x3 - x1) >= 0) ? 1 : -1;
|
||||
int s2_sign_y = ((y3 - y1) >= 0) ? 1 : -1;
|
||||
if (s2_dy > s2_dx) { // swap values
|
||||
int tmp = s2_dx;
|
||||
s2_dx = s2_dy;
|
||||
s2_dy = tmp;
|
||||
s2_axis_swap = true;
|
||||
}
|
||||
int s2_error = 2 * s2_dy - s2_dx;
|
||||
|
||||
// Iterate on side 1 and allow side 2 to be processed to match the advance of the y-axis.
|
||||
for (int i = 0; i <= s1_dx; i++) {
|
||||
if (s1_current_x <= s2_current_x) {
|
||||
this->horizontal_line(s1_current_x, s1_current_y, s2_current_x - s1_current_x + 1, color);
|
||||
} else {
|
||||
this->horizontal_line(s2_current_x, s2_current_y, s1_current_x - s2_current_x + 1, color);
|
||||
}
|
||||
|
||||
// Bresenham's #1
|
||||
// Side 1 s1_current_x and s1_current_y calculation
|
||||
while (s1_error >= 0) {
|
||||
if (s1_axis_swap) {
|
||||
s1_current_x += s1_sign_x;
|
||||
} else {
|
||||
s1_current_y += s1_sign_y;
|
||||
}
|
||||
s1_error = s1_error - 2 * s1_dx;
|
||||
}
|
||||
if (s1_axis_swap) {
|
||||
s1_current_y += s1_sign_y;
|
||||
} else {
|
||||
s1_current_x += s1_sign_x;
|
||||
}
|
||||
s1_error = s1_error + 2 * s1_dy;
|
||||
|
||||
// Bresenham's #2
|
||||
// Side 2 s2_current_x and s2_current_y calculation
|
||||
while (s2_current_y != s1_current_y) {
|
||||
while (s2_error >= 0) {
|
||||
if (s2_axis_swap) {
|
||||
s2_current_x += s2_sign_x;
|
||||
} else {
|
||||
s2_current_y += s2_sign_y;
|
||||
}
|
||||
s2_error = s2_error - 2 * s2_dx;
|
||||
}
|
||||
if (s2_axis_swap) {
|
||||
s2_current_y += s2_sign_y;
|
||||
} else {
|
||||
s2_current_x += s2_sign_x;
|
||||
}
|
||||
s2_error = s2_error + 2 * s2_dy;
|
||||
}
|
||||
}
|
||||
}
|
||||
void Display::filled_triangle(int x1, int y1, int x2, int y2, int x3, int y3, Color color) {
|
||||
// Sort the three points by y-coordinate ascending, so [x1,y1] is the topmost point
|
||||
this->sort_triangle_points_by_y_(&x1, &y1, &x2, &y2, &x3, &y3);
|
||||
|
||||
if (y2 == y3) { // Check for special case of a bottom-flat triangle
|
||||
this->filled_flat_side_triangle_(x1, y1, x2, y2, x3, y3, color);
|
||||
} else if (y1 == y2) { // Check for special case of a top-flat triangle
|
||||
this->filled_flat_side_triangle_(x3, y3, x1, y1, x2, y2, color);
|
||||
} else { // General case: split the no-flat-side triangle in a top-flat triangle and bottom-flat triangle
|
||||
int x_temp = (int) (x1 + ((float) (y2 - y1) / (float) (y3 - y1)) * (x3 - x1)), y_temp = y2;
|
||||
this->filled_flat_side_triangle_(x1, y1, x2, y2, x_temp, y_temp, color);
|
||||
this->filled_flat_side_triangle_(x3, y3, x2, y2, x_temp, y_temp, color);
|
||||
}
|
||||
}
|
||||
|
||||
void Display::print(int x, int y, BaseFont *font, Color color, TextAlign align, const char *text) {
|
||||
int x_start, y_start;
|
||||
|
|
|
@ -242,6 +242,12 @@ class Display : public PollingComponent {
|
|||
/// Fill a circle centered around [center_x,center_y] with the radius radius with the given color.
|
||||
void filled_circle(int center_x, int center_y, int radius, Color color = COLOR_ON);
|
||||
|
||||
/// Draw the outline of a triangle contained between the points [x1,y1], [x2,y2] and [x3,y3] with the given color.
|
||||
void triangle(int x1, int y1, int x2, int y2, int x3, int y3, Color color = COLOR_ON);
|
||||
|
||||
/// Fill a triangle contained between the points [x1,y1], [x2,y2] and [x3,y3] with the given color.
|
||||
void filled_triangle(int x1, int y1, int x2, int y2, int x3, int y3, Color color = COLOR_ON);
|
||||
|
||||
/** Print `text` with the anchor point at [x,y] with `font`.
|
||||
*
|
||||
* @param x The x coordinate of the text alignment anchor point.
|
||||
|
@ -579,6 +585,15 @@ class Display : public PollingComponent {
|
|||
void do_update_();
|
||||
void clear_clipping_();
|
||||
|
||||
/**
|
||||
* This method fills a triangle using only integer variables by using a
|
||||
* modified bresenham algorithm.
|
||||
* It is mandatory that [x2,y2] and [x3,y3] lie on the same horizontal line,
|
||||
* so y2 must be equal to y3.
|
||||
*/
|
||||
void filled_flat_side_triangle_(int x1, int y1, int x2, int y2, int x3, int y3, Color color);
|
||||
void sort_triangle_points_by_y_(int *x1, int *y1, int *x2, int *y2, int *x3, int *y3);
|
||||
|
||||
DisplayRotation rotation_{DISPLAY_ROTATION_0_DEGREES};
|
||||
optional<display_writer_t> writer_{};
|
||||
DisplayPage *page_{nullptr};
|
||||
|
|
|
@ -13,6 +13,8 @@ namespace esp32_rmt_led_strip {
|
|||
|
||||
static const char *const TAG = "esp32_rmt_led_strip";
|
||||
|
||||
static const uint32_t RMT_CLK_FREQ = 80000000;
|
||||
|
||||
static const uint8_t RMT_CLK_DIV = 2;
|
||||
|
||||
void ESP32RMTLEDStripLightOutput::setup() {
|
||||
|
@ -65,7 +67,7 @@ void ESP32RMTLEDStripLightOutput::setup() {
|
|||
|
||||
void ESP32RMTLEDStripLightOutput::set_led_params(uint32_t bit0_high, uint32_t bit0_low, uint32_t bit1_high,
|
||||
uint32_t bit1_low) {
|
||||
float ratio = (float) APB_CLK_FREQ / RMT_CLK_DIV / 1e09f;
|
||||
float ratio = (float) RMT_CLK_FREQ / RMT_CLK_DIV / 1e09f;
|
||||
|
||||
// 0-bit
|
||||
this->bit0_.duration0 = (uint32_t) (ratio * bit0_high);
|
||||
|
|
|
@ -13,8 +13,10 @@ from esphome.const import (
|
|||
CONF_ON_ENROLLMENT_DONE,
|
||||
CONF_ON_ENROLLMENT_FAILED,
|
||||
CONF_ON_ENROLLMENT_SCAN,
|
||||
CONF_ON_FINGER_SCAN_START,
|
||||
CONF_ON_FINGER_SCAN_MATCHED,
|
||||
CONF_ON_FINGER_SCAN_UNMATCHED,
|
||||
CONF_ON_FINGER_SCAN_MISPLACED,
|
||||
CONF_ON_FINGER_SCAN_INVALID,
|
||||
CONF_PASSWORD,
|
||||
CONF_SENSING_PIN,
|
||||
|
@ -35,6 +37,10 @@ FingerprintGrowComponent = fingerprint_grow_ns.class_(
|
|||
"FingerprintGrowComponent", cg.PollingComponent, uart.UARTDevice
|
||||
)
|
||||
|
||||
FingerScanStartTrigger = fingerprint_grow_ns.class_(
|
||||
"FingerScanStartTrigger", automation.Trigger.template()
|
||||
)
|
||||
|
||||
FingerScanMatchedTrigger = fingerprint_grow_ns.class_(
|
||||
"FingerScanMatchedTrigger", automation.Trigger.template(cg.uint16, cg.uint16)
|
||||
)
|
||||
|
@ -43,6 +49,10 @@ FingerScanUnmatchedTrigger = fingerprint_grow_ns.class_(
|
|||
"FingerScanUnmatchedTrigger", automation.Trigger.template()
|
||||
)
|
||||
|
||||
FingerScanMisplacedTrigger = fingerprint_grow_ns.class_(
|
||||
"FingerScanMisplacedTrigger", automation.Trigger.template()
|
||||
)
|
||||
|
||||
FingerScanInvalidTrigger = fingerprint_grow_ns.class_(
|
||||
"FingerScanInvalidTrigger", automation.Trigger.template()
|
||||
)
|
||||
|
@ -99,6 +109,13 @@ CONFIG_SCHEMA = (
|
|||
cv.Optional(CONF_SENSING_PIN): pins.gpio_input_pin_schema,
|
||||
cv.Optional(CONF_PASSWORD): cv.uint32_t,
|
||||
cv.Optional(CONF_NEW_PASSWORD): cv.uint32_t,
|
||||
cv.Optional(CONF_ON_FINGER_SCAN_START): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||
FingerScanStartTrigger
|
||||
),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_FINGER_SCAN_MATCHED): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||
|
@ -113,6 +130,13 @@ CONFIG_SCHEMA = (
|
|||
),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_FINGER_SCAN_MISPLACED): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||
FingerScanMisplacedTrigger
|
||||
),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_FINGER_SCAN_INVALID): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||
|
@ -164,6 +188,10 @@ async def to_code(config):
|
|||
sensing_pin = await cg.gpio_pin_expression(config[CONF_SENSING_PIN])
|
||||
cg.add(var.set_sensing_pin(sensing_pin))
|
||||
|
||||
for conf in config.get(CONF_ON_FINGER_SCAN_START, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
|
||||
for conf in config.get(CONF_ON_FINGER_SCAN_MATCHED, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(
|
||||
|
@ -174,6 +202,10 @@ async def to_code(config):
|
|||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
|
||||
for conf in config.get(CONF_ON_FINGER_SCAN_MISPLACED, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
|
||||
for conf in config.get(CONF_ON_FINGER_SCAN_INVALID, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
|
|
|
@ -15,16 +15,18 @@ void FingerprintGrowComponent::update() {
|
|||
return;
|
||||
}
|
||||
|
||||
if (this->sensing_pin_ != nullptr) {
|
||||
if (this->has_sensing_pin_) {
|
||||
if (this->sensing_pin_->digital_read()) {
|
||||
ESP_LOGV(TAG, "No touch sensing");
|
||||
this->waiting_removal_ = false;
|
||||
return;
|
||||
} else if (!this->waiting_removal_) {
|
||||
this->finger_scan_start_callback_.call();
|
||||
}
|
||||
}
|
||||
|
||||
if (this->waiting_removal_) {
|
||||
if (this->scan_image_(1) == NO_FINGER) {
|
||||
if ((!this->has_sensing_pin_) && (this->scan_image_(1) == NO_FINGER)) {
|
||||
ESP_LOGD(TAG, "Finger removed");
|
||||
this->waiting_removal_ = false;
|
||||
}
|
||||
|
@ -51,6 +53,7 @@ void FingerprintGrowComponent::update() {
|
|||
|
||||
void FingerprintGrowComponent::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up Grow Fingerprint Reader...");
|
||||
this->has_sensing_pin_ = (this->sensing_pin_ != nullptr);
|
||||
if (this->check_password_()) {
|
||||
if (this->new_password_ != -1) {
|
||||
if (this->set_password_())
|
||||
|
@ -91,7 +94,7 @@ void FingerprintGrowComponent::finish_enrollment(uint8_t result) {
|
|||
}
|
||||
|
||||
void FingerprintGrowComponent::scan_and_match_() {
|
||||
if (this->sensing_pin_ != nullptr) {
|
||||
if (this->has_sensing_pin_) {
|
||||
ESP_LOGD(TAG, "Scan and match");
|
||||
} else {
|
||||
ESP_LOGV(TAG, "Scan and match");
|
||||
|
@ -122,33 +125,38 @@ void FingerprintGrowComponent::scan_and_match_() {
|
|||
}
|
||||
|
||||
uint8_t FingerprintGrowComponent::scan_image_(uint8_t buffer) {
|
||||
if (this->sensing_pin_ != nullptr) {
|
||||
if (this->has_sensing_pin_) {
|
||||
ESP_LOGD(TAG, "Getting image %d", buffer);
|
||||
} else {
|
||||
ESP_LOGV(TAG, "Getting image %d", buffer);
|
||||
}
|
||||
this->data_ = {GET_IMAGE};
|
||||
switch (this->send_command_()) {
|
||||
uint8_t send_result = this->send_command_();
|
||||
switch (send_result) {
|
||||
case OK:
|
||||
break;
|
||||
case NO_FINGER:
|
||||
if (this->sensing_pin_ != nullptr) {
|
||||
ESP_LOGD(TAG, "No finger");
|
||||
this->finger_scan_invalid_callback_.call();
|
||||
if (this->has_sensing_pin_) {
|
||||
this->waiting_removal_ = true;
|
||||
ESP_LOGD(TAG, "Finger Misplaced");
|
||||
this->finger_scan_misplaced_callback_.call();
|
||||
} else {
|
||||
ESP_LOGV(TAG, "No finger");
|
||||
}
|
||||
return this->data_[0];
|
||||
return send_result;
|
||||
case IMAGE_FAIL:
|
||||
ESP_LOGE(TAG, "Imaging error");
|
||||
this->finger_scan_invalid_callback_.call();
|
||||
return send_result;
|
||||
default:
|
||||
return this->data_[0];
|
||||
ESP_LOGD(TAG, "Unknown Scan Error: %d", send_result);
|
||||
return send_result;
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "Processing image %d", buffer);
|
||||
this->data_ = {IMAGE_2_TZ, buffer};
|
||||
switch (this->send_command_()) {
|
||||
send_result = this->send_command_();
|
||||
switch (send_result) {
|
||||
case OK:
|
||||
ESP_LOGI(TAG, "Processed image %d", buffer);
|
||||
break;
|
||||
|
@ -162,7 +170,7 @@ uint8_t FingerprintGrowComponent::scan_image_(uint8_t buffer) {
|
|||
this->finger_scan_invalid_callback_.call();
|
||||
break;
|
||||
}
|
||||
return this->data_[0];
|
||||
return send_result;
|
||||
}
|
||||
|
||||
uint8_t FingerprintGrowComponent::save_fingerprint_() {
|
||||
|
@ -225,10 +233,11 @@ bool FingerprintGrowComponent::get_parameters_() {
|
|||
ESP_LOGD(TAG, "Getting parameters");
|
||||
this->data_ = {READ_SYS_PARAM};
|
||||
if (this->send_command_() == OK) {
|
||||
ESP_LOGD(TAG, "Got parameters");
|
||||
if (this->status_sensor_ != nullptr) {
|
||||
ESP_LOGD(TAG, "Got parameters"); // Bear in mind data_[0] is the transfer status,
|
||||
if (this->status_sensor_ != nullptr) { // the parameters table start at data_[1]
|
||||
this->status_sensor_->publish_state(((uint16_t) this->data_[1] << 8) | this->data_[2]);
|
||||
}
|
||||
this->system_identifier_code_ = ((uint16_t) this->data_[3] << 8) | this->data_[4];
|
||||
this->capacity_ = ((uint16_t) this->data_[5] << 8) | this->data_[6];
|
||||
if (this->capacity_sensor_ != nullptr) {
|
||||
this->capacity_sensor_->publish_state(this->capacity_);
|
||||
|
@ -430,13 +439,22 @@ uint8_t FingerprintGrowComponent::send_command_() {
|
|||
|
||||
void FingerprintGrowComponent::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "GROW_FINGERPRINT_READER:");
|
||||
ESP_LOGCONFIG(TAG, " System Identifier Code: 0x%.4X", this->system_identifier_code_);
|
||||
ESP_LOGCONFIG(TAG, " Touch Sensing Pin: %s",
|
||||
this->has_sensing_pin_ ? this->sensing_pin_->dump_summary().c_str() : "None");
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
LOG_SENSOR(" ", "Fingerprint Count", this->fingerprint_count_sensor_);
|
||||
ESP_LOGCONFIG(TAG, " Current Value: %d", (uint16_t) this->fingerprint_count_sensor_->get_state());
|
||||
LOG_SENSOR(" ", "Status", this->status_sensor_);
|
||||
ESP_LOGCONFIG(TAG, " Current Value: %d", (uint8_t) this->status_sensor_->get_state());
|
||||
LOG_SENSOR(" ", "Capacity", this->capacity_sensor_);
|
||||
ESP_LOGCONFIG(TAG, " Current Value: %d", (uint16_t) this->capacity_sensor_->get_state());
|
||||
LOG_SENSOR(" ", "Security Level", this->security_level_sensor_);
|
||||
ESP_LOGCONFIG(TAG, " Current Value: %d", (uint8_t) this->security_level_sensor_->get_state());
|
||||
LOG_SENSOR(" ", "Last Finger ID", this->last_finger_id_sensor_);
|
||||
ESP_LOGCONFIG(TAG, " Current Value: %d", (uint32_t) this->last_finger_id_sensor_->get_state());
|
||||
LOG_SENSOR(" ", "Last Confidence", this->last_confidence_sensor_);
|
||||
ESP_LOGCONFIG(TAG, " Current Value: %d", (uint32_t) this->last_confidence_sensor_->get_state());
|
||||
}
|
||||
|
||||
} // namespace fingerprint_grow
|
||||
|
|
|
@ -118,12 +118,18 @@ class FingerprintGrowComponent : public PollingComponent, public uart::UARTDevic
|
|||
void set_enrolling_binary_sensor(binary_sensor::BinarySensor *enrolling_binary_sensor) {
|
||||
this->enrolling_binary_sensor_ = enrolling_binary_sensor;
|
||||
}
|
||||
void add_on_finger_scan_start_callback(std::function<void()> callback) {
|
||||
this->finger_scan_start_callback_.add(std::move(callback));
|
||||
}
|
||||
void add_on_finger_scan_matched_callback(std::function<void(uint16_t, uint16_t)> callback) {
|
||||
this->finger_scan_matched_callback_.add(std::move(callback));
|
||||
}
|
||||
void add_on_finger_scan_unmatched_callback(std::function<void()> callback) {
|
||||
this->finger_scan_unmatched_callback_.add(std::move(callback));
|
||||
}
|
||||
void add_on_finger_scan_misplaced_callback(std::function<void()> callback) {
|
||||
this->finger_scan_misplaced_callback_.add(std::move(callback));
|
||||
}
|
||||
void add_on_finger_scan_invalid_callback(std::function<void()> callback) {
|
||||
this->finger_scan_invalid_callback_.add(std::move(callback));
|
||||
}
|
||||
|
@ -166,8 +172,10 @@ class FingerprintGrowComponent : public PollingComponent, public uart::UARTDevic
|
|||
uint16_t enrollment_slot_ = ENROLLMENT_SLOT_UNUSED;
|
||||
uint8_t enrollment_buffers_ = 5;
|
||||
bool waiting_removal_ = false;
|
||||
bool has_sensing_pin_ = false;
|
||||
uint32_t last_aura_led_control_ = 0;
|
||||
uint16_t last_aura_led_duration_ = 0;
|
||||
uint16_t system_identifier_code_ = 0;
|
||||
sensor::Sensor *fingerprint_count_sensor_{nullptr};
|
||||
sensor::Sensor *status_sensor_{nullptr};
|
||||
sensor::Sensor *capacity_sensor_{nullptr};
|
||||
|
@ -176,13 +184,22 @@ class FingerprintGrowComponent : public PollingComponent, public uart::UARTDevic
|
|||
sensor::Sensor *last_confidence_sensor_{nullptr};
|
||||
binary_sensor::BinarySensor *enrolling_binary_sensor_{nullptr};
|
||||
CallbackManager<void()> finger_scan_invalid_callback_;
|
||||
CallbackManager<void()> finger_scan_start_callback_;
|
||||
CallbackManager<void(uint16_t, uint16_t)> finger_scan_matched_callback_;
|
||||
CallbackManager<void()> finger_scan_unmatched_callback_;
|
||||
CallbackManager<void()> finger_scan_misplaced_callback_;
|
||||
CallbackManager<void(uint8_t, uint16_t)> enrollment_scan_callback_;
|
||||
CallbackManager<void(uint16_t)> enrollment_done_callback_;
|
||||
CallbackManager<void(uint16_t)> enrollment_failed_callback_;
|
||||
};
|
||||
|
||||
class FingerScanStartTrigger : public Trigger<> {
|
||||
public:
|
||||
explicit FingerScanStartTrigger(FingerprintGrowComponent *parent) {
|
||||
parent->add_on_finger_scan_start_callback([this]() { this->trigger(); });
|
||||
}
|
||||
};
|
||||
|
||||
class FingerScanMatchedTrigger : public Trigger<uint16_t, uint16_t> {
|
||||
public:
|
||||
explicit FingerScanMatchedTrigger(FingerprintGrowComponent *parent) {
|
||||
|
@ -198,6 +215,13 @@ class FingerScanUnmatchedTrigger : public Trigger<> {
|
|||
}
|
||||
};
|
||||
|
||||
class FingerScanMisplacedTrigger : public Trigger<> {
|
||||
public:
|
||||
explicit FingerScanMisplacedTrigger(FingerprintGrowComponent *parent) {
|
||||
parent->add_on_finger_scan_misplaced_callback([this]() { this->trigger(); });
|
||||
}
|
||||
};
|
||||
|
||||
class FingerScanInvalidTrigger : public Trigger<> {
|
||||
public:
|
||||
explicit FingerScanInvalidTrigger(FingerprintGrowComponent *parent) {
|
||||
|
|
|
@ -67,13 +67,13 @@ def validate_pillow_installed(value):
|
|||
except ImportError as err:
|
||||
raise cv.Invalid(
|
||||
"Please install the pillow python package to use this feature. "
|
||||
'(pip install "pillow==10.1.0")'
|
||||
'(pip install "pillow==10.2.0")'
|
||||
) from err
|
||||
|
||||
if version.parse(PIL.__version__) != version.parse("10.1.0"):
|
||||
if version.parse(PIL.__version__) != version.parse("10.2.0"):
|
||||
raise cv.Invalid(
|
||||
"Please update your pillow installation to 10.1.0. "
|
||||
'(pip install "pillow==10.1.0")'
|
||||
"Please update your pillow installation to 10.2.0. "
|
||||
'(pip install "pillow==10.2.0")'
|
||||
)
|
||||
|
||||
return value
|
||||
|
|
|
@ -13,14 +13,14 @@
|
|||
namespace esphome {
|
||||
namespace ft63x6 {
|
||||
|
||||
static const uint8_t FT63X6_ADDR_TOUCH_COUNT = 0x02;
|
||||
|
||||
static const uint8_t FT63X6_ADDR_TOUCH1_ID = 0x05;
|
||||
static const uint8_t FT63X6_ADDR_TOUCH1_STATE = 0x03;
|
||||
static const uint8_t FT63X6_ADDR_TOUCH1_X = 0x03;
|
||||
static const uint8_t FT63X6_ADDR_TOUCH1_ID = 0x05;
|
||||
static const uint8_t FT63X6_ADDR_TOUCH1_Y = 0x05;
|
||||
|
||||
static const uint8_t FT63X6_ADDR_TOUCH2_ID = 0x0B;
|
||||
static const uint8_t FT63X6_ADDR_TOUCH2_STATE = 0x09;
|
||||
static const uint8_t FT63X6_ADDR_TOUCH2_X = 0x09;
|
||||
static const uint8_t FT63X6_ADDR_TOUCH2_ID = 0x0B;
|
||||
static const uint8_t FT63X6_ADDR_TOUCH2_Y = 0x0B;
|
||||
|
||||
static const char *const TAG = "FT63X6Touchscreen";
|
||||
|
@ -40,26 +40,11 @@ void FT63X6Touchscreen::setup() {
|
|||
this->hard_reset_();
|
||||
|
||||
// Get touch resolution
|
||||
this->x_raw_max_ = 320;
|
||||
this->y_raw_max_ = 480;
|
||||
}
|
||||
|
||||
void FT63X6Touchscreen::update_touches() {
|
||||
int touch_count = this->read_touch_count_();
|
||||
if (touch_count == 0) {
|
||||
return;
|
||||
if (this->x_raw_max_ == this->x_raw_min_) {
|
||||
this->x_raw_max_ = 320;
|
||||
}
|
||||
|
||||
uint8_t touch_id = this->read_touch_id_(FT63X6_ADDR_TOUCH1_ID); // id1 = 0 or 1
|
||||
int16_t x = this->read_touch_coordinate_(FT63X6_ADDR_TOUCH1_X);
|
||||
int16_t y = this->read_touch_coordinate_(FT63X6_ADDR_TOUCH1_Y);
|
||||
this->add_raw_touch_position_(touch_id, x, y);
|
||||
|
||||
if (touch_count >= 2) {
|
||||
touch_id = this->read_touch_id_(FT63X6_ADDR_TOUCH2_ID); // id2 = 0 or 1(~id1 & 0x01)
|
||||
x = this->read_touch_coordinate_(FT63X6_ADDR_TOUCH2_X);
|
||||
y = this->read_touch_coordinate_(FT63X6_ADDR_TOUCH2_Y);
|
||||
this->add_raw_touch_position_(touch_id, x, y);
|
||||
if (this->y_raw_max_ == this->y_raw_min_) {
|
||||
this->y_raw_max_ = 480;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -76,23 +61,31 @@ void FT63X6Touchscreen::dump_config() {
|
|||
LOG_I2C_DEVICE(this);
|
||||
LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_);
|
||||
LOG_PIN(" Reset Pin: ", this->reset_pin_);
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
}
|
||||
|
||||
uint8_t FT63X6Touchscreen::read_touch_count_() { return this->read_byte_(FT63X6_ADDR_TOUCH_COUNT); }
|
||||
void FT63X6Touchscreen::update_touches() {
|
||||
uint8_t data[15];
|
||||
uint16_t touch_id, x, y;
|
||||
|
||||
// Touch functions
|
||||
uint16_t FT63X6Touchscreen::read_touch_coordinate_(uint8_t coordinate) {
|
||||
uint8_t read_buf[2];
|
||||
read_buf[0] = this->read_byte_(coordinate);
|
||||
read_buf[1] = this->read_byte_(coordinate + 1);
|
||||
return ((read_buf[0] & 0x0f) << 8) | read_buf[1];
|
||||
}
|
||||
uint8_t FT63X6Touchscreen::read_touch_id_(uint8_t id_address) { return this->read_byte_(id_address) >> 4; }
|
||||
if (!this->read_bytes(0x00, (uint8_t *) data, 15)) {
|
||||
ESP_LOGE(TAG, "Failed to read touch data");
|
||||
this->skip_update_ = true;
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t FT63X6Touchscreen::read_byte_(uint8_t addr) {
|
||||
uint8_t byte = 0;
|
||||
this->read_byte(addr, &byte);
|
||||
return byte;
|
||||
if (((data[FT63X6_ADDR_TOUCH1_STATE] >> 6) & 0x01) == 0) {
|
||||
touch_id = data[FT63X6_ADDR_TOUCH1_ID] >> 4; // id1 = 0 or 1
|
||||
x = encode_uint16(data[FT63X6_ADDR_TOUCH1_X] & 0x0F, data[FT63X6_ADDR_TOUCH1_X + 1]);
|
||||
y = encode_uint16(data[FT63X6_ADDR_TOUCH1_Y] & 0x0F, data[FT63X6_ADDR_TOUCH1_Y + 1]);
|
||||
this->add_raw_touch_position_(touch_id, x, y);
|
||||
}
|
||||
if (((data[FT63X6_ADDR_TOUCH2_STATE] >> 6) & 0x01) == 0) {
|
||||
touch_id = data[FT63X6_ADDR_TOUCH2_ID] >> 4; // id1 = 0 or 1
|
||||
x = encode_uint16(data[FT63X6_ADDR_TOUCH2_X] & 0x0F, data[FT63X6_ADDR_TOUCH2_X + 1]);
|
||||
y = encode_uint16(data[FT63X6_ADDR_TOUCH2_Y] & 0x0F, data[FT63X6_ADDR_TOUCH2_Y + 1]);
|
||||
this->add_raw_touch_position_(touch_id, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ft63x6
|
||||
|
|
|
@ -61,6 +61,7 @@ VALUE_POSITION_TYPE = {
|
|||
"BELOW": ValuePositionType.VALUE_POSITION_TYPE_BELOW,
|
||||
}
|
||||
|
||||
CONF_CONTINUOUS = "continuous"
|
||||
|
||||
GRAPH_TRACE_SCHEMA = cv.Schema(
|
||||
{
|
||||
|
@ -70,6 +71,7 @@ GRAPH_TRACE_SCHEMA = cv.Schema(
|
|||
cv.Optional(CONF_LINE_THICKNESS): cv.positive_int,
|
||||
cv.Optional(CONF_LINE_TYPE): cv.enum(LINE_TYPE, upper=True),
|
||||
cv.Optional(CONF_COLOR): cv.use_id(color.ColorStruct),
|
||||
cv.Optional(CONF_CONTINUOUS): cv.boolean,
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -186,6 +188,8 @@ async def to_code(config):
|
|||
if CONF_COLOR in trace:
|
||||
c = await cg.get_variable(trace[CONF_COLOR])
|
||||
cg.add(tr.set_line_color(c))
|
||||
if CONF_CONTINUOUS in trace:
|
||||
cg.add(tr.set_continuous(trace[CONF_CONTINUOUS]))
|
||||
cg.add(var.add_trace(tr))
|
||||
# Add legend
|
||||
if CONF_LEGEND in config:
|
||||
|
|
|
@ -165,17 +165,42 @@ void Graph::draw(Display *buff, uint16_t x_offset, uint16_t y_offset, Color colo
|
|||
for (auto *trace : traces_) {
|
||||
Color c = trace->get_line_color();
|
||||
uint16_t thick = trace->get_line_thickness();
|
||||
bool continuous = trace->get_continuous();
|
||||
bool has_prev = false;
|
||||
bool prev_b = false;
|
||||
int16_t prev_y = 0;
|
||||
for (uint32_t i = 0; i < this->width_; i++) {
|
||||
float v = (trace->get_tracedata()->get_value(i) - ymin) / yrange;
|
||||
if (!std::isnan(v) && (thick > 0)) {
|
||||
int16_t x = this->width_ - 1 - i;
|
||||
uint8_t b = (i % (thick * LineType::PATTERN_LENGTH)) / thick;
|
||||
if (((uint8_t) trace->get_line_type() & (1 << b)) == (1 << b)) {
|
||||
int16_t y = (int16_t) roundf((this->height_ - 1) * (1.0 - v)) - thick / 2;
|
||||
for (uint16_t t = 0; t < thick; t++) {
|
||||
buff->draw_pixel_at(x_offset + x, y_offset + y + t, c);
|
||||
int16_t x = this->width_ - 1 - i + x_offset;
|
||||
uint8_t bit = 1 << ((i % (thick * LineType::PATTERN_LENGTH)) / thick);
|
||||
bool b = (trace->get_line_type() & bit) == bit;
|
||||
if (b) {
|
||||
int16_t y = (int16_t) roundf((this->height_ - 1) * (1.0 - v)) - thick / 2 + y_offset;
|
||||
if (!continuous || !has_prev || !prev_b || (abs(y - prev_y) <= thick)) {
|
||||
for (uint16_t t = 0; t < thick; t++) {
|
||||
buff->draw_pixel_at(x, y + t, c);
|
||||
}
|
||||
} else {
|
||||
int16_t mid_y = (y + prev_y + thick) / 2;
|
||||
if (y > prev_y) {
|
||||
for (uint16_t t = prev_y + thick; t <= mid_y; t++)
|
||||
buff->draw_pixel_at(x + 1, t, c);
|
||||
for (uint16_t t = mid_y + 1; t < y + thick; t++)
|
||||
buff->draw_pixel_at(x, t, c);
|
||||
} else {
|
||||
for (uint16_t t = prev_y - 1; t >= mid_y; t--)
|
||||
buff->draw_pixel_at(x + 1, t, c);
|
||||
for (uint16_t t = mid_y - 1; t >= y; t--)
|
||||
buff->draw_pixel_at(x, t, c);
|
||||
}
|
||||
}
|
||||
prev_y = y;
|
||||
}
|
||||
prev_b = b;
|
||||
has_prev = true;
|
||||
} else {
|
||||
has_prev = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -116,6 +116,8 @@ class GraphTrace {
|
|||
void set_line_type(enum LineType val) { this->line_type_ = val; }
|
||||
Color get_line_color() { return this->line_color_; }
|
||||
void set_line_color(Color val) { this->line_color_ = val; }
|
||||
bool get_continuous() { return this->continuous_; }
|
||||
void set_continuous(bool continuous) { this->continuous_ = continuous; }
|
||||
std::string get_name() { return name_; }
|
||||
const HistoryData *get_tracedata() { return &data_; }
|
||||
|
||||
|
@ -125,6 +127,7 @@ class GraphTrace {
|
|||
uint8_t line_thickness_{3};
|
||||
enum LineType line_type_ { LINE_TYPE_SOLID };
|
||||
Color line_color_{COLOR_ON};
|
||||
bool continuous_{false};
|
||||
HistoryData data_;
|
||||
|
||||
friend Graph;
|
||||
|
|
|
@ -52,7 +52,7 @@ optional<uint8_t> ImprovSerialComponent::read_byte_() {
|
|||
size_t available;
|
||||
uart_get_buffered_data_len(this->uart_num_, &available);
|
||||
if (available) {
|
||||
uart_read_bytes(this->uart_num_, &data, 1, 20 / portTICK_PERIOD_MS);
|
||||
uart_read_bytes(this->uart_num_, &data, 1, 0);
|
||||
byte = data;
|
||||
}
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ optional<uint8_t> ImprovSerialComponent::read_byte_() {
|
|||
#endif // USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3
|
||||
#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32S3)
|
||||
case logger::UART_SELECTION_USB_SERIAL_JTAG: {
|
||||
if (usb_serial_jtag_read_bytes((char *) &data, 1, 20 / portTICK_PERIOD_MS)) {
|
||||
if (usb_serial_jtag_read_bytes((char *) &data, 1, 0)) {
|
||||
byte = data;
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -55,6 +55,9 @@ void Inkplate6::setup() {
|
|||
this->wakeup_pin_->digital_write(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate buffers. May be called after setup to re-initialise if e.g. greyscale is changed.
|
||||
*/
|
||||
void Inkplate6::initialize_() {
|
||||
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
|
||||
ExternalRAMAllocator<uint32_t> allocator32(ExternalRAMAllocator<uint32_t>::ALLOW_FAILURE);
|
||||
|
|
|
@ -68,8 +68,9 @@ class Inkplate6 : public display::DisplayBuffer, public i2c::I2CDevice {
|
|||
|
||||
void set_greyscale(bool greyscale) {
|
||||
this->greyscale_ = greyscale;
|
||||
this->initialize_();
|
||||
this->block_partial_ = true;
|
||||
if (this->is_ready())
|
||||
this->initialize_();
|
||||
}
|
||||
void set_partial_updating(bool partial_updating) { this->partial_updating_ = partial_updating; }
|
||||
void set_full_update_every(uint32_t full_update_every) { this->full_update_every_ = full_update_every; }
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
from esphome import automation
|
||||
import esphome.codegen as cg
|
||||
|
||||
CODEOWNERS = ["@jesserockz"]
|
||||
CODEOWNERS = ["@jesserockz", "@kbx81"]
|
||||
|
||||
nfc_ns = cg.esphome_ns.namespace("nfc")
|
||||
|
||||
Nfcc = nfc_ns.class_("Nfcc")
|
||||
NfcTag = nfc_ns.class_("NfcTag")
|
||||
|
||||
NfcTagListener = nfc_ns.class_("NfcTagListener")
|
||||
NfcOnTagTrigger = nfc_ns.class_(
|
||||
"NfcOnTagTrigger", automation.Trigger.template(cg.std_string, NfcTag)
|
||||
)
|
||||
|
|
72
esphome/components/nfc/binary_sensor/__init__.py
Normal file
72
esphome/components/nfc/binary_sensor/__init__.py
Normal file
|
@ -0,0 +1,72 @@
|
|||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import binary_sensor
|
||||
from esphome.const import CONF_UID
|
||||
from esphome.core import HexInt
|
||||
from .. import nfc_ns, Nfcc, NfcTagListener
|
||||
|
||||
DEPENDENCIES = ["nfc"]
|
||||
|
||||
CONF_NDEF_CONTAINS = "ndef_contains"
|
||||
CONF_NFCC_ID = "nfcc_id"
|
||||
CONF_TAG_ID = "tag_id"
|
||||
|
||||
NfcTagBinarySensor = nfc_ns.class_(
|
||||
"NfcTagBinarySensor",
|
||||
binary_sensor.BinarySensor,
|
||||
cg.Component,
|
||||
NfcTagListener,
|
||||
cg.Parented.template(Nfcc),
|
||||
)
|
||||
|
||||
|
||||
def validate_uid(value):
|
||||
value = cv.string_strict(value)
|
||||
for x in value.split("-"):
|
||||
if len(x) != 2:
|
||||
raise cv.Invalid(
|
||||
"Each part (separated by '-') of the UID must be two characters "
|
||||
"long."
|
||||
)
|
||||
try:
|
||||
x = int(x, 16)
|
||||
except ValueError as err:
|
||||
raise cv.Invalid(
|
||||
"Valid characters for parts of a UID are 0123456789ABCDEF."
|
||||
) from err
|
||||
if x < 0 or x > 255:
|
||||
raise cv.Invalid(
|
||||
"Valid values for UID parts (separated by '-') are 00 to FF"
|
||||
)
|
||||
return value
|
||||
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
binary_sensor.binary_sensor_schema(NfcTagBinarySensor)
|
||||
.extend(
|
||||
{
|
||||
cv.GenerateID(CONF_NFCC_ID): cv.use_id(Nfcc),
|
||||
cv.Optional(CONF_NDEF_CONTAINS): cv.string,
|
||||
cv.Optional(CONF_TAG_ID): cv.string,
|
||||
cv.Optional(CONF_UID): validate_uid,
|
||||
}
|
||||
)
|
||||
.extend(cv.COMPONENT_SCHEMA),
|
||||
cv.has_exactly_one_key(CONF_NDEF_CONTAINS, CONF_TAG_ID, CONF_UID),
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = await binary_sensor.new_binary_sensor(config)
|
||||
await cg.register_component(var, config)
|
||||
await cg.register_parented(var, config[CONF_NFCC_ID])
|
||||
|
||||
hub = await cg.get_variable(config[CONF_NFCC_ID])
|
||||
cg.add(hub.register_listener(var))
|
||||
if CONF_NDEF_CONTAINS in config:
|
||||
cg.add(var.set_ndef_match_string(config[CONF_NDEF_CONTAINS]))
|
||||
if CONF_TAG_ID in config:
|
||||
cg.add(var.set_tag_name(config[CONF_TAG_ID]))
|
||||
elif CONF_UID in config:
|
||||
addr = [HexInt(int(x, 16)) for x in config[CONF_UID].split("-")]
|
||||
cg.add(var.set_uid(addr))
|
114
esphome/components/nfc/binary_sensor/binary_sensor.cpp
Normal file
114
esphome/components/nfc/binary_sensor/binary_sensor.cpp
Normal file
|
@ -0,0 +1,114 @@
|
|||
#include "binary_sensor.h"
|
||||
#include "../nfc_helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace nfc {
|
||||
|
||||
static const char *const TAG = "nfc.binary_sensor";
|
||||
|
||||
void NfcTagBinarySensor::setup() {
|
||||
this->parent_->register_listener(this);
|
||||
this->publish_initial_state(false);
|
||||
}
|
||||
|
||||
void NfcTagBinarySensor::dump_config() {
|
||||
std::string match_str = "name";
|
||||
|
||||
LOG_BINARY_SENSOR("", "NFC Tag Binary Sensor", this);
|
||||
if (!this->match_string_.empty()) {
|
||||
if (!this->match_tag_name_) {
|
||||
match_str = "contains";
|
||||
}
|
||||
ESP_LOGCONFIG(TAG, " Tag %s: %s", match_str.c_str(), this->match_string_.c_str());
|
||||
return;
|
||||
}
|
||||
if (!this->uid_.empty()) {
|
||||
ESP_LOGCONFIG(TAG, " Tag UID: %s", format_bytes(this->uid_).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void NfcTagBinarySensor::set_ndef_match_string(const std::string &str) {
|
||||
this->match_string_ = str;
|
||||
this->match_tag_name_ = false;
|
||||
}
|
||||
|
||||
void NfcTagBinarySensor::set_tag_name(const std::string &str) {
|
||||
this->match_string_ = str;
|
||||
this->match_tag_name_ = true;
|
||||
}
|
||||
|
||||
void NfcTagBinarySensor::set_uid(const std::vector<uint8_t> &uid) { this->uid_ = uid; }
|
||||
|
||||
bool NfcTagBinarySensor::tag_match_ndef_string(const std::shared_ptr<NdefMessage> &msg) {
|
||||
for (const auto &record : msg->get_records()) {
|
||||
if (record->get_payload().find(this->match_string_) != std::string::npos) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NfcTagBinarySensor::tag_match_tag_name(const std::shared_ptr<NdefMessage> &msg) {
|
||||
for (const auto &record : msg->get_records()) {
|
||||
if (record->get_payload().find(HA_TAG_ID_PREFIX) != std::string::npos) {
|
||||
auto rec_substr = record->get_payload().substr(sizeof(HA_TAG_ID_PREFIX) - 1);
|
||||
if (rec_substr.find(this->match_string_) != std::string::npos) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NfcTagBinarySensor::tag_match_uid(const std::vector<uint8_t> &data) {
|
||||
if (data.size() != this->uid_.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < data.size(); i++) {
|
||||
if (data[i] != this->uid_[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void NfcTagBinarySensor::tag_off(NfcTag &tag) {
|
||||
if (!this->match_string_.empty() && tag.has_ndef_message()) {
|
||||
if (this->match_tag_name_) {
|
||||
if (this->tag_match_tag_name(tag.get_ndef_message())) {
|
||||
this->publish_state(false);
|
||||
}
|
||||
} else {
|
||||
if (this->tag_match_ndef_string(tag.get_ndef_message())) {
|
||||
this->publish_state(false);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!this->uid_.empty() && this->tag_match_uid(tag.get_uid())) {
|
||||
this->publish_state(false);
|
||||
}
|
||||
}
|
||||
|
||||
void NfcTagBinarySensor::tag_on(NfcTag &tag) {
|
||||
if (!this->match_string_.empty() && tag.has_ndef_message()) {
|
||||
if (this->match_tag_name_) {
|
||||
if (this->tag_match_tag_name(tag.get_ndef_message())) {
|
||||
this->publish_state(true);
|
||||
}
|
||||
} else {
|
||||
if (this->tag_match_ndef_string(tag.get_ndef_message())) {
|
||||
this->publish_state(true);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!this->uid_.empty() && this->tag_match_uid(tag.get_uid())) {
|
||||
this->publish_state(true);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace nfc
|
||||
} // namespace esphome
|
38
esphome/components/nfc/binary_sensor/binary_sensor.h
Normal file
38
esphome/components/nfc/binary_sensor/binary_sensor.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
#pragma once
|
||||
|
||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||
#include "esphome/components/nfc/nfc.h"
|
||||
#include "esphome/components/nfc/nfc_tag.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace nfc {
|
||||
|
||||
class NfcTagBinarySensor : public binary_sensor::BinarySensor,
|
||||
public Component,
|
||||
public NfcTagListener,
|
||||
public Parented<Nfcc> {
|
||||
public:
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
|
||||
void set_ndef_match_string(const std::string &str);
|
||||
void set_tag_name(const std::string &str);
|
||||
void set_uid(const std::vector<uint8_t> &uid);
|
||||
|
||||
bool tag_match_ndef_string(const std::shared_ptr<NdefMessage> &msg);
|
||||
bool tag_match_tag_name(const std::shared_ptr<NdefMessage> &msg);
|
||||
bool tag_match_uid(const std::vector<uint8_t> &data);
|
||||
|
||||
void tag_off(NfcTag &tag) override;
|
||||
void tag_on(NfcTag &tag) override;
|
||||
|
||||
protected:
|
||||
bool match_tag_name_{false};
|
||||
std::string match_string_;
|
||||
std::vector<uint8_t> uid_;
|
||||
};
|
||||
|
||||
} // namespace nfc
|
||||
} // namespace esphome
|
|
@ -66,5 +66,19 @@ bool mifare_classic_is_trailer_block(uint8_t block_num);
|
|||
|
||||
uint32_t get_mifare_ultralight_buffer_size(uint32_t message_length);
|
||||
|
||||
class NfcTagListener {
|
||||
public:
|
||||
virtual void tag_off(NfcTag &tag) {}
|
||||
virtual void tag_on(NfcTag &tag) {}
|
||||
};
|
||||
|
||||
class Nfcc {
|
||||
public:
|
||||
void register_listener(NfcTagListener *listener) { this->tag_listeners_.push_back(listener); }
|
||||
|
||||
protected:
|
||||
std::vector<NfcTagListener *> tag_listeners_;
|
||||
};
|
||||
|
||||
} // namespace nfc
|
||||
} // namespace esphome
|
||||
|
|
|
@ -195,7 +195,7 @@ void PMSX003Component::send_command_(uint8_t cmd, uint16_t data) {
|
|||
void PMSX003Component::parse_data_() {
|
||||
switch (this->type_) {
|
||||
case PMSX003_TYPE_5003ST: {
|
||||
float temperature = this->get_16_bit_uint_(30) / 10.0f;
|
||||
float temperature = (int16_t) this->get_16_bit_uint_(30) / 10.0f;
|
||||
float humidity = this->get_16_bit_uint_(32) / 10.0f;
|
||||
|
||||
ESP_LOGD(TAG, "Got Temperature: %.1f°C, Humidity: %.1f%%", temperature, humidity);
|
||||
|
@ -279,7 +279,7 @@ void PMSX003Component::parse_data_() {
|
|||
// Note the pm particles 50um & 100um are not returned,
|
||||
// as PMS5003T uses those data values for temperature and humidity.
|
||||
|
||||
float temperature = this->get_16_bit_uint_(24) / 10.0f;
|
||||
float temperature = (int16_t) this->get_16_bit_uint_(24) / 10.0f;
|
||||
float humidity = this->get_16_bit_uint_(26) / 10.0f;
|
||||
|
||||
ESP_LOGD(TAG,
|
||||
|
|
|
@ -34,7 +34,7 @@ CONF_TAG_TTL = "tag_ttl"
|
|||
CONF_VEN_PIN = "ven_pin"
|
||||
|
||||
pn7150_ns = cg.esphome_ns.namespace("pn7150")
|
||||
PN7150 = pn7150_ns.class_("PN7150", cg.Component)
|
||||
PN7150 = pn7150_ns.class_("PN7150", nfc.Nfcc, cg.Component)
|
||||
|
||||
EmulationOffAction = pn7150_ns.class_("EmulationOffAction", automation.Action)
|
||||
EmulationOnAction = pn7150_ns.class_("EmulationOnAction", automation.Action)
|
||||
|
|
|
@ -566,6 +566,9 @@ void PN7150::erase_tag_(const uint8_t tag_index) {
|
|||
for (auto *trigger : this->triggers_ontagremoved_) {
|
||||
trigger->process(this->discovered_endpoint_[tag_index].tag);
|
||||
}
|
||||
for (auto *listener : this->tag_listeners_) {
|
||||
listener->tag_off(*this->discovered_endpoint_[tag_index].tag);
|
||||
}
|
||||
ESP_LOGI(TAG, "Tag %s removed", nfc::format_uid(this->discovered_endpoint_[tag_index].tag->get_uid()).c_str());
|
||||
this->discovered_endpoint_.erase(this->discovered_endpoint_.begin() + tag_index);
|
||||
}
|
||||
|
@ -881,6 +884,9 @@ void PN7150::process_rf_intf_activated_oid_(nfc::NciMessage &rx) { // an endpoi
|
|||
for (auto *trigger : this->triggers_ontag_) {
|
||||
trigger->process(working_endpoint.tag);
|
||||
}
|
||||
for (auto *listener : this->tag_listeners_) {
|
||||
listener->tag_on(*working_endpoint.tag);
|
||||
}
|
||||
working_endpoint.trig_called = true;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -142,7 +142,7 @@ struct DiscoveredEndpoint {
|
|||
bool trig_called;
|
||||
};
|
||||
|
||||
class PN7150 : public Component {
|
||||
class PN7150 : public nfc::Nfcc, public Component {
|
||||
public:
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
|
|
|
@ -36,7 +36,7 @@ CONF_VEN_PIN = "ven_pin"
|
|||
CONF_WKUP_REQ_PIN = "wkup_req_pin"
|
||||
|
||||
pn7160_ns = cg.esphome_ns.namespace("pn7160")
|
||||
PN7160 = pn7160_ns.class_("PN7160", cg.Component)
|
||||
PN7160 = pn7160_ns.class_("PN7160", nfc.Nfcc, cg.Component)
|
||||
|
||||
EmulationOffAction = pn7160_ns.class_("EmulationOffAction", automation.Action)
|
||||
EmulationOnAction = pn7160_ns.class_("EmulationOnAction", automation.Action)
|
||||
|
|
|
@ -591,6 +591,9 @@ void PN7160::erase_tag_(const uint8_t tag_index) {
|
|||
for (auto *trigger : this->triggers_ontagremoved_) {
|
||||
trigger->process(this->discovered_endpoint_[tag_index].tag);
|
||||
}
|
||||
for (auto *listener : this->tag_listeners_) {
|
||||
listener->tag_off(*this->discovered_endpoint_[tag_index].tag);
|
||||
}
|
||||
ESP_LOGI(TAG, "Tag %s removed", nfc::format_uid(this->discovered_endpoint_[tag_index].tag->get_uid()).c_str());
|
||||
this->discovered_endpoint_.erase(this->discovered_endpoint_.begin() + tag_index);
|
||||
}
|
||||
|
@ -905,6 +908,9 @@ void PN7160::process_rf_intf_activated_oid_(nfc::NciMessage &rx) { // an endpoi
|
|||
for (auto *trigger : this->triggers_ontag_) {
|
||||
trigger->process(working_endpoint.tag);
|
||||
}
|
||||
for (auto *listener : this->tag_listeners_) {
|
||||
listener->tag_on(*working_endpoint.tag);
|
||||
}
|
||||
working_endpoint.trig_called = true;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -157,7 +157,7 @@ struct DiscoveredEndpoint {
|
|||
bool trig_called;
|
||||
};
|
||||
|
||||
class PN7160 : public Component {
|
||||
class PN7160 : public nfc::Nfcc, public Component {
|
||||
public:
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
|
|
|
@ -352,7 +352,7 @@ void SEN5XComponent::update() {
|
|||
float humidity = measurements[4] / 100.0;
|
||||
if (measurements[4] == 0xFFFF)
|
||||
humidity = NAN;
|
||||
float temperature = measurements[5] / 200.0;
|
||||
float temperature = (int16_t) measurements[5] / 200.0;
|
||||
if (measurements[5] == 0xFFFF)
|
||||
temperature = NAN;
|
||||
float voc = measurements[6] / 10.0;
|
||||
|
|
|
@ -86,6 +86,13 @@ class BSDSocketImpl : public Socket {
|
|||
}
|
||||
int listen(int backlog) override { return ::listen(fd_, backlog); }
|
||||
ssize_t read(void *buf, size_t len) override { return ::read(fd_, buf, len); }
|
||||
ssize_t recvfrom(void *buf, size_t len, sockaddr *addr, socklen_t *addr_len) override {
|
||||
#if defined(USE_ESP32)
|
||||
return ::recvfrom(this->fd_, buf, len, 0, addr, addr_len);
|
||||
#else
|
||||
return ::lwip_recvfrom(this->fd_, buf, len, 0, addr, addr_len);
|
||||
#endif
|
||||
}
|
||||
ssize_t readv(const struct iovec *iov, int iovcnt) override {
|
||||
#if defined(USE_ESP32)
|
||||
return ::lwip_readv(fd_, iov, iovcnt);
|
||||
|
|
|
@ -31,6 +31,9 @@ class Socket {
|
|||
virtual int setsockopt(int level, int optname, const void *optval, socklen_t optlen) = 0;
|
||||
virtual int listen(int backlog) = 0;
|
||||
virtual ssize_t read(void *buf, size_t len) = 0;
|
||||
#ifdef USE_SOCKET_IMPL_BSD_SOCKETS
|
||||
virtual ssize_t recvfrom(void *buf, size_t len, sockaddr *addr, socklen_t *addr_len) = 0;
|
||||
#endif
|
||||
virtual ssize_t readv(const struct iovec *iov, int iovcnt) = 0;
|
||||
virtual ssize_t write(const void *buf, size_t len) = 0;
|
||||
virtual ssize_t writev(const struct iovec *iov, int iovcnt) = 0;
|
||||
|
|
0
esphome/components/veml3235/__init__.py
Normal file
0
esphome/components/veml3235/__init__.py
Normal file
84
esphome/components/veml3235/sensor.py
Normal file
84
esphome/components/veml3235/sensor.py
Normal file
|
@ -0,0 +1,84 @@
|
|||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import i2c, sensor
|
||||
from esphome.const import (
|
||||
CONF_GAIN,
|
||||
CONF_INTEGRATION_TIME,
|
||||
DEVICE_CLASS_ILLUMINANCE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_LUX,
|
||||
)
|
||||
|
||||
CODEOWNERS = ["@kbx81"]
|
||||
DEPENDENCIES = ["i2c"]
|
||||
|
||||
CONF_AUTO_GAIN = "auto_gain"
|
||||
CONF_AUTO_GAIN_THRESHOLD_HIGH = "auto_gain_threshold_high"
|
||||
CONF_AUTO_GAIN_THRESHOLD_LOW = "auto_gain_threshold_low"
|
||||
CONF_DIGITAL_GAIN = "digital_gain"
|
||||
|
||||
veml3235_ns = cg.esphome_ns.namespace("veml3235")
|
||||
|
||||
VEML3235Sensor = veml3235_ns.class_(
|
||||
"VEML3235Sensor", sensor.Sensor, cg.PollingComponent, i2c.I2CDevice
|
||||
)
|
||||
VEML3235IntegrationTime = veml3235_ns.enum("VEML3235IntegrationTime")
|
||||
VEML3235_INTEGRATION_TIMES = {
|
||||
"50ms": VEML3235IntegrationTime.VEML3235_INTEGRATION_TIME_50MS,
|
||||
"100ms": VEML3235IntegrationTime.VEML3235_INTEGRATION_TIME_100MS,
|
||||
"200ms": VEML3235IntegrationTime.VEML3235_INTEGRATION_TIME_200MS,
|
||||
"400ms": VEML3235IntegrationTime.VEML3235_INTEGRATION_TIME_400MS,
|
||||
"800ms": VEML3235IntegrationTime.VEML3235_INTEGRATION_TIME_800MS,
|
||||
}
|
||||
VEML3235ComponentDigitalGain = veml3235_ns.enum("VEML3235ComponentDigitalGain")
|
||||
DIGITAL_GAINS = {
|
||||
"1X": VEML3235ComponentDigitalGain.VEML3235_DIGITAL_GAIN_1X,
|
||||
"2X": VEML3235ComponentDigitalGain.VEML3235_DIGITAL_GAIN_2X,
|
||||
}
|
||||
VEML3235ComponentGain = veml3235_ns.enum("VEML3235ComponentGain")
|
||||
GAINS = {
|
||||
"1X": VEML3235ComponentGain.VEML3235_GAIN_1X,
|
||||
"2X": VEML3235ComponentGain.VEML3235_GAIN_2X,
|
||||
"4X": VEML3235ComponentGain.VEML3235_GAIN_4X,
|
||||
"AUTO": VEML3235ComponentGain.VEML3235_GAIN_AUTO,
|
||||
}
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
sensor.sensor_schema(
|
||||
VEML3235Sensor,
|
||||
unit_of_measurement=UNIT_LUX,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_ILLUMINANCE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
.extend(
|
||||
{
|
||||
cv.Optional(CONF_DIGITAL_GAIN, default="1X"): cv.enum(
|
||||
DIGITAL_GAINS, upper=True
|
||||
),
|
||||
cv.Optional(CONF_AUTO_GAIN, default=True): cv.boolean,
|
||||
cv.Optional(CONF_AUTO_GAIN_THRESHOLD_HIGH, default="90%"): cv.percentage,
|
||||
cv.Optional(CONF_AUTO_GAIN_THRESHOLD_LOW, default="20%"): cv.percentage,
|
||||
cv.Optional(CONF_GAIN, default="1X"): cv.enum(GAINS, upper=True),
|
||||
cv.Optional(CONF_INTEGRATION_TIME, default="50ms"): cv.All(
|
||||
cv.positive_time_period_milliseconds,
|
||||
cv.enum(VEML3235_INTEGRATION_TIMES, lower=True),
|
||||
),
|
||||
}
|
||||
)
|
||||
.extend(cv.polling_component_schema("60s"))
|
||||
.extend(i2c.i2c_device_schema(0x10))
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = await sensor.new_sensor(config)
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
|
||||
cg.add(var.set_auto_gain(config[CONF_AUTO_GAIN]))
|
||||
cg.add(var.set_auto_gain_threshold_high(config[CONF_AUTO_GAIN_THRESHOLD_HIGH]))
|
||||
cg.add(var.set_auto_gain_threshold_low(config[CONF_AUTO_GAIN_THRESHOLD_LOW]))
|
||||
cg.add(var.set_digital_gain(DIGITAL_GAINS[config[CONF_DIGITAL_GAIN]]))
|
||||
cg.add(var.set_gain(GAINS[config[CONF_GAIN]]))
|
||||
cg.add(var.set_integration_time(config[CONF_INTEGRATION_TIME]))
|
230
esphome/components/veml3235/veml3235.cpp
Normal file
230
esphome/components/veml3235/veml3235.cpp
Normal file
|
@ -0,0 +1,230 @@
|
|||
#include "veml3235.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace veml3235 {
|
||||
|
||||
static const char *const TAG = "veml3235.sensor";
|
||||
|
||||
void VEML3235Sensor::setup() {
|
||||
uint8_t device_id[] = {0, 0};
|
||||
|
||||
ESP_LOGCONFIG(TAG, "Setting up VEML3235 '%s'...", this->name_.c_str());
|
||||
|
||||
if (!this->refresh_config_reg()) {
|
||||
ESP_LOGE(TAG, "Unable to write configuration");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
if ((this->write(&ID_REG, 1, false) != i2c::ERROR_OK) || !this->read_bytes_raw(device_id, 2)) {
|
||||
ESP_LOGE(TAG, "Unable to read ID");
|
||||
this->mark_failed();
|
||||
return;
|
||||
} else if (device_id[0] != DEVICE_ID) {
|
||||
ESP_LOGE(TAG, "Incorrect device ID - expected 0x%.2x, read 0x%.2x", DEVICE_ID, device_id[0]);
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool VEML3235Sensor::refresh_config_reg(bool force_on) {
|
||||
uint16_t data = this->power_on_ || force_on ? 0 : SHUTDOWN_BITS;
|
||||
|
||||
data |= (uint16_t(this->integration_time_ << CONFIG_REG_IT_BIT));
|
||||
data |= (uint16_t(this->digital_gain_ << CONFIG_REG_DG_BIT));
|
||||
data |= (uint16_t(this->gain_ << CONFIG_REG_G_BIT));
|
||||
data |= 0x1; // mandatory 1 here per RM
|
||||
|
||||
ESP_LOGVV(TAG, "Writing 0x%.4x to register 0x%.2x", data, CONFIG_REG);
|
||||
return this->write_byte_16(CONFIG_REG, data);
|
||||
}
|
||||
|
||||
float VEML3235Sensor::read_lx_() {
|
||||
if (!this->power_on_) { // if off, turn on
|
||||
if (!this->refresh_config_reg(true)) {
|
||||
ESP_LOGW(TAG, "Turning on failed");
|
||||
this->status_set_warning();
|
||||
return NAN;
|
||||
}
|
||||
delay(4); // from RM: a wait time of 4 ms should be observed before the first measurement is picked up, to allow
|
||||
// for a correct start of the signal processor and oscillator
|
||||
}
|
||||
|
||||
uint8_t als_regs[] = {0, 0};
|
||||
if ((this->write(&ALS_REG, 1, false) != i2c::ERROR_OK) || !this->read_bytes_raw(als_regs, 2)) {
|
||||
this->status_set_warning();
|
||||
return NAN;
|
||||
}
|
||||
|
||||
this->status_clear_warning();
|
||||
|
||||
float als_raw_value_multiplier = LUX_MULTIPLIER_BASE;
|
||||
uint16_t als_raw_value = encode_uint16(als_regs[1], als_regs[0]);
|
||||
// determine multiplier value based on gains and integration time
|
||||
if (this->digital_gain_ == VEML3235_DIGITAL_GAIN_1X) {
|
||||
als_raw_value_multiplier *= 2;
|
||||
}
|
||||
switch (this->gain_) {
|
||||
case VEML3235_GAIN_1X:
|
||||
als_raw_value_multiplier *= 4;
|
||||
break;
|
||||
case VEML3235_GAIN_2X:
|
||||
als_raw_value_multiplier *= 2;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
switch (this->integration_time_) {
|
||||
case VEML3235_INTEGRATION_TIME_50MS:
|
||||
als_raw_value_multiplier *= 16;
|
||||
break;
|
||||
case VEML3235_INTEGRATION_TIME_100MS:
|
||||
als_raw_value_multiplier *= 8;
|
||||
break;
|
||||
case VEML3235_INTEGRATION_TIME_200MS:
|
||||
als_raw_value_multiplier *= 4;
|
||||
break;
|
||||
case VEML3235_INTEGRATION_TIME_400MS:
|
||||
als_raw_value_multiplier *= 2;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// finally, determine and return the actual lux value
|
||||
float lx = float(als_raw_value) * als_raw_value_multiplier;
|
||||
ESP_LOGVV(TAG, "'%s': ALS raw = %u, multiplier = %.5f", this->get_name().c_str(), als_raw_value,
|
||||
als_raw_value_multiplier);
|
||||
ESP_LOGD(TAG, "'%s': Illuminance = %.4flx", this->get_name().c_str(), lx);
|
||||
|
||||
if (!this->power_on_) { // turn off if required
|
||||
if (!this->refresh_config_reg()) {
|
||||
ESP_LOGW(TAG, "Turning off failed");
|
||||
this->status_set_warning();
|
||||
}
|
||||
}
|
||||
|
||||
if (this->auto_gain_) {
|
||||
this->adjust_gain_(als_raw_value);
|
||||
}
|
||||
|
||||
return lx;
|
||||
}
|
||||
|
||||
void VEML3235Sensor::adjust_gain_(const uint16_t als_raw_value) {
|
||||
if ((als_raw_value > UINT16_MAX * this->auto_gain_threshold_low_) &&
|
||||
(als_raw_value < UINT16_MAX * this->auto_gain_threshold_high_)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (als_raw_value >= UINT16_MAX * 0.9) { // over-saturated, reset all gains and start over
|
||||
this->digital_gain_ = VEML3235_DIGITAL_GAIN_1X;
|
||||
this->gain_ = VEML3235_GAIN_1X;
|
||||
this->integration_time_ = VEML3235_INTEGRATION_TIME_50MS;
|
||||
this->refresh_config_reg();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->gain_ != VEML3235_GAIN_4X) { // increase gain if possible
|
||||
switch (this->gain_) {
|
||||
case VEML3235_GAIN_1X:
|
||||
this->gain_ = VEML3235_GAIN_2X;
|
||||
break;
|
||||
case VEML3235_GAIN_2X:
|
||||
this->gain_ = VEML3235_GAIN_4X;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
this->refresh_config_reg();
|
||||
return;
|
||||
}
|
||||
// gain is maxed out; reset it and try to increase digital gain
|
||||
if (this->digital_gain_ != VEML3235_DIGITAL_GAIN_2X) { // increase digital gain if possible
|
||||
this->digital_gain_ = VEML3235_DIGITAL_GAIN_2X;
|
||||
this->gain_ = VEML3235_GAIN_1X;
|
||||
this->refresh_config_reg();
|
||||
return;
|
||||
}
|
||||
// digital gain is maxed out; reset it and try to increase integration time
|
||||
if (this->integration_time_ != VEML3235_INTEGRATION_TIME_800MS) { // increase integration time if possible
|
||||
switch (this->integration_time_) {
|
||||
case VEML3235_INTEGRATION_TIME_50MS:
|
||||
this->integration_time_ = VEML3235_INTEGRATION_TIME_100MS;
|
||||
break;
|
||||
case VEML3235_INTEGRATION_TIME_100MS:
|
||||
this->integration_time_ = VEML3235_INTEGRATION_TIME_200MS;
|
||||
break;
|
||||
case VEML3235_INTEGRATION_TIME_200MS:
|
||||
this->integration_time_ = VEML3235_INTEGRATION_TIME_400MS;
|
||||
break;
|
||||
case VEML3235_INTEGRATION_TIME_400MS:
|
||||
this->integration_time_ = VEML3235_INTEGRATION_TIME_800MS;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
this->digital_gain_ = VEML3235_DIGITAL_GAIN_1X;
|
||||
this->gain_ = VEML3235_GAIN_1X;
|
||||
this->refresh_config_reg();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void VEML3235Sensor::dump_config() {
|
||||
uint8_t digital_gain = 1;
|
||||
uint8_t gain = 1;
|
||||
uint16_t integration_time = 0;
|
||||
|
||||
if (this->digital_gain_ == VEML3235_DIGITAL_GAIN_2X) {
|
||||
digital_gain = 2;
|
||||
}
|
||||
switch (this->gain_) {
|
||||
case VEML3235_GAIN_2X:
|
||||
gain = 2;
|
||||
break;
|
||||
case VEML3235_GAIN_4X:
|
||||
gain = 4;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
switch (this->integration_time_) {
|
||||
case VEML3235_INTEGRATION_TIME_50MS:
|
||||
integration_time = 50;
|
||||
break;
|
||||
case VEML3235_INTEGRATION_TIME_100MS:
|
||||
integration_time = 100;
|
||||
break;
|
||||
case VEML3235_INTEGRATION_TIME_200MS:
|
||||
integration_time = 200;
|
||||
break;
|
||||
case VEML3235_INTEGRATION_TIME_400MS:
|
||||
integration_time = 400;
|
||||
break;
|
||||
case VEML3235_INTEGRATION_TIME_800MS:
|
||||
integration_time = 800;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
LOG_SENSOR("", "VEML3235", this);
|
||||
LOG_I2C_DEVICE(this);
|
||||
if (this->is_failed()) {
|
||||
ESP_LOGE(TAG, "Communication failed");
|
||||
}
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
ESP_LOGCONFIG(TAG, " Auto-gain enabled: %s", YESNO(this->auto_gain_));
|
||||
if (this->auto_gain_) {
|
||||
ESP_LOGCONFIG(TAG, " Auto-gain upper threshold: %f%%", this->auto_gain_threshold_high_ * 100.0);
|
||||
ESP_LOGCONFIG(TAG, " Auto-gain lower threshold: %f%%", this->auto_gain_threshold_low_ * 100.0);
|
||||
ESP_LOGCONFIG(TAG, " Values below will be used as initial values only");
|
||||
}
|
||||
ESP_LOGCONFIG(TAG, " Digital gain: %uX", digital_gain);
|
||||
ESP_LOGCONFIG(TAG, " Gain: %uX", gain);
|
||||
ESP_LOGCONFIG(TAG, " Integration time: %ums", integration_time);
|
||||
}
|
||||
|
||||
} // namespace veml3235
|
||||
} // namespace esphome
|
109
esphome/components/veml3235/veml3235.h
Normal file
109
esphome/components/veml3235/veml3235.h
Normal file
|
@ -0,0 +1,109 @@
|
|||
#pragma once
|
||||
|
||||
#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 veml3235 {
|
||||
|
||||
// Register IDs/locations
|
||||
//
|
||||
static const uint8_t CONFIG_REG = 0x00;
|
||||
static const uint8_t W_REG = 0x04;
|
||||
static const uint8_t ALS_REG = 0x05;
|
||||
static const uint8_t ID_REG = 0x09;
|
||||
|
||||
// Bit offsets within CONFIG_REG
|
||||
//
|
||||
static const uint8_t CONFIG_REG_IT_BIT = 12;
|
||||
static const uint8_t CONFIG_REG_DG_BIT = 5;
|
||||
static const uint8_t CONFIG_REG_G_BIT = 3;
|
||||
|
||||
// Other important constants
|
||||
//
|
||||
static const uint8_t DEVICE_ID = 0x35;
|
||||
static const uint16_t SHUTDOWN_BITS = 0x0018;
|
||||
|
||||
// Base multiplier value for lux computation
|
||||
//
|
||||
static const float LUX_MULTIPLIER_BASE = 0.00213;
|
||||
|
||||
// Enum for conversion/integration time settings for the VEML3235.
|
||||
//
|
||||
// Specific values of the enum constants are register values taken from the VEML3235 datasheet.
|
||||
// Longer times mean more accurate results, but will take more energy/more time.
|
||||
//
|
||||
enum VEML3235ComponentIntegrationTime {
|
||||
VEML3235_INTEGRATION_TIME_50MS = 0b000,
|
||||
VEML3235_INTEGRATION_TIME_100MS = 0b001,
|
||||
VEML3235_INTEGRATION_TIME_200MS = 0b010,
|
||||
VEML3235_INTEGRATION_TIME_400MS = 0b011,
|
||||
VEML3235_INTEGRATION_TIME_800MS = 0b100,
|
||||
};
|
||||
|
||||
// Enum for digital gain settings for the VEML3235.
|
||||
// Higher values are better for low light situations, but can increase noise.
|
||||
//
|
||||
enum VEML3235ComponentDigitalGain {
|
||||
VEML3235_DIGITAL_GAIN_1X = 0b0,
|
||||
VEML3235_DIGITAL_GAIN_2X = 0b1,
|
||||
};
|
||||
|
||||
// Enum for gain settings for the VEML3235.
|
||||
// Higher values are better for low light situations, but can increase noise.
|
||||
//
|
||||
enum VEML3235ComponentGain {
|
||||
VEML3235_GAIN_1X = 0b00,
|
||||
VEML3235_GAIN_2X = 0b01,
|
||||
VEML3235_GAIN_4X = 0b11,
|
||||
};
|
||||
|
||||
class VEML3235Sensor : public sensor::Sensor, public PollingComponent, public i2c::I2CDevice {
|
||||
public:
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
void update() override { this->publish_state(this->read_lx_()); }
|
||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||
|
||||
// Used by ESPHome framework. Does NOT actually set the value on the device.
|
||||
void set_auto_gain(bool auto_gain) { this->auto_gain_ = auto_gain; }
|
||||
void set_auto_gain_threshold_high(float auto_gain_threshold_high) {
|
||||
this->auto_gain_threshold_high_ = auto_gain_threshold_high;
|
||||
}
|
||||
void set_auto_gain_threshold_low(float auto_gain_threshold_low) {
|
||||
this->auto_gain_threshold_low_ = auto_gain_threshold_low;
|
||||
}
|
||||
void set_power_on(bool power_on) { this->power_on_ = power_on; }
|
||||
void set_digital_gain(VEML3235ComponentDigitalGain digital_gain) { this->digital_gain_ = digital_gain; }
|
||||
void set_gain(VEML3235ComponentGain gain) { this->gain_ = gain; }
|
||||
void set_integration_time(VEML3235ComponentIntegrationTime integration_time) {
|
||||
this->integration_time_ = integration_time;
|
||||
}
|
||||
|
||||
bool auto_gain() { return this->auto_gain_; }
|
||||
float auto_gain_threshold_high() { return this->auto_gain_threshold_high_; }
|
||||
float auto_gain_threshold_low() { return this->auto_gain_threshold_low_; }
|
||||
VEML3235ComponentDigitalGain digital_gain() { return this->digital_gain_; }
|
||||
VEML3235ComponentGain gain() { return this->gain_; }
|
||||
VEML3235ComponentIntegrationTime integration_time() { return this->integration_time_; }
|
||||
|
||||
// Updates the configuration register on the device
|
||||
bool refresh_config_reg(bool force_on = false);
|
||||
|
||||
protected:
|
||||
float read_lx_();
|
||||
void adjust_gain_(uint16_t als_raw_value);
|
||||
|
||||
bool auto_gain_{true};
|
||||
bool power_on_{true};
|
||||
float auto_gain_threshold_high_{0.9};
|
||||
float auto_gain_threshold_low_{0.2};
|
||||
VEML3235ComponentDigitalGain digital_gain_{VEML3235_DIGITAL_GAIN_1X};
|
||||
VEML3235ComponentGain gain_{VEML3235_GAIN_1X};
|
||||
VEML3235ComponentIntegrationTime integration_time_{VEML3235_INTEGRATION_TIME_50MS};
|
||||
};
|
||||
|
||||
} // namespace veml3235
|
||||
} // namespace esphome
|
|
@ -86,14 +86,14 @@ void VoiceAssistant::setup() {
|
|||
|
||||
#ifdef USE_ESP_ADF
|
||||
this->vad_instance_ = vad_create(VAD_MODE_4);
|
||||
#endif
|
||||
|
||||
this->ring_buffer_ = rb_create(BUFFER_SIZE, sizeof(int16_t));
|
||||
this->ring_buffer_ = RingBuffer::create(BUFFER_SIZE * sizeof(int16_t));
|
||||
if (this->ring_buffer_ == nullptr) {
|
||||
ESP_LOGW(TAG, "Could not allocate ring buffer");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
ExternalRAMAllocator<uint8_t> send_allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
|
||||
this->send_buffer_ = send_allocator.allocate(SEND_BUFFER_SIZE);
|
||||
|
@ -112,14 +112,8 @@ int VoiceAssistant::read_microphone_() {
|
|||
memset(this->input_buffer_, 0, INPUT_BUFFER_SIZE * sizeof(int16_t));
|
||||
return 0;
|
||||
}
|
||||
#ifdef USE_ESP_ADF
|
||||
// Write audio into ring buffer
|
||||
int available = rb_bytes_available(this->ring_buffer_);
|
||||
if (available < bytes_read) {
|
||||
rb_read(this->ring_buffer_, nullptr, bytes_read - available, 0);
|
||||
}
|
||||
rb_write(this->ring_buffer_, (char *) this->input_buffer_, bytes_read, 0);
|
||||
#endif
|
||||
this->ring_buffer_->write((void *) this->input_buffer_, bytes_read);
|
||||
} else {
|
||||
ESP_LOGD(TAG, "microphone not running");
|
||||
}
|
||||
|
@ -141,9 +135,9 @@ void VoiceAssistant::loop() {
|
|||
switch (this->state_) {
|
||||
case State::IDLE: {
|
||||
if (this->continuous_ && this->desired_state_ == State::IDLE) {
|
||||
this->ring_buffer_->reset();
|
||||
#ifdef USE_ESP_ADF
|
||||
if (this->use_wake_word_) {
|
||||
rb_reset(this->ring_buffer_);
|
||||
this->set_state_(State::START_MICROPHONE, State::WAIT_FOR_VAD);
|
||||
} else
|
||||
#endif
|
||||
|
@ -236,19 +230,13 @@ void VoiceAssistant::loop() {
|
|||
break; // State changed when udp server port received
|
||||
}
|
||||
case State::STREAMING_MICROPHONE: {
|
||||
size_t bytes_read = this->read_microphone_();
|
||||
#ifdef USE_ESP_ADF
|
||||
if (rb_bytes_filled(this->ring_buffer_) >= SEND_BUFFER_SIZE) {
|
||||
rb_read(this->ring_buffer_, (char *) this->send_buffer_, SEND_BUFFER_SIZE, 0);
|
||||
this->read_microphone_();
|
||||
if (this->ring_buffer_->available() >= SEND_BUFFER_SIZE) {
|
||||
this->ring_buffer_->read((void *) this->send_buffer_, SEND_BUFFER_SIZE, 0);
|
||||
this->socket_->sendto(this->send_buffer_, SEND_BUFFER_SIZE, 0, (struct sockaddr *) &this->dest_addr_,
|
||||
sizeof(this->dest_addr_));
|
||||
}
|
||||
#else
|
||||
if (bytes_read > 0) {
|
||||
this->socket_->sendto(this->input_buffer_, bytes_read, 0, (struct sockaddr *) &this->dest_addr_,
|
||||
sizeof(this->dest_addr_));
|
||||
}
|
||||
#endif
|
||||
|
||||
break;
|
||||
}
|
||||
case State::STOP_MICROPHONE: {
|
||||
|
@ -473,9 +461,9 @@ void VoiceAssistant::request_start(bool continuous, bool silence_detection) {
|
|||
if (this->state_ == State::IDLE) {
|
||||
this->continuous_ = continuous;
|
||||
this->silence_detection_ = silence_detection;
|
||||
this->ring_buffer_->reset();
|
||||
#ifdef USE_ESP_ADF
|
||||
if (this->use_wake_word_) {
|
||||
rb_reset(this->ring_buffer_);
|
||||
this->set_state_(State::START_MICROPHONE, State::WAIT_FOR_VAD);
|
||||
} else
|
||||
#endif
|
||||
|
@ -618,9 +606,9 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) {
|
|||
case api::enums::VOICE_ASSISTANT_RUN_END: {
|
||||
ESP_LOGD(TAG, "Assist Pipeline ended");
|
||||
if (this->state_ == State::STREAMING_MICROPHONE) {
|
||||
this->ring_buffer_->reset();
|
||||
#ifdef USE_ESP_ADF
|
||||
if (this->use_wake_word_) {
|
||||
rb_reset(this->ring_buffer_);
|
||||
// No need to stop the microphone since we didn't use the speaker
|
||||
this->set_state_(State::WAIT_FOR_VAD, State::WAITING_FOR_VAD);
|
||||
} else
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/ring_buffer.h"
|
||||
|
||||
#include "esphome/components/api/api_connection.h"
|
||||
#include "esphome/components/api/api_pb2.h"
|
||||
|
@ -21,7 +22,6 @@
|
|||
|
||||
#ifdef USE_ESP_ADF
|
||||
#include <esp_vad.h>
|
||||
#include <ringbuf.h>
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
|
@ -177,10 +177,10 @@ class VoiceAssistant : public Component {
|
|||
|
||||
#ifdef USE_ESP_ADF
|
||||
vad_handle_t vad_instance_;
|
||||
ringbuf_handle_t ring_buffer_;
|
||||
uint8_t vad_threshold_{5};
|
||||
uint8_t vad_counter_{0};
|
||||
#endif
|
||||
std::unique_ptr<RingBuffer> ring_buffer_;
|
||||
|
||||
bool use_wake_word_;
|
||||
uint8_t noise_suppression_level_;
|
||||
|
|
|
@ -2004,15 +2004,19 @@ def suppress_invalid():
|
|||
pass
|
||||
|
||||
|
||||
GIT_SCHEMA = {
|
||||
Required(CONF_URL): url,
|
||||
Optional(CONF_REF): git_ref,
|
||||
Optional(CONF_USERNAME): string,
|
||||
Optional(CONF_PASSWORD): string,
|
||||
}
|
||||
LOCAL_SCHEMA = {
|
||||
Required(CONF_PATH): directory,
|
||||
}
|
||||
GIT_SCHEMA = Schema(
|
||||
{
|
||||
Required(CONF_URL): url,
|
||||
Optional(CONF_REF): git_ref,
|
||||
Optional(CONF_USERNAME): string,
|
||||
Optional(CONF_PASSWORD): string,
|
||||
}
|
||||
)
|
||||
LOCAL_SCHEMA = Schema(
|
||||
{
|
||||
Required(CONF_PATH): directory,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def validate_source_shorthand(value):
|
||||
|
@ -2053,8 +2057,8 @@ SOURCE_SCHEMA = Any(
|
|||
validate_source_shorthand,
|
||||
typed_schema(
|
||||
{
|
||||
TYPE_GIT: Schema(GIT_SCHEMA),
|
||||
TYPE_LOCAL: Schema(LOCAL_SCHEMA),
|
||||
TYPE_GIT: GIT_SCHEMA,
|
||||
TYPE_LOCAL: LOCAL_SCHEMA,
|
||||
}
|
||||
),
|
||||
)
|
||||
|
|
|
@ -510,6 +510,8 @@ CONF_ON_ENROLLMENT_FAILED = "on_enrollment_failed"
|
|||
CONF_ON_ENROLLMENT_SCAN = "on_enrollment_scan"
|
||||
CONF_ON_FINGER_SCAN_INVALID = "on_finger_scan_invalid"
|
||||
CONF_ON_FINGER_SCAN_MATCHED = "on_finger_scan_matched"
|
||||
CONF_ON_FINGER_SCAN_MISPLACED = "on_finger_scan_misplaced"
|
||||
CONF_ON_FINGER_SCAN_START = "on_finger_scan_start"
|
||||
CONF_ON_FINGER_SCAN_UNMATCHED = "on_finger_scan_unmatched"
|
||||
CONF_ON_JSON_MESSAGE = "on_json_message"
|
||||
CONF_ON_LOCK = "on_lock"
|
||||
|
|
49
esphome/core/ring_buffer.cpp
Normal file
49
esphome/core/ring_buffer.cpp
Normal file
|
@ -0,0 +1,49 @@
|
|||
#include "ring_buffer.h"
|
||||
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#ifdef USE_ESP32
|
||||
|
||||
#include "helpers.h"
|
||||
|
||||
namespace esphome {
|
||||
|
||||
static const char *const TAG = "ring_buffer";
|
||||
|
||||
std::unique_ptr<RingBuffer> RingBuffer::create(size_t len) {
|
||||
std::unique_ptr<RingBuffer> rb = make_unique<RingBuffer>();
|
||||
|
||||
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
|
||||
rb->storage_ = allocator.allocate(len);
|
||||
if (rb->storage_ == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
rb->handle_ = xStreamBufferCreateStatic(len, 0, rb->storage_, &rb->structure_);
|
||||
return rb;
|
||||
}
|
||||
|
||||
size_t RingBuffer::read(void *data, size_t size, TickType_t ticks_to_wait) {
|
||||
return xStreamBufferReceive(this->handle_, data, size, ticks_to_wait);
|
||||
}
|
||||
|
||||
size_t RingBuffer::write(void *data, size_t len) {
|
||||
size_t free = this->free();
|
||||
if (free < len) {
|
||||
size_t needed = len - free;
|
||||
uint8_t discard[needed];
|
||||
xStreamBufferReceive(this->handle_, discard, needed, 0);
|
||||
}
|
||||
return xStreamBufferSend(this->handle_, data, len, 0);
|
||||
}
|
||||
|
||||
size_t RingBuffer::available() const { return xStreamBufferBytesAvailable(this->handle_); }
|
||||
|
||||
size_t RingBuffer::free() const { return xStreamBufferSpacesAvailable(this->handle_); }
|
||||
|
||||
BaseType_t RingBuffer::reset() { return xStreamBufferReset(this->handle_); }
|
||||
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
34
esphome/core/ring_buffer.h
Normal file
34
esphome/core/ring_buffer.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
#pragma once
|
||||
|
||||
#ifdef USE_ESP32
|
||||
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/stream_buffer.h>
|
||||
|
||||
#include <cinttypes>
|
||||
#include <memory>
|
||||
|
||||
namespace esphome {
|
||||
|
||||
class RingBuffer {
|
||||
public:
|
||||
size_t read(void *data, size_t size, TickType_t ticks_to_wait = 0);
|
||||
|
||||
size_t write(void *data, size_t len);
|
||||
|
||||
size_t available() const;
|
||||
size_t free() const;
|
||||
|
||||
BaseType_t reset();
|
||||
|
||||
static std::unique_ptr<RingBuffer> create(size_t len);
|
||||
|
||||
protected:
|
||||
StreamBufferHandle_t handle_;
|
||||
StaticStreamBuffer_t structure_;
|
||||
uint8_t *storage_;
|
||||
};
|
||||
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
|
@ -1,36 +1,37 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import fnmatch
|
||||
import functools
|
||||
import inspect
|
||||
import logging
|
||||
import math
|
||||
import os
|
||||
|
||||
import uuid
|
||||
from typing import Any
|
||||
|
||||
import yaml
|
||||
import yaml.constructor
|
||||
from yaml import SafeLoader as PurePythonLoader
|
||||
|
||||
try:
|
||||
from yaml import CSafeLoader as FastestAvailableSafeLoader
|
||||
except ImportError:
|
||||
FastestAvailableSafeLoader = PurePythonLoader
|
||||
|
||||
from esphome import core
|
||||
from esphome.config_helpers import read_config_file, Extend, Remove
|
||||
from esphome.config_helpers import Extend, Remove, read_config_file
|
||||
from esphome.core import (
|
||||
CORE,
|
||||
DocumentRange,
|
||||
EsphomeError,
|
||||
IPAddress,
|
||||
Lambda,
|
||||
MACAddress,
|
||||
TimePeriod,
|
||||
DocumentRange,
|
||||
CORE,
|
||||
)
|
||||
from esphome.helpers import add_class_to_obj
|
||||
from esphome.util import OrderedDict, filter_yaml_files
|
||||
|
||||
try:
|
||||
from yaml import CSafeLoader as FastestAvailableSafeLoader
|
||||
except ImportError:
|
||||
from yaml import ( # type: ignore[assignment]
|
||||
SafeLoader as FastestAvailableSafeLoader,
|
||||
)
|
||||
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
# Mostly copied from Home Assistant because that code works fine and
|
||||
|
@ -97,7 +98,7 @@ def _add_data_ref(fn):
|
|||
return wrapped
|
||||
|
||||
|
||||
class ESPHomeLoader(FastestAvailableSafeLoader):
|
||||
class ESPHomeLoaderMixin:
|
||||
"""Loader class that keeps track of line numbers."""
|
||||
|
||||
@_add_data_ref
|
||||
|
@ -282,8 +283,8 @@ class ESPHomeLoader(FastestAvailableSafeLoader):
|
|||
return file, vars
|
||||
|
||||
def substitute_vars(config, vars):
|
||||
from esphome.const import CONF_SUBSTITUTIONS, CONF_DEFAULTS
|
||||
from esphome.components import substitutions
|
||||
from esphome.const import CONF_DEFAULTS, CONF_SUBSTITUTIONS
|
||||
|
||||
org_subs = None
|
||||
result = config
|
||||
|
@ -375,50 +376,64 @@ class ESPHomeLoader(FastestAvailableSafeLoader):
|
|||
return Remove(str(node.value))
|
||||
|
||||
|
||||
ESPHomeLoader.add_constructor("tag:yaml.org,2002:int", ESPHomeLoader.construct_yaml_int)
|
||||
ESPHomeLoader.add_constructor(
|
||||
"tag:yaml.org,2002:float", ESPHomeLoader.construct_yaml_float
|
||||
)
|
||||
ESPHomeLoader.add_constructor(
|
||||
"tag:yaml.org,2002:binary", ESPHomeLoader.construct_yaml_binary
|
||||
)
|
||||
ESPHomeLoader.add_constructor(
|
||||
"tag:yaml.org,2002:omap", ESPHomeLoader.construct_yaml_omap
|
||||
)
|
||||
ESPHomeLoader.add_constructor("tag:yaml.org,2002:str", ESPHomeLoader.construct_yaml_str)
|
||||
ESPHomeLoader.add_constructor("tag:yaml.org,2002:seq", ESPHomeLoader.construct_yaml_seq)
|
||||
ESPHomeLoader.add_constructor("tag:yaml.org,2002:map", ESPHomeLoader.construct_yaml_map)
|
||||
ESPHomeLoader.add_constructor("!env_var", ESPHomeLoader.construct_env_var)
|
||||
ESPHomeLoader.add_constructor("!secret", ESPHomeLoader.construct_secret)
|
||||
ESPHomeLoader.add_constructor("!include", ESPHomeLoader.construct_include)
|
||||
ESPHomeLoader.add_constructor(
|
||||
"!include_dir_list", ESPHomeLoader.construct_include_dir_list
|
||||
)
|
||||
ESPHomeLoader.add_constructor(
|
||||
"!include_dir_merge_list", ESPHomeLoader.construct_include_dir_merge_list
|
||||
)
|
||||
ESPHomeLoader.add_constructor(
|
||||
"!include_dir_named", ESPHomeLoader.construct_include_dir_named
|
||||
)
|
||||
ESPHomeLoader.add_constructor(
|
||||
"!include_dir_merge_named", ESPHomeLoader.construct_include_dir_merge_named
|
||||
)
|
||||
ESPHomeLoader.add_constructor("!lambda", ESPHomeLoader.construct_lambda)
|
||||
ESPHomeLoader.add_constructor("!force", ESPHomeLoader.construct_force)
|
||||
ESPHomeLoader.add_constructor("!extend", ESPHomeLoader.construct_extend)
|
||||
ESPHomeLoader.add_constructor("!remove", ESPHomeLoader.construct_remove)
|
||||
class ESPHomeLoader(ESPHomeLoaderMixin, FastestAvailableSafeLoader):
|
||||
"""Loader class that keeps track of line numbers."""
|
||||
|
||||
|
||||
def load_yaml(fname, clear_secrets=True):
|
||||
class ESPHomePurePythonLoader(ESPHomeLoaderMixin, PurePythonLoader):
|
||||
"""Loader class that keeps track of line numbers."""
|
||||
|
||||
|
||||
for _loader in (ESPHomeLoader, ESPHomePurePythonLoader):
|
||||
_loader.add_constructor("tag:yaml.org,2002:int", _loader.construct_yaml_int)
|
||||
_loader.add_constructor("tag:yaml.org,2002:float", _loader.construct_yaml_float)
|
||||
_loader.add_constructor("tag:yaml.org,2002:binary", _loader.construct_yaml_binary)
|
||||
_loader.add_constructor("tag:yaml.org,2002:omap", _loader.construct_yaml_omap)
|
||||
_loader.add_constructor("tag:yaml.org,2002:str", _loader.construct_yaml_str)
|
||||
_loader.add_constructor("tag:yaml.org,2002:seq", _loader.construct_yaml_seq)
|
||||
_loader.add_constructor("tag:yaml.org,2002:map", _loader.construct_yaml_map)
|
||||
_loader.add_constructor("!env_var", _loader.construct_env_var)
|
||||
_loader.add_constructor("!secret", _loader.construct_secret)
|
||||
_loader.add_constructor("!include", _loader.construct_include)
|
||||
_loader.add_constructor("!include_dir_list", _loader.construct_include_dir_list)
|
||||
_loader.add_constructor(
|
||||
"!include_dir_merge_list", _loader.construct_include_dir_merge_list
|
||||
)
|
||||
_loader.add_constructor("!include_dir_named", _loader.construct_include_dir_named)
|
||||
_loader.add_constructor(
|
||||
"!include_dir_merge_named", _loader.construct_include_dir_merge_named
|
||||
)
|
||||
_loader.add_constructor("!lambda", _loader.construct_lambda)
|
||||
_loader.add_constructor("!force", _loader.construct_force)
|
||||
_loader.add_constructor("!extend", _loader.construct_extend)
|
||||
_loader.add_constructor("!remove", _loader.construct_remove)
|
||||
|
||||
|
||||
def load_yaml(fname: str, clear_secrets: bool = True) -> Any:
|
||||
if clear_secrets:
|
||||
_SECRET_VALUES.clear()
|
||||
_SECRET_CACHE.clear()
|
||||
return _load_yaml_internal(fname)
|
||||
|
||||
|
||||
def _load_yaml_internal(fname):
|
||||
def _load_yaml_internal(fname: str) -> Any:
|
||||
"""Load a YAML file."""
|
||||
content = read_config_file(fname)
|
||||
loader = ESPHomeLoader(content)
|
||||
try:
|
||||
return _load_yaml_internal_with_type(ESPHomeLoader, fname, content)
|
||||
except EsphomeError:
|
||||
# Loading failed, so we now load with the Python loader which has more
|
||||
# readable exceptions
|
||||
return _load_yaml_internal_with_type(ESPHomePurePythonLoader, fname, content)
|
||||
|
||||
|
||||
def _load_yaml_internal_with_type(
|
||||
loader_type: type[ESPHomeLoader] | type[ESPHomePurePythonLoader],
|
||||
fname: str,
|
||||
content: str,
|
||||
) -> Any:
|
||||
"""Load a YAML file."""
|
||||
loader = loader_type(content)
|
||||
loader.name = fname
|
||||
try:
|
||||
return loader.get_single_data() or OrderedDict()
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
pillow==10.1.0
|
||||
pillow==10.2.0
|
||||
cairosvg==2.7.1
|
||||
cryptography==41.0.4
|
||||
|
|
|
@ -4,10 +4,13 @@
|
|||
set -e
|
||||
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
location="venv/bin/activate"
|
||||
if [ ! -n "$DEVCONTAINER" ] && [ ! -n "$VIRTUAL_ENV" ] && [ ! "$ESPHOME_NO_VENV" ]; then
|
||||
python3 -m venv venv
|
||||
source venv/bin/activate
|
||||
if [ -f venv/Scripts/activate ]; then
|
||||
location="venv/Scripts/activate"
|
||||
fi
|
||||
source $location;
|
||||
fi
|
||||
|
||||
# Avoid unsafe git error when running inside devcontainer
|
||||
|
@ -25,4 +28,4 @@ script/platformio_install_deps.py platformio.ini --libraries --tools --platforms
|
|||
|
||||
echo
|
||||
echo
|
||||
echo "Virtual environment created; source venv/bin/activate to use it"
|
||||
echo "Virtual environment created. Run 'source $location' to use it."
|
||||
|
|
|
@ -1370,6 +1370,16 @@ sensor:
|
|||
name: tsl2591 calculated_lux
|
||||
id: tsl2591_cl
|
||||
i2c_id: i2c_bus
|
||||
- platform: veml3235
|
||||
id: veml3235_sensor
|
||||
name: VEML3235 Light Sensor
|
||||
i2c_id: i2c_bus
|
||||
auto_gain: true
|
||||
auto_gain_threshold_high: 90%
|
||||
auto_gain_threshold_low: 15%
|
||||
digital_gain: 1X
|
||||
gain: 1X
|
||||
integration_time: 50ms
|
||||
- platform: tee501
|
||||
name: Office Temperature 3
|
||||
address: 0x48
|
||||
|
@ -2028,6 +2038,18 @@ binary_sensor:
|
|||
- platform: dfrobot_sen0395
|
||||
id: mmwave_detected_uart
|
||||
dfrobot_sen0395_id: mmwave
|
||||
- platform: nfc
|
||||
nfcc_id: nfcc_pn7160_i2c
|
||||
ndef_contains: pulse
|
||||
name: MFC Tag 1
|
||||
- platform: nfc
|
||||
nfcc_id: nfcc_pn7160_i2c
|
||||
tag_id: pulse
|
||||
name: MFC Tag 2
|
||||
- platform: nfc
|
||||
nfcc_id: nfcc_pn7160_i2c
|
||||
uid: 59-FC-AB-15
|
||||
name: MFC Tag 3
|
||||
|
||||
pca9685:
|
||||
frequency: 500
|
||||
|
@ -3461,6 +3483,7 @@ pn532_i2c:
|
|||
i2c_id: i2c_bus
|
||||
|
||||
pn7150_i2c:
|
||||
id: nfcc_pn7150_i2c
|
||||
i2c_id: i2c_bus
|
||||
irq_pin:
|
||||
allow_other_uses: true
|
||||
|
|
|
@ -1258,6 +1258,9 @@ fingerprint_grow:
|
|||
number: 4
|
||||
password: 0x12FE37DC
|
||||
new_password: 0xA65B9840
|
||||
on_finger_scan_start:
|
||||
- homeassistant.event:
|
||||
event: esphome.${device_name}_fingerprint_grow_finger_scan_start
|
||||
on_finger_scan_invalid:
|
||||
- homeassistant.event:
|
||||
event: esphome.${device_name}_fingerprint_grow_finger_scan_invalid
|
||||
|
@ -1270,6 +1273,9 @@ fingerprint_grow:
|
|||
on_finger_scan_unmatched:
|
||||
- homeassistant.event:
|
||||
event: esphome.${device_name}_fingerprint_grow_finger_scan_unmatched
|
||||
on_finger_scan_misplaced:
|
||||
- homeassistant.event:
|
||||
event: esphome.${device_name}_fingerprint_grow_finger_scan_misplaced
|
||||
on_enrollment_scan:
|
||||
- homeassistant.event:
|
||||
event: esphome.${device_name}_fingerprint_grow_enrollment_scan
|
||||
|
|
18
tests/unit_tests/fixtures/yaml_util/broken_includetest.yaml
Normal file
18
tests/unit_tests/fixtures/yaml_util/broken_includetest.yaml
Normal file
|
@ -0,0 +1,18 @@
|
|||
---
|
||||
substitutions:
|
||||
name: original
|
||||
|
||||
wifi: !include
|
||||
file: includes/broken_included.yaml.txt
|
||||
vars:
|
||||
name: my_custom_ssid
|
||||
|
||||
esphome:
|
||||
# should be substituted as 'original',
|
||||
# not overwritten by vars in the !include above
|
||||
name: ${name}
|
||||
name_add_mac_suffix: true
|
||||
platform: esp8266
|
||||
board: !include {file: includes/scalar.yaml, vars: {var1: nodemcu}}
|
||||
|
||||
libraries: !include {file: includes/list.yaml, vars: {var1: Wire}}
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
# yamllint disable-line
|
||||
ssid: ${name}
|
||||
# yamllint disable-line
|
||||
fdf: error
|
|
@ -1,5 +1,6 @@
|
|||
from esphome import yaml_util
|
||||
from esphome.components import substitutions
|
||||
from esphome.core import EsphomeError
|
||||
|
||||
|
||||
def test_include_with_vars(fixture_path):
|
||||
|
@ -11,3 +12,13 @@ def test_include_with_vars(fixture_path):
|
|||
assert actual["esphome"]["libraries"][0] == "Wire"
|
||||
assert actual["esphome"]["board"] == "nodemcu"
|
||||
assert actual["wifi"]["ssid"] == "my_custom_ssid"
|
||||
|
||||
|
||||
def test_loading_a_broken_yaml_file(fixture_path):
|
||||
"""Ensure we fallback to pure python to give good errors."""
|
||||
yaml_file = fixture_path / "yaml_util" / "broken_includetest.yaml"
|
||||
|
||||
try:
|
||||
yaml_util.load_yaml(yaml_file)
|
||||
except EsphomeError as err:
|
||||
assert "broken_included.yaml" in str(err)
|
||||
|
|
Loading…
Reference in a new issue