Merge branch 'dev' into optolink

This commit is contained in:
j0ta29 2023-11-12 17:53:12 +01:00 committed by GitHub
commit d999d6dc28
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
125 changed files with 3137 additions and 836 deletions

View file

@ -3,7 +3,7 @@
# See https://pre-commit.com/hooks.html for more hooks
repos:
- repo: https://github.com/psf/black-pre-commit-mirror
rev: 23.10.1
rev: 23.11.0
hooks:
- id: black
args:

View file

@ -17,6 +17,9 @@ esphome/components/ac_dimmer/* @glmnet
esphome/components/adc/* @esphome/core
esphome/components/adc128s102/* @DeerMaximum
esphome/components/addressable_light/* @justfalter
esphome/components/ade7953/* @angelnu
esphome/components/ade7953_i2c/* @angelnu
esphome/components/ade7953_spi/* @angelnu
esphome/components/airthings_ble/* @jeromelaban
esphome/components/airthings_wave_base/* @jeromelaban @kpfleming @ncareau
esphome/components/airthings_wave_mini/* @ncareau
@ -89,9 +92,9 @@ esphome/components/ektf2232/* @jesserockz
esphome/components/emc2101/* @ellull
esphome/components/ens210/* @itn3rd77
esphome/components/esp32/* @esphome/core
esphome/components/esp32_ble/* @jesserockz
esphome/components/esp32_ble/* @Rapsssito @jesserockz
esphome/components/esp32_ble_client/* @jesserockz
esphome/components/esp32_ble_server/* @clydebarrow @jesserockz
esphome/components/esp32_ble_server/* @Rapsssito @clydebarrow @jesserockz
esphome/components/esp32_camera_web_server/* @ayufan
esphome/components/esp32_can/* @Sympatron
esphome/components/esp32_improv/* @jesserockz
@ -244,6 +247,7 @@ esphome/components/radon_eye_rd200/* @jeffeb3
esphome/components/rc522/* @glmnet
esphome/components/rc522_i2c/* @glmnet
esphome/components/rc522_spi/* @glmnet
esphome/components/resistance_sampler/* @jesserockz
esphome/components/restart/* @esphome/core
esphome/components/rf_bridge/* @jesserockz
esphome/components/rgbct/* @jesserockz
@ -350,6 +354,7 @@ esphome/components/wiegand/* @ssieb
esphome/components/wireguard/* @droscy @lhoracek @thomas0bernard
esphome/components/wl_134/* @hobbypunk90
esphome/components/x9c/* @EtienneMD
esphome/components/xgzp68xx/* @gcormier
esphome/components/xiaomi_lywsd03mmc/* @ahpohl
esphome/components/xiaomi_mhoc303/* @drug123
esphome/components/xiaomi_mhoc401/* @vevsvevs

View file

@ -5,6 +5,7 @@
# One of "docker", "hassio"
ARG BASEIMGTYPE=docker
# https://github.com/hassio-addons/addon-debian-base/releases
FROM ghcr.io/hassio-addons/debian-base:7.2.0 AS base-hassio
# https://hub.docker.com/_/debian?tab=tags&page=1&name=bookworm
@ -12,9 +13,11 @@ FROM debian:12.2-slim AS base-docker
FROM base-${BASEIMGTYPE} AS base
ARG TARGETARCH
ARG TARGETVARIANT
# Note that --break-system-packages is used below because
# https://peps.python.org/pep-0668/ added a safety check that prevents
# installing packages with the same name as a system package. This is
@ -46,7 +49,7 @@ RUN \
libssl-dev=3.0.11-1~deb12u2 \
libffi-dev=3.4.4-1 \
cargo=0.66.0+ds1-1 \
pkg-config=1.8.1-1; \
pkg-config=1.8.1-1 \
gcc-arm-linux-gnueabihf=4:12.2.0-3; \
fi; \
rm -rf \
@ -60,7 +63,6 @@ ENV \
# Store globally installed pio libs in /piolibs
PLATFORMIO_GLOBALLIB_DIR=/piolibs
# Support legacy binaries on Debian multiarch system. There is no "correct" way
# to do this, other than using properly built toolchains...
# See: https://unix.stackexchange.com/questions/553743/correct-way-to-add-lib-ld-linux-so-3-in-debian
@ -71,8 +73,12 @@ RUN \
RUN \
# Ubuntu python3-pip is missing wheel
pip3 install --break-system-packages --no-cache-dir \
platformio==6.1.11 \
if [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]; then \
export PIP_EXTRA_INDEX_URL="https://www.piwheels.org/simple"; \
fi; \
pip3 install \
--break-system-packages --no-cache-dir \
platformio==6.1.11 \
# Change some platformio settings
&& platformio settings set enable_telemetry No \
&& platformio settings set check_platformio_interval 1000000 \
@ -83,8 +89,12 @@ RUN \
# tmpfs is for https://github.com/rust-lang/cargo/issues/8719
COPY requirements.txt requirements_optional.txt script/platformio_install_deps.py platformio.ini /
RUN --mount=type=tmpfs,target=/root/.cargo CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse CARGO_HOME=/root/.cargo \
pip3 install --break-system-packages --no-cache-dir -r /requirements.txt -r /requirements_optional.txt \
RUN --mount=type=tmpfs,target=/root/.cargo if [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]; then \
export PIP_EXTRA_INDEX_URL="https://www.piwheels.org/simple"; \
fi; \
CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse CARGO_HOME=/root/.cargo \
pip3 install \
--break-system-packages --no-cache-dir -r /requirements.txt -r /requirements_optional.txt \
&& /platformio_install_deps.py /platformio.ini --libraries
@ -93,7 +103,11 @@ FROM base AS docker
# Copy esphome and install
COPY . /esphome
RUN pip3 install --break-system-packages --no-cache-dir --no-use-pep517 -e /esphome
RUN if [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]; then \
export PIP_EXTRA_INDEX_URL="https://www.piwheels.org/simple"; \
fi; \
pip3 install \
--break-system-packages --no-cache-dir --no-use-pep517 -e /esphome
# Settings for dashboard
ENV USERNAME="" PASSWORD=""
@ -139,7 +153,11 @@ COPY docker/ha-addon-rootfs/ /
# Copy esphome and install
COPY . /esphome
RUN pip3 install --break-system-packages --no-cache-dir --no-use-pep517 -e /esphome
RUN if [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]; then \
export PIP_EXTRA_INDEX_URL="https://www.piwheels.org/simple"; \
fi; \
pip3 install \
--break-system-packages --no-cache-dir --no-use-pep517 -e /esphome
# Labels
LABEL \
@ -175,7 +193,11 @@ RUN \
/var/lib/apt/lists/*
COPY requirements_test.txt /
RUN pip3 install --break-system-packages --no-cache-dir -r /requirements_test.txt
RUN if [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]; then \
export PIP_EXTRA_INDEX_URL="https://www.piwheels.org/simple"; \
fi; \
pip3 install \
--break-system-packages --no-cache-dir -r /requirements_test.txt
VOLUME ["/esphome"]
WORKDIR /esphome

View file

@ -1,3 +1,4 @@
# PYTHON_ARGCOMPLETE_OK
import argparse
import functools
import logging
@ -7,6 +8,8 @@ import sys
import time
from datetime import datetime
import argcomplete
from esphome import const, writer, yaml_util
import esphome.codegen as cg
from esphome.config import iter_components, read_config, strip_default_ids
@ -966,6 +969,7 @@ def parse_args(argv):
# Finally, run the new-style parser again with the possibly swapped arguments,
# and let it error out if the command is unparsable.
parser.set_defaults(deprecated_argv_suggestion=deprecated_argv_suggestion)
argcomplete.autocomplete(parser)
return parser.parse_args(arguments)

View file

@ -0,0 +1 @@
CODEOWNERS = ["@angelnu"]

View file

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

View file

@ -1,97 +0,0 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/hal.h"
#include "esphome/components/i2c/i2c.h"
#include "esphome/components/sensor/sensor.h"
#include <vector>
namespace esphome {
namespace ade7953 {
class ADE7953 : public i2c::I2CDevice, public PollingComponent {
public:
void set_irq_pin(InternalGPIOPin *irq_pin) { irq_pin_ = irq_pin; }
void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; }
void set_current_a_sensor(sensor::Sensor *current_a_sensor) { current_a_sensor_ = current_a_sensor; }
void set_current_b_sensor(sensor::Sensor *current_b_sensor) { current_b_sensor_ = current_b_sensor; }
void set_active_power_a_sensor(sensor::Sensor *active_power_a_sensor) {
active_power_a_sensor_ = active_power_a_sensor;
}
void set_active_power_b_sensor(sensor::Sensor *active_power_b_sensor) {
active_power_b_sensor_ = active_power_b_sensor;
}
void setup() override {
if (this->irq_pin_ != nullptr) {
this->irq_pin_->setup();
}
this->set_timeout(100, [this]() {
this->ade_write_8_(0x0010, 0x04);
this->ade_write_8_(0x00FE, 0xAD);
this->ade_write_16_(0x0120, 0x0030);
this->is_setup_ = true;
});
}
void dump_config() override;
void update() override;
protected:
i2c::ErrorCode ade_write_8_(uint16_t reg, uint8_t value) {
std::vector<uint8_t> data;
data.push_back(reg >> 8);
data.push_back(reg >> 0);
data.push_back(value);
return write(data.data(), data.size());
}
i2c::ErrorCode ade_write_16_(uint16_t reg, uint16_t value) {
std::vector<uint8_t> data;
data.push_back(reg >> 8);
data.push_back(reg >> 0);
data.push_back(value >> 8);
data.push_back(value >> 0);
return write(data.data(), data.size());
}
i2c::ErrorCode ade_write_32_(uint16_t reg, uint32_t value) {
std::vector<uint8_t> data;
data.push_back(reg >> 8);
data.push_back(reg >> 0);
data.push_back(value >> 24);
data.push_back(value >> 16);
data.push_back(value >> 8);
data.push_back(value >> 0);
return write(data.data(), data.size());
}
i2c::ErrorCode ade_read_32_(uint16_t reg, uint32_t *value) {
uint8_t reg_data[2];
reg_data[0] = reg >> 8;
reg_data[1] = reg >> 0;
i2c::ErrorCode err = write(reg_data, 2);
if (err != i2c::ERROR_OK)
return err;
uint8_t recv[4];
err = read(recv, 4);
if (err != i2c::ERROR_OK)
return err;
*value = 0;
*value |= ((uint32_t) recv[0]) << 24;
*value |= ((uint32_t) recv[1]) << 16;
*value |= ((uint32_t) recv[2]) << 8;
*value |= ((uint32_t) recv[3]);
return i2c::ERROR_OK;
}
InternalGPIOPin *irq_pin_{nullptr};
bool is_setup_{false};
sensor::Sensor *voltage_sensor_{nullptr};
sensor::Sensor *current_a_sensor_{nullptr};
sensor::Sensor *current_b_sensor_{nullptr};
sensor::Sensor *active_power_a_sensor_{nullptr};
sensor::Sensor *active_power_b_sensor_{nullptr};
};
} // namespace ade7953
} // namespace esphome

View file

@ -1,90 +1,5 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor, i2c
from esphome import pins
from esphome.const import (
CONF_ID,
CONF_IRQ_PIN,
CONF_VOLTAGE,
DEVICE_CLASS_CURRENT,
DEVICE_CLASS_POWER,
DEVICE_CLASS_VOLTAGE,
STATE_CLASS_MEASUREMENT,
UNIT_AMPERE,
UNIT_VOLT,
UNIT_WATT,
CONFIG_SCHEMA = CONFIG_SCHEMA = cv.invalid(
"The ade7953 sensor component has been renamed to ade7953_i2c."
)
DEPENDENCIES = ["i2c"]
ade7953_ns = cg.esphome_ns.namespace("ade7953")
ADE7953 = ade7953_ns.class_("ADE7953", cg.PollingComponent, i2c.I2CDevice)
CONF_CURRENT_A = "current_a"
CONF_CURRENT_B = "current_b"
CONF_ACTIVE_POWER_A = "active_power_a"
CONF_ACTIVE_POWER_B = "active_power_b"
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(ADE7953),
cv.Optional(CONF_IRQ_PIN): pins.internal_gpio_input_pin_schema,
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_A): sensor.sensor_schema(
unit_of_measurement=UNIT_AMPERE,
accuracy_decimals=2,
device_class=DEVICE_CLASS_CURRENT,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_CURRENT_B): sensor.sensor_schema(
unit_of_measurement=UNIT_AMPERE,
accuracy_decimals=2,
device_class=DEVICE_CLASS_CURRENT,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_ACTIVE_POWER_A): sensor.sensor_schema(
unit_of_measurement=UNIT_WATT,
accuracy_decimals=1,
device_class=DEVICE_CLASS_POWER,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_ACTIVE_POWER_B): sensor.sensor_schema(
unit_of_measurement=UNIT_WATT,
accuracy_decimals=1,
device_class=DEVICE_CLASS_POWER,
state_class=STATE_CLASS_MEASUREMENT,
),
}
)
.extend(cv.polling_component_schema("60s"))
.extend(i2c.i2c_device_schema(0x38))
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)
if irq_pin_config := config.get(CONF_IRQ_PIN):
irq_pin = await cg.gpio_pin_expression(irq_pin_config)
cg.add(var.set_irq_pin(irq_pin))
for key in [
CONF_VOLTAGE,
CONF_CURRENT_A,
CONF_CURRENT_B,
CONF_ACTIVE_POWER_A,
CONF_ACTIVE_POWER_B,
]:
if key not in config:
continue
conf = config[key]
sens = await sensor.new_sensor(conf)
cg.add(getattr(var, f"set_{key}_sensor")(sens))

View file

@ -0,0 +1,196 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor
from esphome import pins
from esphome.const import (
CONF_IRQ_PIN,
CONF_VOLTAGE,
CONF_FREQUENCY,
DEVICE_CLASS_CURRENT,
DEVICE_CLASS_APPARENT_POWER,
DEVICE_CLASS_POWER,
DEVICE_CLASS_REACTIVE_POWER,
DEVICE_CLASS_POWER_FACTOR,
DEVICE_CLASS_VOLTAGE,
DEVICE_CLASS_FREQUENCY,
STATE_CLASS_MEASUREMENT,
UNIT_VOLT,
UNIT_HERTZ,
UNIT_AMPERE,
UNIT_VOLT_AMPS,
UNIT_WATT,
UNIT_VOLT_AMPS_REACTIVE,
UNIT_PERCENT,
)
CONF_CURRENT_A = "current_a"
CONF_CURRENT_B = "current_b"
CONF_ACTIVE_POWER_A = "active_power_a"
CONF_ACTIVE_POWER_B = "active_power_b"
CONF_APPARENT_POWER_A = "apparent_power_a"
CONF_APPARENT_POWER_B = "apparent_power_b"
CONF_REACTIVE_POWER_A = "reactive_power_a"
CONF_REACTIVE_POWER_B = "reactive_power_b"
CONF_POWER_FACTOR_A = "power_factor_a"
CONF_POWER_FACTOR_B = "power_factor_b"
CONF_VOLTAGE_PGA_GAIN = "voltage_pga_gain"
CONF_CURRENT_PGA_GAIN_A = "current_pga_gain_a"
CONF_CURRENT_PGA_GAIN_B = "current_pga_gain_b"
CONF_VOLTAGE_GAIN = "voltage_gain"
CONF_CURRENT_GAIN_A = "current_gain_a"
CONF_CURRENT_GAIN_B = "current_gain_b"
CONF_ACTIVE_POWER_GAIN_A = "active_power_gain_a"
CONF_ACTIVE_POWER_GAIN_B = "active_power_gain_b"
PGA_GAINS = {
"1x": 0b000,
"2x": 0b001,
"4x": 0b010,
"8x": 0b011,
"16x": 0b100,
"22x": 0b101,
}
ade7953_base_ns = cg.esphome_ns.namespace("ade7953_base")
ADE7953 = ade7953_base_ns.class_("ADE7953", cg.PollingComponent)
ADE7953_CONFIG_SCHEMA = cv.Schema(
{
cv.Optional(CONF_IRQ_PIN): pins.internal_gpio_input_pin_schema,
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_FREQUENCY): sensor.sensor_schema(
unit_of_measurement=UNIT_HERTZ,
accuracy_decimals=2,
device_class=DEVICE_CLASS_FREQUENCY,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_CURRENT_A): sensor.sensor_schema(
unit_of_measurement=UNIT_AMPERE,
accuracy_decimals=2,
device_class=DEVICE_CLASS_CURRENT,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_CURRENT_B): sensor.sensor_schema(
unit_of_measurement=UNIT_AMPERE,
accuracy_decimals=2,
device_class=DEVICE_CLASS_CURRENT,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_ACTIVE_POWER_A): sensor.sensor_schema(
unit_of_measurement=UNIT_WATT,
accuracy_decimals=1,
device_class=DEVICE_CLASS_POWER,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_ACTIVE_POWER_B): sensor.sensor_schema(
unit_of_measurement=UNIT_WATT,
accuracy_decimals=1,
device_class=DEVICE_CLASS_POWER,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_APPARENT_POWER_A): sensor.sensor_schema(
unit_of_measurement=UNIT_VOLT_AMPS,
accuracy_decimals=1,
device_class=DEVICE_CLASS_APPARENT_POWER,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_APPARENT_POWER_B): sensor.sensor_schema(
unit_of_measurement=UNIT_VOLT_AMPS,
accuracy_decimals=1,
device_class=DEVICE_CLASS_APPARENT_POWER,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_REACTIVE_POWER_A): sensor.sensor_schema(
unit_of_measurement=UNIT_VOLT_AMPS_REACTIVE,
accuracy_decimals=1,
device_class=DEVICE_CLASS_REACTIVE_POWER,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_REACTIVE_POWER_B): sensor.sensor_schema(
unit_of_measurement=UNIT_VOLT_AMPS_REACTIVE,
accuracy_decimals=1,
device_class=DEVICE_CLASS_REACTIVE_POWER,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_POWER_FACTOR_A): sensor.sensor_schema(
unit_of_measurement=UNIT_PERCENT,
accuracy_decimals=2,
device_class=DEVICE_CLASS_POWER_FACTOR,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_POWER_FACTOR_B): sensor.sensor_schema(
unit_of_measurement=UNIT_PERCENT,
accuracy_decimals=2,
device_class=DEVICE_CLASS_POWER_FACTOR,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(
CONF_VOLTAGE_PGA_GAIN,
default="1x",
): cv.one_of(*PGA_GAINS, lower=True),
cv.Optional(
CONF_CURRENT_PGA_GAIN_A,
default="1x",
): cv.one_of(*PGA_GAINS, lower=True),
cv.Optional(
CONF_CURRENT_PGA_GAIN_B,
default="1x",
): cv.one_of(*PGA_GAINS, lower=True),
cv.Optional(CONF_VOLTAGE_GAIN, default=0x400000): cv.hex_int_range(
min=0x100000, max=0x800000
),
cv.Optional(CONF_CURRENT_GAIN_A, default=0x400000): cv.hex_int_range(
min=0x100000, max=0x800000
),
cv.Optional(CONF_CURRENT_GAIN_B, default=0x400000): cv.hex_int_range(
min=0x100000, max=0x800000
),
cv.Optional(CONF_ACTIVE_POWER_GAIN_A, default=0x400000): cv.hex_int_range(
min=0x100000, max=0x800000
),
cv.Optional(CONF_ACTIVE_POWER_GAIN_B, default=0x400000): cv.hex_int_range(
min=0x100000, max=0x800000
),
}
).extend(cv.polling_component_schema("60s"))
async def register_ade7953(var, config):
await cg.register_component(var, config)
if irq_pin_config := config.get(CONF_IRQ_PIN):
irq_pin = await cg.gpio_pin_expression(irq_pin_config)
cg.add(var.set_irq_pin(irq_pin))
cg.add(var.set_pga_v(PGA_GAINS[config.get(CONF_VOLTAGE_PGA_GAIN)]))
cg.add(var.set_pga_ia(PGA_GAINS[config.get(CONF_CURRENT_PGA_GAIN_A)]))
cg.add(var.set_pga_ib(PGA_GAINS[config.get(CONF_CURRENT_PGA_GAIN_B)]))
cg.add(var.set_vgain(config.get(CONF_VOLTAGE_GAIN)))
cg.add(var.set_aigain(config.get(CONF_CURRENT_GAIN_A)))
cg.add(var.set_bigain(config.get(CONF_CURRENT_GAIN_B)))
cg.add(var.set_awgain(config.get(CONF_ACTIVE_POWER_GAIN_A)))
cg.add(var.set_bwgain(config.get(CONF_ACTIVE_POWER_GAIN_B)))
for key in [
CONF_VOLTAGE,
CONF_FREQUENCY,
CONF_CURRENT_A,
CONF_CURRENT_B,
CONF_POWER_FACTOR_A,
CONF_POWER_FACTOR_B,
CONF_APPARENT_POWER_A,
CONF_APPARENT_POWER_B,
CONF_ACTIVE_POWER_A,
CONF_ACTIVE_POWER_B,
CONF_REACTIVE_POWER_A,
CONF_REACTIVE_POWER_B,
]:
if key not in config:
continue
conf = config[key]
sens = await sensor.new_sensor(conf)
cg.add(getattr(var, f"set_{key}_sensor")(sens))

View file

@ -0,0 +1,129 @@
#include "ade7953_base.h"
#include "esphome/core/log.h"
namespace esphome {
namespace ade7953_base {
static const char *const TAG = "ade7953";
void ADE7953::setup() {
if (this->irq_pin_ != nullptr) {
this->irq_pin_->setup();
}
// The chip might take up to 100ms to initialise
this->set_timeout(100, [this]() {
// this->ade_write_8(0x0010, 0x04);
this->ade_write_8(0x00FE, 0xAD);
this->ade_write_16(0x0120, 0x0030);
// Set gains
this->ade_write_8(PGA_V_8, pga_v_);
this->ade_write_8(PGA_IA_8, pga_ia_);
this->ade_write_8(PGA_IB_8, pga_ib_);
this->ade_write_32(AVGAIN_32, vgain_);
this->ade_write_32(AIGAIN_32, aigain_);
this->ade_write_32(BIGAIN_32, bigain_);
this->ade_write_32(AWGAIN_32, awgain_);
this->ade_write_32(BWGAIN_32, bwgain_);
// Read back gains for debugging
this->ade_read_8(PGA_V_8, &pga_v_);
this->ade_read_8(PGA_IA_8, &pga_ia_);
this->ade_read_8(PGA_IB_8, &pga_ib_);
this->ade_read_32(AVGAIN_32, &vgain_);
this->ade_read_32(AIGAIN_32, &aigain_);
this->ade_read_32(BIGAIN_32, &bigain_);
this->ade_read_32(AWGAIN_32, &awgain_);
this->ade_read_32(BWGAIN_32, &bwgain_);
this->is_setup_ = true;
});
}
void ADE7953::dump_config() {
LOG_PIN(" IRQ Pin: ", irq_pin_);
LOG_UPDATE_INTERVAL(this);
LOG_SENSOR(" ", "Voltage Sensor", this->voltage_sensor_);
LOG_SENSOR(" ", "Current A Sensor", this->current_a_sensor_);
LOG_SENSOR(" ", "Current B Sensor", this->current_b_sensor_);
LOG_SENSOR(" ", "Power Factor A Sensor", this->power_factor_a_sensor_);
LOG_SENSOR(" ", "Power Factor B Sensor", this->power_factor_b_sensor_);
LOG_SENSOR(" ", "Apparent Power A Sensor", this->apparent_power_a_sensor_);
LOG_SENSOR(" ", "Apparent Power B Sensor", this->apparent_power_b_sensor_);
LOG_SENSOR(" ", "Active Power A Sensor", this->active_power_a_sensor_);
LOG_SENSOR(" ", "Active Power B Sensor", this->active_power_b_sensor_);
LOG_SENSOR(" ", "Rective Power A Sensor", this->reactive_power_a_sensor_);
LOG_SENSOR(" ", "Reactive Power B Sensor", this->reactive_power_b_sensor_);
ESP_LOGCONFIG(TAG, " PGA_V_8: 0x%X", pga_v_);
ESP_LOGCONFIG(TAG, " PGA_IA_8: 0x%X", pga_ia_);
ESP_LOGCONFIG(TAG, " PGA_IB_8: 0x%X", pga_ib_);
ESP_LOGCONFIG(TAG, " VGAIN_32: 0x%08jX", (uintmax_t) vgain_);
ESP_LOGCONFIG(TAG, " AIGAIN_32: 0x%08jX", (uintmax_t) aigain_);
ESP_LOGCONFIG(TAG, " BIGAIN_32: 0x%08jX", (uintmax_t) bigain_);
ESP_LOGCONFIG(TAG, " AWGAIN_32: 0x%08jX", (uintmax_t) awgain_);
ESP_LOGCONFIG(TAG, " BWGAIN_32: 0x%08jX", (uintmax_t) bwgain_);
}
#define ADE_PUBLISH_(name, val, factor) \
if (err == 0 && this->name##_sensor_) { \
float value = (val) / (factor); \
this->name##_sensor_->publish_state(value); \
}
#define ADE_PUBLISH(name, val, factor) ADE_PUBLISH_(name, val, factor)
void ADE7953::update() {
if (!this->is_setup_)
return;
bool err;
uint32_t interrupts_a = 0;
uint32_t interrupts_b = 0;
if (this->irq_pin_ != nullptr) {
// Read and reset interrupts
this->ade_read_32(0x032E, &interrupts_a);
this->ade_read_32(0x0331, &interrupts_b);
}
uint32_t val;
uint16_t val_16;
// Power factor
err = this->ade_read_16(0x010A, &val_16);
ADE_PUBLISH(power_factor_a, (int16_t) val_16, (0x7FFF / 100.0f));
err = this->ade_read_16(0x010B, &val_16);
ADE_PUBLISH(power_factor_b, (int16_t) val_16, (0x7FFF / 100.0f));
// Apparent power
err = this->ade_read_32(0x0310, &val);
ADE_PUBLISH(apparent_power_a, (int32_t) val, 154.0f);
err = this->ade_read_32(0x0311, &val);
ADE_PUBLISH(apparent_power_b, (int32_t) val, 154.0f);
// Active power
err = this->ade_read_32(0x0312, &val);
ADE_PUBLISH(active_power_a, (int32_t) val, 154.0f);
err = this->ade_read_32(0x0313, &val);
ADE_PUBLISH(active_power_b, (int32_t) val, 154.0f);
// Reactive power
err = this->ade_read_32(0x0314, &val);
ADE_PUBLISH(reactive_power_a, (int32_t) val, 154.0f);
err = this->ade_read_32(0x0315, &val);
ADE_PUBLISH(reactive_power_b, (int32_t) val, 154.0f);
// Current
err = this->ade_read_32(0x031A, &val);
ADE_PUBLISH(current_a, (uint32_t) val, 100000.0f);
err = this->ade_read_32(0x031B, &val);
ADE_PUBLISH(current_b, (uint32_t) val, 100000.0f);
// Voltage
err = this->ade_read_32(0x031C, &val);
ADE_PUBLISH(voltage, (uint32_t) val, 26000.0f);
// Frequency
err = this->ade_read_16(0x010E, &val_16);
ADE_PUBLISH(frequency, 223750.0f, 1 + val_16);
}
} // namespace ade7953_base
} // namespace esphome

View file

@ -0,0 +1,121 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/hal.h"
#include "esphome/components/sensor/sensor.h"
#include <vector>
namespace esphome {
namespace ade7953_base {
static const uint8_t PGA_V_8 =
0x007; // PGA_V, (R/W) Default: 0x00, Unsigned, Voltage channel gain configuration (Bits[2:0])
static const uint8_t PGA_IA_8 =
0x008; // PGA_IA, (R/W) Default: 0x00, Unsigned, Current Channel A gain configuration (Bits[2:0])
static const uint8_t PGA_IB_8 =
0x009; // PGA_IB, (R/W) Default: 0x00, Unsigned, Current Channel B gain configuration (Bits[2:0])
static const uint32_t AIGAIN_32 =
0x380; // AIGAIN, (R/W) Default: 0x400000, Unsigned,Current channel gain (Current Channel A)(32 bit)
static const uint32_t AVGAIN_32 = 0x381; // AVGAIN, (R/W) Default: 0x400000, Unsigned,Voltage channel gain(32 bit)
static const uint32_t AWGAIN_32 =
0x382; // AWGAIN, (R/W) Default: 0x400000, Unsigned,Active power gain (Current Channel A)(32 bit)
static const uint32_t AVARGAIN_32 =
0x383; // AVARGAIN, (R/W) Default: 0x400000, Unsigned, Reactive power gain (Current Channel A)(32 bit)
static const uint32_t AVAGAIN_32 =
0x384; // AVAGAIN, (R/W) Default: 0x400000, Unsigned,Apparent power gain (Current Channel A)(32 bit)
static const uint32_t BIGAIN_32 =
0x38C; // BIGAIN, (R/W) Default: 0x400000, Unsigned,Current channel gain (Current Channel B)(32 bit)
static const uint32_t BVGAIN_32 = 0x38D; // BVGAIN, (R/W) Default: 0x400000, Unsigned,Voltage channel gain(32 bit)
static const uint32_t BWGAIN_32 =
0x38E; // BWGAIN, (R/W) Default: 0x400000, Unsigned,Active power gain (Current Channel B)(32 bit)
static const uint32_t BVARGAIN_32 =
0x38F; // BVARGAIN, (R/W) Default: 0x400000, Unsigned, Reactive power gain (Current Channel B)(32 bit)
static const uint32_t BVAGAIN_32 =
0x390; // BVAGAIN, (R/W) Default: 0x400000, Unsigned,Apparent power gain (Current Channel B)(32 bit)
class ADE7953 : public PollingComponent, public sensor::Sensor {
public:
void set_irq_pin(InternalGPIOPin *irq_pin) { irq_pin_ = irq_pin; }
// Set PGA input gains: 0 1x, 1 2x, 0b10 4x
void set_pga_v(uint8_t pga_v) { pga_v_ = pga_v; }
void set_pga_ia(uint8_t pga_ia) { pga_ia_ = pga_ia; }
void set_pga_ib(uint8_t pga_ib) { pga_ib_ = pga_ib; }
// Set input gains
void set_vgain(uint32_t vgain) { vgain_ = vgain; }
void set_aigain(uint32_t aigain) { aigain_ = aigain; }
void set_bigain(uint32_t bigain) { bigain_ = bigain; }
void set_awgain(uint32_t awgain) { awgain_ = awgain; }
void set_bwgain(uint32_t bwgain) { bwgain_ = bwgain; }
void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; }
void set_frequency_sensor(sensor::Sensor *frequency_sensor) { frequency_sensor_ = frequency_sensor; }
void set_power_factor_a_sensor(sensor::Sensor *power_factor_a) { power_factor_a_sensor_ = power_factor_a; }
void set_power_factor_b_sensor(sensor::Sensor *power_factor_b) { power_factor_b_sensor_ = power_factor_b; }
void set_current_a_sensor(sensor::Sensor *current_a_sensor) { current_a_sensor_ = current_a_sensor; }
void set_current_b_sensor(sensor::Sensor *current_b_sensor) { current_b_sensor_ = current_b_sensor; }
void set_apparent_power_a_sensor(sensor::Sensor *apparent_power_a) { apparent_power_a_sensor_ = apparent_power_a; }
void set_apparent_power_b_sensor(sensor::Sensor *apparent_power_b) { apparent_power_b_sensor_ = apparent_power_b; }
void set_active_power_a_sensor(sensor::Sensor *active_power_a_sensor) {
active_power_a_sensor_ = active_power_a_sensor;
}
void set_active_power_b_sensor(sensor::Sensor *active_power_b_sensor) {
active_power_b_sensor_ = active_power_b_sensor;
}
void set_reactive_power_a_sensor(sensor::Sensor *reactive_power_a) { reactive_power_a_sensor_ = reactive_power_a; }
void set_reactive_power_b_sensor(sensor::Sensor *reactive_power_b) { reactive_power_b_sensor_ = reactive_power_b; }
void setup() override;
void dump_config() override;
void update() override;
protected:
InternalGPIOPin *irq_pin_{nullptr};
bool is_setup_{false};
sensor::Sensor *voltage_sensor_{nullptr};
sensor::Sensor *frequency_sensor_{nullptr};
sensor::Sensor *current_a_sensor_{nullptr};
sensor::Sensor *current_b_sensor_{nullptr};
sensor::Sensor *apparent_power_a_sensor_{nullptr};
sensor::Sensor *apparent_power_b_sensor_{nullptr};
sensor::Sensor *active_power_a_sensor_{nullptr};
sensor::Sensor *active_power_b_sensor_{nullptr};
sensor::Sensor *reactive_power_a_sensor_{nullptr};
sensor::Sensor *reactive_power_b_sensor_{nullptr};
sensor::Sensor *power_factor_a_sensor_{nullptr};
sensor::Sensor *power_factor_b_sensor_{nullptr};
uint8_t pga_v_;
uint8_t pga_ia_;
uint8_t pga_ib_;
uint32_t vgain_;
uint32_t aigain_;
uint32_t bigain_;
uint32_t awgain_;
uint32_t bwgain_;
virtual bool ade_write_8(uint16_t reg, uint8_t value) = 0;
virtual bool ade_write_16(uint16_t reg, uint16_t value) = 0;
virtual bool ade_write_32(uint16_t reg, uint32_t value) = 0;
virtual bool ade_read_8(uint16_t reg, uint8_t *value) = 0;
virtual bool ade_read_16(uint16_t reg, uint16_t *value) = 0;
virtual bool ade_read_32(uint16_t reg, uint32_t *value) = 0;
};
} // namespace ade7953_base
} // namespace esphome

View file

@ -0,0 +1 @@
CODEOWNERS = ["@angelnu"]

View file

@ -0,0 +1,80 @@
#include "ade7953_i2c.h"
#include "esphome/core/log.h"
#include "esphome/core/helpers.h"
namespace esphome {
namespace ade7953_i2c {
static const char *const TAG = "ade7953";
void AdE7953I2c::dump_config() {
ESP_LOGCONFIG(TAG, "ADE7953_i2c:");
LOG_I2C_DEVICE(this);
ade7953_base::ADE7953::dump_config();
}
bool AdE7953I2c::ade_write_8(uint16_t reg, uint8_t value) {
std::vector<uint8_t> data(3);
data.push_back(reg >> 8);
data.push_back(reg >> 0);
data.push_back(value);
return this->write(data.data(), data.size()) != i2c::ERROR_OK;
}
bool AdE7953I2c::ade_write_16(uint16_t reg, uint16_t value) {
std::vector<uint8_t> data(4);
data.push_back(reg >> 8);
data.push_back(reg >> 0);
data.push_back(value >> 8);
data.push_back(value >> 0);
return this->write(data.data(), data.size()) != i2c::ERROR_OK;
}
bool AdE7953I2c::ade_write_32(uint16_t reg, uint32_t value) {
std::vector<uint8_t> data(6);
data.push_back(reg >> 8);
data.push_back(reg >> 0);
data.push_back(value >> 24);
data.push_back(value >> 16);
data.push_back(value >> 8);
data.push_back(value >> 0);
return this->write(data.data(), data.size()) != i2c::ERROR_OK;
}
bool AdE7953I2c::ade_read_8(uint16_t reg, uint8_t *value) {
uint8_t reg_data[2];
reg_data[0] = reg >> 8;
reg_data[1] = reg >> 0;
i2c::ErrorCode err = this->write(reg_data, 2);
if (err != i2c::ERROR_OK)
return true;
err = this->read(value, 1);
return (err != i2c::ERROR_OK);
}
bool AdE7953I2c::ade_read_16(uint16_t reg, uint16_t *value) {
uint8_t reg_data[2];
reg_data[0] = reg >> 8;
reg_data[1] = reg >> 0;
i2c::ErrorCode err = this->write(reg_data, 2);
if (err != i2c::ERROR_OK)
return true;
uint8_t recv[2];
err = this->read(recv, 2);
if (err != i2c::ERROR_OK)
return true;
*value = encode_uint16(recv[0], recv[1]);
return false;
}
bool AdE7953I2c::ade_read_32(uint16_t reg, uint32_t *value) {
uint8_t reg_data[2];
reg_data[0] = reg >> 8;
reg_data[1] = reg >> 0;
i2c::ErrorCode err = this->write(reg_data, 2);
if (err != i2c::ERROR_OK)
return true;
uint8_t recv[4];
err = this->read(recv, 4);
if (err != i2c::ERROR_OK)
return true;
*value = encode_uint32(recv[0], recv[1], recv[2], recv[3]);
return false;
}
} // namespace ade7953_i2c
} // namespace esphome

View file

@ -0,0 +1,28 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/hal.h"
#include "esphome/components/i2c/i2c.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/ade7953_base/ade7953_base.h"
#include <vector>
namespace esphome {
namespace ade7953_i2c {
class AdE7953I2c : public ade7953_base::ADE7953, public i2c::I2CDevice {
public:
void dump_config() override;
protected:
bool ade_write_8(uint16_t reg, uint8_t value) override;
bool ade_write_16(uint16_t reg, uint16_t value) override;
bool ade_write_32(uint16_t reg, uint32_t value) override;
bool ade_read_8(uint16_t reg, uint8_t *value) override;
bool ade_read_16(uint16_t reg, uint16_t *value) override;
bool ade_read_32(uint16_t reg, uint32_t *value) override;
};
} // namespace ade7953_i2c
} // namespace esphome

View file

@ -0,0 +1,27 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import i2c, ade7953_base
from esphome.const import CONF_ID
DEPENDENCIES = ["i2c"]
AUTO_LOAD = ["ade7953_base"]
ade7953_ns = cg.esphome_ns.namespace("ade7953_i2c")
ADE7953 = ade7953_ns.class_("AdE7953I2c", ade7953_base.ADE7953, i2c.I2CDevice)
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(ADE7953),
}
)
.extend(ade7953_base.ADE7953_CONFIG_SCHEMA)
.extend(i2c.i2c_device_schema(0x38))
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await i2c.register_i2c_device(var, config)
await ade7953_base.register_ade7953(var, config)

View file

@ -0,0 +1 @@
CODEOWNERS = ["@angelnu"]

View file

@ -0,0 +1,81 @@
#include "ade7953_spi.h"
#include "esphome/core/log.h"
#include "esphome/core/helpers.h"
namespace esphome {
namespace ade7953_spi {
static const char *const TAG = "ade7953";
void AdE7953Spi::setup() {
this->spi_setup();
ade7953_base::ADE7953::setup();
}
void AdE7953Spi::dump_config() {
ESP_LOGCONFIG(TAG, "ADE7953_spi:");
LOG_PIN(" CS Pin: ", this->cs_);
ade7953_base::ADE7953::dump_config();
}
bool AdE7953Spi::ade_write_8(uint16_t reg, uint8_t value) {
this->enable();
this->write_byte16(reg);
this->transfer_byte(0);
this->transfer_byte(value);
this->disable();
return false;
}
bool AdE7953Spi::ade_write_16(uint16_t reg, uint16_t value) {
this->enable();
this->write_byte16(reg);
this->transfer_byte(0);
this->write_byte16(value);
this->disable();
return false;
}
bool AdE7953Spi::ade_write_32(uint16_t reg, uint32_t value) {
this->enable();
this->write_byte16(reg);
this->transfer_byte(0);
this->write_byte16(value >> 16);
this->write_byte16(value & 0xFFFF);
this->disable();
return false;
}
bool AdE7953Spi::ade_read_8(uint16_t reg, uint8_t *value) {
this->enable();
this->write_byte16(reg);
this->transfer_byte(0x80);
*value = this->read_byte();
this->disable();
return false;
}
bool AdE7953Spi::ade_read_16(uint16_t reg, uint16_t *value) {
this->enable();
this->write_byte16(reg);
this->transfer_byte(0x80);
uint8_t recv[2];
this->read_array(recv, 4);
*value = encode_uint16(recv[0], recv[1]);
this->disable();
return false;
}
bool AdE7953Spi::ade_read_32(uint16_t reg, uint32_t *value) {
this->enable();
this->write_byte16(reg);
this->transfer_byte(0x80);
uint8_t recv[4];
this->read_array(recv, 4);
*value = encode_uint32(recv[0], recv[1], recv[2], recv[3]);
this->disable();
return false;
}
} // namespace ade7953_spi
} // namespace esphome

View file

@ -0,0 +1,32 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/hal.h"
#include "esphome/components/spi/spi.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/ade7953_base/ade7953_base.h"
#include <vector>
namespace esphome {
namespace ade7953_spi {
class AdE7953Spi : public ade7953_base::ADE7953,
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_HIGH, spi::CLOCK_PHASE_LEADING,
spi::DATA_RATE_1MHZ> {
public:
void setup() override;
void dump_config() override;
protected:
bool ade_write_8(uint16_t reg, uint8_t value) override;
bool ade_write_16(uint16_t reg, uint16_t value) override;
bool ade_write_32(uint16_t reg, uint32_t value) override;
bool ade_read_8(uint16_t reg, uint8_t *value) override;
bool ade_read_16(uint16_t reg, uint16_t *value) override;
bool ade_read_32(uint16_t reg, uint32_t *value) override;
};
} // namespace ade7953_spi
} // namespace esphome

View file

@ -0,0 +1,27 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import spi, ade7953_base
from esphome.const import CONF_ID
DEPENDENCIES = ["spi"]
AUTO_LOAD = ["ade7953_base"]
ade7953_ns = cg.esphome_ns.namespace("ade7953_spi")
ADE7953 = ade7953_ns.class_("AdE7953Spi", ade7953_base.ADE7953, spi.SPIDevice)
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(ADE7953),
}
)
.extend(ade7953_base.ADE7953_CONFIG_SCHEMA)
.extend(spi.spi_device_schema())
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await spi.register_spi_device(var, config)
await ade7953_base.register_ade7953(var, config)

View file

@ -15,6 +15,7 @@
#include "aht10.h"
#include "esphome/core/log.h"
#include "esphome/core/hal.h"
#include <cinttypes>
namespace esphome {
namespace aht10 {
@ -72,7 +73,7 @@ void AHT10Component::update() {
delay_ms = AHT10_HUMIDITY_DELAY;
bool success = false;
for (int i = 0; i < AHT10_ATTEMPTS; ++i) {
ESP_LOGVV(TAG, "Attempt %d at %6u", i, millis());
ESP_LOGVV(TAG, "Attempt %d at %6" PRIu32, i, millis());
delay(delay_ms);
if (this->read(data, 6) != i2c::ERROR_OK) {
ESP_LOGD(TAG, "Communication with AHT10 failed, waiting...");
@ -96,7 +97,7 @@ void AHT10Component::update() {
}
} else {
// data is valid, we can break the loop
ESP_LOGVV(TAG, "Answer at %6u", millis());
ESP_LOGVV(TAG, "Answer at %6" PRIu32, millis());
success = true;
break;
}

View file

@ -1,71 +1,65 @@
from __future__ import annotations
import asyncio
import logging
from datetime import datetime
from typing import Optional
from typing import Any
from aioesphomeapi import APIClient, ReconnectLogic, APIConnectionError, LogLevel
import zeroconf
from aioesphomeapi import APIClient
from aioesphomeapi.api_pb2 import SubscribeLogsResponse
from aioesphomeapi.log_runner import async_run
from zeroconf.asyncio import AsyncZeroconf
from esphome.const import CONF_KEY, CONF_PASSWORD, CONF_PORT, __version__
from esphome.core import CORE
from esphome.const import CONF_KEY, CONF_PORT, CONF_PASSWORD, __version__
from esphome.util import safe_print
from . import CONF_ENCRYPTION
_LOGGER = logging.getLogger(__name__)
async def async_run_logs(config, address):
"""Run the logs command in the event loop."""
conf = config["api"]
port: int = int(conf[CONF_PORT])
password: str = conf[CONF_PASSWORD]
noise_psk: Optional[str] = None
noise_psk: str | None = None
if CONF_ENCRYPTION in conf:
noise_psk = conf[CONF_ENCRYPTION][CONF_KEY]
_LOGGER.info("Starting log output from %s using esphome API", address)
aiozc = AsyncZeroconf()
cli = APIClient(
address,
port,
password,
client_info=f"ESPHome Logs {__version__}",
noise_psk=noise_psk,
zeroconf_instance=aiozc.zeroconf,
)
first_connect = True
dashboard = CORE.dashboard
def on_log(msg):
time_ = datetime.now().time().strftime("[%H:%M:%S]")
text = msg.message.decode("utf8", "backslashreplace")
safe_print(time_ + text)
async def on_connect():
nonlocal first_connect
try:
await cli.subscribe_logs(
on_log,
log_level=LogLevel.LOG_LEVEL_VERY_VERBOSE,
dump_config=first_connect,
)
first_connect = False
except APIConnectionError:
cli.disconnect()
async def on_disconnect(expected_disconnect: bool) -> None:
_LOGGER.warning("Disconnected from API")
zc = zeroconf.Zeroconf()
reconnect = ReconnectLogic(
client=cli,
on_connect=on_connect,
on_disconnect=on_disconnect,
zeroconf_instance=zc,
)
await reconnect.start()
def on_log(msg: SubscribeLogsResponse) -> None:
"""Handle a new log message."""
time_ = datetime.now()
message: bytes = msg.message
text = message.decode("utf8", "backslashreplace")
if dashboard:
text = text.replace("\033", "\\033")
print(f"[{time_.hour:02}:{time_.minute:02}:{time_.second:02}]{text}")
stop = await async_run(cli, on_log, aio_zeroconf_instance=aiozc)
try:
while True:
await asyncio.sleep(60)
finally:
await aiozc.async_close()
await stop()
def run_logs(config: dict[str, Any], address: str) -> None:
"""Run the logs command."""
try:
asyncio.run(async_run_logs(config, address))
except KeyboardInterrupt:
await reconnect.stop()
zc.close()
def run_logs(config, address):
asyncio.run(async_run_logs(config, address))
pass

View file

@ -5,7 +5,7 @@ namespace esphome {
namespace api {
template<> bool get_execute_arg_value<bool>(const ExecuteServiceArgument &arg) { return arg.bool_; }
template<> int get_execute_arg_value<int>(const ExecuteServiceArgument &arg) {
template<> int32_t get_execute_arg_value<int32_t>(const ExecuteServiceArgument &arg) {
if (arg.legacy_int != 0)
return arg.legacy_int;
return arg.int_;
@ -26,11 +26,13 @@ template<> std::vector<std::string> get_execute_arg_value<std::vector<std::strin
}
template<> enums::ServiceArgType to_service_arg_type<bool>() { return enums::SERVICE_ARG_TYPE_BOOL; }
template<> enums::ServiceArgType to_service_arg_type<int>() { return enums::SERVICE_ARG_TYPE_INT; }
template<> enums::ServiceArgType to_service_arg_type<int32_t>() { return enums::SERVICE_ARG_TYPE_INT; }
template<> enums::ServiceArgType to_service_arg_type<float>() { return enums::SERVICE_ARG_TYPE_FLOAT; }
template<> enums::ServiceArgType to_service_arg_type<std::string>() { return enums::SERVICE_ARG_TYPE_STRING; }
template<> enums::ServiceArgType to_service_arg_type<std::vector<bool>>() { return enums::SERVICE_ARG_TYPE_BOOL_ARRAY; }
template<> enums::ServiceArgType to_service_arg_type<std::vector<int>>() { return enums::SERVICE_ARG_TYPE_INT_ARRAY; }
template<> enums::ServiceArgType to_service_arg_type<std::vector<int32_t>>() {
return enums::SERVICE_ARG_TYPE_INT_ARRAY;
}
template<> enums::ServiceArgType to_service_arg_type<std::vector<float>>() {
return enums::SERVICE_ARG_TYPE_FLOAT_ARRAY;
}

View file

@ -1,6 +1,7 @@
#include "atm90e32.h"
#include "atm90e32_reg.h"
#include "esphome/core/log.h"
#include <cinttypes>
namespace esphome {
namespace atm90e32 {
@ -173,7 +174,7 @@ uint16_t ATM90E32Component::read16_(uint16_t a_register) {
this->disable();
output = (uint16_t(data[0] & 0xFF) << 8) | (data[1] & 0xFF);
ESP_LOGVV(TAG, "read16_ 0x%04X output 0x%04X", a_register, output);
ESP_LOGVV(TAG, "read16_ 0x%04" PRIX16 " output 0x%04" PRIX16, a_register, output);
return output;
}
@ -182,8 +183,10 @@ int ATM90E32Component::read32_(uint16_t addr_h, uint16_t addr_l) {
uint16_t val_l = this->read16_(addr_l);
int32_t val = (val_h << 16) | val_l;
ESP_LOGVV(TAG, "read32_ addr_h 0x%04X val_h 0x%04X addr_l 0x%04X val_l 0x%04X = %d", addr_h, val_h, addr_l, val_l,
val);
ESP_LOGVV(TAG,
"read32_ addr_h 0x%04" PRIX16 " val_h 0x%04" PRIX16 " addr_l 0x%04" PRIX16 " val_l 0x%04" PRIX16
" = %" PRId32,
addr_h, val_h, addr_l, val_l, val);
return val;
}
@ -192,7 +195,7 @@ void ATM90E32Component::write16_(uint16_t a_register, uint16_t val) {
uint8_t addrh = (a_register >> 8) & 0x03;
uint8_t addrl = (a_register & 0xFF);
ESP_LOGVV(TAG, "write16_ 0x%04X val 0x%04X", a_register, val);
ESP_LOGVV(TAG, "write16_ 0x%04" PRIX16 " val 0x%04" PRIX16, a_register, val);
this->enable();
delayMicroseconds(10);
this->write_byte(addrh);

View file

@ -1,5 +1,6 @@
#include "bl0939.h"
#include "esphome/core/log.h"
#include <cinttypes>
namespace esphome {
namespace bl0939 {
@ -80,7 +81,7 @@ void BL0939::setup() {
void BL0939::received_package_(const DataPacket *data) const {
// Bad header
if (data->frame_header != BL0939_PACKET_HEADER) {
ESP_LOGI("bl0939", "Invalid data. Header mismatch: %d", data->frame_header);
ESP_LOGI(TAG, "Invalid data. Header mismatch: %d", data->frame_header);
return;
}
@ -120,8 +121,9 @@ void BL0939::received_package_(const DataPacket *data) const {
energy_sensor_sum_->publish_state(total_energy_consumption);
}
ESP_LOGV("bl0939", "BL0939: U %fV, I1 %fA, I2 %fA, P1 %fW, P2 %fW, CntA %d, CntB %d, ∫P1 %fkWh, ∫P2 %fkWh", v_rms,
ia_rms, ib_rms, a_watt, b_watt, cfa_cnt, cfb_cnt, a_energy_consumption, b_energy_consumption);
ESP_LOGV(TAG,
"BL0939: U %fV, I1 %fA, I2 %fA, P1 %fW, P2 %fW, CntA %" PRId32 ", CntB %" PRId32 ", ∫P1 %fkWh, ∫P2 %fkWh",
v_rms, ia_rms, ib_rms, a_watt, b_watt, cfa_cnt, cfb_cnt, a_energy_consumption, b_energy_consumption);
}
void BL0939::dump_config() { // NOLINT(readability-function-cognitive-complexity)

View file

@ -1,5 +1,6 @@
#include "bl0940.h"
#include "esphome/core/log.h"
#include <cinttypes>
namespace esphome {
namespace bl0940 {
@ -77,7 +78,7 @@ float BL0940::update_temp_(sensor::Sensor *sensor, ube16_t temperature) const {
float converted_temp = ((float) 170 / 448) * (tb / 2 - 32) - 45;
if (sensor != nullptr) {
if (sensor->has_state() && std::abs(converted_temp - sensor->get_state()) > max_temperature_diff_) {
ESP_LOGD("bl0940", "Invalid temperature change. Sensor: '%s', Old temperature: %f, New temperature: %f",
ESP_LOGD(TAG, "Invalid temperature change. Sensor: '%s', Old temperature: %f, New temperature: %f",
sensor->get_name().c_str(), sensor->get_state(), converted_temp);
return 0.0f;
}
@ -89,7 +90,7 @@ float BL0940::update_temp_(sensor::Sensor *sensor, ube16_t temperature) const {
void BL0940::received_package_(const DataPacket *data) const {
// Bad header
if (data->frame_header != BL0940_PACKET_HEADER) {
ESP_LOGI("bl0940", "Invalid data. Header mismatch: %d", data->frame_header);
ESP_LOGI(TAG, "Invalid data. Header mismatch: %d", data->frame_header);
return;
}
@ -115,7 +116,7 @@ void BL0940::received_package_(const DataPacket *data) const {
energy_sensor_->publish_state(total_energy_consumption);
}
ESP_LOGV("bl0940", "BL0940: U %fV, I %fA, P %fW, Cnt %d, ∫P %fkWh, T1 %f°C, T2 %f°C", v_rms, i_rms, watt, cf_cnt,
ESP_LOGV(TAG, "BL0940: U %fV, I %fA, P %fW, Cnt %" PRId32 ", ∫P %fkWh, T1 %f°C, T2 %f°C", v_rms, i_rms, watt, cf_cnt,
total_energy_consumption, tps1, tps2);
}

View file

@ -1,5 +1,6 @@
#include "bl0942.h"
#include "esphome/core/log.h"
#include <cinttypes>
namespace esphome {
namespace bl0942 {
@ -104,8 +105,8 @@ void BL0942::received_package_(DataPacket *data) {
frequency_sensor_->publish_state(frequency);
}
ESP_LOGV(TAG, "BL0942: U %fV, I %fA, P %fW, Cnt %d, ∫P %fkWh, frequency %f°Hz, status 0x%08X", v_rms, i_rms, watt,
cf_cnt, total_energy_consumption, frequency, data->status);
ESP_LOGV(TAG, "BL0942: U %fV, I %fA, P %fW, Cnt %" PRId32 ", ∫P %fkWh, frequency %fHz, status 0x%08X", v_rms, i_rms,
watt, cf_cnt, total_energy_consumption, frequency, data->status);
}
void BL0942::dump_config() { // NOLINT(readability-function-cognitive-complexity)

View file

@ -1,8 +1,8 @@
#include "ble_rssi_sensor.h"
#include "esphome/core/log.h"
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#include "esphome/core/application.h"
#include "esphome/core/helpers.h"
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#include "esphome/core/log.h"
#ifdef USE_ESP32
@ -37,6 +37,10 @@ void BLEClientRSSISensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga
}
case ESP_GATTC_SEARCH_CMPL_EVT:
this->node_state = espbt::ClientState::ESTABLISHED;
if (this->should_update_) {
this->should_update_ = false;
this->get_rssi_();
}
break;
default:
break;
@ -50,6 +54,7 @@ void BLEClientRSSISensor::gap_event_handler(esp_gap_ble_cb_event_t event, esp_bl
if (param->read_rssi_cmpl.status == ESP_BT_STATUS_SUCCESS) {
int8_t rssi = param->read_rssi_cmpl.rssi;
ESP_LOGI(TAG, "ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT RSSI: %d", rssi);
this->status_clear_warning();
this->publish_state(rssi);
}
break;
@ -61,9 +66,12 @@ void BLEClientRSSISensor::gap_event_handler(esp_gap_ble_cb_event_t event, esp_bl
void BLEClientRSSISensor::update() {
if (this->node_state != espbt::ClientState::ESTABLISHED) {
ESP_LOGW(TAG, "[%s] Cannot poll, not connected", this->get_name().c_str());
this->should_update_ = true;
return;
}
this->get_rssi_();
}
void BLEClientRSSISensor::get_rssi_() {
ESP_LOGV(TAG, "requesting rssi from %s", this->parent()->address_str().c_str());
auto status = esp_ble_gap_read_rssi(this->parent()->get_remote_bda());
if (status != ESP_OK) {

View file

@ -1,9 +1,9 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/ble_client/ble_client.h"
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/core/component.h"
#ifdef USE_ESP32
#include <esp_gattc_api.h>
@ -24,6 +24,10 @@ class BLEClientRSSISensor : public sensor::Sensor, public PollingComponent, publ
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) override;
protected:
void get_rssi_();
bool should_update_{false};
};
} // namespace ble_client

View file

@ -8,6 +8,7 @@
#include "bmp3xx.h"
#include "esphome/core/log.h"
#include "esphome/core/hal.h"
#include <cinttypes>
namespace esphome {
namespace bmp3xx {
@ -198,8 +199,9 @@ void BMP3XXComponent::update() {
return;
}
ESP_LOGVV(TAG, "measurement time %d", uint32_t(ceilf(meas_time)));
this->set_timeout("data", uint32_t(ceilf(meas_time)), [this]() {
const uint32_t meas_timeout = uint32_t(ceilf(meas_time));
ESP_LOGVV(TAG, "measurement time %" PRIu32, meas_timeout);
this->set_timeout("data", meas_timeout, [this]() {
float temperature = 0.0f;
float pressure = 0.0f;
if (this->pressure_sensor_ != nullptr) {

View file

@ -51,9 +51,9 @@ void Canbus::send_data(uint32_t can_id, bool use_extended_id, bool remote_transm
void Canbus::add_trigger(CanbusTrigger *trigger) {
if (trigger->use_extended_id_) {
ESP_LOGVV(TAG, "add trigger for extended canid=0x%08x", trigger->can_id_);
ESP_LOGVV(TAG, "add trigger for extended canid=0x%08" PRIx32, trigger->can_id_);
} else {
ESP_LOGVV(TAG, "add trigger for std canid=0x%03x", trigger->can_id_);
ESP_LOGVV(TAG, "add trigger for std canid=0x%03" PRIx32, trigger->can_id_);
}
this->triggers_.push_back(trigger);
};

View file

@ -1,5 +1,6 @@
#include "cd74hc4067.h"
#include "esphome/core/log.h"
#include <cinttypes>
namespace esphome {
namespace cd74hc4067 {
@ -27,7 +28,7 @@ void CD74HC4067Component::dump_config() {
LOG_PIN(" S1 Pin: ", this->pin_s1_);
LOG_PIN(" S2 Pin: ", this->pin_s2_);
LOG_PIN(" S3 Pin: ", this->pin_s3_);
ESP_LOGCONFIG(TAG, "switch delay: %d", this->switch_delay_);
ESP_LOGCONFIG(TAG, "switch delay: %" PRIu32, this->switch_delay_);
}
void CD74HC4067Component::activate_pin(uint8_t pin) {

View file

@ -217,7 +217,7 @@ void CSE7761Component::get_data_() {
this->voltage_sensor_->publish_state(voltage);
}
for (uint32_t channel = 0; channel < 2; channel++) {
for (uint8_t channel = 0; channel < 2; channel++) {
// Active power = PowerPA * PowerPAC * 1000 / 0x80000000
float active_power = (float) this->data_.active_power[channel] / this->coefficient_by_unit_(POWER_PAC); // W
float amps = (float) this->data_.current_rms[channel] / this->coefficient_by_unit_(RMS_IAC); // A

View file

@ -1,5 +1,6 @@
#include "cse7766.h"
#include "esphome/core/log.h"
#include <cinttypes>
namespace esphome {
namespace cse7766 {
@ -162,7 +163,7 @@ void CSE7766Component::update() {
if (counts != 0) {
const auto avg = acc / counts;
ESP_LOGV(TAG, "Got %s_acc=%.2f %s_counts=%d %s=%.1f", name, acc, name, counts, name, avg);
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);
@ -178,7 +179,8 @@ void CSE7766Component::update() {
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=%d", this->energy_total_, this->energy_total_counts_);
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_);

View file

@ -1,15 +1,17 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation
from esphome.const import CONF_ID
from esphome.core import CORE
from esphome.components.esp32 import add_idf_sdkconfig_option, get_esp32_variant, const
DEPENDENCIES = ["esp32"]
CODEOWNERS = ["@jesserockz"]
CODEOWNERS = ["@jesserockz", "@Rapsssito"]
CONFLICTS_WITH = ["esp32_ble_beacon"]
CONF_BLE_ID = "ble_id"
CONF_IO_CAPABILITY = "io_capability"
CONF_ENABLE_ON_BOOT = "enable_on_boot"
NO_BLUETOOTH_VARIANTS = [const.VARIANT_ESP32S2]
@ -20,6 +22,10 @@ GAPEventHandler = esp32_ble_ns.class_("GAPEventHandler")
GATTcEventHandler = esp32_ble_ns.class_("GATTcEventHandler")
GATTsEventHandler = esp32_ble_ns.class_("GATTsEventHandler")
BLEEnabledCondition = esp32_ble_ns.class_("BLEEnabledCondition", automation.Condition)
BLEEnableAction = esp32_ble_ns.class_("BLEEnableAction", automation.Action)
BLEDisableAction = esp32_ble_ns.class_("BLEDisableAction", automation.Action)
IoCapability = esp32_ble_ns.enum("IoCapability")
IO_CAPABILITY = {
"none": IoCapability.IO_CAP_NONE,
@ -35,6 +41,7 @@ CONFIG_SCHEMA = cv.Schema(
cv.Optional(CONF_IO_CAPABILITY, default="none"): cv.enum(
IO_CAPABILITY, lower=True
),
cv.Optional(CONF_ENABLE_ON_BOOT, default=True): cv.boolean,
}
).extend(cv.COMPONENT_SCHEMA)
@ -50,9 +57,25 @@ FINAL_VALIDATE_SCHEMA = validate_variant
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
cg.add(var.set_enable_on_boot(config[CONF_ENABLE_ON_BOOT]))
cg.add(var.set_io_capability(config[CONF_IO_CAPABILITY]))
await cg.register_component(var, config)
if CORE.using_esp_idf:
add_idf_sdkconfig_option("CONFIG_BT_ENABLED", True)
add_idf_sdkconfig_option("CONFIG_BT_BLE_42_FEATURES_SUPPORTED", True)
@automation.register_condition("ble.enabled", BLEEnabledCondition, cv.Schema({}))
async def ble_enabled_to_code(config, condition_id, template_arg, args):
return cg.new_Pvariable(condition_id, template_arg)
@automation.register_action("ble.enable", BLEEnableAction, cv.Schema({}))
async def ble_enable_to_code(config, action_id, template_arg, args):
return cg.new_Pvariable(action_id, template_arg)
@automation.register_action("ble.disable", BLEDisableAction, cv.Schema({}))
async def ble_disable_to_code(config, action_id, template_arg, args):
return cg.new_Pvariable(action_id, template_arg)

View file

@ -5,8 +5,8 @@
#include "esphome/core/log.h"
#include <esp_bt.h>
#include <esp_bt_main.h>
#include <esp_bt_device.h>
#include <esp_bt_main.h>
#include <esp_gap_ble_api.h>
#include <freertos/FreeRTOS.h>
#include <freertos/FreeRTOSConfig.h>
@ -26,30 +26,85 @@ void ESP32BLE::setup() {
global_ble = this;
ESP_LOGCONFIG(TAG, "Setting up BLE...");
if (!ble_setup_()) {
ESP_LOGE(TAG, "BLE could not be set up");
if (!ble_pre_setup_()) {
ESP_LOGE(TAG, "BLE could not be prepared for configuration");
this->mark_failed();
return;
}
#ifdef USE_ESP32_BLE_SERVER
this->advertising_ = new BLEAdvertising(); // NOLINT(cppcoreguidelines-owning-memory)
this->advertising_->set_scan_response(true);
this->advertising_->set_min_preferred_interval(0x06);
this->advertising_->start();
#endif // USE_ESP32_BLE_SERVER
ESP_LOGD(TAG, "BLE setup complete");
this->state_ = BLE_COMPONENT_STATE_DISABLED;
if (this->enable_on_boot_) {
this->enable();
}
}
bool ESP32BLE::ble_setup_() {
void ESP32BLE::enable() {
if (this->state_ != BLE_COMPONENT_STATE_DISABLED)
return;
this->state_ = BLE_COMPONENT_STATE_ENABLE;
}
void ESP32BLE::disable() {
if (this->state_ == BLE_COMPONENT_STATE_DISABLED)
return;
this->state_ = BLE_COMPONENT_STATE_DISABLE;
}
bool ESP32BLE::is_active() { return this->state_ == BLE_COMPONENT_STATE_ACTIVE; }
void ESP32BLE::advertising_start() {
this->advertising_init_();
if (!this->is_active())
return;
this->advertising_->start();
}
void ESP32BLE::advertising_set_service_data(const std::vector<uint8_t> &data) {
this->advertising_init_();
this->advertising_->set_service_data(data);
this->advertising_start();
}
void ESP32BLE::advertising_set_manufacturer_data(const std::vector<uint8_t> &data) {
this->advertising_init_();
this->advertising_->set_manufacturer_data(data);
this->advertising_start();
}
void ESP32BLE::advertising_add_service_uuid(ESPBTUUID uuid) {
this->advertising_init_();
this->advertising_->add_service_uuid(uuid);
this->advertising_start();
}
void ESP32BLE::advertising_remove_service_uuid(ESPBTUUID uuid) {
this->advertising_init_();
this->advertising_->remove_service_uuid(uuid);
this->advertising_start();
}
bool ESP32BLE::ble_pre_setup_() {
esp_err_t err = nvs_flash_init();
if (err != ESP_OK) {
ESP_LOGE(TAG, "nvs_flash_init failed: %d", err);
return false;
}
return true;
}
void ESP32BLE::advertising_init_() {
if (this->advertising_ != nullptr)
return;
this->advertising_ = new BLEAdvertising(); // NOLINT(cppcoreguidelines-owning-memory)
this->advertising_->set_scan_response(true);
this->advertising_->set_min_preferred_interval(0x06);
}
bool ESP32BLE::ble_setup_() {
esp_err_t err;
#ifdef USE_ARDUINO
if (!btStart()) {
ESP_LOGE(TAG, "btStart failed: %d", esp_bt_controller_get_status());
@ -146,7 +201,88 @@ bool ESP32BLE::ble_setup_() {
return true;
}
bool ESP32BLE::ble_dismantle_() {
esp_err_t err = esp_bluedroid_disable();
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_bluedroid_disable failed: %d", err);
return false;
}
err = esp_bluedroid_deinit();
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_bluedroid_deinit failed: %d", err);
return false;
}
#ifdef USE_ARDUINO
if (!btStop()) {
ESP_LOGE(TAG, "btStop failed: %d", esp_bt_controller_get_status());
return false;
}
#else
if (esp_bt_controller_get_status() != ESP_BT_CONTROLLER_STATUS_IDLE) {
// stop bt controller
if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_ENABLED) {
err = esp_bt_controller_disable();
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_bt_controller_disable failed: %s", esp_err_to_name(err));
return false;
}
while (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_ENABLED)
;
}
if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_INITED) {
err = esp_bt_controller_deinit();
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_bt_controller_deinit failed: %s", esp_err_to_name(err));
return false;
}
}
if (esp_bt_controller_get_status() != ESP_BT_CONTROLLER_STATUS_IDLE) {
ESP_LOGE(TAG, "esp bt controller disable failed");
return false;
}
}
#endif
return true;
}
void ESP32BLE::loop() {
switch (this->state_) {
case BLE_COMPONENT_STATE_OFF:
case BLE_COMPONENT_STATE_DISABLED:
return;
case BLE_COMPONENT_STATE_DISABLE: {
ESP_LOGD(TAG, "Disabling BLE...");
for (auto *ble_event_handler : this->ble_status_event_handlers_) {
ble_event_handler->ble_before_disabled_event_handler();
}
if (!ble_dismantle_()) {
ESP_LOGE(TAG, "BLE could not be dismantled");
this->mark_failed();
return;
}
this->state_ = BLE_COMPONENT_STATE_DISABLED;
return;
}
case BLE_COMPONENT_STATE_ENABLE: {
ESP_LOGD(TAG, "Enabling BLE...");
this->state_ = BLE_COMPONENT_STATE_OFF;
if (!ble_setup_()) {
ESP_LOGE(TAG, "BLE could not be set up");
this->mark_failed();
return;
}
this->state_ = BLE_COMPONENT_STATE_ACTIVE;
return;
}
case BLE_COMPONENT_STATE_ACTIVE:
break;
}
BLEEvent *ble_event = this->ble_events_.pop();
while (ble_event != nullptr) {
switch (ble_event->type_) {

View file

@ -1,19 +1,21 @@
#pragma once
#include "ble_advertising.h"
#include "ble_uuid.h"
#include "esphome/core/automation.h"
#include "esphome/core/component.h"
#include "esphome/core/defines.h"
#include "esphome/core/helpers.h"
#include "queue.h"
#include "ble_event.h"
#include "queue.h"
#ifdef USE_ESP32
#include <esp_gap_ble_api.h>
#include <esp_gatts_api.h>
#include <esp_gattc_api.h>
#include <esp_gatts_api.h>
namespace esphome {
namespace esp32_ble {
@ -35,6 +37,19 @@ enum IoCapability {
IO_CAP_KBDISP = ESP_IO_CAP_KBDISP,
};
enum BLEComponentState {
/** Nothing has been initialized yet. */
BLE_COMPONENT_STATE_OFF = 0,
/** BLE should be disabled on next loop. */
BLE_COMPONENT_STATE_DISABLE,
/** BLE is disabled. */
BLE_COMPONENT_STATE_DISABLED,
/** BLE should be enabled on next loop. */
BLE_COMPONENT_STATE_ENABLE,
/** BLE is active. */
BLE_COMPONENT_STATE_ACTIVE,
};
class GAPEventHandler {
public:
virtual void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) = 0;
@ -52,20 +67,36 @@ class GATTsEventHandler {
esp_ble_gatts_cb_param_t *param) = 0;
};
class BLEStatusEventHandler {
public:
virtual void ble_before_disabled_event_handler() = 0;
};
class ESP32BLE : public Component {
public:
void set_io_capability(IoCapability io_capability) { this->io_cap_ = (esp_ble_io_cap_t) io_capability; }
void enable();
void disable();
bool is_active();
void setup() override;
void loop() override;
void dump_config() override;
float get_setup_priority() const override;
BLEAdvertising *get_advertising() { return this->advertising_; }
void advertising_start();
void advertising_set_service_data(const std::vector<uint8_t> &data);
void advertising_set_manufacturer_data(const std::vector<uint8_t> &data);
void advertising_add_service_uuid(ESPBTUUID uuid);
void advertising_remove_service_uuid(ESPBTUUID uuid);
void register_gap_event_handler(GAPEventHandler *handler) { this->gap_event_handlers_.push_back(handler); }
void register_gattc_event_handler(GATTcEventHandler *handler) { this->gattc_event_handlers_.push_back(handler); }
void register_gatts_event_handler(GATTsEventHandler *handler) { this->gatts_event_handlers_.push_back(handler); }
void register_ble_status_event_handler(BLEStatusEventHandler *handler) {
this->ble_status_event_handlers_.push_back(handler);
}
void set_enable_on_boot(bool enable_on_boot) { this->enable_on_boot_ = enable_on_boot; }
protected:
static void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param);
@ -77,19 +108,40 @@ class ESP32BLE : public Component {
void real_gap_event_handler_(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param);
bool ble_setup_();
bool ble_dismantle_();
bool ble_pre_setup_();
void advertising_init_();
std::vector<GAPEventHandler *> gap_event_handlers_;
std::vector<GATTcEventHandler *> gattc_event_handlers_;
std::vector<GATTsEventHandler *> gatts_event_handlers_;
std::vector<BLEStatusEventHandler *> ble_status_event_handlers_;
BLEComponentState state_{BLE_COMPONENT_STATE_OFF};
Queue<BLEEvent> ble_events_;
BLEAdvertising *advertising_;
esp_ble_io_cap_t io_cap_{ESP_IO_CAP_NONE};
bool enable_on_boot_;
};
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
extern ESP32BLE *global_ble;
template<typename... Ts> class BLEEnabledCondition : public Condition<Ts...> {
public:
bool check(Ts... x) override { return global_ble->is_active(); }
};
template<typename... Ts> class BLEEnableAction : public Action<Ts...> {
public:
void play(Ts... x) override { global_ble->enable(); }
};
template<typename... Ts> class BLEDisableAction : public Action<Ts...> {
public:
void play(Ts... x) override { global_ble->disable(); }
};
} // namespace esp32_ble
} // namespace esphome

