mirror of
https://github.com/esphome/esphome.git
synced 2024-12-12 14:57:34 +01:00
Merge branch 'dev' into optolink
This commit is contained in:
commit
d999d6dc28
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
||||
|
||||
|
@ -0,0 +1 @@
|
||||
CODEOWNERS = ["@angelnu"]
|
@ -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
|
@ -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
|
@ -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))
|
||||
|
196
esphome/components/ade7953_base/__init__.py
Normal file
196
esphome/components/ade7953_base/__init__.py
Normal 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))
|
129
esphome/components/ade7953_base/ade7953_base.cpp
Normal file
129
esphome/components/ade7953_base/ade7953_base.cpp
Normal 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
|
121
esphome/components/ade7953_base/ade7953_base.h
Normal file
121
esphome/components/ade7953_base/ade7953_base.h
Normal 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
|
1
esphome/components/ade7953_i2c/__init__.py
Normal file
1
esphome/components/ade7953_i2c/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
CODEOWNERS = ["@angelnu"]
|
80
esphome/components/ade7953_i2c/ade7953_i2c.cpp
Normal file
80
esphome/components/ade7953_i2c/ade7953_i2c.cpp
Normal 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
|
28
esphome/components/ade7953_i2c/ade7953_i2c.h
Normal file
28
esphome/components/ade7953_i2c/ade7953_i2c.h
Normal 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
|
27
esphome/components/ade7953_i2c/sensor.py
Normal file
27
esphome/components/ade7953_i2c/sensor.py
Normal 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)
|
1
esphome/components/ade7953_spi/__init__.py
Normal file
1
esphome/components/ade7953_spi/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
CODEOWNERS = ["@angelnu"]
|
81
esphome/components/ade7953_spi/ade7953_spi.cpp
Normal file
81
esphome/components/ade7953_spi/ade7953_spi.cpp
Normal 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
|
32
esphome/components/ade7953_spi/ade7953_spi.h
Normal file
32
esphome/components/ade7953_spi/ade7953_spi.h
Normal 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
|
27
esphome/components/ade7953_spi/sensor.py
Normal file
27
esphome/components/ade7953_spi/sensor.py
Normal 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)
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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_);
|
||||
|
@ -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)
|
||||
|
@ -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_) {
|
||||
|
@ -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
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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]))
|
||||
|
@ -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;
|
||||
|
@ -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_; }
|
||||
|
@ -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:"); }
|
||||
|
@ -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_;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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]
|
||||
|
@ -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...");
|
||||
|
@ -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_;
|
||||
|
@ -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);
|
||||
});
|
||||
|
@ -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_;
|
||||
|
@ -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),
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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];
|
||||
|
@ -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};
|
||||
|
@ -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(
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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]))
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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
|
268
esphome/components/nextion/nextion_upload_idf.cpp
Normal file
268
esphome/components/nextion/nextion_upload_idf.cpp
Normal 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
|
@ -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);
|
||||
|
||||
|
@ -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,
|
||||
}
|
||||
)
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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,
|
||||
|
134
esphome/components/remote_base/byronsx_protocol.cpp
Normal file
134
esphome/components/remote_base/byronsx_protocol.cpp
Normal 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
|
46
esphome/components/remote_base/byronsx_protocol.h
Normal file
46
esphome/components/remote_base/byronsx_protocol.h
Normal 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
|
@ -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 {};
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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; }
|
||||
|
@ -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"
|
||||
|
6
esphome/components/resistance_sampler/__init__.py
Normal file
6
esphome/components/resistance_sampler/__init__.py
Normal 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"]
|
10
esphome/components/resistance_sampler/resistance_sampler.h
Normal file
10
esphome/components/resistance_sampler/resistance_sampler.h
Normal 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
|
@ -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);
|
||||
|
@ -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() {
|
||||
|
@ -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"
|
||||
)
|
||||
|
@ -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) {
|
||||
|
@ -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):
|
||||
|
19
esphome/components/sml/automation.h
Normal file
19
esphome/components/sml/automation.h
Normal 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
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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_);
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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_));
|
||||
|
@ -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");
|
||||
|
@ -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);
|
||||
}
|
||||
});
|
||||
|
@ -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");
|
||||
|
@ -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]))
|
||||
|
@ -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") {
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user