mirror of
https://github.com/esphome/esphome.git
synced 2025-10-30 06:33:51 +00:00
Merge branch 'dev' into bump-2021.10.0b1
This commit is contained in:
@@ -72,7 +72,7 @@ def choose_upload_log_host(default, check_default, show_ota, show_mqtt, show_api
|
||||
if default == "OTA":
|
||||
return CORE.address
|
||||
if show_mqtt and "mqtt" in CORE.config:
|
||||
options.append(("MQTT ({})".format(CORE.config["mqtt"][CONF_BROKER]), "MQTT"))
|
||||
options.append((f"MQTT ({CORE.config['mqtt'][CONF_BROKER]})", "MQTT"))
|
||||
if default == "OTA":
|
||||
return "MQTT"
|
||||
if default is not None:
|
||||
@@ -184,12 +184,30 @@ def compile_program(args, config):
|
||||
|
||||
|
||||
def upload_using_esptool(config, port):
|
||||
path = CORE.firmware_bin
|
||||
from esphome import platformio_api
|
||||
|
||||
first_baudrate = config[CONF_ESPHOME][CONF_PLATFORMIO_OPTIONS].get(
|
||||
"upload_speed", 460800
|
||||
)
|
||||
|
||||
def run_esptool(baud_rate):
|
||||
idedata = platformio_api.get_idedata(config)
|
||||
|
||||
firmware_offset = "0x10000" if CORE.is_esp32 else "0x0"
|
||||
flash_images = [
|
||||
platformio_api.FlashImage(
|
||||
path=idedata.firmware_bin_path,
|
||||
offset=firmware_offset,
|
||||
),
|
||||
*idedata.extra_flash_images,
|
||||
]
|
||||
|
||||
mcu = "esp8266"
|
||||
if CORE.is_esp32:
|
||||
from esphome.components.esp32 import get_esp32_variant
|
||||
|
||||
mcu = get_esp32_variant().lower()
|
||||
|
||||
cmd = [
|
||||
"esptool.py",
|
||||
"--before",
|
||||
@@ -198,14 +216,15 @@ def upload_using_esptool(config, port):
|
||||
"hard_reset",
|
||||
"--baud",
|
||||
str(baud_rate),
|
||||
"--chip",
|
||||
"esp8266",
|
||||
"--port",
|
||||
port,
|
||||
"--chip",
|
||||
mcu,
|
||||
"write_flash",
|
||||
"0x0",
|
||||
path,
|
||||
"-z",
|
||||
]
|
||||
for img in flash_images:
|
||||
cmd += [img.offset, img.path]
|
||||
|
||||
if os.environ.get("ESPHOME_USE_SUBPROCESS") is None:
|
||||
import esptool
|
||||
@@ -229,11 +248,7 @@ def upload_using_esptool(config, port):
|
||||
def upload_program(config, args, host):
|
||||
# if upload is to a serial port use platformio, otherwise assume ota
|
||||
if get_port_type(host) == "SERIAL":
|
||||
from esphome import platformio_api
|
||||
|
||||
if CORE.is_esp8266:
|
||||
return upload_using_esptool(config, host)
|
||||
return platformio_api.run_upload(config, CORE.verbose, host)
|
||||
return upload_using_esptool(config, host)
|
||||
|
||||
from esphome import espota2
|
||||
|
||||
@@ -245,7 +260,7 @@ def upload_program(config, args, host):
|
||||
|
||||
ota_conf = config[CONF_OTA]
|
||||
remote_port = ota_conf[CONF_PORT]
|
||||
password = ota_conf[CONF_PASSWORD]
|
||||
password = ota_conf.get(CONF_PASSWORD, "")
|
||||
return espota2.run_ota(host, remote_port, password, CORE.firmware_bin)
|
||||
|
||||
|
||||
@@ -415,30 +430,30 @@ def command_update_all(args):
|
||||
click.echo(f"{half_line}{middle_text}{half_line}")
|
||||
|
||||
for f in files:
|
||||
print("Updating {}".format(color(Fore.CYAN, f)))
|
||||
print(f"Updating {color(Fore.CYAN, f)}")
|
||||
print("-" * twidth)
|
||||
print()
|
||||
rc = run_external_process(
|
||||
"esphome", "--dashboard", "run", f, "--no-logs", "--device", "OTA"
|
||||
)
|
||||
if rc == 0:
|
||||
print_bar("[{}] {}".format(color(Fore.BOLD_GREEN, "SUCCESS"), f))
|
||||
print_bar(f"[{color(Fore.BOLD_GREEN, 'SUCCESS')}] {f}")
|
||||
success[f] = True
|
||||
else:
|
||||
print_bar("[{}] {}".format(color(Fore.BOLD_RED, "ERROR"), f))
|
||||
print_bar(f"[{color(Fore.BOLD_RED, 'ERROR')}] {f}")
|
||||
success[f] = False
|
||||
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
|
||||
print_bar("[{}]".format(color(Fore.BOLD_WHITE, "SUMMARY")))
|
||||
print_bar(f"[{color(Fore.BOLD_WHITE, 'SUMMARY')}]")
|
||||
failed = 0
|
||||
for f in files:
|
||||
if success[f]:
|
||||
print(" - {}: {}".format(f, color(Fore.GREEN, "SUCCESS")))
|
||||
print(f" - {f}: {color(Fore.GREEN, 'SUCCESS')}")
|
||||
else:
|
||||
print(" - {}: {}".format(f, color(Fore.BOLD_RED, "FAILED")))
|
||||
print(f" - {f}: {color(Fore.BOLD_RED, 'FAILED')}")
|
||||
failed += 1
|
||||
return failed
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ from esphome.const import (
|
||||
CONF_ELSE,
|
||||
CONF_ID,
|
||||
CONF_THEN,
|
||||
CONF_TIMEOUT,
|
||||
CONF_TRIGGER_ID,
|
||||
CONF_TYPE_ID,
|
||||
CONF_TIME,
|
||||
@@ -244,6 +245,9 @@ def validate_wait_until(value):
|
||||
schema = cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_CONDITION): validate_potentially_and_condition,
|
||||
cv.Optional(CONF_TIMEOUT): cv.templatable(
|
||||
cv.positive_time_period_milliseconds
|
||||
),
|
||||
}
|
||||
)
|
||||
if isinstance(value, dict) and CONF_CONDITION in value:
|
||||
@@ -255,6 +259,9 @@ def validate_wait_until(value):
|
||||
async def wait_until_action_to_code(config, action_id, template_arg, args):
|
||||
conditions = await build_condition(config[CONF_CONDITION], template_arg, args)
|
||||
var = cg.new_Pvariable(action_id, template_arg, conditions)
|
||||
if CONF_TIMEOUT in config:
|
||||
template_ = await cg.templatable(config[CONF_TIMEOUT], args, cg.uint32)
|
||||
cg.add(var.set_timeout_value(template_))
|
||||
await cg.register_component(var, {})
|
||||
return var
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ from esphome.cpp_generator import ( # noqa
|
||||
add_library,
|
||||
add_build_flag,
|
||||
add_define,
|
||||
add_platformio_option,
|
||||
get_variable,
|
||||
get_variable_with_full_id,
|
||||
process_lambda,
|
||||
@@ -66,7 +67,7 @@ from esphome.cpp_types import ( # noqa
|
||||
NAN,
|
||||
esphome_ns,
|
||||
App,
|
||||
Nameable,
|
||||
EntityBase,
|
||||
Component,
|
||||
ComponentPtr,
|
||||
PollingComponent,
|
||||
@@ -78,4 +79,6 @@ from esphome.cpp_types import ( # noqa
|
||||
JsonObjectConstRef,
|
||||
Controller,
|
||||
GPIOPin,
|
||||
InternalGPIOPin,
|
||||
gpio_Flags,
|
||||
)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/esphal.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/components/stepper/stepper.h"
|
||||
|
||||
namespace esphome {
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
#ifdef USE_ARDUINO
|
||||
|
||||
#include "ac_dimmer.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include <cmath>
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
#ifdef USE_ESP8266
|
||||
#include <core_esp8266_waveform.h>
|
||||
#endif
|
||||
#ifdef USE_ESP32_FRAMEWORK_ARDUINO
|
||||
#include <esp32-hal-timer.h>
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace ac_dimmer {
|
||||
@@ -17,12 +23,15 @@ static AcDimmerDataStore *all_dimmers[32]; // NOLINT(cppcoreguidelines-avoid-no
|
||||
/// Time in microseconds the gate should be held high
|
||||
/// 10µs should be long enough for most triacs
|
||||
/// For reference: BT136 datasheet says 2µs nominal (page 7)
|
||||
static const uint32_t GATE_ENABLE_TIME = 10;
|
||||
/// However other factors like gate driver propagation time
|
||||
/// are also considered and a really low value is not important
|
||||
/// See also: https://github.com/esphome/issues/issues/1632
|
||||
static const uint32_t GATE_ENABLE_TIME = 50;
|
||||
|
||||
/// Function called from timer interrupt
|
||||
/// Input is current time in microseconds (micros())
|
||||
/// Returns when next "event" is expected in µs, or 0 if no such event known.
|
||||
uint32_t ICACHE_RAM_ATTR HOT AcDimmerDataStore::timer_intr(uint32_t now) {
|
||||
uint32_t IRAM_ATTR HOT AcDimmerDataStore::timer_intr(uint32_t now) {
|
||||
// If no ZC signal received yet.
|
||||
if (this->crossed_zero_at == 0)
|
||||
return 0;
|
||||
@@ -34,13 +43,13 @@ uint32_t ICACHE_RAM_ATTR HOT AcDimmerDataStore::timer_intr(uint32_t now) {
|
||||
|
||||
if (this->enable_time_us != 0 && time_since_zc >= this->enable_time_us) {
|
||||
this->enable_time_us = 0;
|
||||
this->gate_pin->digital_write(true);
|
||||
this->gate_pin.digital_write(true);
|
||||
// Prevent too short pulses
|
||||
this->disable_time_us = max(this->disable_time_us, time_since_zc + GATE_ENABLE_TIME);
|
||||
this->disable_time_us = std::max(this->disable_time_us, time_since_zc + GATE_ENABLE_TIME);
|
||||
}
|
||||
if (this->disable_time_us != 0 && time_since_zc >= this->disable_time_us) {
|
||||
this->disable_time_us = 0;
|
||||
this->gate_pin->digital_write(false);
|
||||
this->gate_pin.digital_write(false);
|
||||
}
|
||||
|
||||
if (time_since_zc < this->enable_time_us)
|
||||
@@ -60,7 +69,7 @@ uint32_t ICACHE_RAM_ATTR HOT AcDimmerDataStore::timer_intr(uint32_t now) {
|
||||
}
|
||||
|
||||
/// Run timer interrupt code and return in how many µs the next event is expected
|
||||
uint32_t ICACHE_RAM_ATTR HOT timer_interrupt() {
|
||||
uint32_t IRAM_ATTR HOT timer_interrupt() {
|
||||
// run at least with 1kHz
|
||||
uint32_t min_dt_us = 1000;
|
||||
uint32_t now = micros();
|
||||
@@ -77,7 +86,7 @@ uint32_t ICACHE_RAM_ATTR HOT timer_interrupt() {
|
||||
}
|
||||
|
||||
/// GPIO interrupt routine, called when ZC pin triggers
|
||||
void ICACHE_RAM_ATTR HOT AcDimmerDataStore::gpio_intr() {
|
||||
void IRAM_ATTR HOT AcDimmerDataStore::gpio_intr() {
|
||||
uint32_t prev_crossed = this->crossed_zero_at;
|
||||
|
||||
// 50Hz mains frequency should give a half cycle of 10ms a 60Hz will give 8.33ms
|
||||
@@ -94,7 +103,7 @@ void ICACHE_RAM_ATTR HOT AcDimmerDataStore::gpio_intr() {
|
||||
|
||||
if (this->value == 65535) {
|
||||
// fully on, enable output immediately
|
||||
this->gate_pin->digital_write(true);
|
||||
this->gate_pin.digital_write(true);
|
||||
} else if (this->init_cycle) {
|
||||
// send a full cycle
|
||||
this->init_cycle = false;
|
||||
@@ -102,29 +111,29 @@ void ICACHE_RAM_ATTR HOT AcDimmerDataStore::gpio_intr() {
|
||||
this->disable_time_us = cycle_time_us;
|
||||
} else if (this->value == 0) {
|
||||
// fully off, disable output immediately
|
||||
this->gate_pin->digital_write(false);
|
||||
this->gate_pin.digital_write(false);
|
||||
} else {
|
||||
if (this->method == DIM_METHOD_TRAILING) {
|
||||
this->enable_time_us = 1; // cannot be 0
|
||||
this->disable_time_us = max((uint32_t) 10, this->value * this->cycle_time_us / 65535);
|
||||
this->disable_time_us = std::max((uint32_t) 10, this->value * this->cycle_time_us / 65535);
|
||||
} else {
|
||||
// calculate time until enable in µs: (1.0-value)*cycle_time, but with integer arithmetic
|
||||
// also take into account min_power
|
||||
auto min_us = this->cycle_time_us * this->min_power / 1000;
|
||||
this->enable_time_us = max((uint32_t) 1, ((65535 - this->value) * (this->cycle_time_us - min_us)) / 65535);
|
||||
this->enable_time_us = std::max((uint32_t) 1, ((65535 - this->value) * (this->cycle_time_us - min_us)) / 65535);
|
||||
if (this->method == DIM_METHOD_LEADING_PULSE) {
|
||||
// Minimum pulse time should be enough for the triac to trigger when it is close to the ZC zone
|
||||
// this is for brightness near 99%
|
||||
this->disable_time_us = max(this->enable_time_us + GATE_ENABLE_TIME, (uint32_t) cycle_time_us / 10);
|
||||
this->disable_time_us = std::max(this->enable_time_us + GATE_ENABLE_TIME, (uint32_t) cycle_time_us / 10);
|
||||
} else {
|
||||
this->gate_pin->digital_write(false);
|
||||
this->gate_pin.digital_write(false);
|
||||
this->disable_time_us = this->cycle_time_us;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ICACHE_RAM_ATTR HOT AcDimmerDataStore::s_gpio_intr(AcDimmerDataStore *store) {
|
||||
void IRAM_ATTR HOT AcDimmerDataStore::s_gpio_intr(AcDimmerDataStore *store) {
|
||||
// Attaching pin interrupts on the same pin will override the previous interrupt
|
||||
// However, the user expects that multiple dimmers sharing the same ZC pin will work.
|
||||
// We solve this in a bit of a hacky way: On each pin interrupt, we check all dimmers
|
||||
@@ -138,11 +147,11 @@ void ICACHE_RAM_ATTR HOT AcDimmerDataStore::s_gpio_intr(AcDimmerDataStore *store
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#ifdef USE_ESP32
|
||||
// ESP32 implementation, uses basically the same code but needs to wrap
|
||||
// timer_interrupt() function to auto-reschedule
|
||||
static hw_timer_t *dimmer_timer = nullptr;
|
||||
void ICACHE_RAM_ATTR HOT AcDimmerDataStore::s_timer_intr() { timer_interrupt(); }
|
||||
static hw_timer_t *dimmer_timer = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
void IRAM_ATTR HOT AcDimmerDataStore::s_timer_intr() { timer_interrupt(); }
|
||||
#endif
|
||||
|
||||
void AcDimmer::setup() {
|
||||
@@ -171,15 +180,16 @@ void AcDimmer::setup() {
|
||||
if (setup_zero_cross_pin) {
|
||||
this->zero_cross_pin_->setup();
|
||||
this->store_.zero_cross_pin = this->zero_cross_pin_->to_isr();
|
||||
this->zero_cross_pin_->attach_interrupt(&AcDimmerDataStore::s_gpio_intr, &this->store_, FALLING);
|
||||
this->zero_cross_pin_->attach_interrupt(&AcDimmerDataStore::s_gpio_intr, &this->store_,
|
||||
gpio::INTERRUPT_FALLING_EDGE);
|
||||
}
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
#ifdef USE_ESP8266
|
||||
// Uses ESP8266 waveform (soft PWM) class
|
||||
// PWM and AcDimmer can even run at the same time this way
|
||||
setTimer1Callback(&timer_interrupt);
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#ifdef USE_ESP32
|
||||
// 80 Divider -> 1 count=1µs
|
||||
dimmer_timer = timerBegin(0, 80, true);
|
||||
timerAttachInterrupt(dimmer_timer, &AcDimmerDataStore::s_timer_intr, true);
|
||||
@@ -215,3 +225,5 @@ void AcDimmer::dump_config() {
|
||||
|
||||
} // namespace ac_dimmer
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_ARDUINO
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_ARDUINO
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/esphal.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/components/output/float_output.h"
|
||||
|
||||
namespace esphome {
|
||||
@@ -11,11 +13,11 @@ enum DimMethod { DIM_METHOD_LEADING_PULSE = 0, DIM_METHOD_LEADING, DIM_METHOD_TR
|
||||
|
||||
struct AcDimmerDataStore {
|
||||
/// Zero-cross pin
|
||||
ISRInternalGPIOPin *zero_cross_pin;
|
||||
ISRInternalGPIOPin zero_cross_pin;
|
||||
/// Zero-cross pin number - used to share ZC pin across multiple dimmers
|
||||
uint8_t zero_cross_pin_number;
|
||||
/// Output pin to write to
|
||||
ISRInternalGPIOPin *gate_pin;
|
||||
ISRInternalGPIOPin gate_pin;
|
||||
/// Value of the dimmer - 0 to 65535.
|
||||
uint16_t value;
|
||||
/// Minimum power for activation
|
||||
@@ -37,7 +39,7 @@ struct AcDimmerDataStore {
|
||||
|
||||
void gpio_intr();
|
||||
static void s_gpio_intr(AcDimmerDataStore *store);
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#ifdef USE_ESP32
|
||||
static void s_timer_intr();
|
||||
#endif
|
||||
};
|
||||
@@ -47,16 +49,16 @@ class AcDimmer : public output::FloatOutput, public Component {
|
||||
void setup() override;
|
||||
|
||||
void dump_config() override;
|
||||
void set_gate_pin(GPIOPin *gate_pin) { gate_pin_ = gate_pin; }
|
||||
void set_zero_cross_pin(GPIOPin *zero_cross_pin) { zero_cross_pin_ = zero_cross_pin; }
|
||||
void set_gate_pin(InternalGPIOPin *gate_pin) { gate_pin_ = gate_pin; }
|
||||
void set_zero_cross_pin(InternalGPIOPin *zero_cross_pin) { zero_cross_pin_ = zero_cross_pin; }
|
||||
void set_init_with_half_cycle(bool init_with_half_cycle) { init_with_half_cycle_ = init_with_half_cycle; }
|
||||
void set_method(DimMethod method) { method_ = method; }
|
||||
|
||||
protected:
|
||||
void write_state(float state) override;
|
||||
|
||||
GPIOPin *gate_pin_;
|
||||
GPIOPin *zero_cross_pin_;
|
||||
InternalGPIOPin *gate_pin_;
|
||||
InternalGPIOPin *zero_cross_pin_;
|
||||
AcDimmerDataStore store_;
|
||||
bool init_with_half_cycle_;
|
||||
DimMethod method_;
|
||||
@@ -64,3 +66,5 @@ class AcDimmer : public output::FloatOutput, public Component {
|
||||
|
||||
} // namespace ac_dimmer
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_ARDUINO
|
||||
|
||||
@@ -19,17 +19,20 @@ DIM_METHODS = {
|
||||
CONF_GATE_PIN = "gate_pin"
|
||||
CONF_ZERO_CROSS_PIN = "zero_cross_pin"
|
||||
CONF_INIT_WITH_HALF_CYCLE = "init_with_half_cycle"
|
||||
CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.declare_id(AcDimmer),
|
||||
cv.Required(CONF_GATE_PIN): pins.internal_gpio_output_pin_schema,
|
||||
cv.Required(CONF_ZERO_CROSS_PIN): pins.internal_gpio_input_pin_schema,
|
||||
cv.Optional(CONF_INIT_WITH_HALF_CYCLE, default=True): cv.boolean,
|
||||
cv.Optional(CONF_METHOD, default="leading pulse"): cv.enum(
|
||||
DIM_METHODS, upper=True, space="_"
|
||||
),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
output.FLOAT_OUTPUT_SCHEMA.extend(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.declare_id(AcDimmer),
|
||||
cv.Required(CONF_GATE_PIN): pins.internal_gpio_output_pin_schema,
|
||||
cv.Required(CONF_ZERO_CROSS_PIN): pins.internal_gpio_input_pin_schema,
|
||||
cv.Optional(CONF_INIT_WITH_HALF_CYCLE, default=True): cv.boolean,
|
||||
cv.Optional(CONF_METHOD, default="leading pulse"): cv.enum(
|
||||
DIM_METHODS, upper=True, space="_"
|
||||
),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA),
|
||||
cv.only_with_arduino,
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
#include "adc_sensor.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#ifdef USE_ESP8266
|
||||
#ifdef USE_ADC_SENSOR_VCC
|
||||
#include <Esp.h>
|
||||
ADC_MODE(ADC_VCC)
|
||||
#else
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
@@ -10,7 +15,7 @@ namespace adc {
|
||||
|
||||
static const char *const TAG = "adc";
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#ifdef USE_ESP32
|
||||
void ADCSensor::set_attenuation(adc_atten_t attenuation) { this->attenuation_ = attenuation; }
|
||||
|
||||
inline adc1_channel_t gpio_to_adc1(uint8_t pin) {
|
||||
@@ -57,28 +62,28 @@ inline adc1_channel_t gpio_to_adc1(uint8_t pin) {
|
||||
void ADCSensor::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str());
|
||||
#ifndef USE_ADC_SENSOR_VCC
|
||||
GPIOPin(this->pin_, INPUT).setup();
|
||||
pin_->setup();
|
||||
#endif
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
adc1_config_channel_atten(gpio_to_adc1(pin_), attenuation_);
|
||||
#ifdef USE_ESP32
|
||||
adc1_config_channel_atten(gpio_to_adc1(pin_->get_pin()), attenuation_);
|
||||
adc1_config_width(ADC_WIDTH_BIT_12);
|
||||
#if !CONFIG_IDF_TARGET_ESP32C3 && !CONFIG_IDF_TARGET_ESP32H2
|
||||
adc_gpio_init(ADC_UNIT_1, (adc_channel_t) gpio_to_adc1(pin_));
|
||||
adc_gpio_init(ADC_UNIT_1, (adc_channel_t) gpio_to_adc1(pin_->get_pin()));
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
void ADCSensor::dump_config() {
|
||||
LOG_SENSOR("", "ADC Sensor", this);
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
#ifdef USE_ESP8266
|
||||
#ifdef USE_ADC_SENSOR_VCC
|
||||
ESP_LOGCONFIG(TAG, " Pin: VCC");
|
||||
#else
|
||||
ESP_LOGCONFIG(TAG, " Pin: %u", this->pin_);
|
||||
LOG_PIN(" Pin: ", pin_);
|
||||
#endif
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
ESP_LOGCONFIG(TAG, " Pin: %u", this->pin_);
|
||||
#ifdef USE_ESP32
|
||||
LOG_PIN(" Pin: ", pin_);
|
||||
switch (this->attenuation_) {
|
||||
case ADC_ATTEN_DB_0:
|
||||
ESP_LOGCONFIG(TAG, " Attenuation: 0db (max 1.1V)");
|
||||
@@ -105,8 +110,8 @@ void ADCSensor::update() {
|
||||
this->publish_state(value_v);
|
||||
}
|
||||
float ADCSensor::sample() {
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
int raw = adc1_get_raw(gpio_to_adc1(pin_));
|
||||
#ifdef USE_ESP32
|
||||
int raw = adc1_get_raw(gpio_to_adc1(pin_->get_pin()));
|
||||
float value_v = raw / 4095.0f;
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
switch (this->attenuation_) {
|
||||
@@ -146,15 +151,15 @@ float ADCSensor::sample() {
|
||||
return value_v;
|
||||
#endif
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
#ifdef USE_ESP8266
|
||||
#ifdef USE_ADC_SENSOR_VCC
|
||||
return ESP.getVcc() / 1024.0f;
|
||||
return ESP.getVcc() / 1024.0f; // NOLINT(readability-static-accessed-through-instance)
|
||||
#else
|
||||
return analogRead(this->pin_) / 1024.0f; // NOLINT
|
||||
return analogRead(this->pin_->get_pin()) / 1024.0f; // NOLINT
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
#ifdef USE_ESP8266
|
||||
std::string ADCSensor::unique_id() { return get_mac_address() + "-adc"; }
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/esphal.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/voltage_sampler/voltage_sampler.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#ifdef USE_ESP32
|
||||
#include "driver/adc.h"
|
||||
#endif
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace adc {
|
||||
|
||||
class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage_sampler::VoltageSampler {
|
||||
public:
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#ifdef USE_ESP32
|
||||
/// Set the attenuation for this pin. Only available on the ESP32.
|
||||
void set_attenuation(adc_atten_t attenuation);
|
||||
#endif
|
||||
@@ -27,17 +27,17 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
|
||||
void dump_config() override;
|
||||
/// `HARDWARE_LATE` setup priority.
|
||||
float get_setup_priority() const override;
|
||||
void set_pin(uint8_t pin) { this->pin_ = pin; }
|
||||
void set_pin(InternalGPIOPin *pin) { this->pin_ = pin; }
|
||||
float sample() override;
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
#ifdef USE_ESP8266
|
||||
std::string unique_id() override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
uint8_t pin_;
|
||||
InternalGPIOPin *pin_;
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#ifdef USE_ESP32
|
||||
adc_atten_t attenuation_{ADC_ATTEN_DB_0};
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -5,11 +5,13 @@ from esphome.components import sensor, voltage_sampler
|
||||
from esphome.const import (
|
||||
CONF_ATTENUATION,
|
||||
CONF_ID,
|
||||
CONF_INPUT,
|
||||
CONF_PIN,
|
||||
DEVICE_CLASS_VOLTAGE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_VOLT,
|
||||
)
|
||||
from esphome.core import CORE
|
||||
|
||||
|
||||
AUTO_LOAD = ["voltage_sampler"]
|
||||
@@ -23,10 +25,34 @@ ATTENUATION_MODES = {
|
||||
|
||||
|
||||
def validate_adc_pin(value):
|
||||
vcc = str(value).upper()
|
||||
if vcc == "VCC":
|
||||
return cv.only_on_esp8266(vcc)
|
||||
return pins.analog_pin(value)
|
||||
if str(value).upper() == "VCC":
|
||||
return cv.only_on_esp8266("VCC")
|
||||
|
||||
if CORE.is_esp32:
|
||||
from esphome.components.esp32 import is_esp32c3
|
||||
|
||||
value = pins.internal_gpio_input_pin_number(value)
|
||||
if is_esp32c3():
|
||||
if not (0 <= value <= 4): # ADC1
|
||||
raise cv.Invalid("ESP32-C3: Only pins 0 though 4 support ADC.")
|
||||
if not (32 <= value <= 39): # ADC1
|
||||
raise cv.Invalid("ESP32: Only pins 32 though 39 support ADC.")
|
||||
elif CORE.is_esp8266:
|
||||
from esphome.components.esp8266.gpio import CONF_ANALOG
|
||||
|
||||
value = pins.internal_gpio_pin_number({CONF_ANALOG: True, CONF_INPUT: True})(
|
||||
value
|
||||
)
|
||||
|
||||
if value != 17: # A0
|
||||
raise cv.Invalid("ESP8266: Only pin A0 (GPIO17) supports ADC.")
|
||||
return pins.gpio_pin_schema(
|
||||
{CONF_ANALOG: True, CONF_INPUT: True}, internal=True
|
||||
)(value)
|
||||
else:
|
||||
raise NotImplementedError
|
||||
|
||||
return pins.internal_gpio_input_pin_schema(value)
|
||||
|
||||
|
||||
adc_ns = cg.esphome_ns.namespace("adc")
|
||||
@@ -62,7 +88,8 @@ async def to_code(config):
|
||||
if config[CONF_PIN] == "VCC":
|
||||
cg.add_define("USE_ADC_SENSOR_VCC")
|
||||
else:
|
||||
cg.add(var.set_pin(config[CONF_PIN]))
|
||||
pin = await cg.gpio_pin_expression(config[CONF_PIN])
|
||||
cg.add(var.set_pin(pin))
|
||||
|
||||
if CONF_ATTENUATION in config:
|
||||
cg.add(var.set_attenuation(config[CONF_ATTENUATION]))
|
||||
|
||||
@@ -8,9 +8,7 @@ static const char *const TAG = "ade7953";
|
||||
|
||||
void ADE7953::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "ADE7953:");
|
||||
if (this->has_irq_) {
|
||||
ESP_LOGCONFIG(TAG, " IRQ Pin: GPIO%u", this->irq_pin_number_);
|
||||
}
|
||||
LOG_PIN(" IRQ Pin: ", irq_pin_);
|
||||
LOG_I2C_DEVICE(this);
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
LOG_SENSOR(" ", "Voltage Sensor", this->voltage_sensor_);
|
||||
@@ -20,27 +18,28 @@ void ADE7953::dump_config() {
|
||||
LOG_SENSOR(" ", "Active Power B Sensor", this->active_power_b_sensor_);
|
||||
}
|
||||
|
||||
#define ADE_PUBLISH_(name, factor) \
|
||||
if ((name) && this->name##_sensor_) { \
|
||||
float value = *(name) / (factor); \
|
||||
#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, factor) ADE_PUBLISH_(name, factor)
|
||||
#define ADE_PUBLISH(name, val, factor) ADE_PUBLISH_(name, val, factor)
|
||||
|
||||
void ADE7953::update() {
|
||||
if (!this->is_setup_)
|
||||
return;
|
||||
|
||||
auto active_power_a = this->ade_read_<int32_t>(0x0312);
|
||||
ADE_PUBLISH(active_power_a, 154.0f);
|
||||
auto active_power_b = this->ade_read_<int32_t>(0x0313);
|
||||
ADE_PUBLISH(active_power_b, 154.0f);
|
||||
auto current_a = this->ade_read_<uint32_t>(0x031A);
|
||||
ADE_PUBLISH(current_a, 100000.0f);
|
||||
auto current_b = this->ade_read_<uint32_t>(0x031B);
|
||||
ADE_PUBLISH(current_b, 100000.0f);
|
||||
auto voltage = this->ade_read_<uint32_t>(0x031C);
|
||||
ADE_PUBLISH(voltage, 26000.0f);
|
||||
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);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
|
||||
@@ -9,10 +10,7 @@ namespace ade7953 {
|
||||
|
||||
class ADE7953 : public i2c::I2CDevice, public PollingComponent {
|
||||
public:
|
||||
void set_irq_pin(uint8_t irq_pin) {
|
||||
has_irq_ = true;
|
||||
irq_pin_number_ = irq_pin;
|
||||
}
|
||||
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; }
|
||||
@@ -24,15 +22,13 @@ class ADE7953 : public i2c::I2CDevice, public PollingComponent {
|
||||
}
|
||||
|
||||
void setup() override {
|
||||
if (this->has_irq_) {
|
||||
auto pin = GPIOPin(this->irq_pin_number_, INPUT);
|
||||
this->irq_pin_ = &pin;
|
||||
if (this->irq_pin_ != nullptr) {
|
||||
this->irq_pin_->setup();
|
||||
}
|
||||
this->set_timeout(100, [this]() {
|
||||
this->ade_write_<uint8_t>(0x0010, 0x04);
|
||||
this->ade_write_<uint8_t>(0x00FE, 0xAD);
|
||||
this->ade_write_<uint16_t>(0x0120, 0x0030);
|
||||
this->ade_write_8_(0x0010, 0x04);
|
||||
this->ade_write_8_(0x00FE, 0xAD);
|
||||
this->ade_write_16_(0x0120, 0x0030);
|
||||
this->is_setup_ = true;
|
||||
});
|
||||
}
|
||||
@@ -42,31 +38,51 @@ class ADE7953 : public i2c::I2CDevice, public PollingComponent {
|
||||
void update() override;
|
||||
|
||||
protected:
|
||||
template<typename T> bool ade_write_(uint16_t reg, T value) {
|
||||
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);
|
||||
for (int i = sizeof(T) - 1; i >= 0; i--)
|
||||
data.push_back(value >> (i * 8));
|
||||
return this->write_bytes_raw(data);
|
||||
data.push_back(value);
|
||||
return write(data.data(), data.size());
|
||||
}
|
||||
template<typename T> optional<T> ade_read_(uint16_t reg) {
|
||||
uint8_t hi = reg >> 8;
|
||||
uint8_t lo = reg >> 0;
|
||||
if (!this->write_bytes_raw({hi, lo}))
|
||||
return {};
|
||||
auto ret = this->read_bytes_raw<sizeof(T)>();
|
||||
if (!ret.has_value())
|
||||
return {};
|
||||
T result = 0;
|
||||
for (int i = 0, j = sizeof(T) - 1; i < sizeof(T); i++, j--)
|
||||
result |= T((*ret)[i]) << (j * 8);
|
||||
return result;
|
||||
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]) << 24;
|
||||
*value |= ((uint32_t) recv[2]) << 24;
|
||||
*value |= ((uint32_t) recv[3]) << 24;
|
||||
return i2c::ERROR_OK;
|
||||
}
|
||||
|
||||
bool has_irq_ = false;
|
||||
uint8_t irq_pin_number_;
|
||||
GPIOPin *irq_pin_{nullptr};
|
||||
InternalGPIOPin *irq_pin_ = nullptr;
|
||||
bool is_setup_{false};
|
||||
sensor::Sensor *voltage_sensor_{nullptr};
|
||||
sensor::Sensor *current_a_sensor_{nullptr};
|
||||
|
||||
@@ -29,7 +29,7 @@ CONFIG_SCHEMA = (
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(ADE7953),
|
||||
cv.Optional(CONF_IRQ_PIN): pins.input_pin,
|
||||
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,
|
||||
@@ -73,7 +73,8 @@ async def to_code(config):
|
||||
await i2c.register_i2c_device(var, config)
|
||||
|
||||
if CONF_IRQ_PIN in config:
|
||||
cg.add(var.set_irq_pin(config[CONF_IRQ_PIN]))
|
||||
irq_pin = await cg.gpio_pin_expression(config[CONF_IRQ_PIN])
|
||||
cg.add(var.set_irq_pin(irq_pin))
|
||||
|
||||
for key in [
|
||||
CONF_VOLTAGE,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "ads1115.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/hal.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace ads1115 {
|
||||
@@ -159,7 +160,7 @@ float ADS1115Component::request_measurement(ADS1115Sensor *sensor) {
|
||||
float ADS1115Sensor::sample() { return this->parent_->request_measurement(this); }
|
||||
void ADS1115Sensor::update() {
|
||||
float v = this->parent_->request_measurement(this);
|
||||
if (!isnan(v)) {
|
||||
if (!std::isnan(v)) {
|
||||
ESP_LOGD(TAG, "'%s': Got Voltage=%fV", this->get_name().c_str(), v);
|
||||
this->publish_state(v);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include "aht10.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/hal.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace aht10 {
|
||||
@@ -33,8 +34,19 @@ void AHT10Component::setup() {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
uint8_t data;
|
||||
if (!this->read_byte(0, &data, AHT10_DEFAULT_DELAY)) {
|
||||
uint8_t data = 0;
|
||||
if (this->write(&data, 1) != i2c::ERROR_OK) {
|
||||
ESP_LOGD(TAG, "Communication with AHT10 failed!");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
delay(AHT10_DEFAULT_DELAY);
|
||||
if (this->read(&data, 1) != i2c::ERROR_OK) {
|
||||
ESP_LOGD(TAG, "Communication with AHT10 failed!");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
if (this->read(&data, 1) != i2c::ERROR_OK) {
|
||||
ESP_LOGD(TAG, "Communication with AHT10 failed!");
|
||||
this->mark_failed();
|
||||
return;
|
||||
@@ -55,15 +67,26 @@ void AHT10Component::update() {
|
||||
return;
|
||||
}
|
||||
uint8_t data[6];
|
||||
uint8_t delay = AHT10_DEFAULT_DELAY;
|
||||
uint8_t delay_ms = AHT10_DEFAULT_DELAY;
|
||||
if (this->humidity_sensor_ != nullptr)
|
||||
delay = AHT10_HUMIDITY_DELAY;
|
||||
delay_ms = AHT10_HUMIDITY_DELAY;
|
||||
bool success = false;
|
||||
for (int i = 0; i < AHT10_ATTEMPTS; ++i) {
|
||||
ESP_LOGVV(TAG, "Attempt %u at %6ld", i, millis());
|
||||
ESP_LOGVV(TAG, "Attempt %d at %6u", i, millis());
|
||||
delay_microseconds_accurate(4);
|
||||
if (!this->read_bytes(0, data, 6, delay)) {
|
||||
|
||||
uint8_t reg = 0;
|
||||
if (this->write(®, 1) != i2c::ERROR_OK) {
|
||||
ESP_LOGD(TAG, "Communication with AHT10 failed, waiting...");
|
||||
} else if ((data[0] & 0x80) == 0x80) { // Bit[7] = 0b1, device is busy
|
||||
continue;
|
||||
}
|
||||
delay(delay_ms);
|
||||
if (this->read(data, 6) != i2c::ERROR_OK) {
|
||||
ESP_LOGD(TAG, "Communication with AHT10 failed, waiting...");
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((data[0] & 0x80) == 0x80) { // Bit[7] = 0b1, device is busy
|
||||
ESP_LOGD(TAG, "AHT10 is busy, waiting...");
|
||||
} else if (data[1] == 0x0 && data[2] == 0x0 && (data[3] >> 4) == 0x0) {
|
||||
// Unrealistic humidity (0x0)
|
||||
@@ -80,11 +103,12 @@ void AHT10Component::update() {
|
||||
}
|
||||
} else {
|
||||
// data is valid, we can break the loop
|
||||
ESP_LOGVV(TAG, "Answer at %6ld", millis());
|
||||
ESP_LOGVV(TAG, "Answer at %6u", millis());
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ((data[0] & 0x80) == 0x80) {
|
||||
if (!success || (data[0] & 0x80) == 0x80) {
|
||||
ESP_LOGE(TAG, "Measurements reading timed-out!");
|
||||
this->status_set_warning();
|
||||
return;
|
||||
@@ -105,7 +129,7 @@ void AHT10Component::update() {
|
||||
this->temperature_sensor_->publish_state(temperature);
|
||||
}
|
||||
if (this->humidity_sensor_ != nullptr) {
|
||||
if (isnan(humidity))
|
||||
if (std::isnan(humidity))
|
||||
ESP_LOGW(TAG, "Invalid humidity! Sensor reported 0%% Hum");
|
||||
this->humidity_sensor_->publish_state(humidity);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
#include "airthings_listener.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#ifdef USE_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace airthings_ble {
|
||||
|
||||
static const char *TAG = "airthings_ble";
|
||||
static const char *const TAG = "airthings_ble";
|
||||
|
||||
bool AirthingsListener::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
|
||||
for (auto &it : device.get_manufacturer_datas()) {
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#ifdef USE_ESP32
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||
#include <BLEDevice.h>
|
||||
|
||||
namespace esphome {
|
||||
namespace airthings_ble {
|
||||
|
||||
1
esphome/components/airthings_wave_mini/__init__.py
Normal file
1
esphome/components/airthings_wave_mini/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
CODEOWNERS = ["@ncareau"]
|
||||
113
esphome/components/airthings_wave_mini/airthings_wave_mini.cpp
Normal file
113
esphome/components/airthings_wave_mini/airthings_wave_mini.cpp
Normal file
@@ -0,0 +1,113 @@
|
||||
#include "airthings_wave_mini.h"
|
||||
|
||||
#ifdef USE_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace airthings_wave_mini {
|
||||
|
||||
static const char *const TAG = "airthings_wave_mini";
|
||||
|
||||
void AirthingsWaveMini::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||
esp_ble_gattc_cb_param_t *param) {
|
||||
switch (event) {
|
||||
case ESP_GATTC_OPEN_EVT: {
|
||||
if (param->open.status == ESP_GATT_OK) {
|
||||
ESP_LOGI(TAG, "Connected successfully!");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ESP_GATTC_DISCONNECT_EVT: {
|
||||
ESP_LOGW(TAG, "Disconnected!");
|
||||
break;
|
||||
}
|
||||
|
||||
case ESP_GATTC_SEARCH_CMPL_EVT: {
|
||||
this->handle_ = 0;
|
||||
auto chr = this->parent()->get_characteristic(service_uuid_, sensors_data_characteristic_uuid_);
|
||||
if (chr == nullptr) {
|
||||
ESP_LOGW(TAG, "No sensor characteristic found at service %s char %s", service_uuid_.to_string().c_str(),
|
||||
sensors_data_characteristic_uuid_.to_string().c_str());
|
||||
break;
|
||||
}
|
||||
this->handle_ = chr->handle;
|
||||
this->node_state = esp32_ble_tracker::ClientState::ESTABLISHED;
|
||||
|
||||
request_read_values_();
|
||||
break;
|
||||
}
|
||||
|
||||
case ESP_GATTC_READ_CHAR_EVT: {
|
||||
if (param->read.conn_id != this->parent()->conn_id)
|
||||
break;
|
||||
if (param->read.status != ESP_GATT_OK) {
|
||||
ESP_LOGW(TAG, "Error reading char at handle %d, status=%d", param->read.handle, param->read.status);
|
||||
break;
|
||||
}
|
||||
if (param->read.handle == this->handle_) {
|
||||
read_sensors_(param->read.value, param->read.value_len);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void AirthingsWaveMini::read_sensors_(uint8_t *raw_value, uint16_t value_len) {
|
||||
auto value = (WaveMiniReadings *) raw_value;
|
||||
|
||||
if (sizeof(WaveMiniReadings) <= value_len) {
|
||||
this->humidity_sensor_->publish_state(value->humidity / 100.0f);
|
||||
this->pressure_sensor_->publish_state(value->pressure / 50.0f);
|
||||
this->temperature_sensor_->publish_state(value->temperature / 100.0f - 273.15f);
|
||||
if (is_valid_voc_value_(value->voc)) {
|
||||
this->tvoc_sensor_->publish_state(value->voc);
|
||||
}
|
||||
|
||||
// This instance must not stay connected
|
||||
// so other clients can connect to it (e.g. the
|
||||
// mobile app).
|
||||
parent()->set_enabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
bool AirthingsWaveMini::is_valid_voc_value_(uint16_t voc) { return 0 <= voc && voc <= 16383; }
|
||||
|
||||
void AirthingsWaveMini::update() {
|
||||
if (this->node_state != esp32_ble_tracker::ClientState::ESTABLISHED) {
|
||||
if (!parent()->enabled) {
|
||||
ESP_LOGW(TAG, "Reconnecting to device");
|
||||
parent()->set_enabled(true);
|
||||
parent()->connect();
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Connection in progress");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AirthingsWaveMini::request_read_values_() {
|
||||
auto status =
|
||||
esp_ble_gattc_read_char(this->parent()->gattc_if, this->parent()->conn_id, this->handle_, ESP_GATT_AUTH_REQ_NONE);
|
||||
if (status) {
|
||||
ESP_LOGW(TAG, "Error sending read request for sensor, status=%d", status);
|
||||
}
|
||||
}
|
||||
|
||||
void AirthingsWaveMini::dump_config() {
|
||||
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
|
||||
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
|
||||
LOG_SENSOR(" ", "Pressure", this->pressure_sensor_);
|
||||
LOG_SENSOR(" ", "TVOC", this->tvoc_sensor_);
|
||||
}
|
||||
|
||||
AirthingsWaveMini::AirthingsWaveMini()
|
||||
: PollingComponent(10000),
|
||||
service_uuid_(esp32_ble_tracker::ESPBTUUID::from_raw(SERVICE_UUID)),
|
||||
sensors_data_characteristic_uuid_(esp32_ble_tracker::ESPBTUUID::from_raw(CHARACTERISTIC_UUID)) {}
|
||||
|
||||
} // namespace airthings_wave_mini
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_ESP32
|
||||
65
esphome/components/airthings_wave_mini/airthings_wave_mini.h
Normal file
65
esphome/components/airthings_wave_mini/airthings_wave_mini.h
Normal file
@@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_ESP32
|
||||
|
||||
#include <esp_gattc_api.h>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#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"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace airthings_wave_mini {
|
||||
|
||||
static const char *const SERVICE_UUID = "b42e3882-ade7-11e4-89d3-123b93f75cba";
|
||||
static const char *const CHARACTERISTIC_UUID = "b42e3b98-ade7-11e4-89d3-123b93f75cba";
|
||||
|
||||
class AirthingsWaveMini : public PollingComponent, public ble_client::BLEClientNode {
|
||||
public:
|
||||
AirthingsWaveMini();
|
||||
|
||||
void dump_config() override;
|
||||
void update() override;
|
||||
|
||||
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 set_temperature(sensor::Sensor *temperature) { temperature_sensor_ = temperature; }
|
||||
void set_humidity(sensor::Sensor *humidity) { humidity_sensor_ = humidity; }
|
||||
void set_pressure(sensor::Sensor *pressure) { pressure_sensor_ = pressure; }
|
||||
void set_tvoc(sensor::Sensor *tvoc) { tvoc_sensor_ = tvoc; }
|
||||
|
||||
protected:
|
||||
bool is_valid_voc_value_(uint16_t voc);
|
||||
|
||||
void read_sensors_(uint8_t *value, uint16_t value_len);
|
||||
void request_read_values_();
|
||||
|
||||
sensor::Sensor *temperature_sensor_{nullptr};
|
||||
sensor::Sensor *humidity_sensor_{nullptr};
|
||||
sensor::Sensor *pressure_sensor_{nullptr};
|
||||
sensor::Sensor *tvoc_sensor_{nullptr};
|
||||
|
||||
uint16_t handle_;
|
||||
esp32_ble_tracker::ESPBTUUID service_uuid_;
|
||||
esp32_ble_tracker::ESPBTUUID sensors_data_characteristic_uuid_;
|
||||
|
||||
struct WaveMiniReadings {
|
||||
uint16_t unused01;
|
||||
uint16_t temperature;
|
||||
uint16_t pressure;
|
||||
uint16_t humidity;
|
||||
uint16_t voc;
|
||||
uint16_t unused02;
|
||||
uint32_t unused03;
|
||||
uint32_t unused04;
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace airthings_wave_mini
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_ESP32
|
||||
82
esphome/components/airthings_wave_mini/sensor.py
Normal file
82
esphome/components/airthings_wave_mini/sensor.py
Normal file
@@ -0,0 +1,82 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import sensor, ble_client
|
||||
|
||||
from esphome.const import (
|
||||
DEVICE_CLASS_HUMIDITY,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
DEVICE_CLASS_PRESSURE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_PERCENT,
|
||||
UNIT_CELSIUS,
|
||||
UNIT_HECTOPASCAL,
|
||||
CONF_ID,
|
||||
CONF_HUMIDITY,
|
||||
CONF_TVOC,
|
||||
CONF_PRESSURE,
|
||||
CONF_TEMPERATURE,
|
||||
UNIT_PARTS_PER_BILLION,
|
||||
ICON_RADIATOR,
|
||||
)
|
||||
|
||||
DEPENDENCIES = ["ble_client"]
|
||||
|
||||
airthings_wave_mini_ns = cg.esphome_ns.namespace("airthings_wave_mini")
|
||||
AirthingsWaveMini = airthings_wave_mini_ns.class_(
|
||||
"AirthingsWaveMini", cg.PollingComponent, ble_client.BLEClientNode
|
||||
)
|
||||
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(AirthingsWaveMini),
|
||||
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_PERCENT,
|
||||
device_class=DEVICE_CLASS_HUMIDITY,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
accuracy_decimals=2,
|
||||
),
|
||||
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
accuracy_decimals=2,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_HECTOPASCAL,
|
||||
accuracy_decimals=2,
|
||||
device_class=DEVICE_CLASS_PRESSURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_TVOC): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_PARTS_PER_BILLION,
|
||||
icon=ICON_RADIATOR,
|
||||
accuracy_decimals=0,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
}
|
||||
)
|
||||
.extend(cv.polling_component_schema("5min"))
|
||||
.extend(ble_client.BLE_CLIENT_SCHEMA),
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
|
||||
await ble_client.register_ble_node(var, config)
|
||||
|
||||
if CONF_HUMIDITY in config:
|
||||
sens = await sensor.new_sensor(config[CONF_HUMIDITY])
|
||||
cg.add(var.set_humidity(sens))
|
||||
if CONF_TEMPERATURE in config:
|
||||
sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
|
||||
cg.add(var.set_temperature(sens))
|
||||
if CONF_PRESSURE in config:
|
||||
sens = await sensor.new_sensor(config[CONF_PRESSURE])
|
||||
cg.add(var.set_pressure(sens))
|
||||
if CONF_TVOC in config:
|
||||
sens = await sensor.new_sensor(config[CONF_TVOC])
|
||||
cg.add(var.set_tvoc(sens))
|
||||
@@ -1,10 +1,12 @@
|
||||
#include "airthings_wave_plus.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#ifdef USE_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace airthings_wave_plus {
|
||||
|
||||
static const char *const TAG = "airthings_wave_plus";
|
||||
|
||||
void AirthingsWavePlus::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||
esp_ble_gattc_cb_param_t *param) {
|
||||
switch (event) {
|
||||
@@ -21,15 +23,15 @@ void AirthingsWavePlus::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt
|
||||
}
|
||||
|
||||
case ESP_GATTC_SEARCH_CMPL_EVT: {
|
||||
this->handle = 0;
|
||||
auto chr = this->parent()->get_characteristic(service_uuid, sensors_data_characteristic_uuid);
|
||||
this->handle_ = 0;
|
||||
auto chr = this->parent()->get_characteristic(service_uuid_, sensors_data_characteristic_uuid_);
|
||||
if (chr == nullptr) {
|
||||
ESP_LOGW(TAG, "No sensor characteristic found at service %s char %s", service_uuid.to_string().c_str(),
|
||||
sensors_data_characteristic_uuid.to_string().c_str());
|
||||
ESP_LOGW(TAG, "No sensor characteristic found at service %s char %s", service_uuid_.to_string().c_str(),
|
||||
sensors_data_characteristic_uuid_.to_string().c_str());
|
||||
break;
|
||||
}
|
||||
this->handle = chr->handle;
|
||||
this->node_state = espbt::ClientState::Established;
|
||||
this->handle_ = chr->handle;
|
||||
this->node_state = esp32_ble_tracker::ClientState::ESTABLISHED;
|
||||
|
||||
request_read_values_();
|
||||
break;
|
||||
@@ -42,7 +44,7 @@ void AirthingsWavePlus::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt
|
||||
ESP_LOGW(TAG, "Error reading char at handle %d, status=%d", param->read.handle, param->read.status);
|
||||
break;
|
||||
}
|
||||
if (param->read.handle == this->handle) {
|
||||
if (param->read.handle == this->handle_) {
|
||||
read_sensors_(param->read.value, param->read.value_len);
|
||||
}
|
||||
break;
|
||||
@@ -88,16 +90,14 @@ void AirthingsWavePlus::read_sensors_(uint8_t *raw_value, uint16_t value_len) {
|
||||
}
|
||||
}
|
||||
|
||||
bool AirthingsWavePlus::is_valid_radon_value_(short radon) { return 0 <= radon && radon <= 16383; }
|
||||
bool AirthingsWavePlus::is_valid_radon_value_(uint16_t radon) { return 0 <= radon && radon <= 16383; }
|
||||
|
||||
bool AirthingsWavePlus::is_valid_voc_value_(short voc) { return 0 <= voc && voc <= 16383; }
|
||||
bool AirthingsWavePlus::is_valid_voc_value_(uint16_t voc) { return 0 <= voc && voc <= 16383; }
|
||||
|
||||
bool AirthingsWavePlus::is_valid_co2_value_(short co2) { return 0 <= co2 && co2 <= 16383; }
|
||||
|
||||
void AirthingsWavePlus::loop() {}
|
||||
bool AirthingsWavePlus::is_valid_co2_value_(uint16_t co2) { return 0 <= co2 && co2 <= 16383; }
|
||||
|
||||
void AirthingsWavePlus::update() {
|
||||
if (this->node_state != espbt::ClientState::Established) {
|
||||
if (this->node_state != esp32_ble_tracker::ClientState::ESTABLISHED) {
|
||||
if (!parent()->enabled) {
|
||||
ESP_LOGW(TAG, "Reconnecting to device");
|
||||
parent()->set_enabled(true);
|
||||
@@ -110,7 +110,7 @@ void AirthingsWavePlus::update() {
|
||||
|
||||
void AirthingsWavePlus::request_read_values_() {
|
||||
auto status =
|
||||
esp_ble_gattc_read_char(this->parent()->gattc_if, this->parent()->conn_id, this->handle, ESP_GATT_AUTH_REQ_NONE);
|
||||
esp_ble_gattc_read_char(this->parent()->gattc_if, this->parent()->conn_id, this->handle_, ESP_GATT_AUTH_REQ_NONE);
|
||||
if (status) {
|
||||
ESP_LOGW(TAG, "Error sending read request for sensor, status=%d", status);
|
||||
}
|
||||
@@ -126,17 +126,12 @@ void AirthingsWavePlus::dump_config() {
|
||||
LOG_SENSOR(" ", "TVOC", this->tvoc_sensor_);
|
||||
}
|
||||
|
||||
AirthingsWavePlus::AirthingsWavePlus() : PollingComponent(10000) {
|
||||
auto service_bt = *BLEUUID::fromString(std::string("b42e1c08-ade7-11e4-89d3-123b93f75cba")).getNative();
|
||||
auto characteristic_bt = *BLEUUID::fromString(std::string("b42e2a68-ade7-11e4-89d3-123b93f75cba")).getNative();
|
||||
|
||||
service_uuid = espbt::ESPBTUUID::from_uuid(service_bt);
|
||||
sensors_data_characteristic_uuid = espbt::ESPBTUUID::from_uuid(characteristic_bt);
|
||||
}
|
||||
|
||||
void AirthingsWavePlus::setup() {}
|
||||
AirthingsWavePlus::AirthingsWavePlus()
|
||||
: PollingComponent(10000),
|
||||
service_uuid_(esp32_ble_tracker::ESPBTUUID::from_raw(SERVICE_UUID)),
|
||||
sensors_data_characteristic_uuid_(esp32_ble_tracker::ESPBTUUID::from_raw(CHARACTERISTIC_UUID)) {}
|
||||
|
||||
} // namespace airthings_wave_plus
|
||||
} // namespace esphome
|
||||
|
||||
#endif // ARDUINO_ARCH_ESP32
|
||||
#endif // USE_ESP32
|
||||
|
||||
@@ -1,32 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#ifdef USE_ESP32
|
||||
|
||||
#include <esp_gattc_api.h>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#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"
|
||||
#include "esphome/core/log.h"
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#include <esp_gattc_api.h>
|
||||
#include <BLEDevice.h>
|
||||
|
||||
using namespace esphome::ble_client;
|
||||
|
||||
namespace esphome {
|
||||
namespace airthings_wave_plus {
|
||||
|
||||
static const char *TAG = "airthings_wave_plus";
|
||||
static const char *const SERVICE_UUID = "b42e1c08-ade7-11e4-89d3-123b93f75cba";
|
||||
static const char *const CHARACTERISTIC_UUID = "b42e2a68-ade7-11e4-89d3-123b93f75cba";
|
||||
|
||||
class AirthingsWavePlus : public PollingComponent, public BLEClientNode {
|
||||
class AirthingsWavePlus : public PollingComponent, public ble_client::BLEClientNode {
|
||||
public:
|
||||
AirthingsWavePlus();
|
||||
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
void update() override;
|
||||
void loop() override;
|
||||
|
||||
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||
esp_ble_gattc_cb_param_t *param) override;
|
||||
@@ -40,9 +36,9 @@ class AirthingsWavePlus : public PollingComponent, public BLEClientNode {
|
||||
void set_tvoc(sensor::Sensor *tvoc) { tvoc_sensor_ = tvoc; }
|
||||
|
||||
protected:
|
||||
bool is_valid_radon_value_(short radon);
|
||||
bool is_valid_voc_value_(short voc);
|
||||
bool is_valid_co2_value_(short co2);
|
||||
bool is_valid_radon_value_(uint16_t radon);
|
||||
bool is_valid_voc_value_(uint16_t voc);
|
||||
bool is_valid_co2_value_(uint16_t co2);
|
||||
|
||||
void read_sensors_(uint8_t *value, uint16_t value_len);
|
||||
void request_read_values_();
|
||||
@@ -55,9 +51,9 @@ class AirthingsWavePlus : public PollingComponent, public BLEClientNode {
|
||||
sensor::Sensor *co2_sensor_{nullptr};
|
||||
sensor::Sensor *tvoc_sensor_{nullptr};
|
||||
|
||||
uint16_t handle;
|
||||
espbt::ESPBTUUID service_uuid;
|
||||
espbt::ESPBTUUID sensors_data_characteristic_uuid;
|
||||
uint16_t handle_;
|
||||
esp32_ble_tracker::ESPBTUUID service_uuid_;
|
||||
esp32_ble_tracker::ESPBTUUID sensors_data_characteristic_uuid_;
|
||||
|
||||
struct WavePlusReadings {
|
||||
uint8_t version;
|
||||
@@ -76,4 +72,4 @@ class AirthingsWavePlus : public PollingComponent, public BLEClientNode {
|
||||
} // namespace airthings_wave_plus
|
||||
} // namespace esphome
|
||||
|
||||
#endif // ARDUINO_ARCH_ESP32
|
||||
#endif // USE_ESP32
|
||||
|
||||
@@ -34,7 +34,7 @@ AirthingsWavePlus = airthings_wave_plus_ns.class_(
|
||||
)
|
||||
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(AirthingsWavePlus),
|
||||
@@ -82,8 +82,8 @@ CONFIG_SCHEMA = (
|
||||
),
|
||||
}
|
||||
)
|
||||
.extend(cv.polling_component_schema("5mins"))
|
||||
.extend(ble_client.BLE_CLIENT_SCHEMA)
|
||||
.extend(cv.polling_component_schema("5min"))
|
||||
.extend(ble_client.BLE_CLIENT_SCHEMA),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include "am2320.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/hal.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace am2320 {
|
||||
@@ -77,7 +78,7 @@ bool AM2320Component::read_bytes_(uint8_t a_register, uint8_t *data, uint8_t len
|
||||
|
||||
if (conversion > 0)
|
||||
delay(conversion);
|
||||
return this->parent_->raw_receive(this->address_, data, len);
|
||||
return this->read(data, len) == i2c::ERROR_OK;
|
||||
}
|
||||
|
||||
bool AM2320Component::read_data_(uint8_t *data) {
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
#include "am43.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/hal.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#ifdef USE_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace am43 {
|
||||
|
||||
static const char *TAG = "am43";
|
||||
static const char *const TAG = "am43";
|
||||
|
||||
void Am43::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "AM43");
|
||||
@@ -15,8 +16,8 @@ void Am43::dump_config() {
|
||||
}
|
||||
|
||||
void Am43::setup() {
|
||||
this->encoder_ = new Am43Encoder();
|
||||
this->decoder_ = new Am43Decoder();
|
||||
this->encoder_ = make_unique<Am43Encoder>();
|
||||
this->decoder_ = make_unique<Am43Decoder>();
|
||||
this->logged_in_ = false;
|
||||
this->last_battery_update_ = 0;
|
||||
this->current_sensor_ = 0;
|
||||
@@ -30,7 +31,7 @@ void Am43::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_i
|
||||
}
|
||||
case ESP_GATTC_DISCONNECT_EVT: {
|
||||
this->logged_in_ = false;
|
||||
this->node_state = espbt::ClientState::Idle;
|
||||
this->node_state = espbt::ClientState::IDLE;
|
||||
if (this->battery_ != nullptr)
|
||||
this->battery_->publish_state(NAN);
|
||||
if (this->illuminance_ != nullptr)
|
||||
@@ -53,7 +54,7 @@ void Am43::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_i
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
|
||||
this->node_state = espbt::ClientState::Established;
|
||||
this->node_state = espbt::ClientState::ESTABLISHED;
|
||||
this->update();
|
||||
break;
|
||||
}
|
||||
@@ -92,7 +93,7 @@ void Am43::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_i
|
||||
}
|
||||
|
||||
void Am43::update() {
|
||||
if (this->node_state != espbt::ClientState::Established) {
|
||||
if (this->node_state != espbt::ClientState::ESTABLISHED) {
|
||||
ESP_LOGW(TAG, "[%s] Cannot poll, not connected", this->parent_->address_str().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/am43/am43_base.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#ifdef USE_ESP32
|
||||
|
||||
#include <esp_gattc_api.h>
|
||||
|
||||
@@ -28,8 +28,8 @@ class Am43 : public esphome::ble_client::BLEClientNode, public PollingComponent
|
||||
|
||||
protected:
|
||||
uint16_t char_handle_;
|
||||
Am43Encoder *encoder_;
|
||||
Am43Decoder *decoder_;
|
||||
std::unique_ptr<Am43Encoder> encoder_;
|
||||
std::unique_ptr<Am43Decoder> decoder_;
|
||||
bool logged_in_;
|
||||
sensor::Sensor *battery_{nullptr};
|
||||
sensor::Sensor *illuminance_{nullptr};
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#include "am43_base.h"
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
|
||||
namespace esphome {
|
||||
namespace am43 {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
#include "am43_cover.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#ifdef USE_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace am43 {
|
||||
|
||||
static const char *TAG = "am43_cover";
|
||||
static const char *const TAG = "am43_cover";
|
||||
|
||||
using namespace esphome::cover;
|
||||
|
||||
@@ -18,13 +18,13 @@ void Am43Component::dump_config() {
|
||||
|
||||
void Am43Component::setup() {
|
||||
this->position = COVER_OPEN;
|
||||
this->encoder_ = new Am43Encoder();
|
||||
this->decoder_ = new Am43Decoder();
|
||||
this->encoder_ = make_unique<Am43Encoder>();
|
||||
this->decoder_ = make_unique<Am43Decoder>();
|
||||
this->logged_in_ = false;
|
||||
}
|
||||
|
||||
void Am43Component::loop() {
|
||||
if (this->node_state == espbt::ClientState::Established && !this->logged_in_) {
|
||||
if (this->node_state == espbt::ClientState::ESTABLISHED && !this->logged_in_) {
|
||||
auto packet = this->encoder_->get_send_pin_request(this->pin_);
|
||||
auto status =
|
||||
esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, packet->length,
|
||||
@@ -46,7 +46,7 @@ CoverTraits Am43Component::get_traits() {
|
||||
}
|
||||
|
||||
void Am43Component::control(const CoverCall &call) {
|
||||
if (this->node_state != espbt::ClientState::Established) {
|
||||
if (this->node_state != espbt::ClientState::ESTABLISHED) {
|
||||
ESP_LOGW(TAG, "[%s] Cannot send cover control, not connected", this->get_name().c_str());
|
||||
return;
|
||||
}
|
||||
@@ -98,7 +98,7 @@ void Am43Component::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
|
||||
this->node_state = espbt::ClientState::Established;
|
||||
this->node_state = espbt::ClientState::ESTABLISHED;
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_NOTIFY_EVT: {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include "esphome/components/cover/cover.h"
|
||||
#include "esphome/components/am43/am43_base.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#ifdef USE_ESP32
|
||||
|
||||
#include <esp_gattc_api.h>
|
||||
|
||||
@@ -32,8 +32,8 @@ class Am43Component : public cover::Cover, public esphome::ble_client::BLEClient
|
||||
uint16_t char_handle_;
|
||||
uint16_t pin_;
|
||||
bool invert_position_;
|
||||
Am43Encoder *encoder_;
|
||||
Am43Decoder *decoder_;
|
||||
std::unique_ptr<Am43Encoder> encoder_;
|
||||
std::unique_ptr<Am43Decoder> decoder_;
|
||||
bool logged_in_;
|
||||
|
||||
float position_;
|
||||
|
||||
@@ -5,7 +5,7 @@ from esphome.components import display, font
|
||||
import esphome.components.image as espImage
|
||||
import esphome.config_validation as cv
|
||||
import esphome.codegen as cg
|
||||
from esphome.const import CONF_FILE, CONF_ID, CONF_TYPE, CONF_RESIZE
|
||||
from esphome.const import CONF_FILE, CONF_ID, CONF_RAW_DATA_ID, CONF_RESIZE, CONF_TYPE
|
||||
from esphome.core import CORE, HexInt
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -15,8 +15,6 @@ MULTI_CONF = True
|
||||
|
||||
Animation_ = display.display_ns.class_("Animation")
|
||||
|
||||
CONF_RAW_DATA_ID = "raw_data_id"
|
||||
|
||||
ANIMATION_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.declare_id(Animation_),
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
#include "anova.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#ifdef USE_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace anova {
|
||||
|
||||
static const char *TAG = "anova";
|
||||
static const char *const TAG = "anova";
|
||||
|
||||
using namespace esphome::climate;
|
||||
|
||||
void Anova::dump_config() { LOG_CLIMATE("", "Anova BLE Cooker", this); }
|
||||
|
||||
void Anova::setup() {
|
||||
this->codec_ = new AnovaCodec();
|
||||
this->codec_ = make_unique<AnovaCodec>();
|
||||
this->current_request_ = 0;
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ void Anova::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
|
||||
this->node_state = espbt::ClientState::Established;
|
||||
this->node_state = espbt::ClientState::ESTABLISHED;
|
||||
this->current_request_ = 0;
|
||||
this->update();
|
||||
break;
|
||||
@@ -129,13 +129,13 @@ void Anova::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_
|
||||
void Anova::set_unit_of_measurement(const char *unit) { this->fahrenheit_ = !strncmp(unit, "f", 1); }
|
||||
|
||||
void Anova::update() {
|
||||
if (this->node_state != espbt::ClientState::Established)
|
||||
if (this->node_state != espbt::ClientState::ESTABLISHED)
|
||||
return;
|
||||
|
||||
if (this->current_request_ < 2) {
|
||||
auto pkt = this->codec_->get_read_device_status_request();
|
||||
if (this->current_request_ == 0)
|
||||
auto pkt = this->codec_->get_set_unit_request(this->fahrenheit_ ? 'f' : 'c');
|
||||
this->codec_->get_set_unit_request(this->fahrenheit_ ? 'f' : 'c');
|
||||
auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_,
|
||||
pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
|
||||
if (status)
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include "esphome/components/climate/climate.h"
|
||||
#include "anova_base.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#ifdef USE_ESP32
|
||||
|
||||
#include <esp_gattc_api.h>
|
||||
|
||||
@@ -27,7 +27,7 @@ class Anova : public climate::Climate, public esphome::ble_client::BLEClientNode
|
||||
esp_ble_gattc_cb_param_t *param) override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||
climate::ClimateTraits traits() {
|
||||
climate::ClimateTraits traits() override {
|
||||
auto traits = climate::ClimateTraits();
|
||||
traits.set_supports_current_temperature(true);
|
||||
traits.set_supports_heat_mode(true);
|
||||
@@ -39,7 +39,7 @@ class Anova : public climate::Climate, public esphome::ble_client::BLEClientNode
|
||||
void set_unit_of_measurement(const char *);
|
||||
|
||||
protected:
|
||||
AnovaCodec *codec_;
|
||||
std::unique_ptr<AnovaCodec> codec_;
|
||||
void control(const climate::ClimateCall &call) override;
|
||||
uint16_t char_handle_;
|
||||
uint8_t current_request_;
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#include "anova_base.h"
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
namespace esphome {
|
||||
namespace anova {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "apds9960.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/hal.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace apds9960 {
|
||||
|
||||
@@ -215,6 +215,7 @@ message ListEntitiesBinarySensorResponse {
|
||||
string device_class = 5;
|
||||
bool is_status_binary_sensor = 6;
|
||||
bool disabled_by_default = 7;
|
||||
string icon = 8;
|
||||
}
|
||||
message BinarySensorStateResponse {
|
||||
option (id) = 21;
|
||||
@@ -245,6 +246,7 @@ message ListEntitiesCoverResponse {
|
||||
bool supports_tilt = 7;
|
||||
string device_class = 8;
|
||||
bool disabled_by_default = 9;
|
||||
string icon = 10;
|
||||
}
|
||||
|
||||
enum LegacyCoverState {
|
||||
@@ -313,6 +315,7 @@ message ListEntitiesFanResponse {
|
||||
bool supports_direction = 7;
|
||||
int32 supported_speed_count = 8;
|
||||
bool disabled_by_default = 9;
|
||||
string icon = 10;
|
||||
}
|
||||
enum FanSpeed {
|
||||
FAN_SPEED_LOW = 0;
|
||||
@@ -388,6 +391,7 @@ message ListEntitiesLightResponse {
|
||||
float max_mireds = 10;
|
||||
repeated string effects = 11;
|
||||
bool disabled_by_default = 13;
|
||||
string icon = 14;
|
||||
}
|
||||
message LightStateResponse {
|
||||
option (id) = 24;
|
||||
@@ -790,6 +794,7 @@ message ListEntitiesClimateResponse {
|
||||
repeated ClimatePreset supported_presets = 16;
|
||||
repeated string supported_custom_presets = 17;
|
||||
bool disabled_by_default = 18;
|
||||
string icon = 19;
|
||||
}
|
||||
message ClimateStateResponse {
|
||||
option (id) = 47;
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
#include "api_connection.h"
|
||||
#include "esphome/core/entity_base.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/util.h"
|
||||
#include "esphome/components/network/util.h"
|
||||
#include "esphome/core/version.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include <cerrno>
|
||||
|
||||
#ifdef USE_DEEP_SLEEP
|
||||
@@ -48,7 +50,7 @@ void APIConnection::loop() {
|
||||
if (this->remove_)
|
||||
return;
|
||||
|
||||
if (!network_is_connected()) {
|
||||
if (!network::is_connected()) {
|
||||
// when network is disconnected force disconnect immediately
|
||||
// don't wait for timeout
|
||||
this->on_fatal_error();
|
||||
@@ -142,8 +144,21 @@ void APIConnection::loop() {
|
||||
}
|
||||
}
|
||||
|
||||
std::string get_default_unique_id(const std::string &component_type, Nameable *nameable) {
|
||||
return App.get_name() + component_type + nameable->get_object_id();
|
||||
std::string get_default_unique_id(const std::string &component_type, EntityBase *entity) {
|
||||
return App.get_name() + component_type + entity->get_object_id();
|
||||
}
|
||||
|
||||
DisconnectResponse APIConnection::disconnect(const DisconnectRequest &msg) {
|
||||
// remote initiated disconnect_client
|
||||
// don't close yet, we still need to send the disconnect response
|
||||
// close will happen on next loop
|
||||
ESP_LOGD(TAG, "%s requested disconnected", client_info_.c_str());
|
||||
this->next_close_ = true;
|
||||
DisconnectResponse resp;
|
||||
return resp;
|
||||
}
|
||||
void APIConnection::on_disconnect_response(const DisconnectResponse &value) {
|
||||
// pass
|
||||
}
|
||||
|
||||
DisconnectResponse APIConnection::disconnect(const DisconnectRequest &msg) {
|
||||
@@ -179,6 +194,7 @@ bool APIConnection::send_binary_sensor_info(binary_sensor::BinarySensor *binary_
|
||||
msg.device_class = binary_sensor->get_device_class();
|
||||
msg.is_status_binary_sensor = binary_sensor->is_status_binary_sensor();
|
||||
msg.disabled_by_default = binary_sensor->is_disabled_by_default();
|
||||
msg.icon = binary_sensor->get_icon();
|
||||
return this->send_list_entities_binary_sensor_response(msg);
|
||||
}
|
||||
#endif
|
||||
@@ -211,6 +227,7 @@ bool APIConnection::send_cover_info(cover::Cover *cover) {
|
||||
msg.supports_tilt = traits.get_supports_tilt();
|
||||
msg.device_class = cover->get_device_class();
|
||||
msg.disabled_by_default = cover->is_disabled_by_default();
|
||||
msg.icon = cover->get_icon();
|
||||
return this->send_list_entities_cover_response(msg);
|
||||
}
|
||||
void APIConnection::cover_command(const CoverCommandRequest &msg) {
|
||||
@@ -276,6 +293,7 @@ bool APIConnection::send_fan_info(fan::FanState *fan) {
|
||||
msg.supports_direction = traits.supports_direction();
|
||||
msg.supported_speed_count = traits.supported_speed_count();
|
||||
msg.disabled_by_default = fan->is_disabled_by_default();
|
||||
msg.icon = fan->get_icon();
|
||||
return this->send_list_entities_fan_response(msg);
|
||||
}
|
||||
void APIConnection::fan_command(const FanCommandRequest &msg) {
|
||||
@@ -338,6 +356,7 @@ bool APIConnection::send_light_info(light::LightState *light) {
|
||||
msg.unique_id = get_default_unique_id("light", light);
|
||||
|
||||
msg.disabled_by_default = light->is_disabled_by_default();
|
||||
msg.icon = light->get_icon();
|
||||
|
||||
for (auto mode : traits.get_supported_color_modes())
|
||||
msg.supported_color_modes.push_back(static_cast<enums::ColorMode>(mode));
|
||||
@@ -528,6 +547,7 @@ bool APIConnection::send_climate_info(climate::Climate *climate) {
|
||||
msg.unique_id = get_default_unique_id("climate", climate);
|
||||
|
||||
msg.disabled_by_default = climate->is_disabled_by_default();
|
||||
msg.icon = climate->get_icon();
|
||||
|
||||
msg.supports_current_temperature = traits.get_supports_current_temperature();
|
||||
msg.supports_two_point_target_temperature = traits.get_supports_two_point_target_temperature();
|
||||
@@ -600,7 +620,7 @@ bool APIConnection::send_number_info(number::Number *number) {
|
||||
msg.object_id = number->get_object_id();
|
||||
msg.name = number->get_name();
|
||||
msg.unique_id = get_default_unique_id("number", number);
|
||||
msg.icon = number->traits.get_icon();
|
||||
msg.icon = number->get_icon();
|
||||
msg.disabled_by_default = number->is_disabled_by_default();
|
||||
|
||||
msg.min_value = number->traits.get_min_value();
|
||||
@@ -637,7 +657,7 @@ bool APIConnection::send_select_info(select::Select *select) {
|
||||
msg.object_id = select->get_object_id();
|
||||
msg.name = select->get_name();
|
||||
msg.unique_id = get_default_unique_id("select", select);
|
||||
msg.icon = select->traits.get_icon();
|
||||
msg.icon = select->get_icon();
|
||||
msg.disabled_by_default = select->is_disabled_by_default();
|
||||
|
||||
for (const auto &option : select->traits.get_options())
|
||||
@@ -662,7 +682,7 @@ void APIConnection::send_camera_state(std::shared_ptr<esp32_camera::CameraImage>
|
||||
return;
|
||||
if (this->image_reader_.available())
|
||||
return;
|
||||
this->image_reader_.set_image(image);
|
||||
this->image_reader_.set_image(std::move(image));
|
||||
}
|
||||
bool APIConnection::send_camera_info(esp32_camera::ESP32Camera *camera) {
|
||||
ListEntitiesCameraResponse msg;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "proto.h"
|
||||
#include <cstring>
|
||||
|
||||
namespace esphome {
|
||||
namespace api {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "esphome/core/defines.h"
|
||||
|
||||
@@ -75,8 +76,8 @@ class APIFrameHelper {
|
||||
class APINoiseFrameHelper : public APIFrameHelper {
|
||||
public:
|
||||
APINoiseFrameHelper(std::unique_ptr<socket::Socket> socket, std::shared_ptr<APINoiseContext> ctx)
|
||||
: socket_(std::move(socket)), ctx_(ctx) {}
|
||||
~APINoiseFrameHelper();
|
||||
: socket_(std::move(socket)), ctx_(std::move(std::move(ctx))) {}
|
||||
~APINoiseFrameHelper() override;
|
||||
APIError init() override;
|
||||
APIError loop() override;
|
||||
APIError read_packet(ReadPacketBuffer *buffer) override;
|
||||
@@ -136,7 +137,7 @@ class APINoiseFrameHelper : public APIFrameHelper {
|
||||
class APIPlaintextFrameHelper : public APIFrameHelper {
|
||||
public:
|
||||
APIPlaintextFrameHelper(std::unique_ptr<socket::Socket> socket) : socket_(std::move(socket)) {}
|
||||
~APIPlaintextFrameHelper() = default;
|
||||
~APIPlaintextFrameHelper() override = default;
|
||||
APIError init() override;
|
||||
APIError loop() override;
|
||||
APIError read_packet(ReadPacketBuffer *buffer) override;
|
||||
|
||||
@@ -11,7 +11,7 @@ using psk_t = std::array<uint8_t, 32>;
|
||||
|
||||
class APINoiseContext {
|
||||
public:
|
||||
void set_psk(psk_t psk) { psk_ = std::move(psk); }
|
||||
void set_psk(psk_t psk) { psk_ = psk; }
|
||||
const psk_t &get_psk() const { return psk_; }
|
||||
|
||||
protected:
|
||||
|
||||
@@ -531,6 +531,10 @@ bool ListEntitiesBinarySensorResponse::decode_length(uint32_t field_id, ProtoLen
|
||||
this->device_class = value.as_string();
|
||||
return true;
|
||||
}
|
||||
case 8: {
|
||||
this->icon = value.as_string();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -553,6 +557,7 @@ void ListEntitiesBinarySensorResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_string(5, this->device_class);
|
||||
buffer.encode_bool(6, this->is_status_binary_sensor);
|
||||
buffer.encode_bool(7, this->disabled_by_default);
|
||||
buffer.encode_string(8, this->icon);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void ListEntitiesBinarySensorResponse::dump_to(std::string &out) const {
|
||||
@@ -586,6 +591,10 @@ void ListEntitiesBinarySensorResponse::dump_to(std::string &out) const {
|
||||
out.append(" disabled_by_default: ");
|
||||
out.append(YESNO(this->disabled_by_default));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" icon: ");
|
||||
out.append("'").append(this->icon).append("'");
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
@@ -677,6 +686,10 @@ bool ListEntitiesCoverResponse::decode_length(uint32_t field_id, ProtoLengthDeli
|
||||
this->device_class = value.as_string();
|
||||
return true;
|
||||
}
|
||||
case 10: {
|
||||
this->icon = value.as_string();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -701,6 +714,7 @@ void ListEntitiesCoverResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_bool(7, this->supports_tilt);
|
||||
buffer.encode_string(8, this->device_class);
|
||||
buffer.encode_bool(9, this->disabled_by_default);
|
||||
buffer.encode_string(10, this->icon);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void ListEntitiesCoverResponse::dump_to(std::string &out) const {
|
||||
@@ -742,6 +756,10 @@ void ListEntitiesCoverResponse::dump_to(std::string &out) const {
|
||||
out.append(" disabled_by_default: ");
|
||||
out.append(YESNO(this->disabled_by_default));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" icon: ");
|
||||
out.append("'").append(this->icon).append("'");
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
@@ -948,6 +966,10 @@ bool ListEntitiesFanResponse::decode_length(uint32_t field_id, ProtoLengthDelimi
|
||||
this->unique_id = value.as_string();
|
||||
return true;
|
||||
}
|
||||
case 10: {
|
||||
this->icon = value.as_string();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -972,6 +994,7 @@ void ListEntitiesFanResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_bool(7, this->supports_direction);
|
||||
buffer.encode_int32(8, this->supported_speed_count);
|
||||
buffer.encode_bool(9, this->disabled_by_default);
|
||||
buffer.encode_string(10, this->icon);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void ListEntitiesFanResponse::dump_to(std::string &out) const {
|
||||
@@ -1014,6 +1037,10 @@ void ListEntitiesFanResponse::dump_to(std::string &out) const {
|
||||
out.append(" disabled_by_default: ");
|
||||
out.append(YESNO(this->disabled_by_default));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" icon: ");
|
||||
out.append("'").append(this->icon).append("'");
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
@@ -1262,6 +1289,10 @@ bool ListEntitiesLightResponse::decode_length(uint32_t field_id, ProtoLengthDeli
|
||||
this->effects.push_back(value.as_string());
|
||||
return true;
|
||||
}
|
||||
case 14: {
|
||||
this->icon = value.as_string();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -1302,6 +1333,7 @@ void ListEntitiesLightResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_string(11, it, true);
|
||||
}
|
||||
buffer.encode_bool(13, this->disabled_by_default);
|
||||
buffer.encode_string(14, this->icon);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void ListEntitiesLightResponse::dump_to(std::string &out) const {
|
||||
@@ -1365,6 +1397,10 @@ void ListEntitiesLightResponse::dump_to(std::string &out) const {
|
||||
out.append(" disabled_by_default: ");
|
||||
out.append(YESNO(this->disabled_by_default));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" icon: ");
|
||||
out.append("'").append(this->icon).append("'");
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
@@ -3072,6 +3108,10 @@ bool ListEntitiesClimateResponse::decode_length(uint32_t field_id, ProtoLengthDe
|
||||
this->supported_custom_presets.push_back(value.as_string());
|
||||
return true;
|
||||
}
|
||||
case 19: {
|
||||
this->icon = value.as_string();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -3129,6 +3169,7 @@ void ListEntitiesClimateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_string(17, it, true);
|
||||
}
|
||||
buffer.encode_bool(18, this->disabled_by_default);
|
||||
buffer.encode_string(19, this->icon);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void ListEntitiesClimateResponse::dump_to(std::string &out) const {
|
||||
@@ -3221,6 +3262,10 @@ void ListEntitiesClimateResponse::dump_to(std::string &out) const {
|
||||
out.append(" disabled_by_default: ");
|
||||
out.append(YESNO(this->disabled_by_default));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" icon: ");
|
||||
out.append("'").append(this->icon).append("'");
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -269,6 +269,7 @@ class ListEntitiesBinarySensorResponse : public ProtoMessage {
|
||||
std::string device_class{};
|
||||
bool is_status_binary_sensor{false};
|
||||
bool disabled_by_default{false};
|
||||
std::string icon{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
@@ -304,6 +305,7 @@ class ListEntitiesCoverResponse : public ProtoMessage {
|
||||
bool supports_tilt{false};
|
||||
std::string device_class{};
|
||||
bool disabled_by_default{false};
|
||||
std::string icon{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
@@ -360,6 +362,7 @@ class ListEntitiesFanResponse : public ProtoMessage {
|
||||
bool supports_direction{false};
|
||||
int32_t supported_speed_count{0};
|
||||
bool disabled_by_default{false};
|
||||
std::string icon{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
@@ -424,6 +427,7 @@ class ListEntitiesLightResponse : public ProtoMessage {
|
||||
float max_mireds{0.0f};
|
||||
std::vector<std::string> effects{};
|
||||
bool disabled_by_default{false};
|
||||
std::string icon{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
@@ -856,6 +860,7 @@ class ListEntitiesClimateResponse : public ProtoMessage {
|
||||
std::vector<enums::ClimatePreset> supported_presets{};
|
||||
std::vector<std::string> supported_custom_presets{};
|
||||
bool disabled_by_default{false};
|
||||
std::string icon{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/util.h"
|
||||
#include "esphome/core/version.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/components/network/util.h"
|
||||
#include <cerrno>
|
||||
|
||||
#ifdef USE_LOGGER
|
||||
@@ -64,7 +66,7 @@ void APIServer::setup() {
|
||||
#ifdef USE_LOGGER
|
||||
if (logger::global_logger != nullptr) {
|
||||
logger::global_logger->add_on_log_callback([this](int level, const char *tag, const char *message) {
|
||||
for (auto *c : this->clients_) {
|
||||
for (auto &c : this->clients_) {
|
||||
if (!c->remove_)
|
||||
c->send_log_message(level, tag, message);
|
||||
}
|
||||
@@ -76,11 +78,12 @@ void APIServer::setup() {
|
||||
|
||||
#ifdef USE_ESP32_CAMERA
|
||||
if (esp32_camera::global_esp32_camera != nullptr) {
|
||||
esp32_camera::global_esp32_camera->add_image_callback([this](std::shared_ptr<esp32_camera::CameraImage> image) {
|
||||
for (auto *c : this->clients_)
|
||||
if (!c->remove_)
|
||||
c->send_camera_state(image);
|
||||
});
|
||||
esp32_camera::global_esp32_camera->add_image_callback(
|
||||
[this](const std::shared_ptr<esp32_camera::CameraImage> &image) {
|
||||
for (auto &c : this->clients_)
|
||||
if (!c->remove_)
|
||||
c->send_camera_state(image);
|
||||
});
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -95,25 +98,21 @@ void APIServer::loop() {
|
||||
ESP_LOGD(TAG, "Accepted %s", sock->getpeername().c_str());
|
||||
|
||||
auto *conn = new APIConnection(std::move(sock), this);
|
||||
clients_.push_back(conn);
|
||||
clients_.emplace_back(conn);
|
||||
conn->start();
|
||||
}
|
||||
|
||||
// Partition clients into remove and active
|
||||
auto new_end =
|
||||
std::partition(this->clients_.begin(), this->clients_.end(), [](APIConnection *conn) { return !conn->remove_; });
|
||||
auto new_end = std::partition(this->clients_.begin(), this->clients_.end(),
|
||||
[](const std::unique_ptr<APIConnection> &conn) { return !conn->remove_; });
|
||||
// print disconnection messages
|
||||
for (auto it = new_end; it != this->clients_.end(); ++it) {
|
||||
ESP_LOGV(TAG, "Removing connection to %s", (*it)->client_info_.c_str());
|
||||
}
|
||||
// only then delete the pointers, otherwise log routine
|
||||
// would access freed memory
|
||||
for (auto it = new_end; it != this->clients_.end(); ++it)
|
||||
delete *it;
|
||||
// resize vector
|
||||
this->clients_.erase(new_end, this->clients_.end());
|
||||
|
||||
for (auto *client : this->clients_) {
|
||||
for (auto &client : this->clients_) {
|
||||
client->loop();
|
||||
}
|
||||
|
||||
@@ -133,7 +132,7 @@ void APIServer::loop() {
|
||||
}
|
||||
void APIServer::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "API Server:");
|
||||
ESP_LOGCONFIG(TAG, " Address: %s:%u", network_get_address().c_str(), this->port_);
|
||||
ESP_LOGCONFIG(TAG, " Address: %s:%u", network::get_use_address().c_str(), this->port_);
|
||||
#ifdef USE_API_NOISE
|
||||
ESP_LOGCONFIG(TAG, " Using noise encryption: YES");
|
||||
#else
|
||||
@@ -174,7 +173,7 @@ void APIServer::handle_disconnect(APIConnection *conn) {}
|
||||
void APIServer::on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool state) {
|
||||
if (obj->is_internal())
|
||||
return;
|
||||
for (auto *c : this->clients_)
|
||||
for (auto &c : this->clients_)
|
||||
c->send_binary_sensor_state(obj, state);
|
||||
}
|
||||
#endif
|
||||
@@ -183,7 +182,7 @@ void APIServer::on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool s
|
||||
void APIServer::on_cover_update(cover::Cover *obj) {
|
||||
if (obj->is_internal())
|
||||
return;
|
||||
for (auto *c : this->clients_)
|
||||
for (auto &c : this->clients_)
|
||||
c->send_cover_state(obj);
|
||||
}
|
||||
#endif
|
||||
@@ -192,7 +191,7 @@ void APIServer::on_cover_update(cover::Cover *obj) {
|
||||
void APIServer::on_fan_update(fan::FanState *obj) {
|
||||
if (obj->is_internal())
|
||||
return;
|
||||
for (auto *c : this->clients_)
|
||||
for (auto &c : this->clients_)
|
||||
c->send_fan_state(obj);
|
||||
}
|
||||
#endif
|
||||
@@ -201,7 +200,7 @@ void APIServer::on_fan_update(fan::FanState *obj) {
|
||||
void APIServer::on_light_update(light::LightState *obj) {
|
||||
if (obj->is_internal())
|
||||
return;
|
||||
for (auto *c : this->clients_)
|
||||
for (auto &c : this->clients_)
|
||||
c->send_light_state(obj);
|
||||
}
|
||||
#endif
|
||||
@@ -210,7 +209,7 @@ void APIServer::on_light_update(light::LightState *obj) {
|
||||
void APIServer::on_sensor_update(sensor::Sensor *obj, float state) {
|
||||
if (obj->is_internal())
|
||||
return;
|
||||
for (auto *c : this->clients_)
|
||||
for (auto &c : this->clients_)
|
||||
c->send_sensor_state(obj, state);
|
||||
}
|
||||
#endif
|
||||
@@ -219,7 +218,7 @@ void APIServer::on_sensor_update(sensor::Sensor *obj, float state) {
|
||||
void APIServer::on_switch_update(switch_::Switch *obj, bool state) {
|
||||
if (obj->is_internal())
|
||||
return;
|
||||
for (auto *c : this->clients_)
|
||||
for (auto &c : this->clients_)
|
||||
c->send_switch_state(obj, state);
|
||||
}
|
||||
#endif
|
||||
@@ -228,7 +227,7 @@ void APIServer::on_switch_update(switch_::Switch *obj, bool state) {
|
||||
void APIServer::on_text_sensor_update(text_sensor::TextSensor *obj, const std::string &state) {
|
||||
if (obj->is_internal())
|
||||
return;
|
||||
for (auto *c : this->clients_)
|
||||
for (auto &c : this->clients_)
|
||||
c->send_text_sensor_state(obj, state);
|
||||
}
|
||||
#endif
|
||||
@@ -237,7 +236,7 @@ void APIServer::on_text_sensor_update(text_sensor::TextSensor *obj, const std::s
|
||||
void APIServer::on_climate_update(climate::Climate *obj) {
|
||||
if (obj->is_internal())
|
||||
return;
|
||||
for (auto *c : this->clients_)
|
||||
for (auto &c : this->clients_)
|
||||
c->send_climate_state(obj);
|
||||
}
|
||||
#endif
|
||||
@@ -246,7 +245,7 @@ void APIServer::on_climate_update(climate::Climate *obj) {
|
||||
void APIServer::on_number_update(number::Number *obj, float state) {
|
||||
if (obj->is_internal())
|
||||
return;
|
||||
for (auto *c : this->clients_)
|
||||
for (auto &c : this->clients_)
|
||||
c->send_number_state(obj, state);
|
||||
}
|
||||
#endif
|
||||
@@ -255,7 +254,7 @@ void APIServer::on_number_update(number::Number *obj, float state) {
|
||||
void APIServer::on_select_update(select::Select *obj, const std::string &state) {
|
||||
if (obj->is_internal())
|
||||
return;
|
||||
for (auto *c : this->clients_)
|
||||
for (auto &c : this->clients_)
|
||||
c->send_select_state(obj, state);
|
||||
}
|
||||
#endif
|
||||
@@ -266,7 +265,7 @@ APIServer *global_api_server = nullptr; // NOLINT(cppcoreguidelines-avoid-non-c
|
||||
|
||||
void APIServer::set_password(const std::string &password) { this->password_ = password; }
|
||||
void APIServer::send_homeassistant_service_call(const HomeassistantServiceResponse &call) {
|
||||
for (auto *client : this->clients_) {
|
||||
for (auto &client : this->clients_) {
|
||||
client->send_homeassistant_service_call(call);
|
||||
}
|
||||
}
|
||||
@@ -286,7 +285,7 @@ uint16_t APIServer::get_port() const { return this->port_; }
|
||||
void APIServer::set_reboot_timeout(uint32_t reboot_timeout) { this->reboot_timeout_ = reboot_timeout; }
|
||||
#ifdef USE_HOMEASSISTANT_TIME
|
||||
void APIServer::request_time() {
|
||||
for (auto *client : this->clients_) {
|
||||
for (auto &client : this->clients_) {
|
||||
if (!client->remove_ && client->connection_state_ == APIConnection::ConnectionState::CONNECTED)
|
||||
client->send_time_request();
|
||||
}
|
||||
@@ -294,7 +293,7 @@ void APIServer::request_time() {
|
||||
#endif
|
||||
bool APIServer::is_connected() const { return !this->clients_.empty(); }
|
||||
void APIServer::on_shutdown() {
|
||||
for (auto *c : this->clients_) {
|
||||
for (auto &c : this->clients_) {
|
||||
c->send_disconnect_request(DisconnectRequest());
|
||||
}
|
||||
delay(10);
|
||||
|
||||
@@ -32,7 +32,7 @@ class APIServer : public Component, public Controller {
|
||||
void set_reboot_timeout(uint32_t reboot_timeout);
|
||||
|
||||
#ifdef USE_API_NOISE
|
||||
void set_noise_psk(psk_t psk) { noise_ctx_->set_psk(std::move(psk)); }
|
||||
void set_noise_psk(psk_t psk) { noise_ctx_->set_psk(psk); }
|
||||
std::shared_ptr<APINoiseContext> get_noise_ctx() { return noise_ctx_; }
|
||||
#endif // USE_API_NOISE
|
||||
|
||||
@@ -91,7 +91,7 @@ class APIServer : public Component, public Controller {
|
||||
uint16_t port_{6053};
|
||||
uint32_t reboot_timeout_{300000};
|
||||
uint32_t last_connected_{0};
|
||||
std::vector<APIConnection *> clients_;
|
||||
std::vector<std::unique_ptr<APIConnection>> clients_;
|
||||
std::string password_;
|
||||
std::vector<HomeAssistantStateSubscription> state_subs_;
|
||||
std::vector<UserServiceDescriptor *> user_services_;
|
||||
|
||||
@@ -49,7 +49,7 @@ class CustomAPIDevice {
|
||||
template<typename T, typename... Ts>
|
||||
void register_service(void (T::*callback)(Ts...), const std::string &name,
|
||||
const std::array<std::string, sizeof...(Ts)> &arg_names) {
|
||||
auto *service = new CustomAPIDeviceService<T, Ts...>(name, arg_names, (T *) this, callback);
|
||||
auto *service = new CustomAPIDeviceService<T, Ts...>(name, arg_names, (T *) this, callback); // NOLINT
|
||||
global_api_server->register_user_service(service);
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ class CustomAPIDevice {
|
||||
* @param name The name of the arguments for the service, must match the arguments of the function.
|
||||
*/
|
||||
template<typename T> void register_service(void (T::*callback)(), const std::string &name) {
|
||||
auto *service = new CustomAPIDeviceService<T>(name, {}, (T *) this, callback);
|
||||
auto *service = new CustomAPIDeviceService<T>(name, {}, (T *) this, callback); // NOLINT
|
||||
global_api_server->register_user_service(service);
|
||||
}
|
||||
|
||||
|
||||
@@ -246,6 +246,7 @@ class ProtoWriteBuffer {
|
||||
|
||||
class ProtoMessage {
|
||||
public:
|
||||
virtual ~ProtoMessage() = default;
|
||||
virtual void encode(ProtoWriteBuffer buffer) const = 0;
|
||||
void decode(const uint8_t *buffer, size_t length);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||
|
||||
|
||||
@@ -25,8 +25,12 @@ void I2CAS3935Component::write_register(uint8_t reg, uint8_t mask, uint8_t bits,
|
||||
|
||||
uint8_t I2CAS3935Component::read_register(uint8_t reg) {
|
||||
uint8_t value;
|
||||
if (!this->read_byte(reg, &value, 2)) {
|
||||
ESP_LOGW(TAG, "Read failed!");
|
||||
if (write(®, 1) != i2c::ERROR_OK) {
|
||||
ESP_LOGW(TAG, "Writing register failed!");
|
||||
return 0;
|
||||
}
|
||||
if (read(&value, 1) != i2c::ERROR_OK) {
|
||||
ESP_LOGW(TAG, "Reading register failed!");
|
||||
return 0;
|
||||
}
|
||||
return value;
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
# Dummy integration to allow relying on AsyncTCP
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.core import CORE, coroutine_with_priority
|
||||
|
||||
CODEOWNERS = ["@OttoWinter"]
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
cv.Schema({}),
|
||||
cv.only_with_arduino,
|
||||
)
|
||||
|
||||
|
||||
@coroutine_with_priority(200.0)
|
||||
async def to_code(config):
|
||||
@@ -12,4 +18,4 @@ async def to_code(config):
|
||||
cg.add_library("esphome/AsyncTCP-esphome", "1.2.2")
|
||||
elif CORE.is_esp8266:
|
||||
# https://github.com/OttoWinter/ESPAsyncTCP
|
||||
cg.add_library("ESPAsyncTCP-esphome", "1.2.3")
|
||||
cg.add_library("ottowinter/ESPAsyncTCP-esphome", "1.2.3")
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "atc_mithermometer.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#ifdef USE_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace atc_mithermometer {
|
||||
@@ -25,14 +25,14 @@ bool ATCMiThermometer::parse_device(const esp32_ble_tracker::ESPBTDevice &device
|
||||
|
||||
bool success = false;
|
||||
for (auto &service_data : device.get_service_datas()) {
|
||||
auto res = parse_header(service_data);
|
||||
if (res->is_duplicate) {
|
||||
auto res = parse_header_(service_data);
|
||||
if (!res.has_value()) {
|
||||
continue;
|
||||
}
|
||||
if (!(parse_message(service_data.data, *res))) {
|
||||
if (!(parse_message_(service_data.data, *res))) {
|
||||
continue;
|
||||
}
|
||||
if (!(report_results(res, device.address_str()))) {
|
||||
if (!(report_results_(res, device.address_str()))) {
|
||||
continue;
|
||||
}
|
||||
if (res->temperature.has_value() && this->temperature_ != nullptr)
|
||||
@@ -46,14 +46,10 @@ bool ATCMiThermometer::parse_device(const esp32_ble_tracker::ESPBTDevice &device
|
||||
success = true;
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return success;
|
||||
}
|
||||
|
||||
optional<ParseResult> ATCMiThermometer::parse_header(const esp32_ble_tracker::ServiceData &service_data) {
|
||||
optional<ParseResult> ATCMiThermometer::parse_header_(const esp32_ble_tracker::ServiceData &service_data) {
|
||||
ParseResult result;
|
||||
if (!service_data.uuid.contains(0x1A, 0x18)) {
|
||||
ESP_LOGVV(TAG, "parse_header(): no service data UUID magic bytes.");
|
||||
@@ -64,17 +60,15 @@ optional<ParseResult> ATCMiThermometer::parse_header(const esp32_ble_tracker::Se
|
||||
|
||||
static uint8_t last_frame_count = 0;
|
||||
if (last_frame_count == raw[12]) {
|
||||
ESP_LOGVV(TAG, "parse_header(): duplicate data packet received (%d).", static_cast<int>(last_frame_count));
|
||||
result.is_duplicate = true;
|
||||
ESP_LOGVV(TAG, "parse_header(): duplicate data packet received (%hhu).", last_frame_count);
|
||||
return {};
|
||||
}
|
||||
last_frame_count = raw[12];
|
||||
result.is_duplicate = false;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ATCMiThermometer::parse_message(const std::vector<uint8_t> &message, ParseResult &result) {
|
||||
bool ATCMiThermometer::parse_message_(const std::vector<uint8_t> &message, ParseResult &result) {
|
||||
// Byte 0-5 mac in correct order
|
||||
// Byte 6-7 Temperature in uint16
|
||||
// Byte 8 Humidity in percent
|
||||
@@ -107,7 +101,7 @@ bool ATCMiThermometer::parse_message(const std::vector<uint8_t> &message, ParseR
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ATCMiThermometer::report_results(const optional<ParseResult> &result, const std::string &address) {
|
||||
bool ATCMiThermometer::report_results_(const optional<ParseResult> &result, const std::string &address) {
|
||||
if (!result.has_value()) {
|
||||
ESP_LOGVV(TAG, "report_results(): no results available.");
|
||||
return false;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#ifdef USE_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace atc_mithermometer {
|
||||
@@ -14,7 +14,6 @@ struct ParseResult {
|
||||
optional<float> humidity;
|
||||
optional<float> battery_level;
|
||||
optional<float> battery_voltage;
|
||||
bool is_duplicate;
|
||||
int raw_offset;
|
||||
};
|
||||
|
||||
@@ -37,9 +36,9 @@ class ATCMiThermometer : public Component, public esp32_ble_tracker::ESPBTDevice
|
||||
sensor::Sensor *battery_level_{nullptr};
|
||||
sensor::Sensor *battery_voltage_{nullptr};
|
||||
|
||||
optional<ParseResult> parse_header(const esp32_ble_tracker::ServiceData &service_data);
|
||||
bool parse_message(const std::vector<uint8_t> &message, ParseResult &result);
|
||||
bool report_results(const optional<ParseResult> &result, const std::string &address);
|
||||
optional<ParseResult> parse_header_(const esp32_ble_tracker::ServiceData &service_data);
|
||||
bool parse_message_(const std::vector<uint8_t> &message, ParseResult &result);
|
||||
bool report_results_(const optional<ParseResult> &result, const std::string &address);
|
||||
};
|
||||
|
||||
} // namespace atc_mithermometer
|
||||
|
||||
@@ -265,27 +265,57 @@ float ATM90E32Component::get_power_factor_c_() {
|
||||
}
|
||||
float ATM90E32Component::get_forward_active_energy_a_() {
|
||||
uint16_t val = this->read16_(ATM90E32_REGISTER_APENERGYA);
|
||||
return (float) val * 10 / 3200; // convert register value to WattHours
|
||||
if ((UINT32_MAX - this->phase_[0].cumulative_forward_active_energy_) > val) {
|
||||
this->phase_[0].cumulative_forward_active_energy_ += val;
|
||||
} else {
|
||||
this->phase_[0].cumulative_forward_active_energy_ = val;
|
||||
}
|
||||
return ((float) this->phase_[0].cumulative_forward_active_energy_ * 10 / 3200);
|
||||
}
|
||||
float ATM90E32Component::get_forward_active_energy_b_() {
|
||||
uint16_t val = this->read16_(ATM90E32_REGISTER_APENERGYB);
|
||||
return (float) val * 10 / 3200;
|
||||
if (UINT32_MAX - this->phase_[1].cumulative_forward_active_energy_ > val) {
|
||||
this->phase_[1].cumulative_forward_active_energy_ += val;
|
||||
} else {
|
||||
this->phase_[1].cumulative_forward_active_energy_ = val;
|
||||
}
|
||||
return ((float) this->phase_[1].cumulative_forward_active_energy_ * 10 / 3200);
|
||||
}
|
||||
float ATM90E32Component::get_forward_active_energy_c_() {
|
||||
uint16_t val = this->read16_(ATM90E32_REGISTER_APENERGYC);
|
||||
return (float) val * 10 / 3200;
|
||||
if (UINT32_MAX - this->phase_[2].cumulative_forward_active_energy_ > val) {
|
||||
this->phase_[2].cumulative_forward_active_energy_ += val;
|
||||
} else {
|
||||
this->phase_[2].cumulative_forward_active_energy_ = val;
|
||||
}
|
||||
return ((float) this->phase_[2].cumulative_forward_active_energy_ * 10 / 3200);
|
||||
}
|
||||
float ATM90E32Component::get_reverse_active_energy_a_() {
|
||||
uint16_t val = this->read16_(ATM90E32_REGISTER_ANENERGYA);
|
||||
return (float) val * 10 / 3200;
|
||||
if (UINT32_MAX - this->phase_[0].cumulative_reverse_active_energy_ > val) {
|
||||
this->phase_[0].cumulative_reverse_active_energy_ += val;
|
||||
} else {
|
||||
this->phase_[0].cumulative_reverse_active_energy_ = val;
|
||||
}
|
||||
return ((float) this->phase_[0].cumulative_reverse_active_energy_ * 10 / 3200);
|
||||
}
|
||||
float ATM90E32Component::get_reverse_active_energy_b_() {
|
||||
uint16_t val = this->read16_(ATM90E32_REGISTER_ANENERGYB);
|
||||
return (float) val * 10 / 3200;
|
||||
if (UINT32_MAX - this->phase_[1].cumulative_reverse_active_energy_ > val) {
|
||||
this->phase_[1].cumulative_reverse_active_energy_ += val;
|
||||
} else {
|
||||
this->phase_[1].cumulative_reverse_active_energy_ = val;
|
||||
}
|
||||
return ((float) this->phase_[1].cumulative_reverse_active_energy_ * 10 / 3200);
|
||||
}
|
||||
float ATM90E32Component::get_reverse_active_energy_c_() {
|
||||
uint16_t val = this->read16_(ATM90E32_REGISTER_ANENERGYC);
|
||||
return (float) val * 10 / 3200;
|
||||
if (UINT32_MAX - this->phase_[2].cumulative_reverse_active_energy_ > val) {
|
||||
this->phase_[2].cumulative_reverse_active_energy_ += val;
|
||||
} else {
|
||||
this->phase_[2].cumulative_reverse_active_energy_ = val;
|
||||
}
|
||||
return ((float) this->phase_[2].cumulative_reverse_active_energy_ * 10 / 3200);
|
||||
}
|
||||
float ATM90E32Component::get_frequency_() {
|
||||
uint16_t freq = this->read16_(ATM90E32_REGISTER_FREQ);
|
||||
|
||||
@@ -77,6 +77,8 @@ class ATM90E32Component : public PollingComponent,
|
||||
sensor::Sensor *power_factor_sensor_{nullptr};
|
||||
sensor::Sensor *forward_active_energy_sensor_{nullptr};
|
||||
sensor::Sensor *reverse_active_energy_sensor_{nullptr};
|
||||
uint32_t cumulative_forward_active_energy_{0};
|
||||
uint32_t cumulative_reverse_active_energy_{0};
|
||||
} phase_[3];
|
||||
sensor::Sensor *freq_sensor_{nullptr};
|
||||
sensor::Sensor *chip_temperature_sensor_{nullptr};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "b_parasite.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#ifdef USE_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace b_parasite {
|
||||
@@ -14,6 +14,7 @@ void BParasite::dump_config() {
|
||||
LOG_SENSOR(" ", "Temperature", this->temperature_);
|
||||
LOG_SENSOR(" ", "Humidity", this->humidity_);
|
||||
LOG_SENSOR(" ", "Soil Moisture", this->soil_moisture_);
|
||||
LOG_SENSOR(" ", "Illuminance", this->illuminance_);
|
||||
}
|
||||
|
||||
bool BParasite::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
|
||||
@@ -36,6 +37,15 @@ bool BParasite::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
|
||||
|
||||
const auto &data = service_data.data;
|
||||
|
||||
const uint8_t protocol_version = data[0] >> 4;
|
||||
if (protocol_version != 1) {
|
||||
ESP_LOGE(TAG, "Unsupported protocol version: %u", protocol_version);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Some b-parasite versions have an (optional) illuminance sensor.
|
||||
bool has_illuminance = data[0] & 0x1;
|
||||
|
||||
// Counter for deduplicating messages.
|
||||
uint8_t counter = data[1] & 0x0f;
|
||||
if (last_processed_counter_ == counter) {
|
||||
@@ -59,6 +69,9 @@ bool BParasite::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
|
||||
uint16_t soil_moisture = data[8] << 8 | data[9];
|
||||
float moisture_percent = (100.0f * soil_moisture) / (1 << 16);
|
||||
|
||||
// Ambient light in lux.
|
||||
float illuminance = has_illuminance ? data[16] << 8 | data[17] : 0.0f;
|
||||
|
||||
if (battery_voltage_ != nullptr) {
|
||||
battery_voltage_->publish_state(battery_voltage);
|
||||
}
|
||||
@@ -71,6 +84,13 @@ bool BParasite::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
|
||||
if (soil_moisture_ != nullptr) {
|
||||
soil_moisture_->publish_state(moisture_percent);
|
||||
}
|
||||
if (illuminance_ != nullptr) {
|
||||
if (has_illuminance) {
|
||||
illuminance_->publish_state(illuminance);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "No lux information is present in the BLE packet");
|
||||
}
|
||||
}
|
||||
|
||||
last_processed_counter_ = counter;
|
||||
return true;
|
||||
@@ -79,4 +99,4 @@ bool BParasite::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
|
||||
} // namespace b_parasite
|
||||
} // namespace esphome
|
||||
|
||||
#endif // ARDUINO_ARCH_ESP32
|
||||
#endif // USE_ESP32
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#ifdef USE_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace b_parasite {
|
||||
@@ -22,6 +22,7 @@ class BParasite : public Component, public esp32_ble_tracker::ESPBTDeviceListene
|
||||
void set_temperature(sensor::Sensor *temperature) { temperature_ = temperature; }
|
||||
void set_humidity(sensor::Sensor *humidity) { humidity_ = humidity; }
|
||||
void set_soil_moisture(sensor::Sensor *soil_moisture) { soil_moisture_ = soil_moisture; }
|
||||
void set_illuminance(sensor::Sensor *illuminance) { illuminance_ = illuminance; }
|
||||
|
||||
protected:
|
||||
// The received advertisement packet contains an unsigned 4 bits wrap-around counter
|
||||
@@ -32,9 +33,10 @@ class BParasite : public Component, public esp32_ble_tracker::ESPBTDeviceListene
|
||||
sensor::Sensor *temperature_{nullptr};
|
||||
sensor::Sensor *humidity_{nullptr};
|
||||
sensor::Sensor *soil_moisture_{nullptr};
|
||||
sensor::Sensor *illuminance_{nullptr};
|
||||
};
|
||||
|
||||
} // namespace b_parasite
|
||||
} // namespace esphome
|
||||
|
||||
#endif // ARDUINO_ARCH_ESP32
|
||||
#endif // USE_ESP32
|
||||
|
||||
@@ -5,14 +5,17 @@ from esphome.const import (
|
||||
CONF_BATTERY_VOLTAGE,
|
||||
CONF_HUMIDITY,
|
||||
CONF_ID,
|
||||
CONF_ILLUMINANCE,
|
||||
CONF_MOISTURE,
|
||||
CONF_MAC_ADDRESS,
|
||||
CONF_TEMPERATURE,
|
||||
DEVICE_CLASS_HUMIDITY,
|
||||
DEVICE_CLASS_ILLUMINANCE,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
DEVICE_CLASS_VOLTAGE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_CELSIUS,
|
||||
UNIT_LUX,
|
||||
UNIT_PERCENT,
|
||||
UNIT_VOLT,
|
||||
)
|
||||
@@ -55,6 +58,12 @@ CONFIG_SCHEMA = (
|
||||
device_class=DEVICE_CLASS_HUMIDITY,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_ILLUMINANCE): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_LUX,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_ILLUMINANCE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
}
|
||||
)
|
||||
.extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)
|
||||
@@ -74,6 +83,7 @@ async def to_code(config):
|
||||
(CONF_HUMIDITY, var.set_humidity),
|
||||
(CONF_BATTERY_VOLTAGE, var.set_battery_voltage),
|
||||
(CONF_MOISTURE, var.set_soil_moisture),
|
||||
(CONF_ILLUMINANCE, var.set_illuminance),
|
||||
]:
|
||||
if config_key in config:
|
||||
sens = await sensor.new_sensor(config[config_key])
|
||||
|
||||
@@ -69,7 +69,8 @@ void BangBangClimate::compute_state_() {
|
||||
this->switch_to_action_(climate::CLIMATE_ACTION_OFF);
|
||||
return;
|
||||
}
|
||||
if (isnan(this->current_temperature) || isnan(this->target_temperature_low) || isnan(this->target_temperature_high)) {
|
||||
if (std::isnan(this->current_temperature) || std::isnan(this->target_temperature_low) ||
|
||||
std::isnan(this->target_temperature_high)) {
|
||||
// if any control parameters are nan, go to OFF action (not IDLE!)
|
||||
this->switch_to_action_(climate::CLIMATE_ACTION_OFF);
|
||||
return;
|
||||
|
||||
@@ -71,10 +71,11 @@ void BH1750Sensor::update() {
|
||||
float BH1750Sensor::get_setup_priority() const { return setup_priority::DATA; }
|
||||
void BH1750Sensor::read_data_() {
|
||||
uint16_t raw_value;
|
||||
if (!this->parent_->raw_receive_16(this->address_, &raw_value, 1)) {
|
||||
if (this->read(reinterpret_cast<uint8_t *>(&raw_value), 2) != i2c::ERROR_OK) {
|
||||
this->status_set_warning();
|
||||
return;
|
||||
}
|
||||
raw_value = i2c::i2ctohs(raw_value);
|
||||
|
||||
float lx = float(raw_value) / 1.2f;
|
||||
lx *= 69.0f / this->measurement_duration_;
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.cpp_helpers import setup_entity
|
||||
from esphome import automation, core
|
||||
from esphome.automation import Condition, maybe_simple_id
|
||||
from esphome.components import mqtt
|
||||
from esphome.const import (
|
||||
CONF_DELAY,
|
||||
CONF_DEVICE_CLASS,
|
||||
CONF_DISABLED_BY_DEFAULT,
|
||||
CONF_FILTERS,
|
||||
CONF_ID,
|
||||
CONF_INTERNAL,
|
||||
CONF_INVALID_COOLDOWN,
|
||||
CONF_INVERTED,
|
||||
CONF_MAX_LENGTH,
|
||||
@@ -88,7 +87,7 @@ DEVICE_CLASSES = [
|
||||
IS_PLATFORM_COMPONENT = True
|
||||
|
||||
binary_sensor_ns = cg.esphome_ns.namespace("binary_sensor")
|
||||
BinarySensor = binary_sensor_ns.class_("BinarySensor", cg.Nameable)
|
||||
BinarySensor = binary_sensor_ns.class_("BinarySensor", cg.EntityBase)
|
||||
BinarySensorInitiallyOff = binary_sensor_ns.class_(
|
||||
"BinarySensorInitiallyOff", BinarySensor
|
||||
)
|
||||
@@ -231,17 +230,16 @@ def parse_multi_click_timing_str(value):
|
||||
parts = value.lower().split(" ")
|
||||
if len(parts) != 5:
|
||||
raise cv.Invalid(
|
||||
"Multi click timing grammar consists of exactly 5 words, not {}"
|
||||
"".format(len(parts))
|
||||
f"Multi click timing grammar consists of exactly 5 words, not {len(parts)}"
|
||||
)
|
||||
try:
|
||||
state = cv.boolean(parts[0])
|
||||
except cv.Invalid:
|
||||
# pylint: disable=raise-missing-from
|
||||
raise cv.Invalid("First word must either be ON or OFF, not {}".format(parts[0]))
|
||||
raise cv.Invalid(f"First word must either be ON or OFF, not {parts[0]}")
|
||||
|
||||
if parts[1] != "for":
|
||||
raise cv.Invalid("Second word must be 'for', got {}".format(parts[1]))
|
||||
raise cv.Invalid(f"Second word must be 'for', got {parts[1]}")
|
||||
|
||||
if parts[2] == "at":
|
||||
if parts[3] == "least":
|
||||
@@ -250,8 +248,7 @@ def parse_multi_click_timing_str(value):
|
||||
key = CONF_MAX_LENGTH
|
||||
else:
|
||||
raise cv.Invalid(
|
||||
"Third word after at must either be 'least' or 'most', got {}"
|
||||
"".format(parts[3])
|
||||
f"Third word after at must either be 'least' or 'most', got {parts[3]}"
|
||||
)
|
||||
try:
|
||||
length = cv.positive_time_period_milliseconds(parts[4])
|
||||
@@ -296,13 +293,11 @@ def validate_multi_click_timing(value):
|
||||
new_state = v_.get(CONF_STATE, not state)
|
||||
if new_state == state:
|
||||
raise cv.Invalid(
|
||||
"Timings must have alternating state. Indices {} and {} have "
|
||||
"the same state {}".format(i, i + 1, state)
|
||||
f"Timings must have alternating state. Indices {i} and {i + 1} have the same state {state}"
|
||||
)
|
||||
if max_length is not None and max_length < min_length:
|
||||
raise cv.Invalid(
|
||||
"Max length ({}) must be larger than min length ({})."
|
||||
"".format(max_length, min_length)
|
||||
f"Max length ({max_length}) must be larger than min length ({min_length})."
|
||||
)
|
||||
|
||||
state = new_state
|
||||
@@ -318,7 +313,7 @@ def validate_multi_click_timing(value):
|
||||
|
||||
device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_")
|
||||
|
||||
BINARY_SENSOR_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend(
|
||||
BINARY_SENSOR_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(BinarySensor),
|
||||
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(
|
||||
@@ -379,10 +374,8 @@ BINARY_SENSOR_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).exten
|
||||
|
||||
|
||||
async def setup_binary_sensor_core_(var, config):
|
||||
cg.add(var.set_name(config[CONF_NAME]))
|
||||
cg.add(var.set_disabled_by_default(config[CONF_DISABLED_BY_DEFAULT]))
|
||||
if CONF_INTERNAL in config:
|
||||
cg.add(var.set_internal(config[CONF_INTERNAL]))
|
||||
await setup_entity(var, config)
|
||||
|
||||
if CONF_DEVICE_CLASS in config:
|
||||
cg.add(var.set_device_class(config[CONF_DEVICE_CLASS]))
|
||||
if CONF_INVERTED in config:
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||
|
||||
namespace esphome {
|
||||
|
||||
@@ -42,7 +42,7 @@ void BinarySensor::send_state_internal(bool state, bool is_initial) {
|
||||
}
|
||||
}
|
||||
std::string BinarySensor::device_class() { return ""; }
|
||||
BinarySensor::BinarySensor(const std::string &name) : Nameable(name), state(false) {}
|
||||
BinarySensor::BinarySensor(const std::string &name) : EntityBase(name), state(false) {}
|
||||
BinarySensor::BinarySensor() : BinarySensor("") {}
|
||||
void BinarySensor::set_device_class(const std::string &device_class) { this->device_class_ = device_class; }
|
||||
std::string BinarySensor::get_device_class() {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/entity_base.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/components/binary_sensor/filter.h"
|
||||
|
||||
@@ -10,7 +11,7 @@ namespace binary_sensor {
|
||||
|
||||
#define LOG_BINARY_SENSOR(prefix, type, obj) \
|
||||
if ((obj) != nullptr) { \
|
||||
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, type, (obj)->get_name().c_str()); \
|
||||
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \
|
||||
if (!(obj)->get_device_class().empty()) { \
|
||||
ESP_LOGCONFIG(TAG, "%s Device Class: '%s'", prefix, (obj)->get_device_class().c_str()); \
|
||||
} \
|
||||
@@ -22,7 +23,7 @@ namespace binary_sensor {
|
||||
* The sub classes should notify the front-end of new states via the publish_state() method which
|
||||
* handles inverted inputs for you.
|
||||
*/
|
||||
class BinarySensor : public Nameable {
|
||||
class BinarySensor : public EntityBase {
|
||||
public:
|
||||
explicit BinarySensor();
|
||||
/** Construct a binary sensor with the specified name
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/components/ble_client/ble_client.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#ifdef USE_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace ble_client {
|
||||
@@ -11,11 +11,12 @@ class BLEClientConnectTrigger : public Trigger<>, public BLEClientNode {
|
||||
public:
|
||||
explicit BLEClientConnectTrigger(BLEClient *parent) { parent->register_ble_node(this); }
|
||||
void loop() override {}
|
||||
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) {
|
||||
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||
esp_ble_gattc_cb_param_t *param) override {
|
||||
if (event == ESP_GATTC_OPEN_EVT && param->open.status == ESP_GATT_OK)
|
||||
this->trigger();
|
||||
if (event == ESP_GATTC_SEARCH_CMPL_EVT)
|
||||
this->node_state = espbt::ClientState::Established;
|
||||
this->node_state = espbt::ClientState::ESTABLISHED;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -23,11 +24,12 @@ class BLEClientDisconnectTrigger : public Trigger<>, public BLEClientNode {
|
||||
public:
|
||||
explicit BLEClientDisconnectTrigger(BLEClient *parent) { parent->register_ble_node(this); }
|
||||
void loop() override {}
|
||||
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) {
|
||||
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||
esp_ble_gattc_cb_param_t *param) override {
|
||||
if (event == ESP_GATTC_DISCONNECT_EVT && memcmp(param->disconnect.remote_bda, this->parent_->remote_bda, 6) == 0)
|
||||
this->trigger();
|
||||
if (event == ESP_GATTC_SEARCH_CMPL_EVT)
|
||||
this->node_state = espbt::ClientState::Established;
|
||||
this->node_state = espbt::ClientState::ESTABLISHED;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||
#include "ble_client.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#ifdef USE_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace ble_client {
|
||||
@@ -17,12 +17,12 @@ void BLEClient::setup() {
|
||||
ESP_LOGE(TAG, "gattc app register failed. app_id=%d code=%d", this->app_id, ret);
|
||||
this->mark_failed();
|
||||
}
|
||||
this->set_states(espbt::ClientState::Idle);
|
||||
this->set_states_(espbt::ClientState::IDLE);
|
||||
this->enabled = true;
|
||||
}
|
||||
|
||||
void BLEClient::loop() {
|
||||
if (this->state() == espbt::ClientState::Discovered) {
|
||||
if (this->state() == espbt::ClientState::DISCOVERED) {
|
||||
this->connect();
|
||||
}
|
||||
for (auto *node : this->nodes_)
|
||||
@@ -39,11 +39,11 @@ bool BLEClient::parse_device(const espbt::ESPBTDevice &device) {
|
||||
return false;
|
||||
if (device.address_uint64() != this->address)
|
||||
return false;
|
||||
if (this->state() != espbt::ClientState::Idle)
|
||||
if (this->state() != espbt::ClientState::IDLE)
|
||||
return false;
|
||||
|
||||
ESP_LOGD(TAG, "Found device at MAC address [%s]", device.address_str().c_str());
|
||||
this->set_states(espbt::ClientState::Discovered);
|
||||
this->set_states_(espbt::ClientState::DISCOVERED);
|
||||
|
||||
auto addr = device.address_uint64();
|
||||
this->remote_bda[0] = (addr >> 40) & 0xFF;
|
||||
@@ -69,7 +69,7 @@ std::string BLEClient::address_str() const {
|
||||
void BLEClient::set_enabled(bool enabled) {
|
||||
if (enabled == this->enabled)
|
||||
return;
|
||||
if (!enabled && this->state() != espbt::ClientState::Idle) {
|
||||
if (!enabled && this->state() != espbt::ClientState::IDLE) {
|
||||
ESP_LOGI(TAG, "[%s] Disabling BLE client.", this->address_str().c_str());
|
||||
auto ret = esp_ble_gattc_close(this->gattc_if, this->conn_id);
|
||||
if (ret) {
|
||||
@@ -84,9 +84,9 @@ void BLEClient::connect() {
|
||||
auto ret = esp_ble_gattc_open(this->gattc_if, this->remote_bda, BLE_ADDR_TYPE_PUBLIC, true);
|
||||
if (ret) {
|
||||
ESP_LOGW(TAG, "esp_ble_gattc_open error, address=%s status=%d", this->address_str().c_str(), ret);
|
||||
this->set_states(espbt::ClientState::Idle);
|
||||
this->set_states_(espbt::ClientState::IDLE);
|
||||
} else {
|
||||
this->set_states(espbt::ClientState::Connecting);
|
||||
this->set_states_(espbt::ClientState::CONNECTING);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es
|
||||
if (event != ESP_GATTC_REG_EVT && esp_gattc_if != ESP_GATT_IF_NONE && esp_gattc_if != this->gattc_if)
|
||||
return;
|
||||
|
||||
bool all_established = this->all_nodes_established();
|
||||
bool all_established = this->all_nodes_established_();
|
||||
|
||||
switch (event) {
|
||||
case ESP_GATTC_REG_EVT: {
|
||||
@@ -113,7 +113,7 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es
|
||||
ESP_LOGV(TAG, "[%s] ESP_GATTC_OPEN_EVT", this->address_str().c_str());
|
||||
if (param->open.status != ESP_GATT_OK) {
|
||||
ESP_LOGW(TAG, "connect to %s failed, status=%d", this->address_str().c_str(), param->open.status);
|
||||
this->set_states(espbt::ClientState::Idle);
|
||||
this->set_states_(espbt::ClientState::IDLE);
|
||||
break;
|
||||
}
|
||||
this->conn_id = param->open.conn_id;
|
||||
@@ -126,11 +126,11 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es
|
||||
case ESP_GATTC_CFG_MTU_EVT: {
|
||||
if (param->cfg_mtu.status != ESP_GATT_OK) {
|
||||
ESP_LOGW(TAG, "cfg_mtu to %s failed, status %d", this->address_str().c_str(), param->cfg_mtu.status);
|
||||
this->set_states(espbt::ClientState::Idle);
|
||||
this->set_states_(espbt::ClientState::IDLE);
|
||||
break;
|
||||
}
|
||||
ESP_LOGV(TAG, "cfg_mtu status %d, mtu %d", param->cfg_mtu.status, param->cfg_mtu.mtu);
|
||||
esp_ble_gattc_search_service(esp_gattc_if, param->cfg_mtu.conn_id, NULL);
|
||||
esp_ble_gattc_search_service(esp_gattc_if, param->cfg_mtu.conn_id, nullptr);
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_DISCONNECT_EVT: {
|
||||
@@ -139,13 +139,13 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es
|
||||
}
|
||||
ESP_LOGV(TAG, "[%s] ESP_GATTC_DISCONNECT_EVT", this->address_str().c_str());
|
||||
for (auto &svc : this->services_)
|
||||
delete svc;
|
||||
delete svc; // NOLINT(cppcoreguidelines-owning-memory)
|
||||
this->services_.clear();
|
||||
this->set_states(espbt::ClientState::Idle);
|
||||
this->set_states_(espbt::ClientState::IDLE);
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_SEARCH_RES_EVT: {
|
||||
BLEService *ble_service = new BLEService();
|
||||
BLEService *ble_service = new BLEService(); // NOLINT(cppcoreguidelines-owning-memory)
|
||||
ble_service->uuid = espbt::ESPBTUUID::from_uuid(param->search_res.srvc_id.uuid);
|
||||
ble_service->start_handle = param->search_res.start_handle;
|
||||
ble_service->end_handle = param->search_res.end_handle;
|
||||
@@ -160,8 +160,8 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es
|
||||
ESP_LOGI(TAG, " start_handle: 0x%x end_handle: 0x%x", svc->start_handle, svc->end_handle);
|
||||
svc->parse_characteristics();
|
||||
}
|
||||
this->set_states(espbt::ClientState::Connected);
|
||||
this->set_state(espbt::ClientState::Established);
|
||||
this->set_states_(espbt::ClientState::CONNECTED);
|
||||
this->set_state(espbt::ClientState::ESTABLISHED);
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
|
||||
@@ -192,9 +192,9 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es
|
||||
node->gattc_event_handler(event, esp_gattc_if, param);
|
||||
|
||||
// Delete characteristics after clients have used them to save RAM.
|
||||
if (!all_established && this->all_nodes_established()) {
|
||||
if (!all_established && this->all_nodes_established_()) {
|
||||
for (auto &svc : this->services_)
|
||||
delete svc;
|
||||
delete svc; // NOLINT(cppcoreguidelines-owning-memory)
|
||||
this->services_.clear();
|
||||
}
|
||||
}
|
||||
@@ -307,7 +307,7 @@ BLEDescriptor *BLEClient::get_descriptor(uint16_t service, uint16_t chr, uint16_
|
||||
|
||||
BLEService::~BLEService() {
|
||||
for (auto &chr : this->characteristics)
|
||||
delete chr;
|
||||
delete chr; // NOLINT(cppcoreguidelines-owning-memory)
|
||||
}
|
||||
|
||||
void BLEService::parse_characteristics() {
|
||||
@@ -329,7 +329,7 @@ void BLEService::parse_characteristics() {
|
||||
break;
|
||||
}
|
||||
|
||||
BLECharacteristic *characteristic = new BLECharacteristic();
|
||||
BLECharacteristic *characteristic = new BLECharacteristic(); // NOLINT(cppcoreguidelines-owning-memory)
|
||||
characteristic->uuid = espbt::ESPBTUUID::from_uuid(result.uuid);
|
||||
characteristic->properties = result.properties;
|
||||
characteristic->handle = result.char_handle;
|
||||
@@ -344,7 +344,7 @@ void BLEService::parse_characteristics() {
|
||||
|
||||
BLECharacteristic::~BLECharacteristic() {
|
||||
for (auto &desc : this->descriptors)
|
||||
delete desc;
|
||||
delete desc; // NOLINT(cppcoreguidelines-owning-memory)
|
||||
}
|
||||
|
||||
void BLECharacteristic::parse_descriptors() {
|
||||
@@ -366,7 +366,7 @@ void BLECharacteristic::parse_descriptors() {
|
||||
break;
|
||||
}
|
||||
|
||||
BLEDescriptor *desc = new BLEDescriptor();
|
||||
BLEDescriptor *desc = new BLEDescriptor(); // NOLINT(cppcoreguidelines-owning-memory)
|
||||
desc->uuid = espbt::ESPBTUUID::from_uuid(result.uuid);
|
||||
desc->handle = result.handle;
|
||||
desc->characteristic = this;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#ifdef USE_ESP32
|
||||
|
||||
#include <string>
|
||||
#include <array>
|
||||
@@ -82,10 +82,11 @@ class BLEClient : public espbt::ESPBTClient, public Component {
|
||||
void dump_config() override;
|
||||
void loop() override;
|
||||
|
||||
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param);
|
||||
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||
esp_ble_gattc_cb_param_t *param) override;
|
||||
bool parse_device(const espbt::ESPBTDevice &device) override;
|
||||
void on_scan_end() override {}
|
||||
void connect();
|
||||
void connect() override;
|
||||
|
||||
void set_address(uint64_t address) { this->address = address; }
|
||||
|
||||
@@ -116,16 +117,16 @@ class BLEClient : public espbt::ESPBTClient, public Component {
|
||||
std::string address_str() const;
|
||||
|
||||
protected:
|
||||
void set_states(espbt::ClientState st) {
|
||||
void set_states_(espbt::ClientState st) {
|
||||
this->set_state(st);
|
||||
for (auto &node : nodes_)
|
||||
node->node_state = st;
|
||||
}
|
||||
bool all_nodes_established() {
|
||||
if (this->state() != espbt::ClientState::Established)
|
||||
bool all_nodes_established_() {
|
||||
if (this->state() != espbt::ClientState::ESTABLISHED)
|
||||
return false;
|
||||
for (auto &node : nodes_)
|
||||
if (node->node_state != espbt::ClientState::Established)
|
||||
if (node->node_state != espbt::ClientState::ESTABLISHED)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/components/ble_client/sensor/ble_sensor.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#ifdef USE_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace ble_client {
|
||||
@@ -11,10 +11,11 @@ namespace ble_client {
|
||||
class BLESensorNotifyTrigger : public Trigger<float>, public BLESensor {
|
||||
public:
|
||||
explicit BLESensorNotifyTrigger(BLESensor *sensor) { sensor_ = sensor; }
|
||||
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) {
|
||||
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||
esp_ble_gattc_cb_param_t *param) override {
|
||||
switch (event) {
|
||||
case ESP_GATTC_SEARCH_CMPL_EVT: {
|
||||
this->sensor_->node_state = espbt::ClientState::Established;
|
||||
this->sensor_->node_state = espbt::ClientState::ESTABLISHED;
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_NOTIFY_EVT: {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#ifdef USE_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace ble_client {
|
||||
@@ -71,7 +71,7 @@ void BLESensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga
|
||||
ESP_LOGW(TAG, "esp_ble_gattc_register_for_notify failed, status=%d", status);
|
||||
}
|
||||
} else {
|
||||
this->node_state = espbt::ClientState::Established;
|
||||
this->node_state = espbt::ClientState::ESTABLISHED;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -84,7 +84,7 @@ void BLESensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga
|
||||
}
|
||||
if (param->read.handle == this->handle) {
|
||||
this->status_clear_warning();
|
||||
this->publish_state(this->parse_data(param->read.value, param->read.value_len));
|
||||
this->publish_state(this->parse_data_(param->read.value, param->read.value_len));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -93,11 +93,11 @@ void BLESensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga
|
||||
break;
|
||||
ESP_LOGV(TAG, "[%s] ESP_GATTC_NOTIFY_EVT: handle=0x%x, value=0x%x", this->get_name().c_str(),
|
||||
param->notify.handle, param->notify.value[0]);
|
||||
this->publish_state(this->parse_data(param->notify.value, param->notify.value_len));
|
||||
this->publish_state(this->parse_data_(param->notify.value, param->notify.value_len));
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
|
||||
this->node_state = espbt::ClientState::Established;
|
||||
this->node_state = espbt::ClientState::ESTABLISHED;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -105,7 +105,7 @@ void BLESensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga
|
||||
}
|
||||
}
|
||||
|
||||
float BLESensor::parse_data(uint8_t *value, uint16_t value_len) {
|
||||
float BLESensor::parse_data_(uint8_t *value, uint16_t value_len) {
|
||||
if (this->data_to_value_func_.has_value()) {
|
||||
std::vector<uint8_t> data(value, value + value_len);
|
||||
return (*this->data_to_value_func_)(data);
|
||||
@@ -115,7 +115,7 @@ float BLESensor::parse_data(uint8_t *value, uint16_t value_len) {
|
||||
}
|
||||
|
||||
void BLESensor::update() {
|
||||
if (this->node_state != espbt::ClientState::Established) {
|
||||
if (this->node_state != espbt::ClientState::ESTABLISHED) {
|
||||
ESP_LOGW(TAG, "[%s] Cannot poll, not connected", this->get_name().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#ifdef USE_ESP32
|
||||
#include <esp_gattc_api.h>
|
||||
|
||||
namespace esphome {
|
||||
@@ -32,13 +32,13 @@ class BLESensor : public sensor::Sensor, public PollingComponent, public BLEClie
|
||||
void set_descr_uuid16(uint16_t uuid) { this->descr_uuid_ = espbt::ESPBTUUID::from_uint16(uuid); }
|
||||
void set_descr_uuid32(uint32_t uuid) { this->descr_uuid_ = espbt::ESPBTUUID::from_uint32(uuid); }
|
||||
void set_descr_uuid128(uint8_t *uuid) { this->descr_uuid_ = espbt::ESPBTUUID::from_raw(uuid); }
|
||||
void set_data_to_value(data_to_value_t &&lambda_) { this->data_to_value_func_ = lambda_; }
|
||||
void set_data_to_value(data_to_value_t &&lambda) { this->data_to_value_func_ = lambda; }
|
||||
void set_enable_notify(bool notify) { this->notify_ = notify; }
|
||||
uint16_t handle;
|
||||
|
||||
protected:
|
||||
uint32_t hash_base() override;
|
||||
float parse_data(uint8_t *value, uint16_t value_len);
|
||||
float parse_data_(uint8_t *value, uint16_t value_len);
|
||||
optional<data_to_value_t> data_to_value_func_{};
|
||||
bool notify_;
|
||||
espbt::ESPBTUUID service_uuid_;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/application.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#ifdef USE_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace ble_client {
|
||||
@@ -21,10 +21,10 @@ void BLEClientSwitch::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_i
|
||||
this->publish_state(this->parent_->enabled);
|
||||
break;
|
||||
case ESP_GATTC_OPEN_EVT:
|
||||
this->node_state = espbt::ClientState::Established;
|
||||
this->node_state = espbt::ClientState::ESTABLISHED;
|
||||
break;
|
||||
case ESP_GATTC_DISCONNECT_EVT:
|
||||
this->node_state = espbt::ClientState::Idle;
|
||||
this->node_state = espbt::ClientState::IDLE;
|
||||
this->publish_state(this->parent_->enabled);
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||
#include "esphome/components/switch/switch.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#ifdef USE_ESP32
|
||||
#include <esp_gattc_api.h>
|
||||
|
||||
namespace esphome {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "ble_presence_device.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#ifdef USE_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace ble_presence {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#ifdef USE_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace ble_presence {
|
||||
@@ -93,8 +93,8 @@ class BLEPresenceDevice : public binary_sensor::BinarySensorInitiallyOff,
|
||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||
|
||||
protected:
|
||||
enum MATCH_TYPE { MATCH_BY_MAC_ADDRESS, MATCH_BY_SERVICE_UUID, MATCH_BY_IBEACON_UUID };
|
||||
MATCH_TYPE match_by_;
|
||||
enum MatchType { MATCH_BY_MAC_ADDRESS, MATCH_BY_SERVICE_UUID, MATCH_BY_IBEACON_UUID };
|
||||
MatchType match_by_;
|
||||
|
||||
bool found_{false};
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "ble_rssi_sensor.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#ifdef USE_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace ble_rssi {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#ifdef USE_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace ble_rssi {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "ble_scanner.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#ifdef USE_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace ble_scanner {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||
#include "esphome/components/text_sensor/text_sensor.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#ifdef USE_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace ble_scanner {
|
||||
@@ -15,7 +15,7 @@ namespace ble_scanner {
|
||||
class BLEScanner : public text_sensor::TextSensor, public esp32_ble_tracker::ESPBTDeviceListener, public Component {
|
||||
public:
|
||||
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override {
|
||||
this->publish_state("{\"timestamp\":" + to_string(::time(NULL)) +
|
||||
this->publish_state("{\"timestamp\":" + to_string(::time(nullptr)) +
|
||||
","
|
||||
"\"address\":\"" +
|
||||
device.address_str() +
|
||||
|
||||
@@ -113,14 +113,14 @@ void BME280Component::setup() {
|
||||
this->calibration_.h5 = read_u8_(BME280_REGISTER_DIG_H5 + 1) << 4 | (read_u8_(BME280_REGISTER_DIG_H5) >> 4);
|
||||
this->calibration_.h6 = read_u8_(BME280_REGISTER_DIG_H6);
|
||||
|
||||
uint8_t humid_register = 0;
|
||||
if (!this->read_byte(BME280_REGISTER_CONTROLHUMID, &humid_register)) {
|
||||
uint8_t humid_control_val = 0;
|
||||
if (!this->read_byte(BME280_REGISTER_CONTROLHUMID, &humid_control_val)) {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
humid_register &= ~0b00000111;
|
||||
humid_register |= this->humidity_oversampling_ & 0b111;
|
||||
if (!this->write_byte(BME280_REGISTER_CONTROLHUMID, humid_register)) {
|
||||
humid_control_val &= ~0b00000111;
|
||||
humid_control_val |= this->humidity_oversampling_ & 0b111;
|
||||
if (!this->write_byte(BME280_REGISTER_CONTROLHUMID, humid_control_val)) {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
@@ -169,11 +169,11 @@ inline uint8_t oversampling_to_time(BME280Oversampling over_sampling) { return (
|
||||
void BME280Component::update() {
|
||||
// Enable sensor
|
||||
ESP_LOGV(TAG, "Sending conversion request...");
|
||||
uint8_t meas_register = 0;
|
||||
meas_register |= (this->temperature_oversampling_ & 0b111) << 5;
|
||||
meas_register |= (this->pressure_oversampling_ & 0b111) << 2;
|
||||
meas_register |= BME280_MODE_FORCED;
|
||||
if (!this->write_byte(BME280_REGISTER_CONTROL, meas_register)) {
|
||||
uint8_t meas_value = 0;
|
||||
meas_value |= (this->temperature_oversampling_ & 0b111) << 5;
|
||||
meas_value |= (this->pressure_oversampling_ & 0b111) << 2;
|
||||
meas_value |= BME280_MODE_FORCED;
|
||||
if (!this->write_byte(BME280_REGISTER_CONTROL, meas_value)) {
|
||||
this->status_set_warning();
|
||||
return;
|
||||
}
|
||||
@@ -186,7 +186,7 @@ void BME280Component::update() {
|
||||
this->set_timeout("data", uint32_t(ceilf(meas_time)), [this]() {
|
||||
int32_t t_fine = 0;
|
||||
float temperature = this->read_temperature_(&t_fine);
|
||||
if (isnan(temperature)) {
|
||||
if (std::isnan(temperature)) {
|
||||
ESP_LOGW(TAG, "Invalid temperature, cannot read pressure & humidity values.");
|
||||
this->status_set_warning();
|
||||
return;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "bme680.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/hal.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace bme680 {
|
||||
|
||||
@@ -381,7 +381,7 @@ void BME680BSECComponent::delay_ms(uint32_t period) {
|
||||
|
||||
void BME680BSECComponent::load_state_() {
|
||||
uint32_t hash = fnv1_hash("bme680_bsec_state_" + to_string(this->address_));
|
||||
this->bsec_state_ = global_preferences.make_preference<uint8_t[BSEC_MAX_STATE_BLOB_SIZE]>(hash, true);
|
||||
this->bsec_state_ = global_preferences->make_preference<uint8_t[BSEC_MAX_STATE_BLOB_SIZE]>(hash, true);
|
||||
|
||||
uint8_t state[BSEC_MAX_STATE_BLOB_SIZE];
|
||||
if (this->bsec_state_.load(&state)) {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "esphome/components/text_sensor/text_sensor.h"
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
#include "esphome/core/preferences.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include <map>
|
||||
|
||||
#ifdef USE_BSEC
|
||||
|
||||
@@ -123,11 +123,11 @@ inline uint8_t oversampling_to_time(BMP280Oversampling over_sampling) { return (
|
||||
void BMP280Component::update() {
|
||||
// Enable sensor
|
||||
ESP_LOGV(TAG, "Sending conversion request...");
|
||||
uint8_t meas_register = 0;
|
||||
meas_register |= (this->temperature_oversampling_ & 0b111) << 5;
|
||||
meas_register |= (this->pressure_oversampling_ & 0b111) << 2;
|
||||
meas_register |= 0b01; // Forced mode
|
||||
if (!this->write_byte(BMP280_REGISTER_CONTROL, meas_register)) {
|
||||
uint8_t meas_value = 0;
|
||||
meas_value |= (this->temperature_oversampling_ & 0b111) << 5;
|
||||
meas_value |= (this->pressure_oversampling_ & 0b111) << 2;
|
||||
meas_value |= 0b01; // Forced mode
|
||||
if (!this->write_byte(BMP280_REGISTER_CONTROL, meas_value)) {
|
||||
this->status_set_warning();
|
||||
return;
|
||||
}
|
||||
@@ -139,7 +139,7 @@ void BMP280Component::update() {
|
||||
this->set_timeout("data", uint32_t(ceilf(meas_time)), [this]() {
|
||||
int32_t t_fine = 0;
|
||||
float temperature = this->read_temperature_(&t_fine);
|
||||
if (isnan(temperature)) {
|
||||
if (std::isnan(temperature)) {
|
||||
ESP_LOGW(TAG, "Invalid temperature, cannot read pressure values.");
|
||||
this->status_set_warning();
|
||||
return;
|
||||
|
||||
@@ -3,7 +3,7 @@ import esphome.config_validation as cv
|
||||
from esphome.components import web_server_base
|
||||
from esphome.components.web_server_base import CONF_WEB_SERVER_BASE_ID
|
||||
from esphome.const import CONF_ID
|
||||
from esphome.core import coroutine_with_priority
|
||||
from esphome.core import coroutine_with_priority, CORE
|
||||
|
||||
AUTO_LOAD = ["web_server_base"]
|
||||
DEPENDENCIES = ["wifi"]
|
||||
@@ -12,14 +12,17 @@ CODEOWNERS = ["@OttoWinter"]
|
||||
captive_portal_ns = cg.esphome_ns.namespace("captive_portal")
|
||||
CaptivePortal = captive_portal_ns.class_("CaptivePortal", cg.Component)
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(CaptivePortal),
|
||||
cv.GenerateID(CONF_WEB_SERVER_BASE_ID): cv.use_id(
|
||||
web_server_base.WebServerBase
|
||||
),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(CaptivePortal),
|
||||
cv.GenerateID(CONF_WEB_SERVER_BASE_ID): cv.use_id(
|
||||
web_server_base.WebServerBase
|
||||
),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA),
|
||||
cv.only_with_arduino,
|
||||
)
|
||||
|
||||
|
||||
@coroutine_with_priority(64.0)
|
||||
@@ -29,3 +32,7 @@ async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID], paren)
|
||||
await cg.register_component(var, config)
|
||||
cg.add_define("USE_CAPTIVE_PORTAL")
|
||||
|
||||
if CORE.is_esp32:
|
||||
cg.add_library("DNSServer", None)
|
||||
cg.add_library("WiFi", None)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#ifdef USE_ARDUINO
|
||||
|
||||
#include "captive_portal.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/application.h"
|
||||
@@ -76,16 +78,16 @@ void CaptivePortal::start() {
|
||||
this->base_->add_ota_handler();
|
||||
}
|
||||
|
||||
this->dns_server_ = new DNSServer();
|
||||
this->dns_server_ = make_unique<DNSServer>();
|
||||
this->dns_server_->setErrorReplyCode(DNSReplyCode::NoError);
|
||||
IPAddress ip = wifi::global_wifi_component->wifi_soft_ap_ip();
|
||||
this->dns_server_->start(53, "*", ip);
|
||||
network::IPAddress ip = wifi::global_wifi_component->wifi_soft_ap_ip();
|
||||
this->dns_server_->start(53, "*", (uint32_t) ip);
|
||||
|
||||
this->base_->get_server()->onNotFound([this](AsyncWebServerRequest *req) {
|
||||
bool not_found = false;
|
||||
if (!this->active_) {
|
||||
not_found = true;
|
||||
} else if (req->host() == wifi::global_wifi_component->wifi_soft_ap_ip().toString()) {
|
||||
} else if (req->host().c_str() == wifi::global_wifi_component->wifi_soft_ap_ip().str()) {
|
||||
not_found = true;
|
||||
}
|
||||
|
||||
@@ -94,8 +96,8 @@ void CaptivePortal::start() {
|
||||
return;
|
||||
}
|
||||
|
||||
auto url = "http://" + wifi::global_wifi_component->wifi_soft_ap_ip().toString();
|
||||
req->redirect(url);
|
||||
auto url = "http://" + wifi::global_wifi_component->wifi_soft_ap_ip().str();
|
||||
req->redirect(url.c_str());
|
||||
});
|
||||
|
||||
this->initialized_ = true;
|
||||
@@ -151,3 +153,5 @@ CaptivePortal *global_captive_portal = nullptr; // NOLINT(cppcoreguidelines-avo
|
||||
|
||||
} // namespace captive_portal
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_ARDUINO
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_ARDUINO
|
||||
|
||||
#include <memory>
|
||||
#include <DNSServer.h>
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
@@ -26,7 +29,7 @@ class CaptivePortal : public AsyncWebHandler, public Component {
|
||||
this->active_ = false;
|
||||
this->base_->deinit();
|
||||
this->dns_server_->stop();
|
||||
delete this->dns_server_;
|
||||
this->dns_server_ = nullptr;
|
||||
}
|
||||
|
||||
bool canHandle(AsyncWebServerRequest *request) override {
|
||||
@@ -65,10 +68,12 @@ class CaptivePortal : public AsyncWebHandler, public Component {
|
||||
web_server_base::WebServerBase *base_;
|
||||
bool initialized_{false};
|
||||
bool active_{false};
|
||||
DNSServer *dns_server_{nullptr};
|
||||
std::unique_ptr<DNSServer> dns_server_{nullptr};
|
||||
};
|
||||
|
||||
extern CaptivePortal *global_captive_portal; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
|
||||
} // namespace captive_portal
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_ARDUINO
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "ccs811.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/hal.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace ccs811 {
|
||||
@@ -85,8 +86,11 @@ void CCS811Component::setup() {
|
||||
}
|
||||
}
|
||||
void CCS811Component::update() {
|
||||
if (!this->status_has_data_())
|
||||
if (!this->status_has_data_()) {
|
||||
ESP_LOGD(TAG, "Status indicates no data ready!");
|
||||
this->status_set_warning();
|
||||
return;
|
||||
}
|
||||
|
||||
// page 12 - alg result data
|
||||
auto alg_data = this->read_bytes<4>(0x02);
|
||||
@@ -124,12 +128,12 @@ void CCS811Component::send_env_data_() {
|
||||
float humidity = NAN;
|
||||
if (this->humidity_ != nullptr)
|
||||
humidity = this->humidity_->state;
|
||||
if (isnan(humidity) || humidity < 0 || humidity > 100)
|
||||
if (std::isnan(humidity) || humidity < 0 || humidity > 100)
|
||||
humidity = 50;
|
||||
float temperature = NAN;
|
||||
if (this->temperature_ != nullptr)
|
||||
temperature = this->temperature_->state;
|
||||
if (isnan(temperature) || temperature < -25 || temperature > 50)
|
||||
if (std::isnan(temperature) || temperature < -25 || temperature > 50)
|
||||
temperature = 25;
|
||||
// temperature has a 25° offset to allow negative temperatures
|
||||
temperature += 25;
|
||||
|
||||
@@ -1,27 +1,41 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.cpp_helpers import setup_entity
|
||||
from esphome import automation
|
||||
from esphome.components import mqtt
|
||||
from esphome.const import (
|
||||
CONF_ACTION_STATE_TOPIC,
|
||||
CONF_AWAY,
|
||||
CONF_AWAY_COMMAND_TOPIC,
|
||||
CONF_AWAY_STATE_TOPIC,
|
||||
CONF_CURRENT_TEMPERATURE_STATE_TOPIC,
|
||||
CONF_CUSTOM_FAN_MODE,
|
||||
CONF_CUSTOM_PRESET,
|
||||
CONF_DISABLED_BY_DEFAULT,
|
||||
CONF_FAN_MODE,
|
||||
CONF_FAN_MODE_COMMAND_TOPIC,
|
||||
CONF_FAN_MODE_STATE_TOPIC,
|
||||
CONF_ID,
|
||||
CONF_INTERNAL,
|
||||
CONF_MAX_TEMPERATURE,
|
||||
CONF_MIN_TEMPERATURE,
|
||||
CONF_MODE,
|
||||
CONF_MODE_COMMAND_TOPIC,
|
||||
CONF_MODE_STATE_TOPIC,
|
||||
CONF_PRESET,
|
||||
CONF_SWING_MODE,
|
||||
CONF_SWING_MODE_COMMAND_TOPIC,
|
||||
CONF_SWING_MODE_STATE_TOPIC,
|
||||
CONF_TARGET_TEMPERATURE,
|
||||
CONF_TARGET_TEMPERATURE_COMMAND_TOPIC,
|
||||
CONF_TARGET_TEMPERATURE_STATE_TOPIC,
|
||||
CONF_TARGET_TEMPERATURE_HIGH,
|
||||
CONF_TARGET_TEMPERATURE_HIGH_COMMAND_TOPIC,
|
||||
CONF_TARGET_TEMPERATURE_HIGH_STATE_TOPIC,
|
||||
CONF_TARGET_TEMPERATURE_LOW,
|
||||
CONF_TARGET_TEMPERATURE_LOW_COMMAND_TOPIC,
|
||||
CONF_TARGET_TEMPERATURE_LOW_STATE_TOPIC,
|
||||
CONF_TEMPERATURE_STEP,
|
||||
CONF_VISUAL,
|
||||
CONF_MQTT_ID,
|
||||
CONF_NAME,
|
||||
CONF_FAN_MODE,
|
||||
CONF_SWING_MODE,
|
||||
)
|
||||
from esphome.core import CORE, coroutine_with_priority
|
||||
|
||||
@@ -30,7 +44,7 @@ IS_PLATFORM_COMPONENT = True
|
||||
CODEOWNERS = ["@esphome/core"]
|
||||
climate_ns = cg.esphome_ns.namespace("climate")
|
||||
|
||||
Climate = climate_ns.class_("Climate", cg.Nameable)
|
||||
Climate = climate_ns.class_("Climate", cg.EntityBase)
|
||||
ClimateCall = climate_ns.class_("ClimateCall")
|
||||
ClimateTraits = climate_ns.class_("ClimateTraits")
|
||||
|
||||
@@ -88,7 +102,7 @@ validate_climate_swing_mode = cv.enum(CLIMATE_SWING_MODES, upper=True)
|
||||
# Actions
|
||||
ControlAction = climate_ns.class_("ControlAction", automation.Action)
|
||||
|
||||
CLIMATE_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
|
||||
CLIMATE_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(Climate),
|
||||
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTClimateComponent),
|
||||
@@ -99,16 +113,61 @@ CLIMATE_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).ext
|
||||
cv.Optional(CONF_TEMPERATURE_STEP): cv.temperature,
|
||||
}
|
||||
),
|
||||
# TODO: MQTT topic options
|
||||
cv.Optional(CONF_ACTION_STATE_TOPIC): cv.All(
|
||||
cv.requires_component("mqtt"), cv.publish_topic
|
||||
),
|
||||
cv.Optional(CONF_AWAY_COMMAND_TOPIC): cv.All(
|
||||
cv.requires_component("mqtt"), cv.publish_topic
|
||||
),
|
||||
cv.Optional(CONF_AWAY_STATE_TOPIC): cv.All(
|
||||
cv.requires_component("mqtt"), cv.publish_topic
|
||||
),
|
||||
cv.Optional(CONF_CURRENT_TEMPERATURE_STATE_TOPIC): cv.All(
|
||||
cv.requires_component("mqtt"), cv.publish_topic
|
||||
),
|
||||
cv.Optional(CONF_FAN_MODE_COMMAND_TOPIC): cv.All(
|
||||
cv.requires_component("mqtt"), cv.publish_topic
|
||||
),
|
||||
cv.Optional(CONF_FAN_MODE_STATE_TOPIC): cv.All(
|
||||
cv.requires_component("mqtt"), cv.publish_topic
|
||||
),
|
||||
cv.Optional(CONF_MODE_COMMAND_TOPIC): cv.All(
|
||||
cv.requires_component("mqtt"), cv.publish_topic
|
||||
),
|
||||
cv.Optional(CONF_MODE_STATE_TOPIC): cv.All(
|
||||
cv.requires_component("mqtt"), cv.publish_topic
|
||||
),
|
||||
cv.Optional(CONF_SWING_MODE_COMMAND_TOPIC): cv.All(
|
||||
cv.requires_component("mqtt"), cv.publish_topic
|
||||
),
|
||||
cv.Optional(CONF_SWING_MODE_STATE_TOPIC): cv.All(
|
||||
cv.requires_component("mqtt"), cv.publish_topic
|
||||
),
|
||||
cv.Optional(CONF_TARGET_TEMPERATURE_COMMAND_TOPIC): cv.All(
|
||||
cv.requires_component("mqtt"), cv.publish_topic
|
||||
),
|
||||
cv.Optional(CONF_TARGET_TEMPERATURE_STATE_TOPIC): cv.All(
|
||||
cv.requires_component("mqtt"), cv.publish_topic
|
||||
),
|
||||
cv.Optional(CONF_TARGET_TEMPERATURE_HIGH_COMMAND_TOPIC): cv.All(
|
||||
cv.requires_component("mqtt"), cv.publish_topic
|
||||
),
|
||||
cv.Optional(CONF_TARGET_TEMPERATURE_HIGH_STATE_TOPIC): cv.All(
|
||||
cv.requires_component("mqtt"), cv.publish_topic
|
||||
),
|
||||
cv.Optional(CONF_TARGET_TEMPERATURE_LOW_COMMAND_TOPIC): cv.All(
|
||||
cv.requires_component("mqtt"), cv.publish_topic
|
||||
),
|
||||
cv.Optional(CONF_TARGET_TEMPERATURE_LOW_STATE_TOPIC): cv.All(
|
||||
cv.requires_component("mqtt"), cv.publish_topic
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def setup_climate_core_(var, config):
|
||||
cg.add(var.set_name(config[CONF_NAME]))
|
||||
cg.add(var.set_disabled_by_default(config[CONF_DISABLED_BY_DEFAULT]))
|
||||
if CONF_INTERNAL in config:
|
||||
cg.add(var.set_internal(config[CONF_INTERNAL]))
|
||||
await setup_entity(var, config)
|
||||
|
||||
visual = config[CONF_VISUAL]
|
||||
if CONF_MIN_TEMPERATURE in visual:
|
||||
cg.add(var.set_visual_min_temperature_override(visual[CONF_MIN_TEMPERATURE]))
|
||||
@@ -121,6 +180,82 @@ async def setup_climate_core_(var, config):
|
||||
mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var)
|
||||
await mqtt.register_mqtt_component(mqtt_, config)
|
||||
|
||||
if CONF_ACTION_STATE_TOPIC in config:
|
||||
cg.add(mqtt_.set_custom_action_state_topic(config[CONF_ACTION_STATE_TOPIC]))
|
||||
if CONF_AWAY_COMMAND_TOPIC in config:
|
||||
cg.add(mqtt_.set_custom_away_command_topic(config[CONF_AWAY_COMMAND_TOPIC]))
|
||||
if CONF_AWAY_STATE_TOPIC in config:
|
||||
cg.add(mqtt_.set_custom_away_state_topic(config[CONF_AWAY_STATE_TOPIC]))
|
||||
if CONF_CURRENT_TEMPERATURE_STATE_TOPIC in config:
|
||||
cg.add(
|
||||
mqtt_.set_custom_current_temperature_state_topic(
|
||||
config[CONF_CURRENT_TEMPERATURE_STATE_TOPIC]
|
||||
)
|
||||
)
|
||||
if CONF_FAN_MODE_COMMAND_TOPIC in config:
|
||||
cg.add(
|
||||
mqtt_.set_custom_fan_mode_command_topic(
|
||||
config[CONF_FAN_MODE_COMMAND_TOPIC]
|
||||
)
|
||||
)
|
||||
if CONF_FAN_MODE_STATE_TOPIC in config:
|
||||
cg.add(
|
||||
mqtt_.set_custom_fan_mode_state_topic(config[CONF_FAN_MODE_STATE_TOPIC])
|
||||
)
|
||||
if CONF_MODE_COMMAND_TOPIC in config:
|
||||
cg.add(mqtt_.set_custom_mode_command_topic(config[CONF_MODE_COMMAND_TOPIC]))
|
||||
if CONF_MODE_STATE_TOPIC in config:
|
||||
cg.add(mqtt_.set_custom_state_topic(config[CONF_MODE_STATE_TOPIC]))
|
||||
|
||||
if CONF_SWING_MODE_COMMAND_TOPIC in config:
|
||||
cg.add(
|
||||
mqtt_.set_custom_swing_mode_command_topic(
|
||||
config[CONF_SWING_MODE_COMMAND_TOPIC]
|
||||
)
|
||||
)
|
||||
if CONF_SWING_MODE_STATE_TOPIC in config:
|
||||
cg.add(
|
||||
mqtt_.set_custom_swing_mode_state_topic(
|
||||
config[CONF_SWING_MODE_STATE_TOPIC]
|
||||
)
|
||||
)
|
||||
if CONF_TARGET_TEMPERATURE_COMMAND_TOPIC in config:
|
||||
cg.add(
|
||||
mqtt_.set_custom_target_temperature_command_topic(
|
||||
config[CONF_TARGET_TEMPERATURE_COMMAND_TOPIC]
|
||||
)
|
||||
)
|
||||
if CONF_TARGET_TEMPERATURE_STATE_TOPIC in config:
|
||||
cg.add(
|
||||
mqtt_.set_custom_target_temperature_state_topic(
|
||||
config[CONF_TARGET_TEMPERATURE_STATE_TOPIC]
|
||||
)
|
||||
)
|
||||
if CONF_TARGET_TEMPERATURE_HIGH_COMMAND_TOPIC in config:
|
||||
cg.add(
|
||||
mqtt_.set_custom_target_temperature_high_command_topic(
|
||||
config[CONF_TARGET_TEMPERATURE_HIGH_COMMAND_TOPIC]
|
||||
)
|
||||
)
|
||||
if CONF_TARGET_TEMPERATURE_HIGH_STATE_TOPIC in config:
|
||||
cg.add(
|
||||
mqtt_.set_custom_target_temperature_high_state_topic(
|
||||
config[CONF_TARGET_TEMPERATURE_HIGH_STATE_TOPIC]
|
||||
)
|
||||
)
|
||||
if CONF_TARGET_TEMPERATURE_LOW_COMMAND_TOPIC in config:
|
||||
cg.add(
|
||||
mqtt_.set_custom_target_temperature_low_command_topic(
|
||||
config[CONF_TARGET_TEMPERATURE_LOW_COMMAND_TOPIC]
|
||||
)
|
||||
)
|
||||
if CONF_TARGET_TEMPERATURE_LOW_STATE_TOPIC in config:
|
||||
cg.add(
|
||||
mqtt_.set_custom_target_temperature_state_topic(
|
||||
config[CONF_TARGET_TEMPERATURE_LOW_STATE_TOPIC]
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
async def register_climate(var, config):
|
||||
if not CORE.has_id(config[CONF_ID]):
|
||||
|
||||
@@ -9,8 +9,8 @@ void ClimateCall::perform() {
|
||||
ESP_LOGD(TAG, "'%s' - Setting", this->parent_->get_name().c_str());
|
||||
this->validate_();
|
||||
if (this->mode_.has_value()) {
|
||||
const char *mode_s = climate_mode_to_string(*this->mode_);
|
||||
ESP_LOGD(TAG, " Mode: %s", mode_s);
|
||||
const LogString *mode_s = climate_mode_to_string(*this->mode_);
|
||||
ESP_LOGD(TAG, " Mode: %s", LOG_STR_ARG(mode_s));
|
||||
}
|
||||
if (this->custom_fan_mode_.has_value()) {
|
||||
this->fan_mode_.reset();
|
||||
@@ -18,8 +18,8 @@ void ClimateCall::perform() {
|
||||
}
|
||||
if (this->fan_mode_.has_value()) {
|
||||
this->custom_fan_mode_.reset();
|
||||
const char *fan_mode_s = climate_fan_mode_to_string(*this->fan_mode_);
|
||||
ESP_LOGD(TAG, " Fan: %s", fan_mode_s);
|
||||
const LogString *fan_mode_s = climate_fan_mode_to_string(*this->fan_mode_);
|
||||
ESP_LOGD(TAG, " Fan: %s", LOG_STR_ARG(fan_mode_s));
|
||||
}
|
||||
if (this->custom_preset_.has_value()) {
|
||||
this->preset_.reset();
|
||||
@@ -27,12 +27,12 @@ void ClimateCall::perform() {
|
||||
}
|
||||
if (this->preset_.has_value()) {
|
||||
this->custom_preset_.reset();
|
||||
const char *preset_s = climate_preset_to_string(*this->preset_);
|
||||
ESP_LOGD(TAG, " Preset: %s", preset_s);
|
||||
const LogString *preset_s = climate_preset_to_string(*this->preset_);
|
||||
ESP_LOGD(TAG, " Preset: %s", LOG_STR_ARG(preset_s));
|
||||
}
|
||||
if (this->swing_mode_.has_value()) {
|
||||
const char *swing_mode_s = climate_swing_mode_to_string(*this->swing_mode_);
|
||||
ESP_LOGD(TAG, " Swing: %s", swing_mode_s);
|
||||
const LogString *swing_mode_s = climate_swing_mode_to_string(*this->swing_mode_);
|
||||
ESP_LOGD(TAG, " Swing: %s", LOG_STR_ARG(swing_mode_s));
|
||||
}
|
||||
if (this->target_temperature_.has_value()) {
|
||||
ESP_LOGD(TAG, " Target Temperature: %.2f", *this->target_temperature_);
|
||||
@@ -50,7 +50,7 @@ void ClimateCall::validate_() {
|
||||
if (this->mode_.has_value()) {
|
||||
auto mode = *this->mode_;
|
||||
if (!traits.supports_mode(mode)) {
|
||||
ESP_LOGW(TAG, " Mode %s is not supported by this device!", climate_mode_to_string(mode));
|
||||
ESP_LOGW(TAG, " Mode %s is not supported by this device!", LOG_STR_ARG(climate_mode_to_string(mode)));
|
||||
this->mode_.reset();
|
||||
}
|
||||
}
|
||||
@@ -63,7 +63,8 @@ void ClimateCall::validate_() {
|
||||
} else if (this->fan_mode_.has_value()) {
|
||||
auto fan_mode = *this->fan_mode_;
|
||||
if (!traits.supports_fan_mode(fan_mode)) {
|
||||
ESP_LOGW(TAG, " Fan Mode %s is not supported by this device!", climate_fan_mode_to_string(fan_mode));
|
||||
ESP_LOGW(TAG, " Fan Mode %s is not supported by this device!",
|
||||
LOG_STR_ARG(climate_fan_mode_to_string(fan_mode)));
|
||||
this->fan_mode_.reset();
|
||||
}
|
||||
}
|
||||
@@ -76,14 +77,15 @@ void ClimateCall::validate_() {
|
||||
} else if (this->preset_.has_value()) {
|
||||
auto preset = *this->preset_;
|
||||
if (!traits.supports_preset(preset)) {
|
||||
ESP_LOGW(TAG, " Preset %s is not supported by this device!", climate_preset_to_string(preset));
|
||||
ESP_LOGW(TAG, " Preset %s is not supported by this device!", LOG_STR_ARG(climate_preset_to_string(preset)));
|
||||
this->preset_.reset();
|
||||
}
|
||||
}
|
||||
if (this->swing_mode_.has_value()) {
|
||||
auto swing_mode = *this->swing_mode_;
|
||||
if (!traits.supports_swing_mode(swing_mode)) {
|
||||
ESP_LOGW(TAG, " Swing Mode %s is not supported by this device!", climate_swing_mode_to_string(swing_mode));
|
||||
ESP_LOGW(TAG, " Swing Mode %s is not supported by this device!",
|
||||
LOG_STR_ARG(climate_swing_mode_to_string(swing_mode)));
|
||||
this->swing_mode_.reset();
|
||||
}
|
||||
}
|
||||
@@ -93,7 +95,7 @@ void ClimateCall::validate_() {
|
||||
ESP_LOGW(TAG, " Cannot set target temperature for climate device "
|
||||
"with two-point target temperature!");
|
||||
this->target_temperature_.reset();
|
||||
} else if (isnan(target)) {
|
||||
} else if (std::isnan(target)) {
|
||||
ESP_LOGW(TAG, " Target temperature must not be NAN!");
|
||||
this->target_temperature_.reset();
|
||||
}
|
||||
@@ -105,11 +107,11 @@ void ClimateCall::validate_() {
|
||||
this->target_temperature_high_.reset();
|
||||
}
|
||||
}
|
||||
if (this->target_temperature_low_.has_value() && isnan(*this->target_temperature_low_)) {
|
||||
if (this->target_temperature_low_.has_value() && std::isnan(*this->target_temperature_low_)) {
|
||||
ESP_LOGW(TAG, " Target temperature low must not be NAN!");
|
||||
this->target_temperature_low_.reset();
|
||||
}
|
||||
if (this->target_temperature_high_.has_value() && isnan(*this->target_temperature_high_)) {
|
||||
if (this->target_temperature_high_.has_value() && std::isnan(*this->target_temperature_high_)) {
|
||||
ESP_LOGW(TAG, " Target temperature low must not be NAN!");
|
||||
this->target_temperature_high_.reset();
|
||||
}
|
||||
@@ -316,17 +318,23 @@ void Climate::add_on_state_callback(std::function<void()> &&callback) {
|
||||
static const uint32_t RESTORE_STATE_VERSION = 0x848EA6ADUL;
|
||||
|
||||
optional<ClimateDeviceRestoreState> Climate::restore_state_() {
|
||||
this->rtc_ =
|
||||
global_preferences.make_preference<ClimateDeviceRestoreState>(this->get_object_id_hash() ^ RESTORE_STATE_VERSION);
|
||||
this->rtc_ = global_preferences->make_preference<ClimateDeviceRestoreState>(this->get_object_id_hash() ^
|
||||
RESTORE_STATE_VERSION);
|
||||
ClimateDeviceRestoreState recovered{};
|
||||
if (!this->rtc_.load(&recovered))
|
||||
return {};
|
||||
return recovered;
|
||||
}
|
||||
void Climate::save_state_() {
|
||||
#if defined(USE_ESP_IDF) && !defined(CLANG_TIDY)
|
||||
#pragma GCC diagnostic ignored "-Wclass-memaccess"
|
||||
#endif
|
||||
ClimateDeviceRestoreState state{};
|
||||
// initialize as zero to prevent random data on stack triggering erase
|
||||
memset(&state, 0, sizeof(ClimateDeviceRestoreState));
|
||||
#if USE_ESP_IDF && !defined(CLANG_TIDY)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
state.mode = this->mode;
|
||||
auto traits = this->get_traits();
|
||||
@@ -373,24 +381,24 @@ void Climate::publish_state() {
|
||||
ESP_LOGD(TAG, "'%s' - Sending state:", this->name_.c_str());
|
||||
auto traits = this->get_traits();
|
||||
|
||||
ESP_LOGD(TAG, " Mode: %s", climate_mode_to_string(this->mode));
|
||||
ESP_LOGD(TAG, " Mode: %s", LOG_STR_ARG(climate_mode_to_string(this->mode)));
|
||||
if (traits.get_supports_action()) {
|
||||
ESP_LOGD(TAG, " Action: %s", climate_action_to_string(this->action));
|
||||
ESP_LOGD(TAG, " Action: %s", LOG_STR_ARG(climate_action_to_string(this->action)));
|
||||
}
|
||||
if (traits.get_supports_fan_modes() && this->fan_mode.has_value()) {
|
||||
ESP_LOGD(TAG, " Fan Mode: %s", climate_fan_mode_to_string(this->fan_mode.value()));
|
||||
ESP_LOGD(TAG, " Fan Mode: %s", LOG_STR_ARG(climate_fan_mode_to_string(this->fan_mode.value())));
|
||||
}
|
||||
if (!traits.get_supported_custom_fan_modes().empty() && this->custom_fan_mode.has_value()) {
|
||||
ESP_LOGD(TAG, " Custom Fan Mode: %s", this->custom_fan_mode.value().c_str());
|
||||
}
|
||||
if (traits.get_supports_presets() && this->preset.has_value()) {
|
||||
ESP_LOGD(TAG, " Preset: %s", climate_preset_to_string(this->preset.value()));
|
||||
ESP_LOGD(TAG, " Preset: %s", LOG_STR_ARG(climate_preset_to_string(this->preset.value())));
|
||||
}
|
||||
if (!traits.get_supported_custom_presets().empty() && this->custom_preset.has_value()) {
|
||||
ESP_LOGD(TAG, " Custom Preset: %s", this->custom_preset.value().c_str());
|
||||
}
|
||||
if (traits.get_supports_swing_modes()) {
|
||||
ESP_LOGD(TAG, " Swing Mode: %s", climate_swing_mode_to_string(this->swing_mode));
|
||||
ESP_LOGD(TAG, " Swing Mode: %s", LOG_STR_ARG(climate_swing_mode_to_string(this->swing_mode)));
|
||||
}
|
||||
if (traits.get_supports_current_temperature()) {
|
||||
ESP_LOGD(TAG, " Current Temperature: %.2f°C", this->current_temperature);
|
||||
@@ -432,7 +440,7 @@ void Climate::set_visual_max_temperature_override(float visual_max_temperature_o
|
||||
void Climate::set_visual_temperature_step_override(float visual_temperature_step_override) {
|
||||
this->visual_temperature_step_override_ = visual_temperature_step_override;
|
||||
}
|
||||
Climate::Climate(const std::string &name) : Nameable(name) {}
|
||||
Climate::Climate(const std::string &name) : EntityBase(name) {}
|
||||
Climate::Climate() : Climate("") {}
|
||||
ClimateCall Climate::make_call() { return ClimateCall(this); }
|
||||
|
||||
@@ -534,12 +542,12 @@ void Climate::dump_traits_(const char *tag) {
|
||||
if (!traits.get_supported_modes().empty()) {
|
||||
ESP_LOGCONFIG(tag, " [x] Supported modes:");
|
||||
for (ClimateMode m : traits.get_supported_modes())
|
||||
ESP_LOGCONFIG(tag, " - %s", climate_mode_to_string(m));
|
||||
ESP_LOGCONFIG(tag, " - %s", LOG_STR_ARG(climate_mode_to_string(m)));
|
||||
}
|
||||
if (!traits.get_supported_fan_modes().empty()) {
|
||||
ESP_LOGCONFIG(tag, " [x] Supported fan modes:");
|
||||
for (ClimateFanMode m : traits.get_supported_fan_modes())
|
||||
ESP_LOGCONFIG(tag, " - %s", climate_fan_mode_to_string(m));
|
||||
ESP_LOGCONFIG(tag, " - %s", LOG_STR_ARG(climate_fan_mode_to_string(m)));
|
||||
}
|
||||
if (!traits.get_supported_custom_fan_modes().empty()) {
|
||||
ESP_LOGCONFIG(tag, " [x] Supported custom fan modes:");
|
||||
@@ -549,7 +557,7 @@ void Climate::dump_traits_(const char *tag) {
|
||||
if (!traits.get_supported_presets().empty()) {
|
||||
ESP_LOGCONFIG(tag, " [x] Supported presets:");
|
||||
for (ClimatePreset p : traits.get_supported_presets())
|
||||
ESP_LOGCONFIG(tag, " - %s", climate_preset_to_string(p));
|
||||
ESP_LOGCONFIG(tag, " - %s", LOG_STR_ARG(climate_preset_to_string(p)));
|
||||
}
|
||||
if (!traits.get_supported_custom_presets().empty()) {
|
||||
ESP_LOGCONFIG(tag, " [x] Supported custom presets:");
|
||||
@@ -559,7 +567,7 @@ void Climate::dump_traits_(const char *tag) {
|
||||
if (!traits.get_supported_swing_modes().empty()) {
|
||||
ESP_LOGCONFIG(tag, " [x] Supported swing modes:");
|
||||
for (ClimateSwingMode m : traits.get_supported_swing_modes())
|
||||
ESP_LOGCONFIG(tag, " - %s", climate_swing_mode_to_string(m));
|
||||
ESP_LOGCONFIG(tag, " - %s", LOG_STR_ARG(climate_swing_mode_to_string(m)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/entity_base.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/preferences.h"
|
||||
#include "esphome/core/log.h"
|
||||
@@ -12,7 +13,7 @@ namespace climate {
|
||||
|
||||
#define LOG_CLIMATE(prefix, type, obj) \
|
||||
if ((obj) != nullptr) { \
|
||||
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, type, (obj)->get_name().c_str()); \
|
||||
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \
|
||||
}
|
||||
|
||||
class Climate;
|
||||
@@ -163,7 +164,7 @@ struct ClimateDeviceRestoreState {
|
||||
* mode etc). These are read-only for the user and rw for integrations. The reason these are public
|
||||
* is for simple access to them from lambdas `if (id(my_climate).mode == climate::CLIMATE_MODE_HEAT_COOL) ...`
|
||||
*/
|
||||
class Climate : public Nameable {
|
||||
class Climate : public EntityBase {
|
||||
public:
|
||||
/// Construct a climate device with empty name (will be set later).
|
||||
Climate();
|
||||
|
||||
@@ -3,105 +3,105 @@
|
||||
namespace esphome {
|
||||
namespace climate {
|
||||
|
||||
const char *climate_mode_to_string(ClimateMode mode) {
|
||||
const LogString *climate_mode_to_string(ClimateMode mode) {
|
||||
switch (mode) {
|
||||
case CLIMATE_MODE_OFF:
|
||||
return "OFF";
|
||||
case CLIMATE_MODE_AUTO:
|
||||
return "AUTO";
|
||||
case CLIMATE_MODE_COOL:
|
||||
return "COOL";
|
||||
case CLIMATE_MODE_HEAT:
|
||||
return "HEAT";
|
||||
case CLIMATE_MODE_FAN_ONLY:
|
||||
return "FAN_ONLY";
|
||||
case CLIMATE_MODE_DRY:
|
||||
return "DRY";
|
||||
return LOG_STR("OFF");
|
||||
case CLIMATE_MODE_HEAT_COOL:
|
||||
return "HEAT_COOL";
|
||||
return LOG_STR("HEAT_COOL");
|
||||
case CLIMATE_MODE_AUTO:
|
||||
return LOG_STR("AUTO");
|
||||
case CLIMATE_MODE_COOL:
|
||||
return LOG_STR("COOL");
|
||||
case CLIMATE_MODE_HEAT:
|
||||
return LOG_STR("HEAT");
|
||||
case CLIMATE_MODE_FAN_ONLY:
|
||||
return LOG_STR("FAN_ONLY");
|
||||
case CLIMATE_MODE_DRY:
|
||||
return LOG_STR("DRY");
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
return LOG_STR("UNKNOWN");
|
||||
}
|
||||
}
|
||||
const char *climate_action_to_string(ClimateAction action) {
|
||||
const LogString *climate_action_to_string(ClimateAction action) {
|
||||
switch (action) {
|
||||
case CLIMATE_ACTION_OFF:
|
||||
return "OFF";
|
||||
return LOG_STR("OFF");
|
||||
case CLIMATE_ACTION_COOLING:
|
||||
return "COOLING";
|
||||
return LOG_STR("COOLING");
|
||||
case CLIMATE_ACTION_HEATING:
|
||||
return "HEATING";
|
||||
return LOG_STR("HEATING");
|
||||
case CLIMATE_ACTION_IDLE:
|
||||
return "IDLE";
|
||||
return LOG_STR("IDLE");
|
||||
case CLIMATE_ACTION_DRYING:
|
||||
return "DRYING";
|
||||
return LOG_STR("DRYING");
|
||||
case CLIMATE_ACTION_FAN:
|
||||
return "FAN";
|
||||
return LOG_STR("FAN");
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
return LOG_STR("UNKNOWN");
|
||||
}
|
||||
}
|
||||
|
||||
const char *climate_fan_mode_to_string(ClimateFanMode fan_mode) {
|
||||
const LogString *climate_fan_mode_to_string(ClimateFanMode fan_mode) {
|
||||
switch (fan_mode) {
|
||||
case climate::CLIMATE_FAN_ON:
|
||||
return "ON";
|
||||
return LOG_STR("ON");
|
||||
case climate::CLIMATE_FAN_OFF:
|
||||
return "OFF";
|
||||
return LOG_STR("OFF");
|
||||
case climate::CLIMATE_FAN_AUTO:
|
||||
return "AUTO";
|
||||
return LOG_STR("AUTO");
|
||||
case climate::CLIMATE_FAN_LOW:
|
||||
return "LOW";
|
||||
return LOG_STR("LOW");
|
||||
case climate::CLIMATE_FAN_MEDIUM:
|
||||
return "MEDIUM";
|
||||
return LOG_STR("MEDIUM");
|
||||
case climate::CLIMATE_FAN_HIGH:
|
||||
return "HIGH";
|
||||
return LOG_STR("HIGH");
|
||||
case climate::CLIMATE_FAN_MIDDLE:
|
||||
return "MIDDLE";
|
||||
return LOG_STR("MIDDLE");
|
||||
case climate::CLIMATE_FAN_FOCUS:
|
||||
return "FOCUS";
|
||||
return LOG_STR("FOCUS");
|
||||
case climate::CLIMATE_FAN_DIFFUSE:
|
||||
return "DIFFUSE";
|
||||
return LOG_STR("DIFFUSE");
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
return LOG_STR("UNKNOWN");
|
||||
}
|
||||
}
|
||||
|
||||
const char *climate_swing_mode_to_string(ClimateSwingMode swing_mode) {
|
||||
const LogString *climate_swing_mode_to_string(ClimateSwingMode swing_mode) {
|
||||
switch (swing_mode) {
|
||||
case climate::CLIMATE_SWING_OFF:
|
||||
return "OFF";
|
||||
return LOG_STR("OFF");
|
||||
case climate::CLIMATE_SWING_BOTH:
|
||||
return "BOTH";
|
||||
return LOG_STR("BOTH");
|
||||
case climate::CLIMATE_SWING_VERTICAL:
|
||||
return "VERTICAL";
|
||||
return LOG_STR("VERTICAL");
|
||||
case climate::CLIMATE_SWING_HORIZONTAL:
|
||||
return "HORIZONTAL";
|
||||
return LOG_STR("HORIZONTAL");
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
return LOG_STR("UNKNOWN");
|
||||
}
|
||||
}
|
||||
|
||||
const char *climate_preset_to_string(ClimatePreset preset) {
|
||||
const LogString *climate_preset_to_string(ClimatePreset preset) {
|
||||
switch (preset) {
|
||||
case climate::CLIMATE_PRESET_NONE:
|
||||
return "NONE";
|
||||
return LOG_STR("NONE");
|
||||
case climate::CLIMATE_PRESET_HOME:
|
||||
return "HOME";
|
||||
return LOG_STR("HOME");
|
||||
case climate::CLIMATE_PRESET_ECO:
|
||||
return "ECO";
|
||||
return LOG_STR("ECO");
|
||||
case climate::CLIMATE_PRESET_AWAY:
|
||||
return "AWAY";
|
||||
return LOG_STR("AWAY");
|
||||
case climate::CLIMATE_PRESET_BOOST:
|
||||
return "BOOST";
|
||||
return LOG_STR("BOOST");
|
||||
case climate::CLIMATE_PRESET_COMFORT:
|
||||
return "COMFORT";
|
||||
return LOG_STR("COMFORT");
|
||||
case climate::CLIMATE_PRESET_SLEEP:
|
||||
return "SLEEP";
|
||||
return LOG_STR("SLEEP");
|
||||
case climate::CLIMATE_PRESET_ACTIVITY:
|
||||
return "ACTIVITY";
|
||||
return LOG_STR("ACTIVITY");
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
return LOG_STR("UNKNOWN");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace climate {
|
||||
@@ -96,19 +97,19 @@ enum ClimatePreset : uint8_t {
|
||||
};
|
||||
|
||||
/// Convert the given ClimateMode to a human-readable string.
|
||||
const char *climate_mode_to_string(ClimateMode mode);
|
||||
const LogString *climate_mode_to_string(ClimateMode mode);
|
||||
|
||||
/// Convert the given ClimateAction to a human-readable string.
|
||||
const char *climate_action_to_string(ClimateAction action);
|
||||
const LogString *climate_action_to_string(ClimateAction action);
|
||||
|
||||
/// Convert the given ClimateFanMode to a human-readable string.
|
||||
const char *climate_fan_mode_to_string(ClimateFanMode mode);
|
||||
const LogString *climate_fan_mode_to_string(ClimateFanMode mode);
|
||||
|
||||
/// Convert the given ClimateSwingMode to a human-readable string.
|
||||
const char *climate_swing_mode_to_string(ClimateSwingMode mode);
|
||||
const LogString *climate_swing_mode_to_string(ClimateSwingMode mode);
|
||||
|
||||
/// Convert the given ClimateSwingMode to a human-readable string.
|
||||
const char *climate_preset_to_string(ClimatePreset preset);
|
||||
const LogString *climate_preset_to_string(ClimatePreset preset);
|
||||
|
||||
} // namespace climate
|
||||
} // namespace esphome
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "climate_traits.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include <cstdio>
|
||||
|
||||
namespace esphome {
|
||||
namespace climate {
|
||||
|
||||
@@ -52,7 +52,7 @@ void ClimateIR::setup() {
|
||||
this->swing_mode = climate::CLIMATE_SWING_OFF;
|
||||
}
|
||||
// Never send nan to HA
|
||||
if (isnan(this->target_temperature))
|
||||
if (std::isnan(this->target_temperature))
|
||||
this->target_temperature = 24;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,18 +4,21 @@ from esphome import automation
|
||||
from esphome.automation import maybe_simple_id, Condition
|
||||
from esphome.components import mqtt
|
||||
from esphome.const import (
|
||||
CONF_DISABLED_BY_DEFAULT,
|
||||
CONF_ID,
|
||||
CONF_INTERNAL,
|
||||
CONF_DEVICE_CLASS,
|
||||
CONF_STATE,
|
||||
CONF_POSITION,
|
||||
CONF_POSITION_COMMAND_TOPIC,
|
||||
CONF_POSITION_STATE_TOPIC,
|
||||
CONF_TILT,
|
||||
CONF_TILT_COMMAND_TOPIC,
|
||||
CONF_TILT_STATE_TOPIC,
|
||||
CONF_STOP,
|
||||
CONF_MQTT_ID,
|
||||
CONF_NAME,
|
||||
CONF_TRIGGER_ID,
|
||||
)
|
||||
from esphome.core import CORE, coroutine_with_priority
|
||||
from esphome.cpp_helpers import setup_entity
|
||||
|
||||
IS_PLATFORM_COMPONENT = True
|
||||
|
||||
@@ -36,7 +39,7 @@ DEVICE_CLASSES = [
|
||||
|
||||
cover_ns = cg.esphome_ns.namespace("cover")
|
||||
|
||||
Cover = cover_ns.class_("Cover", cg.Nameable)
|
||||
Cover = cover_ns.class_("Cover", cg.EntityBase)
|
||||
|
||||
COVER_OPEN = cover_ns.COVER_OPEN
|
||||
COVER_CLOSED = cover_ns.COVER_CLOSED
|
||||
@@ -59,33 +62,84 @@ validate_cover_operation = cv.enum(COVER_OPERATIONS, upper=True)
|
||||
OpenAction = cover_ns.class_("OpenAction", automation.Action)
|
||||
CloseAction = cover_ns.class_("CloseAction", automation.Action)
|
||||
StopAction = cover_ns.class_("StopAction", automation.Action)
|
||||
ToggleAction = cover_ns.class_("ToggleAction", automation.Action)
|
||||
ControlAction = cover_ns.class_("ControlAction", automation.Action)
|
||||
CoverPublishAction = cover_ns.class_("CoverPublishAction", automation.Action)
|
||||
CoverIsOpenCondition = cover_ns.class_("CoverIsOpenCondition", Condition)
|
||||
CoverIsClosedCondition = cover_ns.class_("CoverIsClosedCondition", Condition)
|
||||
|
||||
COVER_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
|
||||
# Triggers
|
||||
CoverOpenTrigger = cover_ns.class_("CoverOpenTrigger", automation.Trigger.template())
|
||||
CoverClosedTrigger = cover_ns.class_(
|
||||
"CoverClosedTrigger", automation.Trigger.template()
|
||||
)
|
||||
|
||||
CONF_ON_OPEN = "on_open"
|
||||
CONF_ON_CLOSED = "on_closed"
|
||||
|
||||
COVER_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(Cover),
|
||||
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTCoverComponent),
|
||||
cv.Optional(CONF_DEVICE_CLASS): cv.one_of(*DEVICE_CLASSES, lower=True),
|
||||
# TODO: MQTT topic options
|
||||
cv.Optional(CONF_POSITION_COMMAND_TOPIC): cv.All(
|
||||
cv.requires_component("mqtt"), cv.subscribe_topic
|
||||
),
|
||||
cv.Optional(CONF_POSITION_STATE_TOPIC): cv.All(
|
||||
cv.requires_component("mqtt"), cv.subscribe_topic
|
||||
),
|
||||
cv.Optional(CONF_TILT_COMMAND_TOPIC): cv.All(
|
||||
cv.requires_component("mqtt"), cv.subscribe_topic
|
||||
),
|
||||
cv.Optional(CONF_TILT_STATE_TOPIC): cv.All(
|
||||
cv.requires_component("mqtt"), cv.subscribe_topic
|
||||
),
|
||||
cv.Optional(CONF_ON_OPEN): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(CoverOpenTrigger),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_CLOSED): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(CoverClosedTrigger),
|
||||
}
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def setup_cover_core_(var, config):
|
||||
cg.add(var.set_name(config[CONF_NAME]))
|
||||
cg.add(var.set_disabled_by_default(config[CONF_DISABLED_BY_DEFAULT]))
|
||||
if CONF_INTERNAL in config:
|
||||
cg.add(var.set_internal(config[CONF_INTERNAL]))
|
||||
await setup_entity(var, config)
|
||||
|
||||
if CONF_DEVICE_CLASS in config:
|
||||
cg.add(var.set_device_class(config[CONF_DEVICE_CLASS]))
|
||||
|
||||
for conf in config.get(CONF_ON_OPEN, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
for conf in config.get(CONF_ON_CLOSED, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
|
||||
if CONF_MQTT_ID in config:
|
||||
mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var)
|
||||
await mqtt.register_mqtt_component(mqtt_, config)
|
||||
|
||||
if CONF_POSITION_STATE_TOPIC in config:
|
||||
cg.add(
|
||||
mqtt_.set_custom_position_state_topic(config[CONF_POSITION_STATE_TOPIC])
|
||||
)
|
||||
if CONF_POSITION_COMMAND_TOPIC in config:
|
||||
cg.add(
|
||||
mqtt_.set_custom_position_command_topic(
|
||||
config[CONF_POSITION_COMMAND_TOPIC]
|
||||
)
|
||||
)
|
||||
if CONF_TILT_STATE_TOPIC in config:
|
||||
cg.add(mqtt_.set_custom_tilt_state_topic(config[CONF_TILT_STATE_TOPIC]))
|
||||
if CONF_TILT_COMMAND_TOPIC in config:
|
||||
cg.add(mqtt_.set_custom_tilt_command_topic(config[CONF_TILT_COMMAND_TOPIC]))
|
||||
|
||||
|
||||
async def register_cover(var, config):
|
||||
if not CORE.has_id(config[CONF_ID]):
|
||||
@@ -119,6 +173,12 @@ async def cover_stop_to_code(config, action_id, template_arg, args):
|
||||
return cg.new_Pvariable(action_id, template_arg, paren)
|
||||
|
||||
|
||||
@automation.register_action("cover.toggle", ToggleAction, COVER_ACTION_SCHEMA)
|
||||
def cover_toggle_to_code(config, action_id, template_arg, args):
|
||||
paren = yield cg.get_variable(config[CONF_ID])
|
||||
yield cg.new_Pvariable(action_id, template_arg, paren)
|
||||
|
||||
|
||||
COVER_CONTROL_ACTION_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.use_id(Cover),
|
||||
|
||||
@@ -37,6 +37,16 @@ template<typename... Ts> class StopAction : public Action<Ts...> {
|
||||
Cover *cover_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class ToggleAction : public Action<Ts...> {
|
||||
public:
|
||||
explicit ToggleAction(Cover *cover) : cover_(cover) {}
|
||||
|
||||
void play(Ts... x) override { this->cover_->make_call().set_command_toggle().perform(); }
|
||||
|
||||
protected:
|
||||
Cover *cover_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class ControlAction : public Action<Ts...> {
|
||||
public:
|
||||
explicit ControlAction(Cover *cover) : cover_(cover) {}
|
||||
@@ -89,6 +99,7 @@ template<typename... Ts> class CoverIsOpenCondition : public Condition<Ts...> {
|
||||
protected:
|
||||
Cover *cover_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class CoverIsClosedCondition : public Condition<Ts...> {
|
||||
public:
|
||||
CoverIsClosedCondition(Cover *cover) : cover_(cover) {}
|
||||
@@ -98,5 +109,27 @@ template<typename... Ts> class CoverIsClosedCondition : public Condition<Ts...>
|
||||
Cover *cover_;
|
||||
};
|
||||
|
||||
class CoverOpenTrigger : public Trigger<> {
|
||||
public:
|
||||
CoverOpenTrigger(Cover *a_cover) {
|
||||
a_cover->add_on_state_callback([this, a_cover]() {
|
||||
if (a_cover->is_fully_open()) {
|
||||
this->trigger();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
class CoverClosedTrigger : public Trigger<> {
|
||||
public:
|
||||
CoverClosedTrigger(Cover *a_cover) {
|
||||
a_cover->add_on_state_callback([this, a_cover]() {
|
||||
if (a_cover->is_fully_closed()) {
|
||||
this->trigger();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace cover
|
||||
} // namespace esphome
|
||||
|
||||
@@ -31,7 +31,7 @@ const char *cover_operation_to_str(CoverOperation op) {
|
||||
}
|
||||
}
|
||||
|
||||
Cover::Cover(const std::string &name) : Nameable(name), position{COVER_OPEN} {}
|
||||
Cover::Cover(const std::string &name) : EntityBase(name), position{COVER_OPEN} {}
|
||||
|
||||
uint32_t Cover::hash_base() { return 1727367479UL; }
|
||||
|
||||
@@ -43,6 +43,8 @@ CoverCall &CoverCall::set_command(const char *command) {
|
||||
this->set_command_close();
|
||||
} else if (strcasecmp(command, "STOP") == 0) {
|
||||
this->set_command_stop();
|
||||
} else if (strcasecmp(command, "TOGGLE") == 0) {
|
||||
this->set_command_toggle();
|
||||
} else {
|
||||
ESP_LOGW(TAG, "'%s' - Unrecognized command %s", this->parent_->get_name().c_str(), command);
|
||||
}
|
||||
@@ -60,6 +62,10 @@ CoverCall &CoverCall::set_command_stop() {
|
||||
this->stop_ = true;
|
||||
return *this;
|
||||
}
|
||||
CoverCall &CoverCall::set_command_toggle() {
|
||||
this->toggle_ = true;
|
||||
return *this;
|
||||
}
|
||||
CoverCall &CoverCall::set_position(float position) {
|
||||
this->position_ = position;
|
||||
return *this;
|
||||
@@ -85,10 +91,14 @@ void CoverCall::perform() {
|
||||
if (this->tilt_.has_value()) {
|
||||
ESP_LOGD(TAG, " Tilt: %.0f%%", *this->tilt_ * 100.0f);
|
||||
}
|
||||
if (this->toggle_.has_value()) {
|
||||
ESP_LOGD(TAG, " Command: TOGGLE");
|
||||
}
|
||||
this->parent_->control(*this);
|
||||
}
|
||||
const optional<float> &CoverCall::get_position() const { return this->position_; }
|
||||
const optional<float> &CoverCall::get_tilt() const { return this->tilt_; }
|
||||
const optional<bool> &CoverCall::get_toggle() const { return this->toggle_; }
|
||||
void CoverCall::validate_() {
|
||||
auto traits = this->parent_->get_traits();
|
||||
if (this->position_.has_value()) {
|
||||
@@ -111,6 +121,12 @@ void CoverCall::validate_() {
|
||||
this->tilt_ = clamp(tilt, 0.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
if (this->toggle_.has_value()) {
|
||||
if (!traits.get_supports_toggle()) {
|
||||
ESP_LOGW(TAG, "'%s' - This cover device does not support toggle!", this->parent_->get_name().c_str());
|
||||
this->toggle_.reset();
|
||||
}
|
||||
}
|
||||
if (this->stop_) {
|
||||
if (this->position_.has_value()) {
|
||||
ESP_LOGW(TAG, "Cannot set position when stopping a cover!");
|
||||
@@ -120,6 +136,10 @@ void CoverCall::validate_() {
|
||||
ESP_LOGW(TAG, "Cannot set tilt when stopping a cover!");
|
||||
this->tilt_.reset();
|
||||
}
|
||||
if (this->toggle_.has_value()) {
|
||||
ESP_LOGW(TAG, "Cannot set toggle when stopping a cover!");
|
||||
this->toggle_.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
CoverCall &CoverCall::set_stop(bool stop) {
|
||||
@@ -180,7 +200,7 @@ void Cover::publish_state(bool save) {
|
||||
}
|
||||
}
|
||||
optional<CoverRestoreState> Cover::restore_state_() {
|
||||
this->rtc_ = global_preferences.make_preference<CoverRestoreState>(this->get_object_id_hash());
|
||||
this->rtc_ = global_preferences->make_preference<CoverRestoreState>(this->get_object_id_hash());
|
||||
CoverRestoreState recovered{};
|
||||
if (!this->rtc_.load(&recovered))
|
||||
return {};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/entity_base.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/preferences.h"
|
||||
#include "cover_traits.h"
|
||||
@@ -13,7 +14,7 @@ const extern float COVER_CLOSED;
|
||||
|
||||
#define LOG_COVER(prefix, type, obj) \
|
||||
if ((obj) != nullptr) { \
|
||||
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, type, (obj)->get_name().c_str()); \
|
||||
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \
|
||||
auto traits_ = (obj)->get_traits(); \
|
||||
if (traits_.get_is_assumed_state()) { \
|
||||
ESP_LOGCONFIG(TAG, "%s Assumed State: YES", prefix); \
|
||||
@@ -29,7 +30,7 @@ class CoverCall {
|
||||
public:
|
||||
CoverCall(Cover *parent);
|
||||
|
||||
/// Set the command as a string, "STOP", "OPEN", "CLOSE".
|
||||
/// Set the command as a string, "STOP", "OPEN", "CLOSE", "TOGGLE".
|
||||
CoverCall &set_command(const char *command);
|
||||
/// Set the command to open the cover.
|
||||
CoverCall &set_command_open();
|
||||
@@ -37,6 +38,8 @@ class CoverCall {
|
||||
CoverCall &set_command_close();
|
||||
/// Set the command to stop the cover.
|
||||
CoverCall &set_command_stop();
|
||||
/// Set the command to toggle the cover.
|
||||
CoverCall &set_command_toggle();
|
||||
/// Set the call to a certain target position.
|
||||
CoverCall &set_position(float position);
|
||||
/// Set the call to a certain target tilt.
|
||||
@@ -50,6 +53,7 @@ class CoverCall {
|
||||
const optional<float> &get_position() const;
|
||||
bool get_stop() const;
|
||||
const optional<float> &get_tilt() const;
|
||||
const optional<bool> &get_toggle() const;
|
||||
|
||||
protected:
|
||||
void validate_();
|
||||
@@ -58,6 +62,7 @@ class CoverCall {
|
||||
bool stop_{false};
|
||||
optional<float> position_{};
|
||||
optional<float> tilt_{};
|
||||
optional<bool> toggle_{};
|
||||
};
|
||||
|
||||
/// Struct used to store the restored state of a cover
|
||||
@@ -103,7 +108,7 @@ const char *cover_operation_to_str(CoverOperation op);
|
||||
* to control all values of the cover. Also implement get_traits() to return what operations
|
||||
* the cover supports.
|
||||
*/
|
||||
class Cover : public Nameable {
|
||||
class Cover : public EntityBase {
|
||||
public:
|
||||
explicit Cover();
|
||||
explicit Cover(const std::string &name);
|
||||
|
||||
@@ -13,11 +13,14 @@ class CoverTraits {
|
||||
void set_supports_position(bool supports_position) { this->supports_position_ = supports_position; }
|
||||
bool get_supports_tilt() const { return this->supports_tilt_; }
|
||||
void set_supports_tilt(bool supports_tilt) { this->supports_tilt_ = supports_tilt; }
|
||||
bool get_supports_toggle() const { return this->supports_toggle_; }
|
||||
void set_supports_toggle(bool supports_toggle) { this->supports_toggle_ = supports_toggle; }
|
||||
|
||||
protected:
|
||||
bool is_assumed_state_{false};
|
||||
bool supports_position_{false};
|
||||
bool supports_tilt_{false};
|
||||
bool supports_toggle_{false};
|
||||
};
|
||||
|
||||
} // namespace cover
|
||||
|
||||
@@ -8,18 +8,6 @@ namespace ct_clamp {
|
||||
|
||||
static const char *const TAG = "ct_clamp";
|
||||
|
||||
void CTClampSensor::setup() {
|
||||
this->is_calibrating_offset_ = true;
|
||||
this->high_freq_.start();
|
||||
this->set_timeout("calibrate_offset", this->sample_duration_, [this]() {
|
||||
this->high_freq_.stop();
|
||||
this->is_calibrating_offset_ = false;
|
||||
if (this->num_samples_ != 0) {
|
||||
this->offset_ = this->sample_sum_ / this->num_samples_;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void CTClampSensor::dump_config() {
|
||||
LOG_SENSOR("", "CT Clamp Sensor", this);
|
||||
ESP_LOGCONFIG(TAG, " Sample Duration: %.2fs", this->sample_duration_ / 1e3f);
|
||||
@@ -27,9 +15,6 @@ void CTClampSensor::dump_config() {
|
||||
}
|
||||
|
||||
void CTClampSensor::update() {
|
||||
if (this->is_calibrating_offset_)
|
||||
return;
|
||||
|
||||
// Update only starts the sampling phase, in loop() the actual sampling is happening.
|
||||
|
||||
// Request a high loop() execution interval during sampling phase.
|
||||
@@ -46,44 +31,39 @@ void CTClampSensor::update() {
|
||||
return;
|
||||
}
|
||||
|
||||
float raw = this->sample_sum_ / this->num_samples_;
|
||||
float irms = std::sqrt(raw);
|
||||
ESP_LOGD(TAG, "'%s' - Raw Value: %.2fA", this->name_.c_str(), irms);
|
||||
this->publish_state(irms);
|
||||
const float rms_ac_dc_squared = this->sample_squared_sum_ / this->num_samples_;
|
||||
const float rms_dc = this->sample_sum_ / this->num_samples_;
|
||||
const float rms_ac = std::sqrt(rms_ac_dc_squared - rms_dc * rms_dc);
|
||||
ESP_LOGD(TAG, "'%s' - Raw AC Value: %.3fA after %d different samples (%d SPS)", this->name_.c_str(), rms_ac,
|
||||
this->num_samples_, 1000 * this->num_samples_ / this->sample_duration_);
|
||||
this->publish_state(rms_ac);
|
||||
});
|
||||
|
||||
// Set sampling values
|
||||
this->is_sampling_ = true;
|
||||
this->last_value_ = 0.0;
|
||||
this->num_samples_ = 0;
|
||||
this->sample_sum_ = 0.0f;
|
||||
this->sample_squared_sum_ = 0.0f;
|
||||
this->is_sampling_ = true;
|
||||
}
|
||||
|
||||
void CTClampSensor::loop() {
|
||||
if (!this->is_sampling_ && !this->is_calibrating_offset_)
|
||||
if (!this->is_sampling_)
|
||||
return;
|
||||
|
||||
// Perform a single sample
|
||||
float value = this->source_->sample();
|
||||
if (isnan(value))
|
||||
if (std::isnan(value))
|
||||
return;
|
||||
|
||||
if (this->is_calibrating_offset_) {
|
||||
this->sample_sum_ += value;
|
||||
this->num_samples_++;
|
||||
// Assuming a sine wave, avoid requesting values faster than the ADC can provide them
|
||||
if (this->last_value_ == value)
|
||||
return;
|
||||
}
|
||||
this->last_value_ = value;
|
||||
|
||||
// Adjust DC offset via low pass filter (exponential moving average)
|
||||
const float alpha = 0.001f;
|
||||
this->offset_ = this->offset_ * (1 - alpha) + value * alpha;
|
||||
|
||||
// Filtered value centered around the mid-point (0V)
|
||||
float filtered = value - this->offset_;
|
||||
|
||||
// IRMS is sqrt(∑v_i²)
|
||||
float sq = filtered * filtered;
|
||||
this->sample_sum_ += sq;
|
||||
this->num_samples_++;
|
||||
this->sample_sum_ += value;
|
||||
this->sample_squared_sum_ += value * value;
|
||||
}
|
||||
|
||||
} // namespace ct_clamp
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/esphal.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/voltage_sampler/voltage_sampler.h"
|
||||
|
||||
@@ -10,7 +10,6 @@ namespace ct_clamp {
|
||||
|
||||
class CTClampSensor : public sensor::Sensor, public PollingComponent {
|
||||
public:
|
||||
void setup() override;
|
||||
void update() override;
|
||||
void loop() override;
|
||||
void dump_config() override;
|
||||
@@ -35,17 +34,20 @@ class CTClampSensor : public sensor::Sensor, public PollingComponent {
|
||||
*
|
||||
* Diagram: https://learn.openenergymonitor.org/electricity-monitoring/ct-sensors/interface-with-arduino
|
||||
*
|
||||
* This is automatically calculated with an exponential moving average/digital low pass filter.
|
||||
*
|
||||
* 0.5 is a good initial approximation to start with for most ESP8266 setups.
|
||||
* The current clamp only measures AC, so any DC component is an unwanted artifact from the
|
||||
* sampling circuit. The AC component is essentially the same as the calculating the Standard-Deviation,
|
||||
* which can be done by cumulating 3 values per sample:
|
||||
* 1) Number of samples
|
||||
* 2) Sum of samples
|
||||
* 3) Sum of sample squared
|
||||
* https://en.wikipedia.org/wiki/Root_mean_square
|
||||
*/
|
||||
float offset_ = 0.5f;
|
||||
|
||||
float last_value_ = 0.0f;
|
||||
float sample_sum_ = 0.0f;
|
||||
float sample_squared_sum_ = 0.0f;
|
||||
uint32_t num_samples_ = 0;
|
||||
bool is_sampling_ = false;
|
||||
/// Calibrate offset value once at boot
|
||||
bool is_calibrating_offset_ = false;
|
||||
};
|
||||
|
||||
} // namespace ct_clamp
|
||||
|
||||
1
esphome/components/current_based/__init__.py
Normal file
1
esphome/components/current_based/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
CODEOWNERS = ["@djwmarcx"]
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user