View file

@ -20,16 +20,21 @@ static const esp_bt_uuid_t NOTIFY_DESC_UUID = {
void BLEClientBase::setup() {
static uint8_t connection_index = 0;
this->connection_index_ = connection_index++;
auto ret = esp_ble_gattc_app_register(this->app_id);
if (ret) {
ESP_LOGE(TAG, "gattc app register failed. app_id=%d code=%d", this->app_id, ret);
this->mark_failed();
}
this->set_state(espbt::ClientState::IDLE);
}
void BLEClientBase::loop() {
if (!esp32_ble::global_ble->is_active()) {
this->set_state(espbt::ClientState::INIT);
return;
}
if (this->state_ == espbt::ClientState::INIT) {
auto ret = esp_ble_gattc_app_register(this->app_id);
if (ret) {
ESP_LOGE(TAG, "gattc app register failed. app_id=%d code=%d", this->app_id, ret);
this->mark_failed();
}
this->set_state(espbt::ClientState::IDLE);
}
// READY_TO_CONNECT means we have discovered the device
// and the scanner has been stopped by the tracker.
if (this->state_ == espbt::ClientState::READY_TO_CONNECT) {

View file

@ -6,7 +6,7 @@ from esphome.core import CORE
from esphome.components.esp32 import add_idf_sdkconfig_option
AUTO_LOAD = ["esp32_ble"]
CODEOWNERS = ["@jesserockz", "@clydebarrow"]
CODEOWNERS = ["@jesserockz", "@clydebarrow", "@Rapsssito"]
CONFLICTS_WITH = ["esp32_ble_beacon"]
DEPENDENCIES = ["esp32"]
@ -41,6 +41,7 @@ async def to_code(config):
parent = await cg.get_variable(config[esp32_ble.CONF_BLE_ID])
cg.add(parent.register_gatts_event_handler(var))
cg.add(parent.register_ble_status_event_handler(var))
cg.add(var.set_parent(parent))
cg.add(var.set_manufacturer(config[CONF_MANUFACTURER]))

View file

@ -11,6 +11,13 @@ namespace esp32_ble_server {
static const char *const TAG = "esp32_ble_server.characteristic";
BLECharacteristic::~BLECharacteristic() {
for (auto *descriptor : this->descriptors_) {
delete descriptor; // NOLINT(cppcoreguidelines-owning-memory)
}
vSemaphoreDelete(this->set_value_lock_);
}
BLECharacteristic::BLECharacteristic(const ESPBTUUID uuid, uint32_t properties) : uuid_(uuid) {
this->set_value_lock_ = xSemaphoreCreateBinary();
xSemaphoreGive(this->set_value_lock_);
@ -98,6 +105,11 @@ void BLECharacteristic::notify(bool notification) {
void BLECharacteristic::add_descriptor(BLEDescriptor *descriptor) { this->descriptors_.push_back(descriptor); }
void BLECharacteristic::remove_descriptor(BLEDescriptor *descriptor) {
this->descriptors_.erase(std::remove(this->descriptors_.begin(), this->descriptors_.end(), descriptor),
this->descriptors_.end());
}
void BLECharacteristic::do_create(BLEService *service) {
this->service_ = service;
esp_attr_control_t control;

View file

@ -25,6 +25,7 @@ class BLEService;
class BLECharacteristic {
public:
BLECharacteristic(ESPBTUUID uuid, uint32_t properties);
~BLECharacteristic();
void set_value(const uint8_t *data, size_t length);
void set_value(std::vector<uint8_t> value);
@ -52,6 +53,7 @@ class BLECharacteristic {
void on_write(const std::function<void(const std::vector<uint8_t> &)> &&func) { this->on_write_ = func; }
void add_descriptor(BLEDescriptor *descriptor);
void remove_descriptor(BLEDescriptor *descriptor);
BLEService *get_service() { return this->service_; }
ESPBTUUID get_uuid() { return this->uuid_; }

View file

@ -30,13 +30,13 @@ void BLEServer::setup() {
ESP_LOGE(TAG, "BLE Server was marked failed by ESP32BLE");
return;
}
ESP_LOGD(TAG, "Setting up BLE Server...");
global_ble_server = this;
}
void BLEServer::loop() {
if (!this->parent_->is_active()) {
return;
}
switch (this->state_) {
case RUNNING:
return;
@ -53,10 +53,16 @@ void BLEServer::loop() {
}
case REGISTERING: {
if (this->registered_) {
this->device_information_service_ = this->create_service(DEVICE_INFORMATION_SERVICE_UUID);
this->create_device_characteristics_();
// Create all services previously created
for (auto &pair : this->services_) {
pair.second->do_create(this);
}
if (this->device_information_service_ == nullptr) {
this->create_service(ESPBTUUID::from_uint16(DEVICE_INFORMATION_SERVICE_UUID));
this->device_information_service_ =
this->get_service(ESPBTUUID::from_uint16(DEVICE_INFORMATION_SERVICE_UUID));
this->create_device_characteristics_();
}
this->state_ = STARTING_SERVICE;
}
break;
@ -67,7 +73,6 @@ void BLEServer::loop() {
}
if (this->device_information_service_->is_running()) {
this->state_ = RUNNING;
this->can_proceed_ = true;
this->restart_advertising_();
ESP_LOGD(TAG, "BLE server setup successfully");
} else if (!this->device_information_service_->is_starting()) {
@ -78,10 +83,13 @@ void BLEServer::loop() {
}
}
bool BLEServer::is_running() { return this->parent_->is_active() && this->state_ == RUNNING; }
bool BLEServer::can_proceed() { return this->is_running() || !this->parent_->is_active(); }
void BLEServer::restart_advertising_() {
if (this->state_ == RUNNING) {
esp32_ble::global_ble->get_advertising()->set_manufacturer_data(this->manufacturer_data_);
esp32_ble::global_ble->get_advertising()->start();
if (this->is_running()) {
this->parent_->advertising_set_manufacturer_data(this->manufacturer_data_);
}
}
@ -107,24 +115,36 @@ bool BLEServer::create_device_characteristics_() {
return true;
}
std::shared_ptr<BLEService> BLEServer::create_service(const uint8_t *uuid, bool advertise) {
return this->create_service(ESPBTUUID::from_raw(uuid), advertise);
}
std::shared_ptr<BLEService> BLEServer::create_service(uint16_t uuid, bool advertise) {
return this->create_service(ESPBTUUID::from_uint16(uuid), advertise);
}
std::shared_ptr<BLEService> BLEServer::create_service(const std::string &uuid, bool advertise) {
return this->create_service(ESPBTUUID::from_raw(uuid), advertise);
}
std::shared_ptr<BLEService> BLEServer::create_service(ESPBTUUID uuid, bool advertise, uint16_t num_handles,
uint8_t inst_id) {
ESP_LOGV(TAG, "Creating service - %s", uuid.to_string().c_str());
std::shared_ptr<BLEService> service = std::make_shared<BLEService>(uuid, num_handles, inst_id);
this->services_.emplace_back(service);
if (advertise) {
esp32_ble::global_ble->get_advertising()->add_service_uuid(uuid);
void BLEServer::create_service(ESPBTUUID uuid, bool advertise, uint16_t num_handles, uint8_t inst_id) {
ESP_LOGV(TAG, "Creating BLE service - %s", uuid.to_string().c_str());
// If the service already exists, do nothing
BLEService *service = this->get_service(uuid);
if (service != nullptr) {
ESP_LOGW(TAG, "BLE service %s already exists", uuid.to_string().c_str());
return;
}
service = new BLEService(uuid, num_handles, inst_id, advertise); // NOLINT(cppcoreguidelines-owning-memory)
this->services_.emplace(uuid.to_string(), service);
service->do_create(this);
}
void BLEServer::remove_service(ESPBTUUID uuid) {
ESP_LOGV(TAG, "Removing BLE service - %s", uuid.to_string().c_str());
BLEService *service = this->get_service(uuid);
if (service == nullptr) {
ESP_LOGW(TAG, "BLE service %s not found", uuid.to_string().c_str());
return;
}
service->do_delete();
delete service; // NOLINT(cppcoreguidelines-owning-memory)
this->services_.erase(uuid.to_string());
}
BLEService *BLEServer::get_service(ESPBTUUID uuid) {
BLEService *service = nullptr;
if (this->services_.count(uuid.to_string()) > 0) {
service = this->services_.at(uuid.to_string());
}
return service;
}
@ -144,7 +164,7 @@ void BLEServer::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t ga
ESP_LOGD(TAG, "BLE Client disconnected");
if (this->remove_client_(param->disconnect.conn_id))
this->connected_clients_--;
esp32_ble::global_ble->get_advertising()->start();
this->parent_->advertising_start();
for (auto *component : this->service_components_) {
component->on_client_disconnect();
}
@ -159,11 +179,22 @@ void BLEServer::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t ga
break;
}
for (const auto &service : this->services_) {
service->gatts_event_handler(event, gatts_if, param);
for (const auto &pair : this->services_) {
pair.second->gatts_event_handler(event, gatts_if, param);
}
}
void BLEServer::ble_before_disabled_event_handler() {
// Delete all clients
this->clients_.clear();
// Delete all services
for (auto &pair : this->services_) {
pair.second->do_delete();
}
this->registered_ = false;
this->state_ = INIT;
}
float BLEServer::get_setup_priority() const { return setup_priority::AFTER_BLUETOOTH + 10; }
void BLEServer::dump_config() { ESP_LOGCONFIG(TAG, "ESP32 BLE Server:"); }

View file

@ -11,9 +11,9 @@
#include "esphome/core/helpers.h"
#include "esphome/core/preferences.h"
#include <map>
#include <memory>
#include <vector>
#include <unordered_map>
#ifdef USE_ESP32
@ -33,15 +33,16 @@ class BLEServiceComponent {
virtual void stop();
};
class BLEServer : public Component, public GATTsEventHandler, public Parented<ESP32BLE> {
class BLEServer : public Component, public GATTsEventHandler, public BLEStatusEventHandler, public Parented<ESP32BLE> {
public:
void setup() override;
void loop() override;
void dump_config() override;
float get_setup_priority() const override;
bool can_proceed() override { return this->can_proceed_; }
bool can_proceed() override;
void teardown();
bool is_running();
void set_manufacturer(const std::string &manufacturer) { this->manufacturer_ = manufacturer; }
void set_model(const std::string &model) { this->model_ = model; }
@ -50,32 +51,28 @@ class BLEServer : public Component, public GATTsEventHandler, public Parented<ES
this->restart_advertising_();
}
std::shared_ptr<BLEService> create_service(const uint8_t *uuid, bool advertise = false);
std::shared_ptr<BLEService> create_service(uint16_t uuid, bool advertise = false);
std::shared_ptr<BLEService> create_service(const std::string &uuid, bool advertise = false);
std::shared_ptr<BLEService> create_service(ESPBTUUID uuid, bool advertise = false, uint16_t num_handles = 15,
uint8_t inst_id = 0);
void create_service(ESPBTUUID uuid, bool advertise = false, uint16_t num_handles = 15, uint8_t inst_id = 0);
void remove_service(ESPBTUUID uuid);
BLEService *get_service(ESPBTUUID uuid);
esp_gatt_if_t get_gatts_if() { return this->gatts_if_; }
uint32_t get_connected_client_count() { return this->connected_clients_; }
const std::map<uint16_t, void *> &get_clients() { return this->clients_; }
const std::unordered_map<uint16_t, void *> &get_clients() { return this->clients_; }
void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if,
esp_ble_gatts_cb_param_t *param) override;
void ble_before_disabled_event_handler() override;
void register_service_component(BLEServiceComponent *component) { this->service_components_.push_back(component); }
protected:
bool create_device_characteristics_();
void restart_advertising_();
void add_client_(uint16_t conn_id, void *client) {
this->clients_.insert(std::pair<uint16_t, void *>(conn_id, client));
}
void add_client_(uint16_t conn_id, void *client) { this->clients_.emplace(conn_id, client); }
bool remove_client_(uint16_t conn_id) { return this->clients_.erase(conn_id) > 0; }
bool can_proceed_{false};
std::string manufacturer_;
optional<std::string> model_;
std::vector<uint8_t> manufacturer_data_;
@ -83,10 +80,9 @@ class BLEServer : public Component, public GATTsEventHandler, public Parented<ES
bool registered_{false};
uint32_t connected_clients_{0};
std::map<uint16_t, void *> clients_;
std::vector<std::shared_ptr<BLEService>> services_;
std::shared_ptr<BLEService> device_information_service_;
std::unordered_map<uint16_t, void *> clients_;
std::unordered_map<std::string, BLEService *> services_;
BLEService *device_information_service_;
std::vector<BLEServiceComponent *> service_components_;

View file

@ -9,8 +9,8 @@ namespace esp32_ble_server {
static const char *const TAG = "esp32_ble_server.service";
BLEService::BLEService(ESPBTUUID uuid, uint16_t num_handles, uint8_t inst_id)
: uuid_(uuid), num_handles_(num_handles), inst_id_(inst_id) {}
BLEService::BLEService(ESPBTUUID uuid, uint16_t num_handles, uint8_t inst_id, bool advertise)
: uuid_(uuid), num_handles_(num_handles), inst_id_(inst_id), advertise_(advertise) {}
BLEService::~BLEService() {
for (auto &chr : this->characteristics_)
@ -58,6 +58,20 @@ void BLEService::do_create(BLEServer *server) {
this->init_state_ = CREATING;
}
void BLEService::do_delete() {
if (this->init_state_ == DELETING || this->init_state_ == DELETED)
return;
this->init_state_ = DELETING;
this->created_characteristic_count_ = 0;
this->last_created_characteristic_ = nullptr;
this->stop_();
esp_err_t err = esp_ble_gatts_delete_service(this->handle_);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_ble_gatts_delete_service failed: %d", err);
return;
}
}
bool BLEService::do_create_characteristics_() {
if (this->created_characteristic_count_ >= this->characteristics_.size() &&
(this->last_created_characteristic_ == nullptr || this->last_created_characteristic_->is_created()))
@ -75,24 +89,34 @@ bool BLEService::do_create_characteristics_() {
void BLEService::start() {
if (this->do_create_characteristics_())
return;
should_start_ = true;
esp_err_t err = esp_ble_gatts_start_service(this->handle_);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_ble_gatts_start_service failed: %d", err);
return;
}
if (this->advertise_)
esp32_ble::global_ble->advertising_add_service_uuid(this->uuid_);
this->running_state_ = STARTING;
}
void BLEService::stop() {
should_start_ = false;
this->stop_();
}
void BLEService::stop_() {
if (this->running_state_ == STOPPING || this->running_state_ == STOPPED)
return;
this->running_state_ = STOPPING;
esp_err_t err = esp_ble_gatts_stop_service(this->handle_);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_ble_gatts_stop_service failed: %d", err);
return;
}
esp32_ble::global_ble->get_advertising()->remove_service_uuid(this->uuid_);
esp32_ble::global_ble->get_advertising()->start();
this->running_state_ = STOPPING;
if (this->advertise_)
esp32_ble::global_ble->advertising_remove_service_uuid(this->uuid_);
}
bool BLEService::is_created() { return this->init_state_ == CREATED; }
@ -116,9 +140,16 @@ void BLEService::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t g
this->inst_id_ == param->create.service_id.id.inst_id) {
this->handle_ = param->create.service_handle;
this->init_state_ = CREATED;
if (this->should_start_)
this->start();
}
break;
}
case ESP_GATTS_DELETE_EVT:
if (param->del.service_handle == this->handle_) {
this->init_state_ = DELETED;
}
break;
case ESP_GATTS_START_EVT: {
if (param->start.service_handle == this->handle_) {
this->running_state_ = RUNNING;

View file

@ -22,7 +22,7 @@ using namespace esp32_ble;
class BLEService {
public:
BLEService(ESPBTUUID uuid, uint16_t num_handles, uint8_t inst_id);
BLEService(ESPBTUUID uuid, uint16_t num_handles, uint8_t inst_id, bool advertise);
~BLEService();
BLECharacteristic *get_characteristic(ESPBTUUID uuid);
BLECharacteristic *get_characteristic(uint16_t uuid);
@ -38,6 +38,7 @@ class BLEService {
BLEServer *get_server() { return this->server_; }
void do_create(BLEServer *server);
void do_delete();
void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param);
void start();
@ -48,6 +49,7 @@ class BLEService {
bool is_running() { return this->running_state_ == RUNNING; }
bool is_starting() { return this->running_state_ == STARTING; }
bool is_deleted() { return this->init_state_ == DELETED; }
protected:
std::vector<BLECharacteristic *> characteristics_;
@ -58,8 +60,11 @@ class BLEService {
uint16_t num_handles_;
uint16_t handle_{0xFFFF};
uint8_t inst_id_;
bool advertise_{false};
bool should_start_{false};
bool do_create_characteristics_();
void stop_();
enum InitState : uint8_t {
FAILED = 0x00,
@ -67,6 +72,8 @@ class BLEService {
CREATING,
CREATING_DEPENDENTS,
CREATED,
DELETING,
DELETED,
} init_state_{INIT};
enum RunningState : uint8_t {

View file

@ -212,6 +212,7 @@ async def to_code(config):
parent = await cg.get_variable(config[esp32_ble.CONF_BLE_ID])
cg.add(parent.register_gap_event_handler(var))
cg.add(parent.register_gattc_event_handler(var))
cg.add(parent.register_ble_status_event_handler(var))
cg.add(var.set_parent(parent))
params = config[CONF_SCAN_PARAMETERS]

View file

@ -64,17 +64,19 @@ void ESP32BLETracker::setup() {
}
});
#endif
if (this->scan_continuous_) {
if (xSemaphoreTake(this->scan_end_lock_, 0L)) {
this->start_scan_(true);
} else {
ESP_LOGW(TAG, "Cannot start scan!");
}
}
}
void ESP32BLETracker::loop() {
if (!this->parent_->is_active()) {
this->ble_was_disabled_ = true;
return;
} else if (this->ble_was_disabled_) {
this->ble_was_disabled_ = false;
// If the BLE stack was disabled, we need to start the scan again.
if (this->scan_continuous_) {
this->start_scan();
}
}
int connecting = 0;
int discovered = 0;
int searching = 0;
@ -182,8 +184,7 @@ void ESP32BLETracker::loop() {
xSemaphoreGive(this->scan_end_lock_);
} else {
ESP_LOGD(TAG, "Stopping scan after failure...");
esp_ble_gap_stop_scanning();
this->cancel_timeout("scan");
this->stop_scan_();
}
if (this->scan_start_failed_) {
ESP_LOGE(TAG, "Scan start failed: %d", this->scan_start_failed_);
@ -212,8 +213,7 @@ void ESP32BLETracker::loop() {
client->set_state(ClientState::READY_TO_CONNECT);
} else {
ESP_LOGD(TAG, "Pausing scan to make connection...");
esp_ble_gap_stop_scanning();
this->cancel_timeout("scan");
this->stop_scan_();
}
break;
}
@ -232,11 +232,31 @@ void ESP32BLETracker::start_scan() {
void ESP32BLETracker::stop_scan() {
ESP_LOGD(TAG, "Stopping scan.");
this->scan_continuous_ = false;
esp_ble_gap_stop_scanning();
this->stop_scan_();
}
void ESP32BLETracker::ble_before_disabled_event_handler() {
this->stop_scan_();
xSemaphoreGive(this->scan_end_lock_);
}
void ESP32BLETracker::stop_scan_() {
this->cancel_timeout("scan");
if (this->scanner_idle_) {
return;
}
esp_err_t err = esp_ble_gap_stop_scanning();
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_ble_gap_stop_scanning failed: %d", err);
return;
}
}
void ESP32BLETracker::start_scan_(bool first) {
if (!this->parent_->is_active()) {
ESP_LOGW(TAG, "Cannot start scan while ESP32BLE is disabled.");
return;
}
// The lock must be held when calling this function.
if (xSemaphoreTake(this->scan_end_lock_, 0L)) {
ESP_LOGE(TAG, "start_scan called without holding scan_end_lock_");
@ -249,15 +269,23 @@ void ESP32BLETracker::start_scan_(bool first) {
listener->on_scan_end();
}
this->already_discovered_.clear();
this->scanner_idle_ = false;
this->scan_params_.scan_type = this->scan_active_ ? BLE_SCAN_TYPE_ACTIVE : BLE_SCAN_TYPE_PASSIVE;
this->scan_params_.own_addr_type = BLE_ADDR_TYPE_PUBLIC;
this->scan_params_.scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL;
this->scan_params_.scan_interval = this->scan_interval_;
this->scan_params_.scan_window = this->scan_window_;
esp_ble_gap_set_scan_params(&this->scan_params_);
esp_ble_gap_start_scanning(this->scan_duration_);
esp_err_t err = esp_ble_gap_set_scan_params(&this->scan_params_);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_ble_gap_set_scan_params failed: %d", err);
return;
}
err = esp_ble_gap_start_scanning(this->scan_duration_);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_ble_gap_start_scanning failed: %d", err);
return;
}
this->scanner_idle_ = false;
this->set_timeout("scan", this->scan_duration_ * 2000, []() {
ESP_LOGE(TAG, "ESP-IDF BLE scan never terminated, rebooting to restore BLE stack...");

View file

@ -177,7 +177,11 @@ class ESPBTClient : public ESPBTDeviceListener {
ClientState state_;
};
class ESP32BLETracker : public Component, public GAPEventHandler, public GATTcEventHandler, public Parented<ESP32BLE> {
class ESP32BLETracker : public Component,
public GAPEventHandler,
public GATTcEventHandler,
public BLEStatusEventHandler,
public Parented<ESP32BLE> {
public:
void set_scan_duration(uint32_t scan_duration) { scan_duration_ = scan_duration; }
void set_scan_interval(uint32_t scan_interval) { scan_interval_ = scan_interval; }
@ -204,8 +208,10 @@ class ESP32BLETracker : public Component, public GAPEventHandler, public GATTcEv
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) override;
void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override;
void ble_before_disabled_event_handler() override;
protected:
void stop_scan_();
/// Start a single scan by setting up the parameters and doing some esp-idf calls.
void start_scan_(bool first);
/// Called when a scan ends
@ -236,6 +242,7 @@ class ESP32BLETracker : public Component, public GAPEventHandler, public GATTcEv
bool scan_continuous_;
bool scan_active_;
bool scanner_idle_;
bool ble_was_disabled_{true};
bool raw_advertisements_{false};
bool parse_advertisements_{false};
SemaphoreHandle_t scan_result_lock_;

View file

@ -16,9 +16,6 @@ static const char *const ESPHOME_MY_LINK = "https://my.home-assistant.io/redirec
ESP32ImprovComponent::ESP32ImprovComponent() { global_improv_component = this; }
void ESP32ImprovComponent::setup() {
this->service_ = global_ble_server->create_service(improv::SERVICE_UUID, true);
this->setup_characteristics();
#ifdef USE_BINARY_SENSOR
if (this->authorizer_ != nullptr) {
this->authorizer_->add_on_state_callback([this](bool state) {
@ -70,6 +67,19 @@ void ESP32ImprovComponent::setup_characteristics() {
}
void ESP32ImprovComponent::loop() {
if (!global_ble_server->is_running()) {
this->state_ = improv::STATE_STOPPED;
this->incoming_data_.clear();
return;
}
if (this->service_ == nullptr) {
// Setup the service
ESP_LOGD(TAG, "Creating Improv service");
global_ble_server->create_service(ESPBTUUID::from_raw(improv::SERVICE_UUID), true);
this->service_ = global_ble_server->get_service(ESPBTUUID::from_raw(improv::SERVICE_UUID));
this->setup_characteristics();
}
if (!this->incoming_data_.empty())
this->process_incoming_data_();
uint32_t now = millis();
@ -80,11 +90,10 @@ void ESP32ImprovComponent::loop() {
if (this->service_->is_created() && this->should_start_ && this->setup_complete_) {
if (this->service_->is_running()) {
esp32_ble::global_ble->get_advertising()->start();
esp32_ble::global_ble->advertising_start();
this->set_state_(improv::STATE_AWAITING_AUTHORIZATION);
this->set_error_(improv::ERROR_NONE);
this->should_start_ = false;
ESP_LOGD(TAG, "Service started!");
} else {
this->service_->start();
@ -138,10 +147,7 @@ void ESP32ImprovComponent::loop() {
#endif
std::vector<uint8_t> data = improv::build_rpc_response(improv::WIFI_SETTINGS, urls);
this->send_response_(data);
this->set_timeout("end-service", 1000, [this] {
this->service_->stop();
this->set_state_(improv::STATE_STOPPED);
});
this->stop();
}
break;
}
@ -206,8 +212,7 @@ void ESP32ImprovComponent::set_state_(improv::State state) {
service_data[6] = 0x00; // Reserved
service_data[7] = 0x00; // Reserved
esp32_ble::global_ble->get_advertising()->set_service_data(service_data);
esp32_ble::global_ble->get_advertising()->start();
esp32_ble::global_ble->advertising_set_service_data(service_data);
}
void ESP32ImprovComponent::set_error_(improv::Error error) {
@ -237,7 +242,10 @@ void ESP32ImprovComponent::start() {
}
void ESP32ImprovComponent::stop() {
this->should_start_ = false;
this->set_timeout("end-service", 1000, [this] {
if (this->state_ == improv::STATE_STOPPED || this->service_ == nullptr)
return;
this->service_->stop();
this->set_state_(improv::STATE_STOPPED);
});

View file

@ -68,7 +68,7 @@ class ESP32ImprovComponent : public Component, public BLEServiceComponent {
std::vector<uint8_t> incoming_data_;
wifi::WiFiAP connecting_sta_;
std::shared_ptr<BLEService> service_;
BLEService *service_ = nullptr;
BLECharacteristic *status_;
BLECharacteristic *error_;
BLECharacteristic *rpc_;

View file

@ -93,19 +93,19 @@ CONFIG_SCHEMA = cv.All(
cv.Inclusive(
CONF_BIT0_HIGH,
"custom",
): cv.positive_time_period_microseconds,
): cv.positive_time_period_nanoseconds,
cv.Inclusive(
CONF_BIT0_LOW,
"custom",
): cv.positive_time_period_microseconds,
): cv.positive_time_period_nanoseconds,
cv.Inclusive(
CONF_BIT1_HIGH,
"custom",
): cv.positive_time_period_microseconds,
): cv.positive_time_period_nanoseconds,
cv.Inclusive(
CONF_BIT1_LOW,
"custom",
): cv.positive_time_period_microseconds,
): cv.positive_time_period_nanoseconds,
}
),
cv.has_exactly_one_key(CONF_CHIPSET, CONF_BIT0_HIGH),

View file

@ -5,6 +5,7 @@
#ifdef USE_ESP32
#include <cinttypes>
#include <lwip/dns.h>
#include "esp_event.h"
@ -275,7 +276,7 @@ void EthernetComponent::got_ip_event_handler(void *arg, esp_event_base_t event_b
#if ENABLE_IPV6
void EthernetComponent::got_ip6_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id,
void *event_data) {
ESP_LOGV(TAG, "[Ethernet event] ETH Got IP6 (num=%d)", event_id);
ESP_LOGV(TAG, "[Ethernet event] ETH Got IP6 (num=%" PRId32 ")", event_id);
global_eth_component->got_ipv6_ = true;
global_eth_component->ipv6_count_ += 1;
}

View file

@ -1,5 +1,6 @@
#include "fingerprint_grow.h"
#include "esphome/core/log.h"
#include <cinttypes>
namespace esphome {
namespace fingerprint_grow {
@ -204,7 +205,7 @@ bool FingerprintGrowComponent::check_password_() {
}
bool FingerprintGrowComponent::set_password_() {
ESP_LOGI(TAG, "Setting new password: %d", this->new_password_);
ESP_LOGI(TAG, "Setting new password: %" PRIu32, this->new_password_);
this->data_ = {SET_PASSWORD, (uint8_t) (this->new_password_ >> 24), (uint8_t) (this->new_password_ >> 16),
(uint8_t) (this->new_password_ >> 8), (uint8_t) (this->new_password_ & 0xFF)};
if (this->send_command_() == OK) {

View file

@ -68,6 +68,7 @@ void LD2420Component::dump_config() {
ESP_LOGCONFIG(TAG, "LD2420:");
ESP_LOGCONFIG(TAG, " Firmware Version : %7s", this->ld2420_firmware_ver_);
ESP_LOGCONFIG(TAG, "LD2420 Number:");
#ifdef USE_NUMBER
LOG_NUMBER(TAG, " Gate Timeout:", this->gate_timeout_number_);
LOG_NUMBER(TAG, " Gate Max Distance:", this->max_gate_distance_number_);
LOG_NUMBER(TAG, " Gate Min Distance:", this->min_gate_distance_number_);
@ -76,10 +77,13 @@ void LD2420Component::dump_config() {
LOG_NUMBER(TAG, " Gate Move Threshold:", this->gate_move_threshold_numbers_[gate]);
LOG_NUMBER(TAG, " Gate Still Threshold::", this->gate_still_threshold_numbers_[gate]);
}
#endif
#ifdef USE_BUTTON
LOG_BUTTON(TAG, " Apply Config:", this->apply_config_button_);
LOG_BUTTON(TAG, " Revert Edits:", this->revert_config_button_);
LOG_BUTTON(TAG, " Factory Reset:", this->factory_reset_button_);
LOG_BUTTON(TAG, " Restart Module:", this->restart_module_button_);
#endif
ESP_LOGCONFIG(TAG, "LD2420 Select:");
LOG_SELECT(TAG, " Operating Mode", this->operating_selector_);
if (this->get_firmware_int_(ld2420_firmware_ver_) < CALIBRATE_VERSION_MIN) {
@ -183,9 +187,11 @@ void LD2420Component::factory_reset_action() {
return;
}
this->set_min_max_distances_timeout(FACTORY_MAX_GATE, FACTORY_MIN_GATE, FACTORY_TIMEOUT);
#ifdef USE_NUMBER
this->gate_timeout_number_->state = FACTORY_TIMEOUT;
this->min_gate_distance_number_->state = FACTORY_MIN_GATE;
this->max_gate_distance_number_->state = FACTORY_MAX_GATE;
#endif
for (uint8_t gate = 0; gate < LD2420_TOTAL_GATES; gate++) {
this->new_config.move_thresh[gate] = FACTORY_MOVE_THRESH[gate];
this->new_config.still_thresh[gate] = FACTORY_STILL_THRESH[gate];

View file

@ -3,8 +3,8 @@
#include <utility>
#include <vector>
#include "light_effect.h"
#include "esphome/core/automation.h"
#include "light_effect.h"
namespace esphome {
namespace light {
@ -27,8 +27,8 @@ class PulseLightEffect : public LightEffect {
auto call = this->state_->turn_on();
float out = this->on_ ? this->max_brightness : this->min_brightness;
call.set_brightness_if_supported(out);
call.set_transition_length_if_supported(this->on_ ? this->transition_on_length_ : this->transition_off_length_);
this->on_ = !this->on_;
call.set_transition_length_if_supported(this->transition_length_);
// don't tell HA every change
call.set_publish(false);
call.set_save(false);
@ -37,7 +37,8 @@ class PulseLightEffect : public LightEffect {
this->last_color_change_ = now;
}
void set_transition_length(uint32_t transition_length) { this->transition_length_ = transition_length; }
void set_transition_on_length(uint32_t transition_length) { this->transition_on_length_ = transition_length; }
void set_transition_off_length(uint32_t transition_length) { this->transition_off_length_ = transition_length; }
void set_update_interval(uint32_t update_interval) { this->update_interval_ = update_interval; }
@ -49,7 +50,8 @@ class PulseLightEffect : public LightEffect {
protected:
bool on_ = false;
uint32_t last_color_change_{0};
uint32_t transition_length_{};
uint32_t transition_on_length_{};
uint32_t transition_off_length_{};
uint32_t update_interval_{};
float min_brightness{0.0};
float max_brightness{1.0};

View file

@ -76,6 +76,8 @@ CONF_ADDRESSABLE_RANDOM_TWINKLE = "addressable_random_twinkle"
CONF_ADDRESSABLE_FIREWORKS = "addressable_fireworks"
CONF_ADDRESSABLE_FLICKER = "addressable_flicker"
CONF_AUTOMATION = "automation"
CONF_ON_LENGTH = "on_length"
CONF_OFF_LENGTH = "off_length"
BINARY_EFFECTS = []
MONOCHROMATIC_EFFECTS = []
@ -170,9 +172,15 @@ async def automation_effect_to_code(config, effect_id):
PulseLightEffect,
"Pulse",
{
cv.Optional(
CONF_TRANSITION_LENGTH, default="1s"
): cv.positive_time_period_milliseconds,
cv.Optional(CONF_TRANSITION_LENGTH, default="1s"): cv.Any(
cv.positive_time_period_milliseconds,
cv.Schema(
{
cv.Required(CONF_ON_LENGTH): cv.positive_time_period_milliseconds,
cv.Required(CONF_OFF_LENGTH): cv.positive_time_period_milliseconds,
}
),
),
cv.Optional(
CONF_UPDATE_INTERVAL, default="1s"
): cv.positive_time_period_milliseconds,
@ -182,7 +190,21 @@ async def automation_effect_to_code(config, effect_id):
)
async def pulse_effect_to_code(config, effect_id):
effect = cg.new_Pvariable(effect_id, config[CONF_NAME])
cg.add(effect.set_transition_length(config[CONF_TRANSITION_LENGTH]))
if isinstance(config[CONF_TRANSITION_LENGTH], dict):
cg.add(
effect.set_transition_on_length(
config[CONF_TRANSITION_LENGTH][CONF_ON_LENGTH]
)
)
cg.add(
effect.set_transition_off_length(
config[CONF_TRANSITION_LENGTH][CONF_OFF_LENGTH]
)
)
else:
transition_length = config[CONF_TRANSITION_LENGTH]
cg.add(effect.set_transition_on_length(transition_length))
cg.add(effect.set_transition_off_length(transition_length))
cg.add(effect.set_update_interval(config[CONF_UPDATE_INTERVAL]))
cg.add(
effect.set_min_max_brightness(

View file

@ -133,33 +133,47 @@ def validate_config(value):
# Populate default fields
out = value.copy()
topic_prefix = value[CONF_TOPIC_PREFIX]
# If the topic prefix is not null and these messages are not configured, then set them to the default
# If the topic prefix is null and these messages are not configured, then set them to null
if CONF_BIRTH_MESSAGE not in value:
out[CONF_BIRTH_MESSAGE] = {
CONF_TOPIC: f"{topic_prefix}/status",
CONF_PAYLOAD: "online",
CONF_QOS: 0,
CONF_RETAIN: True,
}
if topic_prefix != "":
out[CONF_BIRTH_MESSAGE] = {
CONF_TOPIC: f"{topic_prefix}/status",
CONF_PAYLOAD: "online",
CONF_QOS: 0,
CONF_RETAIN: True,
}
else:
out[CONF_BIRTH_MESSAGE] = {}
if CONF_WILL_MESSAGE not in value:
out[CONF_WILL_MESSAGE] = {
CONF_TOPIC: f"{topic_prefix}/status",
CONF_PAYLOAD: "offline",
CONF_QOS: 0,
CONF_RETAIN: True,
}
if topic_prefix != "":
out[CONF_WILL_MESSAGE] = {
CONF_TOPIC: f"{topic_prefix}/status",
CONF_PAYLOAD: "offline",
CONF_QOS: 0,
CONF_RETAIN: True,
}
else:
out[CONF_WILL_MESSAGE] = {}
if CONF_SHUTDOWN_MESSAGE not in value:
out[CONF_SHUTDOWN_MESSAGE] = {
CONF_TOPIC: f"{topic_prefix}/status",
CONF_PAYLOAD: "offline",
CONF_QOS: 0,
CONF_RETAIN: True,
}
if topic_prefix != "":
out[CONF_SHUTDOWN_MESSAGE] = {
CONF_TOPIC: f"{topic_prefix}/status",
CONF_PAYLOAD: "offline",
CONF_QOS: 0,
CONF_RETAIN: True,
}
else:
out[CONF_SHUTDOWN_MESSAGE] = {}
if CONF_LOG_TOPIC not in value:
out[CONF_LOG_TOPIC] = {
CONF_TOPIC: f"{topic_prefix}/debug",
CONF_QOS: 0,
CONF_RETAIN: True,
}
if topic_prefix != "":
out[CONF_LOG_TOPIC] = {
CONF_TOPIC: f"{topic_prefix}/debug",
CONF_QOS: 0,
CONF_RETAIN: True,
}
else:
out[CONF_LOG_TOPIC] = {}
return out

View file

@ -23,8 +23,13 @@ std::string MQTTComponent::get_discovery_topic_(const MQTTDiscoveryInfo &discove
}
std::string MQTTComponent::get_default_topic_for_(const std::string &suffix) const {
return global_mqtt_client->get_topic_prefix() + "/" + this->component_type() + "/" + this->get_default_object_id_() +
"/" + suffix;
const std::string &topic_prefix = global_mqtt_client->get_topic_prefix();
if (topic_prefix.empty()) {
// If the topic_prefix is null, the default topic should be null
return "";
}
return topic_prefix + "/" + this->component_type() + "/" + this->get_default_object_id_() + "/" + suffix;
}
std::string MQTTComponent::get_state_topic_() const {
@ -245,17 +250,25 @@ std::string MQTTComponent::friendly_name() const { return this->get_entity()->ge
std::string MQTTComponent::get_icon() const { return this->get_entity()->get_icon(); }
bool MQTTComponent::is_disabled_by_default() const { return this->get_entity()->is_disabled_by_default(); }
bool MQTTComponent::is_internal() {
if ((this->get_state_topic_().empty()) || (this->get_command_topic_().empty())) {
// If both state_topic and command_topic are empty, then the entity is internal to mqtt
if (this->has_custom_state_topic_) {
// If the custom state_topic is null, return true as it is internal and should not publish
// else, return false, as it is explicitly set to a topic, so it is not internal and should publish
return this->get_state_topic_().empty();
}
if (this->has_custom_command_topic_) {
// If the custom command_topic is null, return true as it is internal and should not publish
// else, return false, as it is explicitly set to a topic, so it is not internal and should publish
return this->get_command_topic_().empty();
}
// No custom topics have been set
if (this->get_default_topic_for_("").empty()) {
// If the default topic prefix is null, then the component, by default, is internal and should not publish
return true;
}
if (this->has_custom_state_topic_ || this->has_custom_command_topic_) {
// If a custom state_topic or command_topic is set, then the entity is not internal to mqtt
return false;
}
// Use ESPHome's entity internal state
// Use ESPHome's component internal state if topic_prefix is not null with no custom state_topic or command_topic
return this->get_entity()->is_internal();
}

View file

@ -21,6 +21,7 @@ CONF_ON_SETUP = "on_setup"
CONF_ON_PAGE = "on_page"
CONF_TOUCH_SLEEP_TIMEOUT = "touch_sleep_timeout"
CONF_WAKE_UP_PAGE = "wake_up_page"
CONF_START_UP_PAGE = "start_up_page"
CONF_AUTO_WAKE_ON_TOUCH = "auto_wake_on_touch"
CONF_WAVE_MAX_LENGTH = "wave_max_length"
CONF_BACKGROUND_COLOR = "background_color"

View file

@ -18,6 +18,7 @@ from .base_component import (
CONF_TFT_URL,
CONF_TOUCH_SLEEP_TIMEOUT,
CONF_WAKE_UP_PAGE,
CONF_START_UP_PAGE,
CONF_AUTO_WAKE_ON_TOUCH,
)
@ -35,7 +36,7 @@ CONFIG_SCHEMA = (
display.BASIC_DISPLAY_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(Nextion),
cv.Optional(CONF_TFT_URL): cv.All(cv.string, cv.only_with_arduino),
cv.Optional(CONF_TFT_URL): cv.url,
cv.Optional(CONF_BRIGHTNESS, default=1.0): cv.percentage,
cv.Optional(CONF_ON_SETUP): automation.validate_automation(
{
@ -59,6 +60,7 @@ CONFIG_SCHEMA = (
),
cv.Optional(CONF_TOUCH_SLEEP_TIMEOUT): cv.int_range(min=3, max=65535),
cv.Optional(CONF_WAKE_UP_PAGE): cv.positive_int,
cv.Optional(CONF_START_UP_PAGE): cv.positive_int,
cv.Optional(CONF_AUTO_WAKE_ON_TOUCH, default=True): cv.boolean,
}
)
@ -83,10 +85,10 @@ async def to_code(config):
if CONF_TFT_URL in config:
cg.add_define("USE_NEXTION_TFT_UPLOAD")
cg.add(var.set_tft_url(config[CONF_TFT_URL]))
if CORE.is_esp32:
if CORE.is_esp32 and CORE.using_arduino:
cg.add_library("WiFiClientSecure", None)
cg.add_library("HTTPClient", None)
if CORE.is_esp8266:
elif CORE.is_esp8266 and CORE.using_arduino:
cg.add_library("ESP8266HTTPClient", None)
if CONF_TOUCH_SLEEP_TIMEOUT in config:
@ -95,6 +97,9 @@ async def to_code(config):
if CONF_WAKE_UP_PAGE in config:
cg.add(var.set_wake_up_page_internal(config[CONF_WAKE_UP_PAGE]))
if CONF_START_UP_PAGE in config:
cg.add(var.set_start_up_page_internal(config[CONF_START_UP_PAGE]))
if CONF_AUTO_WAKE_ON_TOUCH in config:
cg.add(var.set_auto_wake_on_touch_internal(config[CONF_AUTO_WAKE_ON_TOUCH]))

View file

@ -128,12 +128,16 @@ void Nextion::dump_config() {
ESP_LOGCONFIG(TAG, " Wake On Touch: %s", this->auto_wake_on_touch_ ? "True" : "False");
if (this->touch_sleep_timeout_ != 0) {
ESP_LOGCONFIG(TAG, " Touch Timeout: %d", this->touch_sleep_timeout_);
ESP_LOGCONFIG(TAG, " Touch Timeout: %" PRIu32, this->touch_sleep_timeout_);
}
if (this->wake_up_page_ != -1) {
ESP_LOGCONFIG(TAG, " Wake Up Page : %d", this->wake_up_page_);
}
if (this->start_up_page_ != -1) {
ESP_LOGCONFIG(TAG, " Start Up Page : %d", this->start_up_page_);
}
}
float Nextion::get_setup_priority() const { return setup_priority::DATA; }
@ -231,7 +235,11 @@ void Nextion::loop() {
this->send_command_("bkcmd=3"); // Always, returns 0x00 to 0x23 result of serial command.
this->set_backlight_brightness(this->brightness_);
this->goto_page("0");
// Check if a startup page has been set and send the command
if (this->start_up_page_ != -1) {
this->goto_page(this->start_up_page_);
}
this->set_auto_wake_on_touch(this->auto_wake_on_touch_);
@ -860,6 +868,12 @@ uint16_t Nextion::recv_ret_string_(std::string &response, uint32_t timeout, bool
start = millis();
while ((timeout == 0 && this->available()) || millis() - start <= timeout) {
if (!this->available()) {
App.feed_wdt();
delay(1);
continue;
}
this->read_byte(&c);
if (c == 0xFF) {
nr_of_ff_bytes++;
@ -878,7 +892,7 @@ uint16_t Nextion::recv_ret_string_(std::string &response, uint32_t timeout, bool
}
}
App.feed_wdt();
delay(1);
delay(2);
if (exit_flag || ff_flag) {
break;

View file

@ -12,14 +12,18 @@
#include "esphome/components/display/display_color_utils.h"
#ifdef USE_NEXTION_TFT_UPLOAD
#ifdef ARDUINO
#ifdef USE_ESP32
#include <HTTPClient.h>
#endif
#endif // USE_ESP32
#ifdef USE_ESP8266
#include <ESP8266HTTPClient.h>
#include <WiFiClientSecure.h>
#endif
#endif
#endif // USE_ESP8266
#elif defined(USE_ESP_IDF)
#include <esp_http_client.h>
#endif // ARDUINO vs ESP-IDF
#endif // USE_NEXTION_TFT_UPLOAD
namespace esphome {
namespace nextion {
@ -334,6 +338,18 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe
* Switches to the page named `main`. Pages are named in the Nextion Editor.
*/
void goto_page(const char *page);
/**
* Show the page with a given id.
* @param page The id of the page.
*
* Example:
* ```cpp
* it.goto_page(2);
* ```
*
* Switches to the page named `main`. Pages are named in the Nextion Editor.
*/
void goto_page(uint8_t page);
/**
* Hide a component.
* @param component The component name.
@ -605,6 +621,20 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe
* The display will wake up to page 2.
*/
void set_wake_up_page(uint8_t page_id = 255);
/**
* Sets which page Nextion loads when connecting to ESPHome.
* @param page_id The page id, from 0 to the lage page in Nextion. Set 255 (not set to any existing page) to
* wakes up to current page.
*
* Example:
* ```cpp
* it.set_start_up_page(2);
* ```
*
* The display will go to page 2 when it establishes a connection to ESPHome.
*/
void set_start_up_page(uint8_t page_id = 255);
/**
* Sets if Nextion should auto-wake from sleep when touch press occurs.
* @param auto_wake True or false. When auto_wake is true and Nextion is in sleep mode,
@ -659,16 +689,18 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe
#ifdef USE_NEXTION_TFT_UPLOAD
/**
* Set the tft file URL. https seems problamtic with arduino..
* Set the tft file URL. https seems problematic with arduino..
*/
void set_tft_url(const std::string &tft_url) { this->tft_url_ = tft_url; }
#endif
/**
* Upload the tft file and softreset the Nextion
* Upload the tft file and soft reset Nextion
* @return bool True: Transfer completed successfuly, False: Transfer failed.
*/
void upload_tft();
bool upload_tft();
void dump_config() override;
/**
@ -736,6 +768,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe
this->touch_sleep_timeout_ = touch_sleep_timeout;
}
void set_wake_up_page_internal(uint8_t wake_up_page) { this->wake_up_page_ = wake_up_page; }
void set_start_up_page_internal(uint8_t start_up_page) { this->start_up_page_ = start_up_page; }
void set_auto_wake_on_touch_internal(bool auto_wake_on_touch) { this->auto_wake_on_touch_ = auto_wake_on_touch; }
protected:
@ -758,6 +791,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe
bool is_updating_ = false;
uint32_t touch_sleep_timeout_ = 0;
int wake_up_page_ = -1;
int start_up_page_ = -1;
bool auto_wake_on_touch_ = true;
/**
@ -789,16 +823,16 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe
BearSSL::WiFiClientSecure *wifi_client_secure_{nullptr};
WiFiClient *get_wifi_client_();
#endif
int content_length_ = 0;
int tft_size_ = 0;
#ifdef ARDUINO
/**
* will request chunk_size chunks from the web server
* and send each to the nextion
* @param int contentLength Total size of the file
* @param uint32_t chunk_size
* @return true if success, false for failure.
* @param HTTPClient http HTTP client handler.
* @param int range_start Position of next byte to transfer.
* @return position of last byte transferred, -1 for failure.
*/
int content_length_ = 0;
int tft_size_ = 0;
int upload_by_chunks_(HTTPClient *http, int range_start);
bool upload_with_range_(uint32_t range_start, uint32_t range_end);
@ -811,7 +845,30 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe
* @return true if success, false for failure.
*/
bool upload_from_buffer_(const uint8_t *file_buf, size_t buf_size);
void upload_end_();
/**
* Ends the upload process, restart Nextion and, if successful,
* restarts ESP
* @param bool url successful True: Transfer completed successfuly, False: Transfer failed.
* @return bool True: Transfer completed successfuly, False: Transfer failed.
*/
bool upload_end_(bool successful);
#elif defined(USE_ESP_IDF)
/**
* will request 4096 bytes chunks from the web server
* and send each to Nextion
* @param std::string url Full url for download.
* @param int range_start Position of next byte to transfer.
* @return position of last byte transferred, -1 for failure.
*/
int upload_range(const std::string &url, int range_start);
/**
* Ends the upload process, restart Nextion and, if successful,
* restarts ESP
* @param bool url successful True: Transfer completed successfuly, False: Transfer failed.
* @return bool True: Transfer completed successfuly, False: Transfer failed.
*/
bool upload_end(bool successful);
#endif // ARDUINO vs ESP-IDF
#endif // USE_NEXTION_TFT_UPLOAD

View file

@ -13,6 +13,8 @@ void Nextion::set_wake_up_page(uint8_t page_id) {
this->add_no_result_to_queue_with_set_internal_("wake_up_page", "wup", page_id, true);
}
void Nextion::set_start_up_page(uint8_t page_id) { this->start_up_page_ = page_id; }
void Nextion::set_touch_sleep_timeout(uint16_t timeout) {
if (timeout < 3) {
ESP_LOGD(TAG, "Sleep timeout out of bounds, range 3-65535");
@ -53,7 +55,7 @@ void Nextion::set_protocol_reparse_mode(bool active_mode) {
// Set Colors
void Nextion::set_component_background_color(const char *component, uint32_t color) {
this->add_no_result_to_queue_with_printf_("set_component_background_color", "%s.bco=%d", component, color);
this->add_no_result_to_queue_with_printf_("set_component_background_color", "%s.bco=%" PRIu32, component, color);
}
void Nextion::set_component_background_color(const char *component, const char *color) {
@ -66,7 +68,8 @@ void Nextion::set_component_background_color(const char *component, Color color)
}
void Nextion::set_component_pressed_background_color(const char *component, uint32_t color) {
this->add_no_result_to_queue_with_printf_("set_component_pressed_background_color", "%s.bco2=%d", component, color);
this->add_no_result_to_queue_with_printf_("set_component_pressed_background_color", "%s.bco2=%" PRIu32, component,
color);
}
void Nextion::set_component_pressed_background_color(const char *component, const char *color) {
@ -87,7 +90,7 @@ void Nextion::set_component_picc(const char *component, uint8_t pic_id) {
}
void Nextion::set_component_font_color(const char *component, uint32_t color) {
this->add_no_result_to_queue_with_printf_("set_component_font_color", "%s.pco=%d", component, color);
this->add_no_result_to_queue_with_printf_("set_component_font_color", "%s.pco=%" PRIu32, component, color);
}
void Nextion::set_component_font_color(const char *component, const char *color) {
@ -100,7 +103,7 @@ void Nextion::set_component_font_color(const char *component, Color color) {
}
void Nextion::set_component_pressed_font_color(const char *component, uint32_t color) {
this->add_no_result_to_queue_with_printf_("set_component_pressed_font_color", "%s.pco2=%d", component, color);
this->add_no_result_to_queue_with_printf_("set_component_pressed_font_color", "%s.pco2=%" PRIu32, component, color);
}
void Nextion::set_component_pressed_font_color(const char *component, const char *color) {
@ -124,6 +127,7 @@ void Nextion::set_component_text_printf(const char *component, const char *forma
// General Nextion
void Nextion::goto_page(const char *page) { this->add_no_result_to_queue_with_printf_("goto_page", "page %s", page); }
void Nextion::goto_page(uint8_t page) { this->add_no_result_to_queue_with_printf_("goto_page", "page %i", page); }
void Nextion::set_backlight_brightness(float brightness) {
if (brightness < 0 || brightness > 1.0) {

View file

@ -1,5 +1,6 @@
#include "nextion.h"
#ifdef ARDUINO
#ifdef USE_NEXTION_TFT_UPLOAD
#include "esphome/core/application.h"
@ -128,15 +129,15 @@ int Nextion::upload_by_chunks_(HTTPClient *http, int range_start) {
return range_end + 1;
}
void Nextion::upload_tft() {
bool Nextion::upload_tft() {
if (this->is_updating_) {
ESP_LOGD(TAG, "Currently updating");
return;
return false;
}
if (!network::is_connected()) {
ESP_LOGD(TAG, "network is not connected");
return;
return false;
}
this->is_updating_ = true;
@ -164,7 +165,7 @@ void Nextion::upload_tft() {
ESP_LOGD(TAG, "connection failed");
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
allocator.deallocate(this->transfer_buffer_, this->transfer_buffer_size_);
return;
return false;
} else {
ESP_LOGD(TAG, "Connected");
}
@ -192,7 +193,7 @@ void Nextion::upload_tft() {
}
if ((code != 200 && code != 206) || tries > 5) {
this->upload_end_();
return this->upload_end_(false);
}
String content_range_string = http.header("Content-Range");
@ -203,7 +204,7 @@ void Nextion::upload_tft() {
if (this->content_length_ < 4096) {
ESP_LOGE(TAG, "Failed to get file size");
this->upload_end_();
return this->upload_end_(false);
}
ESP_LOGD(TAG, "Updating Nextion %s...", this->device_model_.c_str());
@ -246,7 +247,7 @@ void Nextion::upload_tft() {
ESP_LOGD(TAG, "preparation for tft update done");
} else {
ESP_LOGD(TAG, "preparation for tft update failed %d \"%s\"", response[0], response.c_str());
this->upload_end_();
return this->upload_end_(false);
}
// Nextion wants 4096 bytes at a time. Make chunk_size a multiple of 4096
@ -280,7 +281,7 @@ void Nextion::upload_tft() {
this->transfer_buffer_ = allocator.allocate(chunk_size);
if (!this->transfer_buffer_)
this->upload_end_();
return this->upload_end_(false);
}
this->transfer_buffer_size_ = chunk_size;
@ -295,7 +296,7 @@ void Nextion::upload_tft() {
result = this->upload_by_chunks_(&http, result);
if (result < 0) {
ESP_LOGD(TAG, "Error updating Nextion!");
this->upload_end_();
return this->upload_end_(false);
}
App.feed_wdt();
// NOLINTNEXTLINE(readability-static-accessed-through-instance)
@ -303,15 +304,19 @@ void Nextion::upload_tft() {
}
ESP_LOGD(TAG, "Successfully updated Nextion!");
this->upload_end_();
return this->upload_end_(true);
}
void Nextion::upload_end_() {
bool Nextion::upload_end_(bool successful) {
this->is_updating_ = false;
ESP_LOGD(TAG, "Restarting Nextion");
this->soft_reset();
delay(1500); // NOLINT
ESP_LOGD(TAG, "Restarting esphome");
ESP.restart(); // NOLINT(readability-static-accessed-through-instance)
if (successful) {
delay(1500); // NOLINT
ESP_LOGD(TAG, "Restarting esphome");
ESP.restart(); // NOLINT(readability-static-accessed-through-instance)
}
return successful;
}
#ifdef USE_ESP8266
@ -337,3 +342,4 @@ WiFiClient *Nextion::get_wifi_client_() {
} // namespace esphome
#endif // USE_NEXTION_TFT_UPLOAD
#endif // ARDUINO

View file

@ -0,0 +1,268 @@
#include "nextion.h"
#ifdef USE_ESP_IDF
#ifdef USE_NEXTION_TFT_UPLOAD
#include "esphome/core/application.h"
#include "esphome/core/defines.h"
#include "esphome/core/util.h"
#include "esphome/core/log.h"
#include "esphome/components/network/util.h"
#include <esp_heap_caps.h>
#include <esp_http_client.h>
namespace esphome {
namespace nextion {
static const char *const TAG = "nextion_upload";
// Followed guide
// https://unofficialnextion.com/t/nextion-upload-protocol-v1-2-the-fast-one/1044/2
int Nextion::upload_range(const std::string &url, int range_start) {
ESP_LOGVV(TAG, "url: %s", url.c_str());
uint range_size = this->tft_size_ - range_start;
ESP_LOGVV(TAG, "tft_size_: %i", this->tft_size_);
ESP_LOGV(TAG, "Available heap: %u", esp_get_free_heap_size());
int range_end = (range_start == 0) ? std::min(this->tft_size_, 16383) : this->tft_size_;
if (range_size <= 0 or range_end <= range_start) {
ESP_LOGE(TAG, "Invalid range");
ESP_LOGD(TAG, "Range start: %i", range_start);
ESP_LOGD(TAG, "Range end: %i", range_end);
ESP_LOGD(TAG, "Range size: %i", range_size);
return -1;
}
esp_http_client_config_t config = {
.url = url.c_str(),
.cert_pem = nullptr,
};
esp_http_client_handle_t client = esp_http_client_init(&config);
char range_header[64];
sprintf(range_header, "bytes=%d-%d", range_start, range_end);
ESP_LOGV(TAG, "Requesting range: %s", range_header);
esp_http_client_set_header(client, "Range", range_header);
ESP_LOGVV(TAG, "Available heap: %u", esp_get_free_heap_size());
ESP_LOGV(TAG, "Opening http connetion");
esp_err_t err;
if ((err = esp_http_client_open(client, 0)) != ESP_OK) {
ESP_LOGE(TAG, "Failed to open HTTP connection: %s", esp_err_to_name(err));
esp_http_client_cleanup(client);
return -1;
}
ESP_LOGV(TAG, "Fetch content length");
int content_length = esp_http_client_fetch_headers(client);
ESP_LOGV(TAG, "content_length = %d", content_length);
if (content_length <= 0) {
ESP_LOGE(TAG, "Failed to get content length: %d", content_length);
esp_http_client_cleanup(client);
return -1;
}
int total_read_len = 0, read_len;
ESP_LOGV(TAG, "Allocate buffer");
uint8_t *buffer = new uint8_t[4096];
std::string recv_string;
if (buffer == nullptr) {
ESP_LOGE(TAG, "Failed to allocate memory for buffer");
ESP_LOGV(TAG, "Available heap: %u", esp_get_free_heap_size());
} else {
ESP_LOGV(TAG, "Memory for buffer allocated successfully");
while (true) {
App.feed_wdt();
ESP_LOGVV(TAG, "Available heap: %u", esp_get_free_heap_size());
int read_len = esp_http_client_read(client, reinterpret_cast<char *>(buffer), 4096);
ESP_LOGVV(TAG, "Read %d bytes from HTTP client, writing to UART", read_len);
if (read_len > 0) {
this->write_array(buffer, read_len);
ESP_LOGVV(TAG, "Write to UART successful");
this->recv_ret_string_(recv_string, 5000, true);
this->content_length_ -= read_len;
ESP_LOGD(TAG, "Uploaded %0.2f %%, remaining %d bytes",
100.0 * (this->tft_size_ - this->content_length_) / this->tft_size_, this->content_length_);
if (recv_string[0] != 0x05) { // 0x05 == "ok"
ESP_LOGD(
TAG, "recv_string [%s]",
format_hex_pretty(reinterpret_cast<const uint8_t *>(recv_string.data()), recv_string.size()).c_str());
}
// handle partial upload request
if (recv_string[0] == 0x08 && recv_string.size() == 5) {
uint32_t result = 0;
for (int j = 0; j < 4; ++j) {
result += static_cast<uint8_t>(recv_string[j + 1]) << (8 * j);
}
if (result > 0) {
ESP_LOGI(TAG, "Nextion reported new range %" PRIu32, result);
this->content_length_ = this->tft_size_ - result;
// Deallocate the buffer when done
delete[] buffer;
ESP_LOGVV(TAG, "Memory for buffer deallocated");
esp_http_client_cleanup(client);
esp_http_client_close(client);
return result;
}
}
recv_string.clear();
} else if (read_len == 0) {
ESP_LOGV(TAG, "End of HTTP response reached");
break; // Exit the loop if there is no more data to read
} else {
ESP_LOGE(TAG, "Failed to read from HTTP client, error code: %d", read_len);
break; // Exit the loop on error
}
}
// Deallocate the buffer when done
delete[] buffer;
ESP_LOGVV(TAG, "Memory for buffer deallocated");
}
esp_http_client_cleanup(client);
esp_http_client_close(client);
return range_end + 1;
}
bool Nextion::upload_tft() {
ESP_LOGD(TAG, "Nextion TFT upload requested");
ESP_LOGD(TAG, "url: %s", this->tft_url_.c_str());
if (this->is_updating_) {
ESP_LOGW(TAG, "Currently updating");
return false;
}
if (!network::is_connected()) {
ESP_LOGE(TAG, "Network is not connected");
return false;
}
this->is_updating_ = true;
// Define the configuration for the HTTP client
ESP_LOGV(TAG, "Establishing connection to HTTP server");
ESP_LOGVV(TAG, "Available heap: %u", esp_get_free_heap_size());
esp_http_client_config_t config = {
.url = this->tft_url_.c_str(),
.cert_pem = nullptr,
.method = HTTP_METHOD_HEAD,
.timeout_ms = 15000,
};
// Initialize the HTTP client with the configuration
ESP_LOGV(TAG, "Initializing HTTP client");
ESP_LOGV(TAG, "Available heap: %u", esp_get_free_heap_size());
esp_http_client_handle_t http = esp_http_client_init(&config);
if (!http) {
ESP_LOGE(TAG, "Failed to initialize HTTP client.");
return this->upload_end(false);
}
// Perform the HTTP request
ESP_LOGV(TAG, "Check if the client could connect");
ESP_LOGV(TAG, "Available heap: %u", esp_get_free_heap_size());
esp_err_t err = esp_http_client_perform(http);
if (err != ESP_OK) {
ESP_LOGE(TAG, "HTTP request failed: %s", esp_err_to_name(err));
esp_http_client_cleanup(http);
return this->upload_end(false);
}
// Check the HTTP Status Code
int status_code = esp_http_client_get_status_code(http);
ESP_LOGV(TAG, "HTTP Status Code: %d", status_code);
size_t tft_file_size = esp_http_client_get_content_length(http);
ESP_LOGD(TAG, "TFT file size: %zu", tft_file_size);
if (tft_file_size < 4096) {
ESP_LOGE(TAG, "File size check failed. Size: %zu", tft_file_size);
esp_http_client_cleanup(http);
return this->upload_end(false);
} else {
ESP_LOGV(TAG, "File size check passed. Proceeding...");
}
this->content_length_ = tft_file_size;
this->tft_size_ = tft_file_size;
ESP_LOGD(TAG, "Updating Nextion");
// The Nextion will ignore the update command if it is sleeping
this->send_command_("sleep=0");
this->set_backlight_brightness(1.0);
vTaskDelay(pdMS_TO_TICKS(250)); // NOLINT
App.feed_wdt();
char command[128];
// Tells the Nextion the content length of the tft file and baud rate it will be sent at
// Once the Nextion accepts the command it will wait until the file is successfully uploaded
// If it fails for any reason a power cycle of the display will be needed
sprintf(command, "whmi-wris %d,%" PRIu32 ",1", this->content_length_, this->parent_->get_baud_rate());
// Clear serial receive buffer
uint8_t d;
while (this->available()) {
this->read_byte(&d);
};
this->send_command_(command);
std::string response;
ESP_LOGV(TAG, "Waiting for upgrade response");
this->recv_ret_string_(response, 2048, true); // This can take some time to return
// The Nextion display will, if it's ready to accept data, send a 0x05 byte.
ESP_LOGD(TAG, "Upgrade response is [%s]",
format_hex_pretty(reinterpret_cast<const uint8_t *>(response.data()), response.size()).c_str());
if (response.find(0x05) != std::string::npos) {
ESP_LOGV(TAG, "Preparation for tft update done");
} else {
ESP_LOGE(TAG, "Preparation for tft update failed %d \"%s\"", response[0], response.c_str());
esp_http_client_cleanup(http);
return this->upload_end(false);
}
ESP_LOGD(TAG, "Updating tft from \"%s\" with a file size of %d, Heap Size %" PRIu32, this->tft_url_.c_str(),
content_length_, esp_get_free_heap_size());
ESP_LOGV(TAG, "Starting transfer by chunks loop");
int result = 0;
while (content_length_ > 0) {
result = upload_range(this->tft_url_.c_str(), result);
if (result < 0) {
ESP_LOGE(TAG, "Error updating Nextion!");
esp_http_client_cleanup(http);
return this->upload_end(false);
}
App.feed_wdt();
ESP_LOGV(TAG, "Heap Size %" PRIu32 ", Bytes left %d", esp_get_free_heap_size(), content_length_);
}
ESP_LOGD(TAG, "Successfully updated Nextion!");
ESP_LOGD(TAG, "Close HTTP connection");
esp_http_client_close(http);
esp_http_client_cleanup(http);
return upload_end(true);
}
bool Nextion::upload_end(bool successful) {
this->is_updating_ = false;
ESP_LOGD(TAG, "Restarting Nextion");
this->soft_reset();
vTaskDelay(pdMS_TO_TICKS(1500)); // NOLINT
if (successful) {
ESP_LOGD(TAG, "Restarting esphome");
esp_restart(); // NOLINT(readability-static-accessed-through-instance)
}
return successful;
}
} // namespace nextion
} // namespace esphome
#endif // USE_NEXTION_TFT_UPLOAD
#endif // USE_ESP_IDF

View file

@ -1,4 +1,5 @@
#include "ndef_message.h"
#include <cinttypes>
namespace esphome {
namespace nfc {
@ -32,7 +33,7 @@ NdefMessage::NdefMessage(std::vector<uint8_t> &data) {
id_length = data[index++];
}
ESP_LOGVV(TAG, "Lengths: type=%d, payload=%d, id=%d", type_length, payload_length, id_length);
ESP_LOGVV(TAG, "Lengths: type=%d, payload=%" PRIu32 ", id=%d", type_length, payload_length, id_length);
std::string type_str(data.begin() + index, data.begin() + index + type_length);

View file

@ -2,7 +2,7 @@ from math import log
import esphome.config_validation as cv
import esphome.codegen as cg
from esphome.components import sensor
from esphome.components import sensor, resistance_sampler
from esphome.const import (
CONF_CALIBRATION,
CONF_REFERENCE_RESISTANCE,
@ -15,6 +15,8 @@ from esphome.const import (
UNIT_CELSIUS,
)
AUTO_LOAD = ["resistance_sampler"]
ntc_ns = cg.esphome_ns.namespace("ntc")
NTC = ntc_ns.class_("NTC", cg.Component, sensor.Sensor)
@ -124,7 +126,7 @@ CONFIG_SCHEMA = (
)
.extend(
{
cv.Required(CONF_SENSOR): cv.use_id(sensor.Sensor),
cv.Required(CONF_SENSOR): cv.use_id(resistance_sampler.ResistanceSampler),
cv.Required(CONF_CALIBRATION): process_calibration,
}
)

View file

@ -1,5 +1,6 @@
#include "pid_autotuner.h"
#include "esphome/core/log.h"
#include <cinttypes>
#ifndef M_PI
#define M_PI 3.1415926535897932384626433
@ -108,7 +109,7 @@ PIDAutotuner::PIDAutotuneResult PIDAutotuner::update(float setpoint, float proce
}
uint32_t phase = this->relay_function_.phase_count;
ESP_LOGVV(TAG, "%s: >", this->id_.c_str());
ESP_LOGVV(TAG, " Phase %u, enough=%u", phase, enough_data_phase_);
ESP_LOGVV(TAG, " Phase %" PRIu32 ", enough=%" PRIu32, phase, enough_data_phase_);
if (this->enough_data_phase_ == 0) {
this->enough_data_phase_ = phase;
@ -186,8 +187,8 @@ void PIDAutotuner::dump_config() {
ESP_LOGD(TAG, " Autotune is still running!");
ESP_LOGD(TAG, " Status: Trying to reach %.2f °C", setpoint_ - relay_function_.current_target_error());
ESP_LOGD(TAG, " Stats so far:");
ESP_LOGD(TAG, " Phases: %u", relay_function_.phase_count);
ESP_LOGD(TAG, " Detected %u zero-crossings", frequency_detector_.zerocrossing_intervals.size()); // NOLINT
ESP_LOGD(TAG, " Phases: %" PRIu32, relay_function_.phase_count);
ESP_LOGD(TAG, " Detected %zu zero-crossings", frequency_detector_.zerocrossing_intervals.size());
ESP_LOGD(TAG, " Current Phase Min: %.2f, Max: %.2f", amplitude_detector_.phase_min,
amplitude_detector_.phase_max);
}

View file

@ -1,5 +1,6 @@
#include "pzem004t.h"
#include "esphome/core/log.h"
#include <cinttypes>
namespace esphome {
namespace pzem004t {
@ -75,7 +76,7 @@ void PZEM004T::loop() {
uint32_t energy = (uint32_t(resp[1]) << 16) | (uint32_t(resp[2]) << 8) | (uint32_t(resp[3]));
if (this->energy_sensor_ != nullptr)
this->energy_sensor_->publish_state(energy);
ESP_LOGD(TAG, "Got Energy %u Wh", energy);
ESP_LOGD(TAG, "Got Energy %" PRIu32 " Wh", energy);
this->write_state_(DONE);
break;
}

View file

@ -242,6 +242,55 @@ async def build_dumpers(config):
return dumpers
# ByronSX
(
ByronSXData,
ByronSXBinarySensor,
ByronSXTrigger,
ByronSXAction,
ByronSXDumper,
) = declare_protocol("ByronSX")
BYRONSX_SCHEMA = cv.Schema(
{
cv.Required(CONF_ADDRESS): cv.All(cv.hex_int, cv.Range(min=0, max=0xFF)),
cv.Optional(CONF_COMMAND, default=0x10): cv.All(
cv.hex_int, cv.one_of(1, 2, 3, 5, 6, 9, 0xD, 0xE, 0x10, int=True)
),
}
)
@register_binary_sensor("byronsx", ByronSXBinarySensor, BYRONSX_SCHEMA)
def byronsx_binary_sensor(var, config):
cg.add(
var.set_data(
cg.StructInitializer(
ByronSXData,
("address", config[CONF_ADDRESS]),
("command", config[CONF_COMMAND]),
)
)
)
@register_trigger("byronsx", ByronSXTrigger, ByronSXData)
def byronsx_trigger(var, config):
pass
@register_dumper("byronsx", ByronSXDumper)
def byronsx_dumper(var, config):
pass
@register_action("byronsx", ByronSXAction, BYRONSX_SCHEMA)
async def byronsx_action(var, config, args):
template_ = await cg.templatable(config[CONF_ADDRESS], args, cg.uint8)
cg.add(var.set_address(template_))
template_ = await cg.templatable(config[CONF_COMMAND], args, cg.uint8)
cg.add(var.set_command(template_))
# CanalSat
(
CanalSatData,

View file

@ -0,0 +1,134 @@
#include "byronsx_protocol.h"
#include "esphome/core/log.h"
namespace esphome {
namespace remote_base {
static const char *const TAG = "remote.byronsx";
static const uint32_t BIT_TIME_US = 333;
static const uint8_t NBITS_ADDRESS = 8;
static const uint8_t NBITS_COMMAND = 4;
static const uint8_t NBITS_START_BIT = 1;
static const uint8_t NBITS_DATA = NBITS_ADDRESS + NBITS_COMMAND /*+ NBITS_COMMAND*/;
/*
ByronSX Protocol
Each transmitted packet appears to consist of thirteen bits of PWM encoded
data. Each bit period of aprox 1ms consists of a transmitter OFF period
followed by a transmitter ON period. The 'on' and 'off' periods are either
short (approx 332us) or long (664us).
A short 'off' followed by a long 'on' represents a '1' bit.
A long 'off' followed by a short 'on' represents a '0' bit.
A the beginning of each packet is and initial 'off' period of approx 5.6ms
followed by a short 'on'.
The data payload consists of twelve bits which appear to be an eight bit
address floowied by a 4 bit chime number.
SAAAAAAAACCCC
Whese:
S = the initial short start pulse
A = The eight address bits
C - The four chime bits
--------------------
I have also used 'RFLink' software (RLink Firmware Version: 1.1 Revision: 48)
to capture these packets, eg:
20;19;Byron;ID=004f;SWITCH=02;CMD=ON;CHIME=02;
This module transmits and interprets packets in the same way as RFLink.
marshn
*/
void ByronSXProtocol::encode(RemoteTransmitData *dst, const ByronSXData &data) {
uint32_t out_data = 0x0;
ESP_LOGD(TAG, "Send ByronSX: address=%04x command=%03x", data.address, data.command);
out_data = data.address;
out_data <<= NBITS_COMMAND;
out_data |= data.command;
ESP_LOGV(TAG, "Send ByronSX: out_data %03x", out_data);
// Initial Mark start bit
dst->mark(1 * BIT_TIME_US);
for (uint32_t mask = 1UL << (NBITS_DATA - 1); mask != 0; mask >>= 1) {
if (out_data & mask) {
dst->space(2 * BIT_TIME_US);
dst->mark(1 * BIT_TIME_US);
} else {
dst->space(1 * BIT_TIME_US);
dst->mark(2 * BIT_TIME_US);
}
}
// final space at end of packet
dst->space(17 * BIT_TIME_US);
}
optional<ByronSXData> ByronSXProtocol::decode(RemoteReceiveData src) {
ByronSXData out{
.address = 0,
.command = 0,
};
if (src.size() != (NBITS_DATA + NBITS_START_BIT) * 2) {
return {};
}
// Skip start bit
if (!src.expect_mark(BIT_TIME_US)) {
return {};
}
ESP_LOGVV(TAG, "%3d: %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d", src.size(), src.peek(0),
src.peek(1), src.peek(2), src.peek(3), src.peek(4), src.peek(5), src.peek(6), src.peek(7), src.peek(8),
src.peek(9), src.peek(10), src.peek(11), src.peek(12), src.peek(13), src.peek(14), src.peek(15),
src.peek(16), src.peek(17), src.peek(18), src.peek(19));
ESP_LOGVV(TAG, " %d %d %d %d %d %d", src.peek(20), src.peek(21), src.peek(22), src.peek(23), src.peek(24),
src.peek(25));
// Read data bits
uint32_t out_data = 0;
int8_t bit = NBITS_DATA;
while (--bit >= 0) {
if (src.expect_space(2 * BIT_TIME_US) && src.expect_mark(BIT_TIME_US)) {
out_data |= 1 << bit;
} else if (src.expect_space(BIT_TIME_US) && src.expect_mark(2 * BIT_TIME_US)) {
out_data |= 0 << bit;
} else {
ESP_LOGV(TAG, "Decode ByronSX: Fail 2, %2d %08x", bit, out_data);
return {};
}
ESP_LOGVV(TAG, "Decode ByronSX: Data, %2d %08x", bit, out_data);
}
// last bit followed by a long space
if (!src.peek_space_at_least(BIT_TIME_US)) {
ESP_LOGV(TAG, "Decode ByronSX: Fail 4 ");
return {};
}
out.command = (uint8_t) (out_data & 0xF);
out_data >>= NBITS_COMMAND;
out.address = (uint16_t) (out_data & 0xFF);
return out;
}
void ByronSXProtocol::dump(const ByronSXData &data) {
ESP_LOGD(TAG, "Received ByronSX: address=0x%08X, command=0x%02x", data.address, data.command);
}
} // namespace remote_base
} // namespace esphome

View file

@ -0,0 +1,46 @@
#pragma once
#include "esphome/core/component.h"
#include "remote_base.h"
namespace esphome {
namespace remote_base {
struct ByronSXData {
uint8_t address;
uint8_t command;
bool operator==(const ByronSXData &rhs) const {
// Treat 0x10 as a special, wildcard command/chime
// This allows us to match on just the address if wanted.
if (address != rhs.address) {
return false;
}
return (rhs.command == 0x10 || command == rhs.command);
}
};
class ByronSXProtocol : public RemoteProtocol<ByronSXData> {
public:
void encode(RemoteTransmitData *dst, const ByronSXData &data) override;
optional<ByronSXData> decode(RemoteReceiveData src) override;
void dump(const ByronSXData &data) override;
};
DECLARE_REMOTE_PROTOCOL(ByronSX)
template<typename... Ts> class ByronSXAction : public RemoteTransmitterActionBase<Ts...> {
public:
TEMPLATABLE_VALUE(uint8_t, address)
TEMPLATABLE_VALUE(uint8_t, command)
void encode(RemoteTransmitData *dst, Ts... x) override {
ByronSXData data{};
data.address = this->address_.value(x...);
data.command = this->command_.value(x...);
ByronSXProtocol().encode(dst, data);
}
};
} // namespace remote_base
} // namespace esphome

View file

@ -138,9 +138,12 @@ optional<DraytonData> DraytonProtocol::decode(RemoteReceiveData src) {
return {};
}
ESP_LOGVV(TAG, "Decode Drayton: %d, %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d", src.size(),
src.peek(0), src.peek(1), src.peek(2), src.peek(3), src.peek(4), src.peek(5), src.peek(6), src.peek(7),
src.peek(8), src.peek(9), src.peek(10), src.peek(11), src.peek(12), src.peek(13), src.peek(14),
ESP_LOGVV(TAG,
"Decode Drayton: %" PRId32 ", %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32
" %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32
" %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 " %" PRId32 "",
src.size(), src.peek(0), src.peek(1), src.peek(2), src.peek(3), src.peek(4), src.peek(5), src.peek(6),
src.peek(7), src.peek(8), src.peek(9), src.peek(10), src.peek(11), src.peek(12), src.peek(13), src.peek(14),
src.peek(15), src.peek(16), src.peek(17), src.peek(18), src.peek(19));
// If first preamble item is a space, skip it
@ -150,7 +153,8 @@ optional<DraytonData> DraytonProtocol::decode(RemoteReceiveData src) {
// Look for sync pulse, after. If sucessful index points to space of sync symbol
for (uint16_t preamble = 0; preamble <= NBITS_PREAMBLE * 2; preamble += 2) {
ESP_LOGVV(TAG, "Decode Drayton: preamble %d %d %d", preamble, src.peek(preamble), src.peek(preamble + 1));
ESP_LOGVV(TAG, "Decode Drayton: preamble %d %" PRId32 " %" PRId32, preamble, src.peek(preamble),
src.peek(preamble + 1));
if (src.peek_mark(2 * BIT_TIME_US, preamble) &&
(src.peek_space(2 * BIT_TIME_US, preamble + 1) || src.peek_space(3 * BIT_TIME_US, preamble + 1))) {
src.advance(preamble + 1);
@ -177,7 +181,7 @@ optional<DraytonData> DraytonProtocol::decode(RemoteReceiveData src) {
// if there is no transition at the start of the bit period, then the transition in the middle of
// the previous bit period.
while (--bit >= 1) {
ESP_LOGVV(TAG, "Decode Drayton: Data, %2d %08x", bit, out_data);
ESP_LOGVV(TAG, "Decode Drayton: Data, %2d %08" PRIx32, bit, out_data);
if ((src.expect_space(BIT_TIME_US) || src.expect_space(2 * BIT_TIME_US)) &&
(src.expect_mark(BIT_TIME_US) || src.peek_mark(2 * BIT_TIME_US))) {
out_data |= 0 << bit;
@ -185,7 +189,7 @@ optional<DraytonData> DraytonProtocol::decode(RemoteReceiveData src) {
(src.expect_space(BIT_TIME_US) || src.peek_space(2 * BIT_TIME_US))) {
out_data |= 1 << bit;
} else {
ESP_LOGVV(TAG, "Decode Drayton: Fail 2, %2d %08x", bit, out_data);
ESP_LOGVV(TAG, "Decode Drayton: Fail 2, %2d %08" PRIx32, bit, out_data);
return {};
}
}

View file

@ -125,7 +125,7 @@ void RemoteTransmitterBase::send_(uint32_t send_times, uint32_t send_wait) {
const auto &vec = this->temp_.get_data();
char buffer[256];
uint32_t buffer_offset = 0;
buffer_offset += sprintf(buffer, "Sending times=%u wait=%ums: ", send_times, send_wait);
buffer_offset += sprintf(buffer, "Sending times=%" PRIu32 " wait=%" PRIu32 "ms: ", send_times, send_wait);
for (size_t i = 0; i < vec.size(); i++) {
const int32_t value = vec[i];
@ -133,9 +133,9 @@ void RemoteTransmitterBase::send_(uint32_t send_times, uint32_t send_wait) {
int written;
if (i + 1 < vec.size()) {
written = snprintf(buffer + buffer_offset, remaining_length, "%d, ", value);
written = snprintf(buffer + buffer_offset, remaining_length, "%" PRId32 ", ", value);
} else {
written = snprintf(buffer + buffer_offset, remaining_length, "%d", value);
written = snprintf(buffer + buffer_offset, remaining_length, "%" PRId32, value);
}
if (written < 0 || written >= int(remaining_length)) {
@ -145,9 +145,9 @@ void RemoteTransmitterBase::send_(uint32_t send_times, uint32_t send_wait) {
buffer_offset = 0;
written = sprintf(buffer, " ");
if (i + 1 < vec.size()) {
written += sprintf(buffer + written, "%d, ", value);
written += sprintf(buffer + written, "%" PRId32 ", ", value);
} else {
written += sprintf(buffer + written, "%d", value);
written += sprintf(buffer + written, "%" PRId32, value);
}
}

View file

@ -92,14 +92,18 @@ void RemoteReceiverComponent::decode_rmt_(rmt_item32_t *item, size_t len) {
ESP_LOGVV(TAG, "START:");
for (size_t i = 0; i < item_count; i++) {
if (item[i].level0) {
ESP_LOGVV(TAG, "%u A: ON %uus (%u ticks)", i, this->to_microseconds_(item[i].duration0), item[i].duration0);
ESP_LOGVV(TAG, "%zu A: ON %" PRIu32 "us (%u ticks)", i, this->to_microseconds_(item[i].duration0),
item[i].duration0);
} else {
ESP_LOGVV(TAG, "%u A: OFF %uus (%u ticks)", i, this->to_microseconds_(item[i].duration0), item[i].duration0);
ESP_LOGVV(TAG, "%zu A: OFF %" PRIu32 "us (%u ticks)", i, this->to_microseconds_(item[i].duration0),
item[i].duration0);
}
if (item[i].level1) {
ESP_LOGVV(TAG, "%u B: ON %uus (%u ticks)", i, this->to_microseconds_(item[i].duration1), item[i].duration1);
ESP_LOGVV(TAG, "%zu B: ON %" PRIu32 "us (%u ticks)", i, this->to_microseconds_(item[i].duration1),
item[i].duration1);
} else {
ESP_LOGVV(TAG, "%u B: OFF %uus (%u ticks)", i, this->to_microseconds_(item[i].duration1), item[i].duration1);
ESP_LOGVV(TAG, "%zu B: OFF %" PRIu32 "us (%u ticks)", i, this->to_microseconds_(item[i].duration1),
item[i].duration1);
}
}
ESP_LOGVV(TAG, "\n");

View file

@ -1,7 +1,8 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/resistance_sampler/resistance_sampler.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/core/component.h"
namespace esphome {
namespace resistance {
@ -11,7 +12,7 @@ enum ResistanceConfiguration {
DOWNSTREAM,
};
class ResistanceSensor : public Component, public sensor::Sensor {
class ResistanceSensor : public Component, public sensor::Sensor, resistance_sampler::ResistanceSampler {
public:
void set_sensor(Sensor *sensor) { sensor_ = sensor; }
void set_configuration(ResistanceConfiguration configuration) { configuration_ = configuration; }

View file

@ -1,6 +1,6 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor
from esphome.components import sensor, resistance_sampler
from esphome.const import (
CONF_SENSOR,
STATE_CLASS_MEASUREMENT,
@ -8,8 +8,15 @@ from esphome.const import (
ICON_FLASH,
)
AUTO_LOAD = ["resistance_sampler"]
resistance_ns = cg.esphome_ns.namespace("resistance")
ResistanceSensor = resistance_ns.class_("ResistanceSensor", cg.Component, sensor.Sensor)
ResistanceSensor = resistance_ns.class_(
"ResistanceSensor",
cg.Component,
sensor.Sensor,
resistance_sampler.ResistanceSampler,
)
CONF_REFERENCE_VOLTAGE = "reference_voltage"
CONF_CONFIGURATION = "configuration"

View file

@ -0,0 +1,6 @@
import esphome.codegen as cg
resistance_sampler_ns = cg.esphome_ns.namespace("resistance_sampler")
ResistanceSampler = resistance_sampler_ns.class_("ResistanceSampler")
CODEOWNERS = ["@jesserockz"]

View file

@ -0,0 +1,10 @@
#pragma once
namespace esphome {
namespace resistance_sampler {
/// Abstract interface to mark components that provide resistance values.
class ResistanceSampler {};
} // namespace resistance_sampler
} // namespace esphome

View file

@ -1,5 +1,6 @@
#include "rf_bridge.h"
#include "esphome/core/log.h"
#include <cinttypes>
#include <cstring>
namespace esphome {
@ -53,8 +54,10 @@ bool RFBridgeComponent::parse_bridge_byte_(uint8_t byte) {
ESP_LOGD(TAG, "Learning success");
}
ESP_LOGI(TAG, "Received RFBridge Code: sync=0x%04X low=0x%04X high=0x%04X code=0x%06X", data.sync, data.low,
data.high, data.code);
ESP_LOGI(TAG,
"Received RFBridge Code: sync=0x%04" PRIX16 " low=0x%04" PRIX16 " high=0x%04" PRIX16
" code=0x%06" PRIX32,
data.sync, data.low, data.high, data.code);
this->data_callback_.call(data);
break;
}
@ -144,8 +147,8 @@ void RFBridgeComponent::loop() {
}
void RFBridgeComponent::send_code(RFBridgeData data) {
ESP_LOGD(TAG, "Sending code: sync=0x%04X low=0x%04X high=0x%04X code=0x%06X", data.sync, data.low, data.high,
data.code);
ESP_LOGD(TAG, "Sending code: sync=0x%04" PRIX16 " low=0x%04" PRIX16 " high=0x%04" PRIX16 " code=0x%06" PRIX32,
data.sync, data.low, data.high, data.code);
this->write(RF_CODE_START);
this->write(RF_CODE_RFOUT);
this->write((data.sync >> 8) & 0xFF);

View file

@ -1,6 +1,7 @@
#include "servo.h"
#include "esphome/core/log.h"
#include "esphome/core/hal.h"
#include <cinttypes>
namespace esphome {
namespace servo {
@ -14,8 +15,8 @@ void Servo::dump_config() {
ESP_LOGCONFIG(TAG, " Idle Level: %.1f%%", this->idle_level_ * 100.0f);
ESP_LOGCONFIG(TAG, " Min Level: %.1f%%", this->min_level_ * 100.0f);
ESP_LOGCONFIG(TAG, " Max Level: %.1f%%", this->max_level_ * 100.0f);
ESP_LOGCONFIG(TAG, " auto detach time: %d ms", this->auto_detach_time_);
ESP_LOGCONFIG(TAG, " run duration: %d ms", this->transition_length_);
ESP_LOGCONFIG(TAG, " auto detach time: %" PRIu32 " ms", this->auto_detach_time_);
ESP_LOGCONFIG(TAG, " run duration: %" PRIu32 " ms", this->transition_length_);
}
void Servo::loop() {

View file

@ -2,7 +2,7 @@ import esphome.config_validation as cv
CODEOWNERS = ["@SenexCrenshaw"]
CONFIG_SCHEMA = CONFIG_SCHEMA = cv.invalid(
CONFIG_SCHEMA = cv.invalid(
"SGP40 is deprecated.\nPlease use the SGP4x platform instead.\nSGP4x supports both SPG40 and SGP41.\n"
" See https://esphome.io/components/sensor/sgp4x.html"
)

View file

@ -166,7 +166,7 @@ bool SGP4xComponent::measure_gas_indices_(int32_t &voc, int32_t &nox) {
if (nox_sensor_) {
nox = nox_algorithm_.process(nox_sraw);
}
ESP_LOGV(TAG, "VOC = %d, NOx = %d", voc, nox);
ESP_LOGV(TAG, "VOC = %" PRId32 ", NOx = %" PRId32, voc, nox);
// Store baselines after defined interval or if the difference between current and stored baseline becomes too
// much
if (this->store_baseline_ && this->seconds_since_last_store_ > SHORTEST_BASELINE_STORE_INTERVAL) {

View file

@ -1,9 +1,10 @@
import re
from esphome import automation
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import uart
from esphome.const import CONF_ID
from esphome.const import CONF_ID, CONF_TRIGGER_ID
CODEOWNERS = ["@alengwenus"]
@ -16,10 +17,26 @@ MULTI_CONF = True
CONF_SML_ID = "sml_id"
CONF_OBIS_CODE = "obis_code"
CONF_SERVER_ID = "server_id"
CONF_ON_DATA = "on_data"
sml_ns = cg.esphome_ns.namespace("sml")
DataTrigger = sml_ns.class_(
"DataTrigger",
automation.Trigger.template(
cg.std_vector.template(cg.uint8).operator("ref"), cg.bool_
),
)
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(Sml),
cv.Optional(CONF_ON_DATA): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(DataTrigger),
}
),
}
).extend(uart.UART_DEVICE_SCHEMA)
@ -28,6 +45,19 @@ async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await uart.register_uart_device(var, config)
for conf in config.get(CONF_ON_DATA, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(
trigger,
[
(
cg.std_vector.template(cg.uint8).operator("ref").operator("const"),
"bytes",
),
(cg.bool_, "valid"),
],
conf,
)
def obis_code(value):

View file

@ -0,0 +1,19 @@
#pragma once
#include "esphome/core/automation.h"
#include "sml.h"
#include <vector>
namespace esphome {
namespace sml {
class DataTrigger : public Trigger<const std::vector<uint8_t> &, bool> {
public:
explicit DataTrigger(Sml *sml) {
sml->add_on_data_callback([this](const std::vector<uint8_t> &data, bool valid) { this->trigger(data, valid); });
}
};
} // namespace sml
} // namespace esphome

View file

@ -18,8 +18,10 @@ enum SmlType : uint8_t {
enum SmlMessageType : uint16_t { SML_PUBLIC_OPEN_RES = 0x0101, SML_GET_LIST_RES = 0x701 };
// masks with two-bit mapping 0x1b -> 0b01; 0x01 -> 0b10; 0x1a -> 0b11
const uint16_t START_MASK = 0x55aa; // 0x1b 1b 1b 1b 1b 01 01 01 01
const uint16_t START_MASK = 0x55aa; // 0x1b 1b 1b 1b 01 01 01 01
const uint16_t END_MASK = 0x0157; // 0x1b 1b 1b 1b 1a
const std::vector<uint8_t> START_SEQ = {0x1b, 0x1b, 0x1b, 0x1b, 0x01, 0x01, 0x01, 0x01};
} // namespace sml
} // namespace esphome

View file

@ -35,16 +35,24 @@ void Sml::loop() {
case START_BYTES_DETECTED: {
this->record_ = true;
this->sml_data_.clear();
// add start sequence (for callbacks)
this->sml_data_.insert(this->sml_data_.begin(), START_SEQ.begin(), START_SEQ.end());
break;
};
case END_BYTES_DETECTED: {
if (this->record_) {
this->record_ = false;
if (!check_sml_data(this->sml_data_))
bool valid = check_sml_data(this->sml_data_);
// call callbacks
this->data_callbacks_.call(this->sml_data_, valid);
if (!valid)
break;
// remove footer bytes
// remove start/end sequence
this->sml_data_.erase(this->sml_data_.begin(), this->sml_data_.begin() + START_SEQ.size());
this->sml_data_.resize(this->sml_data_.size() - 8);
this->process_sml_file_(this->sml_data_);
}
@ -54,6 +62,10 @@ void Sml::loop() {
}
}
void Sml::add_on_data_callback(std::function<void(std::vector<uint8_t>, bool)> &&callback) {
this->data_callbacks_.add(std::move(callback));
}
void Sml::process_sml_file_(const bytes &sml_data) {
SmlFile sml_file = SmlFile(sml_data);
std::vector<ObisInfo> obis_info = sml_file.get_obis_info();
@ -100,14 +112,14 @@ bool check_sml_data(const bytes &buffer) {
}
uint16_t crc_received = (buffer.at(buffer.size() - 2) << 8) | buffer.at(buffer.size() - 1);
uint16_t crc_calculated = crc16(buffer.data(), buffer.size() - 2, 0x6e23, 0x8408, true, true);
uint16_t crc_calculated = crc16(buffer.data() + 8, buffer.size() - 10, 0x6e23, 0x8408, true, true);
crc_calculated = (crc_calculated >> 8) | (crc_calculated << 8);
if (crc_received == crc_calculated) {
ESP_LOGV(TAG, "Checksum verification successful with CRC16/X25.");
return true;
}
crc_calculated = crc16(buffer.data(), buffer.size() - 2, 0xed50, 0x8408);
crc_calculated = crc16(buffer.data() + 8, buffer.size() - 10, 0xed50, 0x8408);
if (crc_received == crc_calculated) {
ESP_LOGV(TAG, "Checksum verification successful with CRC16/KERMIT.");
return true;

View file

@ -3,6 +3,7 @@
#include <string>
#include <vector>
#include "esphome/core/component.h"
#include "esphome/core/helpers.h"
#include "esphome/components/uart/uart.h"
#include "sml_parser.h"
@ -23,6 +24,7 @@ class Sml : public Component, public uart::UARTDevice {
void loop() override;
void dump_config() override;
std::vector<SmlListener *> sml_listeners_{};
void add_on_data_callback(std::function<void(std::vector<uint8_t>, bool)> &&callback);
protected:
void process_sml_file_(const bytes &sml_data);
@ -35,6 +37,8 @@ class Sml : public Component, public uart::UARTDevice {
bool record_ = false;
uint16_t incoming_mask_ = 0;
bytes sml_data_;
CallbackManager<void(const std::vector<uint8_t> &, bool)> data_callbacks_{};
};
bool check_sml_data(const bytes &buffer);

View file

@ -1,8 +1,10 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import pins
from esphome.components import spi
from esphome.const import (
CONF_ID,
CONF_SPI_ID,
CONF_MODE,
CONF_NUMBER,
CONF_INVERTED,
@ -10,13 +12,20 @@ from esphome.const import (
CONF_CLOCK_PIN,
CONF_OUTPUT,
)
from esphome.core import EsphomeError
DEPENDENCIES = []
MULTI_CONF = True
sn74hc595_ns = cg.esphome_ns.namespace("sn74hc595")
SN74HC595Component = sn74hc595_ns.class_("SN74HC595Component", cg.Component)
SN74HC595GPIOComponent = sn74hc595_ns.class_(
"SN74HC595GPIOComponent", SN74HC595Component
)
SN74HC595SPIComponent = sn74hc595_ns.class_(
"SN74HC595SPIComponent", SN74HC595Component, spi.SPIDevice
)
SN74HC595GPIOPin = sn74hc595_ns.class_(
"SN74HC595GPIOPin", cg.GPIOPin, cg.Parented.template(SN74HC595Component)
)
@ -25,25 +34,51 @@ CONF_SN74HC595 = "sn74hc595"
CONF_LATCH_PIN = "latch_pin"
CONF_OE_PIN = "oe_pin"
CONF_SR_COUNT = "sr_count"
CONFIG_SCHEMA = cv.Schema(
{
cv.Required(CONF_ID): cv.declare_id(SN74HC595Component),
cv.Required(CONF_DATA_PIN): pins.gpio_output_pin_schema,
cv.Required(CONF_CLOCK_PIN): pins.gpio_output_pin_schema,
cv.Required(CONF_LATCH_PIN): pins.gpio_output_pin_schema,
cv.Optional(CONF_OE_PIN): pins.gpio_output_pin_schema,
cv.Optional(CONF_SR_COUNT, default=1): cv.int_range(min=1, max=256),
}
).extend(cv.COMPONENT_SCHEMA)
CONFIG_SCHEMA = cv.Any(
cv.Schema(
{
cv.Required(CONF_ID): cv.declare_id(SN74HC595GPIOComponent),
cv.Required(CONF_DATA_PIN): pins.gpio_output_pin_schema,
cv.Required(CONF_CLOCK_PIN): pins.gpio_output_pin_schema,
cv.Required(CONF_LATCH_PIN): pins.gpio_output_pin_schema,
cv.Optional(CONF_OE_PIN): pins.gpio_output_pin_schema,
cv.Optional(CONF_SR_COUNT, default=1): cv.int_range(min=1, max=256),
}
).extend(cv.COMPONENT_SCHEMA),
cv.Schema(
{
cv.Required(CONF_ID): cv.declare_id(SN74HC595SPIComponent),
cv.Required(CONF_LATCH_PIN): pins.gpio_output_pin_schema,
cv.Optional(CONF_OE_PIN): pins.gpio_output_pin_schema,
cv.Optional(CONF_SR_COUNT, default=1): cv.int_range(min=1, max=256),
}
)
.extend(cv.COMPONENT_SCHEMA)
.extend(spi.spi_device_schema(cs_pin_required=False))
.extend(
{
cv.Required(CONF_SPI_ID): cv.use_id(spi.SPIComponent),
}
),
msg='Either "data_pin" and "clock_pin" must be set or "spi_id" must be set.',
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
data_pin = await cg.gpio_pin_expression(config[CONF_DATA_PIN])
cg.add(var.set_data_pin(data_pin))
clock_pin = await cg.gpio_pin_expression(config[CONF_CLOCK_PIN])
cg.add(var.set_clock_pin(clock_pin))
if CONF_DATA_PIN in config:
data_pin = await cg.gpio_pin_expression(config[CONF_DATA_PIN])
cg.add(var.set_data_pin(data_pin))
clock_pin = await cg.gpio_pin_expression(config[CONF_CLOCK_PIN])
cg.add(var.set_clock_pin(clock_pin))
elif CONF_SPI_ID in config:
await spi.register_spi_device(var, config)
else:
raise EsphomeError("Not supported")
latch_pin = await cg.gpio_pin_expression(config[CONF_LATCH_PIN])
cg.add(var.set_latch_pin(latch_pin))
if CONF_OE_PIN in config:

View file

@ -6,26 +6,39 @@ namespace sn74hc595 {
static const char *const TAG = "sn74hc595";
void SN74HC595Component::setup() {
void SN74HC595Component::pre_setup_() {
ESP_LOGCONFIG(TAG, "Setting up SN74HC595...");
if (this->have_oe_pin_) { // disable output
this->oe_pin_->setup();
this->oe_pin_->digital_write(true);
}
// initialize output pins
this->clock_pin_->setup();
this->data_pin_->setup();
}
void SN74HC595Component::post_setup_() {
this->latch_pin_->setup();
this->clock_pin_->digital_write(false);
this->data_pin_->digital_write(false);
this->latch_pin_->digital_write(false);
// send state to shift register
this->write_gpio_();
this->write_gpio();
}
void SN74HC595GPIOComponent::setup() {
this->pre_setup_();
this->clock_pin_->setup();
this->data_pin_->setup();
this->clock_pin_->digital_write(false);
this->data_pin_->digital_write(false);
this->post_setup_();
}
#ifdef USE_SPI
void SN74HC595SPIComponent::setup() {
this->pre_setup_();
this->spi_setup();
this->post_setup_();
}
#endif
void SN74HC595Component::dump_config() { ESP_LOGCONFIG(TAG, "SN74HC595:"); }
void SN74HC595Component::digital_write_(uint16_t pin, bool value) {
@ -34,17 +47,38 @@ void SN74HC595Component::digital_write_(uint16_t pin, bool value) {
(this->sr_count_ * 8) - 1);
return;
}
this->output_bits_[pin] = value;
this->write_gpio_();
if (value) {
this->output_bytes_[pin / 8] |= (1 << (pin % 8));
} else {
this->output_bytes_[pin / 8] &= ~(1 << (pin % 8));
}
this->write_gpio();
}
void SN74HC595Component::write_gpio_() {
for (auto bit = this->output_bits_.rbegin(); bit != this->output_bits_.rend(); bit++) {
this->data_pin_->digital_write(*bit);
this->clock_pin_->digital_write(true);
this->clock_pin_->digital_write(false);
void SN74HC595GPIOComponent::write_gpio() {
for (auto byte = this->output_bytes_.rbegin(); byte != this->output_bytes_.rend(); byte++) {
for (int8_t i = 7; i >= 0; i--) {
bool bit = (*byte >> i) & 1;
this->data_pin_->digital_write(bit);
this->clock_pin_->digital_write(true);
this->clock_pin_->digital_write(false);
}
}
SN74HC595Component::write_gpio();
}
#ifdef USE_SPI
void SN74HC595SPIComponent::write_gpio() {
for (auto byte = this->output_bytes_.rbegin(); byte != this->output_bytes_.rend(); byte++) {
this->enable();
this->transfer_byte(*byte);
this->disable();
}
SN74HC595Component::write_gpio();
}
#endif
void SN74HC595Component::write_gpio() {
// pulse latch to activate new values
this->latch_pin_->digital_write(true);
this->latch_pin_->digital_write(false);
@ -60,11 +94,7 @@ float SN74HC595Component::get_setup_priority() const { return setup_priority::IO
void SN74HC595GPIOPin::digital_write(bool value) {
this->parent_->digital_write_(this->pin_, value != this->inverted_);
}
std::string SN74HC595GPIOPin::dump_summary() const {
char buffer[32];
snprintf(buffer, sizeof(buffer), "%u via SN74HC595", pin_);
return buffer;
}
std::string SN74HC595GPIOPin::dump_summary() const { return str_snprintf("%u via SN74HC595", 18, pin_); }
} // namespace sn74hc595
} // namespace esphome

View file

@ -1,9 +1,14 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/defines.h"
#include "esphome/core/hal.h"
#include "esphome/core/helpers.h"
#ifdef USE_SPI
#include "esphome/components/spi/spi.h"
#endif
#include <vector>
namespace esphome {
@ -13,34 +18,33 @@ class SN74HC595Component : public Component {
public:
SN74HC595Component() = default;
void setup() override;
void setup() override = 0;
float get_setup_priority() const override;
void dump_config() override;
void set_data_pin(GPIOPin *pin) { data_pin_ = pin; }
void set_clock_pin(GPIOPin *pin) { clock_pin_ = pin; }
void set_latch_pin(GPIOPin *pin) { latch_pin_ = pin; }
void set_latch_pin(GPIOPin *pin) { this->latch_pin_ = pin; }
void set_oe_pin(GPIOPin *pin) {
oe_pin_ = pin;
have_oe_pin_ = true;
this->oe_pin_ = pin;
this->have_oe_pin_ = true;
}
void set_sr_count(uint8_t count) {
sr_count_ = count;
this->output_bits_.resize(count * 8);
this->sr_count_ = count;
this->output_bytes_.resize(count);
}
protected:
friend class SN74HC595GPIOPin;
void digital_write_(uint16_t pin, bool value);
void write_gpio_();
virtual void write_gpio();
void pre_setup_();
void post_setup_();
GPIOPin *data_pin_;
GPIOPin *clock_pin_;
GPIOPin *latch_pin_;
GPIOPin *oe_pin_;
uint8_t sr_count_;
bool have_oe_pin_{false};
std::vector<bool> output_bits_;
std::vector<uint8_t> output_bytes_;
};
/// Helper class to expose a SC74HC595 pin as an internal output GPIO pin.
@ -60,5 +64,31 @@ class SN74HC595GPIOPin : public GPIOPin, public Parented<SN74HC595Component> {
bool inverted_;
};
class SN74HC595GPIOComponent : public SN74HC595Component {
public:
void setup() override;
void set_data_pin(GPIOPin *pin) { data_pin_ = pin; }
void set_clock_pin(GPIOPin *pin) { clock_pin_ = pin; }
protected:
void write_gpio() override;
GPIOPin *data_pin_;
GPIOPin *clock_pin_;
};
#ifdef USE_SPI
class SN74HC595SPIComponent : public SN74HC595Component,
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW,
spi::CLOCK_PHASE_LEADING, spi::DATA_RATE_4MHZ> {
public:
void setup() override;
protected:
void write_gpio() override;
};
#endif
} // namespace sn74hc595
} // namespace esphome

View file

@ -18,7 +18,7 @@ class SPIDelegateHw : public SPIDelegate {
#elif defined(ESP8266)
// Arduino ESP8266 library has mangled values for SPI modes :-(
auto mode = (this->mode_ & 0x01) + ((this->mode_ & 0x02) << 3);
ESP_LOGV(TAG, "8266 mangled SPI mode 0x%X", mode);
ESP_LOGVV(TAG, "8266 mangled SPI mode 0x%X", mode);
SPISettings const settings(this->data_rate_, this->bit_order_, mode);
#else
SPISettings const settings(this->data_rate_, this->bit_order_, this->mode_);

View file

@ -4,6 +4,7 @@
#include "esphome/core/application.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include <cinttypes>
#include <utility>
namespace esphome {
@ -875,7 +876,7 @@ void Sprinkler::queue_valve(optional<size_t> valve_number, optional<uint32_t> ru
if (this->is_a_valid_valve(valve_number.value()) && (this->queued_valves_.size() < this->max_queue_size_)) {
SprinklerQueueItem item{valve_number.value(), run_duration.value()};
this->queued_valves_.insert(this->queued_valves_.begin(), item);
ESP_LOGD(TAG, "Valve %u placed into queue with run duration of %u seconds", valve_number.value_or(0),
ESP_LOGD(TAG, "Valve %zu placed into queue with run duration of %" PRIu32 " seconds", valve_number.value_or(0),
run_duration.value_or(0));
}
}
@ -954,7 +955,7 @@ void Sprinkler::pause() {
this->paused_valve_ = this->active_valve();
this->resume_duration_ = this->time_remaining_active_valve();
this->shutdown(false);
ESP_LOGD(TAG, "Paused valve %u with %u seconds remaining", this->paused_valve_.value_or(0),
ESP_LOGD(TAG, "Paused valve %zu with %" PRIu32 " seconds remaining", this->paused_valve_.value_or(0),
this->resume_duration_.value_or(0));
}
@ -967,7 +968,7 @@ void Sprinkler::resume() {
if (this->paused_valve_.has_value() && (this->resume_duration_.has_value())) {
// Resume only if valve has not been completed yet
if (!this->valve_cycle_complete_(this->paused_valve_.value())) {
ESP_LOGD(TAG, "Resuming valve %u with %u seconds remaining", this->paused_valve_.value_or(0),
ESP_LOGD(TAG, "Resuming valve %zu with %" PRIu32 " seconds remaining", this->paused_valve_.value_or(0),
this->resume_duration_.value_or(0));
this->fsm_request_(this->paused_valve_.value(), this->resume_duration_.value());
}
@ -1389,7 +1390,8 @@ void Sprinkler::load_next_valve_run_request_(const optional<size_t> first_valve)
this->next_req_.set_run_duration(
this->valve_run_duration_adjusted(this->next_valve_number_in_cycle_(first_valve).value_or(0)));
} else if ((this->repeat_count_++ < this->repeat().value_or(0))) {
ESP_LOGD(TAG, "Repeating - starting cycle %u of %u", this->repeat_count_ + 1, this->repeat().value_or(0) + 1);
ESP_LOGD(TAG, "Repeating - starting cycle %" PRIu32 " of %" PRIu32, this->repeat_count_ + 1,
this->repeat().value_or(0) + 1);
// if there are repeats remaining and no more valves were left in the cycle, start a new cycle
this->prep_full_cycle_();
if (this->next_valve_number_in_cycle_().has_value()) { // this should always succeed here, but just in case...
@ -1420,7 +1422,7 @@ void Sprinkler::start_valve_(SprinklerValveRunRequest *req) {
for (auto &vo : this->valve_op_) { // find the first available SprinklerValveOperator, load it and start it up
if (vo.state() == IDLE) {
auto run_duration = req->run_duration() ? req->run_duration() : this->valve_run_duration_adjusted(req->valve());
ESP_LOGD(TAG, "%s is starting valve %u for %u seconds, cycle %u of %u",
ESP_LOGD(TAG, "%s is starting valve %zu for %" PRIu32 " seconds, cycle %" PRIu32 " of %" PRIu32,
this->req_as_str_(req->request_is_from()).c_str(), req->valve(), run_duration, this->repeat_count_ + 1,
this->repeat().value_or(0) + 1);
req->set_valve_operator(&vo);
@ -1645,7 +1647,7 @@ void Sprinkler::start_timer_(const SprinklerTimerIndex timer_index) {
this->timer_[timer_index].start_time = millis();
this->timer_[timer_index].active = true;
}
ESP_LOGVV(TAG, "Timer %u started for %u sec", static_cast<size_t>(timer_index),
ESP_LOGVV(TAG, "Timer %zu started for %" PRIu32 " sec", static_cast<size_t>(timer_index),
this->timer_duration_(timer_index) / 1000);
}
@ -1684,48 +1686,48 @@ void Sprinkler::sm_timer_callback_() {
void Sprinkler::dump_config() {
ESP_LOGCONFIG(TAG, "Sprinkler Controller -- %s", this->name_.c_str());
if (this->manual_selection_delay_.has_value()) {
ESP_LOGCONFIG(TAG, " Manual Selection Delay: %u seconds", this->manual_selection_delay_.value_or(0));
ESP_LOGCONFIG(TAG, " Manual Selection Delay: %" PRIu32 " seconds", this->manual_selection_delay_.value_or(0));
}
if (this->repeat().has_value()) {
ESP_LOGCONFIG(TAG, " Repeat Cycles: %u times", this->repeat().value_or(0));
ESP_LOGCONFIG(TAG, " Repeat Cycles: %" PRIu32 " times", this->repeat().value_or(0));
}
if (this->start_delay_) {
if (this->start_delay_is_valve_delay_) {
ESP_LOGCONFIG(TAG, " Pump Start Valve Delay: %u seconds", this->start_delay_);
ESP_LOGCONFIG(TAG, " Pump Start Valve Delay: %" PRIu32 " seconds", this->start_delay_);
} else {
ESP_LOGCONFIG(TAG, " Pump Start Pump Delay: %u seconds", this->start_delay_);
ESP_LOGCONFIG(TAG, " Pump Start Pump Delay: %" PRIu32 " seconds", this->start_delay_);
}
}
if (this->stop_delay_) {
if (this->stop_delay_is_valve_delay_) {
ESP_LOGCONFIG(TAG, " Pump Stop Valve Delay: %u seconds", this->stop_delay_);
ESP_LOGCONFIG(TAG, " Pump Stop Valve Delay: %" PRIu32 " seconds", this->stop_delay_);
} else {
ESP_LOGCONFIG(TAG, " Pump Stop Pump Delay: %u seconds", this->stop_delay_);
ESP_LOGCONFIG(TAG, " Pump Stop Pump Delay: %" PRIu32 " seconds", this->stop_delay_);
}
}
if (this->switching_delay_.has_value()) {
if (this->valve_overlap_) {
ESP_LOGCONFIG(TAG, " Valve Overlap: %u seconds", this->switching_delay_.value_or(0));
ESP_LOGCONFIG(TAG, " Valve Overlap: %" PRIu32 " seconds", this->switching_delay_.value_or(0));
} else {
ESP_LOGCONFIG(TAG, " Valve Open Delay: %u seconds", this->switching_delay_.value_or(0));
ESP_LOGCONFIG(TAG, " Valve Open Delay: %" PRIu32 " seconds", this->switching_delay_.value_or(0));
ESP_LOGCONFIG(TAG, " Pump Switch Off During Valve Open Delay: %s",
YESNO(this->pump_switch_off_during_valve_open_delay_));
}
}
for (size_t valve_number = 0; valve_number < this->number_of_valves(); valve_number++) {
ESP_LOGCONFIG(TAG, " Valve %u:", valve_number);
ESP_LOGCONFIG(TAG, " Valve %zu:", valve_number);
ESP_LOGCONFIG(TAG, " Name: %s", this->valve_name(valve_number));
ESP_LOGCONFIG(TAG, " Run Duration: %u seconds", this->valve_run_duration(valve_number));
ESP_LOGCONFIG(TAG, " Run Duration: %" PRIu32 " seconds", this->valve_run_duration(valve_number));
if (this->valve_[valve_number].valve_switch.pulse_duration()) {
ESP_LOGCONFIG(TAG, " Pulse Duration: %u milliseconds",
ESP_LOGCONFIG(TAG, " Pulse Duration: %" PRIu32 " milliseconds",
this->valve_[valve_number].valve_switch.pulse_duration());
}
}
if (!this->pump_.empty()) {
ESP_LOGCONFIG(TAG, " Total number of pumps: %u", this->pump_.size());
ESP_LOGCONFIG(TAG, " Total number of pumps: %zu", this->pump_.size());
}
if (!this->valve_.empty()) {
ESP_LOGCONFIG(TAG, " Total number of valves: %u", this->valve_.size());
ESP_LOGCONFIG(TAG, " Total number of valves: %zu", this->valve_.size());
}
}

View file

@ -1,6 +1,7 @@
#include "status_led_light.h"
#include "esphome/core/log.h"
#include "esphome/core/application.h"
#include <cinttypes>
namespace esphome {
namespace status_led {
@ -11,7 +12,7 @@ void StatusLEDLightOutput::loop() {
uint32_t new_state = App.get_app_state() & STATUS_LED_MASK;
if (new_state != this->last_app_state_) {
ESP_LOGV(TAG, "New app state 0x%08X", new_state);
ESP_LOGV(TAG, "New app state 0x%08" PRIX32, new_state);
}
if ((new_state & STATUS_LED_ERROR) != 0u) {

View file

@ -1,5 +1,6 @@
#include "thermostat_climate.h"
#include "esphome/core/log.h"
#include <cinttypes>
namespace esphome {
namespace thermostat {
@ -1283,11 +1284,13 @@ void ThermostatClimate::dump_config() {
ESP_LOGCONFIG(TAG, " Overrun: %.1f°C", this->cooling_overrun_);
if ((this->supplemental_cool_delta_ > 0) || (this->timer_duration_(thermostat::TIMER_COOLING_MAX_RUN_TIME) > 0)) {
ESP_LOGCONFIG(TAG, " Supplemental Delta: %.1f°C", this->supplemental_cool_delta_);
ESP_LOGCONFIG(TAG, " Maximum Run Time: %us",
ESP_LOGCONFIG(TAG, " Maximum Run Time: %" PRIu32 "s",
this->timer_duration_(thermostat::TIMER_COOLING_MAX_RUN_TIME) / 1000);
}
ESP_LOGCONFIG(TAG, " Minimum Off Time: %us", this->timer_duration_(thermostat::TIMER_COOLING_OFF) / 1000);
ESP_LOGCONFIG(TAG, " Minimum Run Time: %us", this->timer_duration_(thermostat::TIMER_COOLING_ON) / 1000);
ESP_LOGCONFIG(TAG, " Minimum Off Time: %" PRIu32 "s",
this->timer_duration_(thermostat::TIMER_COOLING_OFF) / 1000);
ESP_LOGCONFIG(TAG, " Minimum Run Time: %" PRIu32 "s",
this->timer_duration_(thermostat::TIMER_COOLING_ON) / 1000);
}
if (this->supports_heat_) {
ESP_LOGCONFIG(TAG, " Heating Parameters:");
@ -1295,24 +1298,28 @@ void ThermostatClimate::dump_config() {
ESP_LOGCONFIG(TAG, " Overrun: %.1f°C", this->heating_overrun_);
if ((this->supplemental_heat_delta_ > 0) || (this->timer_duration_(thermostat::TIMER_HEATING_MAX_RUN_TIME) > 0)) {
ESP_LOGCONFIG(TAG, " Supplemental Delta: %.1f°C", this->supplemental_heat_delta_);
ESP_LOGCONFIG(TAG, " Maximum Run Time: %us",
ESP_LOGCONFIG(TAG, " Maximum Run Time: %" PRIu32 "s",
this->timer_duration_(thermostat::TIMER_HEATING_MAX_RUN_TIME) / 1000);
}
ESP_LOGCONFIG(TAG, " Minimum Off Time: %us", this->timer_duration_(thermostat::TIMER_HEATING_OFF) / 1000);
ESP_LOGCONFIG(TAG, " Minimum Run Time: %us", this->timer_duration_(thermostat::TIMER_HEATING_ON) / 1000);
ESP_LOGCONFIG(TAG, " Minimum Off Time: %" PRIu32 "s",
this->timer_duration_(thermostat::TIMER_HEATING_OFF) / 1000);
ESP_LOGCONFIG(TAG, " Minimum Run Time: %" PRIu32 "s",
this->timer_duration_(thermostat::TIMER_HEATING_ON) / 1000);
}
if (this->supports_fan_only_) {
ESP_LOGCONFIG(TAG, " Fanning Minimum Off Time: %us", this->timer_duration_(thermostat::TIMER_FANNING_OFF) / 1000);
ESP_LOGCONFIG(TAG, " Fanning Minimum Run Time: %us", this->timer_duration_(thermostat::TIMER_FANNING_ON) / 1000);
ESP_LOGCONFIG(TAG, " Fanning Minimum Off Time: %" PRIu32 "s",
this->timer_duration_(thermostat::TIMER_FANNING_OFF) / 1000);
ESP_LOGCONFIG(TAG, " Fanning Minimum Run Time: %" PRIu32 "s",
this->timer_duration_(thermostat::TIMER_FANNING_ON) / 1000);
}
if (this->supports_fan_mode_on_ || this->supports_fan_mode_off_ || this->supports_fan_mode_auto_ ||
this->supports_fan_mode_low_ || this->supports_fan_mode_medium_ || this->supports_fan_mode_high_ ||
this->supports_fan_mode_middle_ || this->supports_fan_mode_focus_ || this->supports_fan_mode_diffuse_ ||
this->supports_fan_mode_quiet_) {
ESP_LOGCONFIG(TAG, " Minimum Fan Mode Switching Time: %us",
ESP_LOGCONFIG(TAG, " Minimum Fan Mode Switching Time: %" PRIu32 "s",
this->timer_duration_(thermostat::TIMER_FAN_MODE) / 1000);
}
ESP_LOGCONFIG(TAG, " Minimum Idle Time: %us", this->timer_[thermostat::TIMER_IDLE_ON].time / 1000);
ESP_LOGCONFIG(TAG, " Minimum Idle Time: %" PRIu32 "s", this->timer_[thermostat::TIMER_IDLE_ON].time / 1000);
ESP_LOGCONFIG(TAG, " Supports AUTO: %s", YESNO(this->supports_auto_));
ESP_LOGCONFIG(TAG, " Supports HEAT/COOL: %s", YESNO(this->supports_heat_cool_));
ESP_LOGCONFIG(TAG, " Supports COOL: %s", YESNO(this->supports_cool_));

View file

@ -1,6 +1,7 @@
#include "tof10120_sensor.h"
#include "esphome/core/log.h"
#include "esphome/core/hal.h"
#include <cinttypes>
// Very basic support for TOF10120 distance sensor
@ -44,7 +45,7 @@ void TOF10120Sensor::update() {
}
uint32_t distance_mm = (data[0] << 8) | data[1];
ESP_LOGI(TAG, "Data read: %dmm", distance_mm);
ESP_LOGI(TAG, "Data read: %" PRIu32 "mm", distance_mm);
if (distance_mm == TOF10120_OUT_OF_RANGE_VALUE) {
ESP_LOGW(TAG, "Distance measurement out of range");

View file

@ -1,5 +1,6 @@
#include "esphome/core/log.h"
#include "tuya_sensor.h"
#include <cinttypes>
namespace esphome {
namespace tuya {
@ -18,7 +19,7 @@ void TuyaSensor::setup() {
ESP_LOGV(TAG, "MCU reported sensor %u is: %u", datapoint.id, datapoint.value_enum);
this->publish_state(datapoint.value_enum);
} else if (datapoint.type == TuyaDatapointType::BITMASK) {
ESP_LOGV(TAG, "MCU reported sensor %u is: %x", datapoint.id, datapoint.value_bitmask);
ESP_LOGV(TAG, "MCU reported sensor %u is: %" PRIx32, datapoint.id, datapoint.value_bitmask);
this->publish_state(datapoint.value_bitmask);
}
});

View file

@ -1,6 +1,7 @@
#include "vbus.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include <cinttypes>
namespace esphome {
namespace vbus {
@ -64,8 +65,8 @@ void VBus::loop() {
uint16_t id = (this->buffer_[8] << 8) + this->buffer_[7];
uint32_t value =
(this->buffer_[12] << 24) + (this->buffer_[11] << 16) + (this->buffer_[10] << 8) + this->buffer_[9];
ESP_LOGV(TAG, "P1 C%04x %04x->%04x: %04x %04x (%d)", this->command_, this->source_, this->dest_, id, value,
value);
ESP_LOGV(TAG, "P1 C%04x %04x->%04x: %04x %04" PRIx32 " (%" PRIu32 ")", this->command_, this->source_,
this->dest_, id, value, value);
} else if ((this->protocol_ == 0x10) && (this->buffer_.size() == 9)) {
if (!checksum(this->buffer_.data(), 0, 9)) {
ESP_LOGE(TAG, "P1 checksum failed");

View file

@ -9,6 +9,7 @@ from esphome.const import (
CONF_ID,
CONF_JS_INCLUDE,
CONF_JS_URL,
CONF_ENABLE_PRIVATE_NETWORK_ACCESS,
CONF_PORT,
CONF_AUTH,
CONF_USERNAME,
@ -68,6 +69,7 @@ CONFIG_SCHEMA = cv.All(
cv.Optional(CONF_CSS_INCLUDE): cv.file_,
cv.Optional(CONF_JS_URL): cv.string,
cv.Optional(CONF_JS_INCLUDE): cv.file_,
cv.Optional(CONF_ENABLE_PRIVATE_NETWORK_ACCESS, default=True): cv.boolean,
cv.Optional(CONF_AUTH): cv.Schema(
{
cv.Required(CONF_USERNAME): cv.All(
@ -158,6 +160,8 @@ async def to_code(config):
cg.add(var.set_js_url(config[CONF_JS_URL]))
cg.add(var.set_allow_ota(config[CONF_OTA]))
cg.add(var.set_expose_log(config[CONF_LOG]))
if config[CONF_ENABLE_PRIVATE_NETWORK_ACCESS]:
cg.add_define("USE_WEBSERVER_PRIVATE_NETWORK_ACCESS")
if CONF_AUTH in config:
cg.add(paren.set_auth_username(config[CONF_AUTH][CONF_USERNAME]))
cg.add(paren.set_auth_password(config[CONF_AUTH][CONF_PASSWORD]))

View file

@ -34,6 +34,13 @@ namespace web_server {
static const char *const TAG = "web_server";
#ifdef USE_WEBSERVER_PRIVATE_NETWORK_ACCESS
static const char *const HEADER_PNA_NAME = "Private-Network-Access-Name";
static const char *const HEADER_PNA_ID = "Private-Network-Access-ID";
static const char *const HEADER_CORS_REQ_PNA = "Access-Control-Request-Private-Network";
static const char *const HEADER_CORS_ALLOW_PNA = "Access-Control-Allow-Private-Network";
#endif
#if USE_WEBSERVER_VERSION == 1
void write_row(AsyncResponseStream *stream, EntityBase *obj, const std::string &klass, const std::string &action,
const std::function<void(AsyncResponseStream &stream, EntityBase *obj)> &action_func = nullptr) {
@ -359,6 +366,17 @@ void WebServer::handle_index_request(AsyncWebServerRequest *request) {
}
#endif
#ifdef USE_WEBSERVER_PRIVATE_NETWORK_ACCESS
void WebServer::handle_pna_cors_request(AsyncWebServerRequest *request) {
AsyncWebServerResponse *response = request->beginResponse(200, "");
response->addHeader(HEADER_CORS_ALLOW_PNA, "true");
response->addHeader(HEADER_PNA_NAME, App.get_name().c_str());
std::string mac = get_mac_address_pretty();
response->addHeader(HEADER_PNA_ID, mac.c_str());
request->send(response);
}
#endif
#ifdef USE_WEBSERVER_CSS_INCLUDE
void WebServer::handle_css_request(AsyncWebServerRequest *request) {
AsyncWebServerResponse *response =
@ -1145,6 +1163,18 @@ bool WebServer::canHandle(AsyncWebServerRequest *request) {
return true;
#endif
#ifdef USE_WEBSERVER_PRIVATE_NETWORK_ACCESS
if (request->method() == HTTP_OPTIONS && request->hasHeader(HEADER_CORS_REQ_PNA)) {
#ifdef USE_ARDUINO
// Header needs to be added to interesting header list for it to not be
// nuked by the time we handle the request later.
// Only required in Arduino framework.
request->addInterestingHeader(HEADER_CORS_REQ_PNA);
#endif
return true;
}
#endif
UrlMatch match = match_url(request->url().c_str(), true);
if (!match.valid)
return false;
@ -1240,6 +1270,13 @@ void WebServer::handleRequest(AsyncWebServerRequest *request) {
}
#endif
#ifdef USE_WEBSERVER_PRIVATE_NETWORK_ACCESS
if (request->method() == HTTP_OPTIONS && request->hasHeader(HEADER_CORS_REQ_PNA)) {
this->handle_pna_cors_request(request);
return;
}
#endif
UrlMatch match = match_url(request->url().c_str());
#ifdef USE_SENSOR
if (match.domain == "sensor") {

View file

@ -130,6 +130,11 @@ class WebServer : public Controller, public Component, public AsyncWebHandler {
void handle_js_request(AsyncWebServerRequest *request);
#endif
#ifdef USE_WEBSERVER_PRIVATE_NETWORK_ACCESS
// Handle Private Network Access CORS OPTIONS request
void handle_pna_cors_request(AsyncWebServerRequest *request);
#endif
#ifdef USE_SENSOR
void on_sensor_update(sensor::Sensor *obj, float state) override;
/// Handle a sensor request under '/sensor/<id>'.

Some files were not shown because too many files have changed in this diff Show more