Merge branch 'dev' into bump-2022.2.0b1

This commit is contained in:
Jesse Hills 2022-02-09 23:47:36 +13:00
commit 272d6f2a8b
No known key found for this signature in database
GPG key ID: BEAAE804EFD8E83A
426 changed files with 8662 additions and 3041 deletions

View file

@ -68,8 +68,6 @@ Checks: >-
-modernize-use-nodiscard, -modernize-use-nodiscard,
-mpi-*, -mpi-*,
-objc-*, -objc-*,
-readability-braces-around-statements,
-readability-const-return-type,
-readability-convert-member-functions-to-static, -readability-convert-member-functions-to-static,
-readability-else-after-return, -readability-else-after-return,
-readability-function-cognitive-complexity, -readability-function-cognitive-complexity,
@ -77,10 +75,6 @@ Checks: >-
-readability-isolate-declaration, -readability-isolate-declaration,
-readability-magic-numbers, -readability-magic-numbers,
-readability-make-member-function-const, -readability-make-member-function-const,
-readability-named-parameter,
-readability-qualified-auto,
-readability-redundant-access-specifiers,
-readability-redundant-member-init,
-readability-redundant-string-init, -readability-redundant-string-init,
-readability-uppercase-literal-suffix, -readability-uppercase-literal-suffix,
-readability-use-anyofallof, -readability-use-anyofallof,
@ -114,6 +108,8 @@ CheckOptions:
value: 'make_unique' value: 'make_unique'
- key: modernize-make-unique.MakeSmartPtrFunctionHeader - key: modernize-make-unique.MakeSmartPtrFunctionHeader
value: 'esphome/core/helpers.h' value: 'esphome/core/helpers.h'
- key: readability-braces-around-statements.ShortStatementLines
value: 2
- key: readability-identifier-naming.LocalVariableCase - key: readability-identifier-naming.LocalVariableCase
value: 'lower_case' value: 'lower_case'
- key: readability-identifier-naming.ClassCase - key: readability-identifier-naming.ClassCase
@ -160,3 +156,5 @@ CheckOptions:
value: 'lower_case' value: 'lower_case'
- key: readability-identifier-naming.VirtualMethodSuffix - key: readability-identifier-naming.VirtualMethodSuffix
value: '' value: ''
- key: readability-qualified-auto.AddConstToQualified
value: 0

View file

@ -137,7 +137,7 @@ jobs:
--build-type "${{ matrix.build_type }}" \ --build-type "${{ matrix.build_type }}" \
manifest manifest
deploy-hassio-repo: deploy-ha-addon-repo:
if: github.repository == 'esphome/esphome' && github.event_name == 'release' if: github.repository == 'esphome/esphome' && github.event_name == 'release'
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [deploy-docker] needs: [deploy-docker]
@ -150,5 +150,5 @@ jobs:
-u ":$TOKEN" \ -u ":$TOKEN" \
-X POST \ -X POST \
-H "Accept: application/vnd.github.v3+json" \ -H "Accept: application/vnd.github.v3+json" \
https://api.github.com/repos/esphome/hassio/actions/workflows/bump-version.yml/dispatches \ https://api.github.com/repos/esphome/home-assistant-addon/actions/workflows/bump-version.yml/dispatches \
-d "{\"ref\":\"main\",\"inputs\":{\"version\":\"$TAG\"}}" -d "{\"ref\":\"main\",\"inputs\":{\"version\":\"$TAG\"}}"

View file

@ -3,4 +3,4 @@ ports:
onOpen: open-preview onOpen: open-preview
tasks: tasks:
- before: pyenv local $(pyenv version | grep '^3\.' | cut -d ' ' -f 1) && script/setup - before: pyenv local $(pyenv version | grep '^3\.' | cut -d ' ' -f 1) && script/setup
command: python -m esphome config dashboard command: python -m esphome dashboard config

View file

@ -2,7 +2,7 @@
# See https://pre-commit.com/hooks.html for more hooks # See https://pre-commit.com/hooks.html for more hooks
repos: repos:
- repo: https://github.com/ambv/black - repo: https://github.com/ambv/black
rev: 20.8b1 rev: 22.1.0
hooks: hooks:
- id: black - id: black
args: args:
@ -10,7 +10,7 @@ repos:
- --quiet - --quiet
files: ^((esphome|script|tests)/.+)?[^/]+\.py$ files: ^((esphome|script|tests)/.+)?[^/]+\.py$
- repo: https://gitlab.com/pycqa/flake8 - repo: https://gitlab.com/pycqa/flake8
rev: 3.8.4 rev: 4.0.1
hooks: hooks:
- id: flake8 - id: flake8
additional_dependencies: additional_dependencies:

View file

@ -54,6 +54,7 @@ esphome/components/dfplayer/* @glmnet
esphome/components/dht/* @OttoWinter esphome/components/dht/* @OttoWinter
esphome/components/ds1307/* @badbadc0ffee esphome/components/ds1307/* @badbadc0ffee
esphome/components/dsmr/* @glmnet @zuidwijk esphome/components/dsmr/* @glmnet @zuidwijk
esphome/components/ektf2232/* @jesserockz
esphome/components/esp32/* @esphome/core esphome/components/esp32/* @esphome/core
esphome/components/esp32_ble/* @jesserockz esphome/components/esp32_ble/* @jesserockz
esphome/components/esp32_ble_server/* @jesserockz esphome/components/esp32_ble_server/* @jesserockz
@ -88,9 +89,12 @@ esphome/components/json/* @OttoWinter
esphome/components/kalman_combinator/* @Cat-Ion esphome/components/kalman_combinator/* @Cat-Ion
esphome/components/ledc/* @OttoWinter esphome/components/ledc/* @OttoWinter
esphome/components/light/* @esphome/core esphome/components/light/* @esphome/core
esphome/components/lilygo_t5_47/touchscreen/* @jesserockz
esphome/components/lock/* @esphome/core
esphome/components/logger/* @esphome/core esphome/components/logger/* @esphome/core
esphome/components/ltr390/* @sjtrny esphome/components/ltr390/* @sjtrny
esphome/components/max7219digit/* @rspaargaren esphome/components/max7219digit/* @rspaargaren
esphome/components/max9611/* @mckaymatthew
esphome/components/mcp23008/* @jesserockz esphome/components/mcp23008/* @jesserockz
esphome/components/mcp23017/* @jesserockz esphome/components/mcp23017/* @jesserockz
esphome/components/mcp23s08/* @SenexCrenshaw @jesserockz esphome/components/mcp23s08/* @SenexCrenshaw @jesserockz
@ -107,10 +111,12 @@ esphome/components/mdns/* @esphome/core
esphome/components/midea/* @dudanov esphome/components/midea/* @dudanov
esphome/components/midea_ir/* @dudanov esphome/components/midea_ir/* @dudanov
esphome/components/mitsubishi/* @RubyBailey esphome/components/mitsubishi/* @RubyBailey
esphome/components/mlx90393/* @functionpointer
esphome/components/modbus_controller/* @martgras esphome/components/modbus_controller/* @martgras
esphome/components/modbus_controller/binary_sensor/* @martgras esphome/components/modbus_controller/binary_sensor/* @martgras
esphome/components/modbus_controller/number/* @martgras esphome/components/modbus_controller/number/* @martgras
esphome/components/modbus_controller/output/* @martgras esphome/components/modbus_controller/output/* @martgras
esphome/components/modbus_controller/select/* @martgras @stegm
esphome/components/modbus_controller/sensor/* @martgras esphome/components/modbus_controller/sensor/* @martgras
esphome/components/modbus_controller/switch/* @martgras esphome/components/modbus_controller/switch/* @martgras
esphome/components/modbus_controller/text_sensor/* @martgras esphome/components/modbus_controller/text_sensor/* @martgras
@ -136,6 +142,9 @@ esphome/components/preferences/* @esphome/core
esphome/components/psram/* @esphome/core esphome/components/psram/* @esphome/core
esphome/components/pulse_meter/* @stevebaxter esphome/components/pulse_meter/* @stevebaxter
esphome/components/pvvx_mithermometer/* @pasiz esphome/components/pvvx_mithermometer/* @pasiz
esphome/components/qr_code/* @wjtje
esphome/components/radon_eye_ble/* @jeffeb3
esphome/components/radon_eye_rd200/* @jeffeb3
esphome/components/rc522/* @glmnet esphome/components/rc522/* @glmnet
esphome/components/rc522_i2c/* @glmnet esphome/components/rc522_i2c/* @glmnet
esphome/components/rc522_spi/* @glmnet esphome/components/rc522_spi/* @glmnet
@ -187,6 +196,7 @@ esphome/components/tmp102/* @timsavage
esphome/components/tmp117/* @Azimath esphome/components/tmp117/* @Azimath
esphome/components/tof10120/* @wstrzalka esphome/components/tof10120/* @wstrzalka
esphome/components/toshiba/* @kbx81 esphome/components/toshiba/* @kbx81
esphome/components/touchscreen/* @jesserockz
esphome/components/tsl2591/* @wjcarpenter esphome/components/tsl2591/* @wjcarpenter
esphome/components/tuya/binary_sensor/* @jesserockz esphome/components/tuya/binary_sensor/* @jesserockz
esphome/components/tuya/climate/* @jesserockz esphome/components/tuya/climate/* @jesserockz
@ -197,8 +207,10 @@ esphome/components/tuya/text_sensor/* @dentra
esphome/components/uart/* @esphome/core esphome/components/uart/* @esphome/core
esphome/components/ultrasonic/* @OttoWinter esphome/components/ultrasonic/* @OttoWinter
esphome/components/version/* @esphome/core esphome/components/version/* @esphome/core
esphome/components/wake_on_lan/* @willwill2will54
esphome/components/web_server_base/* @OttoWinter esphome/components/web_server_base/* @OttoWinter
esphome/components/whirlpool/* @glmnet esphome/components/whirlpool/* @glmnet
esphome/components/xiaomi_lywsd03mmc/* @ahpohl esphome/components/xiaomi_lywsd03mmc/* @ahpohl
esphome/components/xiaomi_mhoc303/* @drug123
esphome/components/xiaomi_mhoc401/* @vevsvevs esphome/components/xiaomi_mhoc401/* @vevsvevs
esphome/components/xpt2046/* @numo68 esphome/components/xpt2046/* @numo68

View file

@ -4,4 +4,5 @@ include requirements.txt
include esphome/dashboard/templates/*.html include esphome/dashboard/templates/*.html
recursive-include esphome/dashboard/static *.ico *.js *.css *.woff* LICENSE recursive-include esphome/dashboard/static *.ico *.js *.css *.woff* LICENSE
recursive-include esphome *.cpp *.h *.tcc recursive-include esphome *.cpp *.h *.tcc
recursive-include esphome *.py.script
recursive-include esphome LICENSE.txt recursive-include esphome LICENSE.txt

View file

@ -8,9 +8,9 @@ ARG BASEIMGTYPE=docker
FROM ghcr.io/hassio-addons/debian-base/amd64:5.2.3 AS base-hassio-amd64 FROM ghcr.io/hassio-addons/debian-base/amd64:5.2.3 AS base-hassio-amd64
FROM ghcr.io/hassio-addons/debian-base/aarch64:5.2.3 AS base-hassio-arm64 FROM ghcr.io/hassio-addons/debian-base/aarch64:5.2.3 AS base-hassio-arm64
FROM ghcr.io/hassio-addons/debian-base/armv7:5.2.3 AS base-hassio-armv7 FROM ghcr.io/hassio-addons/debian-base/armv7:5.2.3 AS base-hassio-armv7
FROM debian:bullseye-20211220-slim AS base-docker-amd64 FROM debian:bullseye-20220125-slim AS base-docker-amd64
FROM debian:bullseye-20211220-slim AS base-docker-arm64 FROM debian:bullseye-20220125-slim AS base-docker-arm64
FROM debian:bullseye-20211220-slim AS base-docker-armv7 FROM debian:bullseye-20220125-slim AS base-docker-armv7
# Use TARGETARCH/TARGETVARIANT defined by docker # Use TARGETARCH/TARGETVARIANT defined by docker
# https://docs.docker.com/engine/reference/builder/#automatic-platform-args-in-the-global-scope # https://docs.docker.com/engine/reference/builder/#automatic-platform-args-in-the-global-scope
@ -23,7 +23,7 @@ RUN \
python3=3.9.2-3 \ python3=3.9.2-3 \
python3-pip=20.3.4-4 \ python3-pip=20.3.4-4 \
python3-setuptools=52.0.0-4 \ python3-setuptools=52.0.0-4 \
python3-pil=8.1.2+dfsg-0.3 \ python3-pil=8.1.2+dfsg-0.3+deb11u1 \
python3-cryptography=3.3.2-1 \ python3-cryptography=3.3.2-1 \
iputils-ping=3:20210202-1 \ iputils-ping=3:20210202-1 \
git=1:2.30.2-1 \ git=1:2.30.2-1 \
@ -102,7 +102,7 @@ RUN \
ARG BUILD_VERSION=dev ARG BUILD_VERSION=dev
# Copy root filesystem # Copy root filesystem
COPY docker/hassio-rootfs/ / COPY docker/ha-addon-rootfs/ /
# First install requirements to leverage caching when requirements don't change # First install requirements to leverage caching when requirements don't change
COPY requirements.txt requirements_optional.txt docker/platformio_install_deps.py platformio.ini / COPY requirements.txt requirements_optional.txt docker/platformio_install_deps.py platformio.ini /

View file

@ -10,7 +10,7 @@ server {
ssl_certificate_key /ssl/%%keyfile%%; ssl_certificate_key /ssl/%%keyfile%%;
# Clear Hass.io Ingress header # Clear Hass.io Ingress header
proxy_set_header X-Hassio-Ingress ""; proxy_set_header X-HA-Ingress "";
# Redirect http requests to https on the same port. # Redirect http requests to https on the same port.
# https://rageagainstshell.com/2016/11/redirect-http-to-https-on-the-same-port-in-nginx/ # https://rageagainstshell.com/2016/11/redirect-http-to-https-on-the-same-port-in-nginx/

View file

@ -4,7 +4,7 @@ server {
include /etc/nginx/includes/server_params.conf; include /etc/nginx/includes/server_params.conf;
include /etc/nginx/includes/proxy_params.conf; include /etc/nginx/includes/proxy_params.conf;
# Clear Hass.io Ingress header # Clear Hass.io Ingress header
proxy_set_header X-Hassio-Ingress ""; proxy_set_header X-HA-Ingress "";
location / { location / {
proxy_pass http://esphome; proxy_pass http://esphome;

View file

@ -3,8 +3,8 @@ server {
include /etc/nginx/includes/server_params.conf; include /etc/nginx/includes/server_params.conf;
include /etc/nginx/includes/proxy_params.conf; include /etc/nginx/includes/proxy_params.conf;
# Set Hass.io Ingress header # Set Home Assistant Ingress header
proxy_set_header X-Hassio-Ingress "YES"; proxy_set_header X-HA-Ingress "YES";
location / { location / {
# Only allow from Hass.io supervisor # Only allow from Hass.io supervisor

View file

@ -4,7 +4,7 @@
# Runs the ESPHome dashboard # Runs the ESPHome dashboard
# ============================================================================== # ==============================================================================
export ESPHOME_IS_HASSIO=true export ESPHOME_IS_HA_ADDON=true
if bashio::config.true 'leave_front_door_open'; then if bashio::config.true 'leave_front_door_open'; then
export DISABLE_HA_AUTHENTICATION=true export DISABLE_HA_AUTHENTICATION=true
@ -32,4 +32,4 @@ export PLATFORMIO_CACHE_DIR="${pio_cache_base}/cache"
export PLATFORMIO_GLOBALLIB_DIR=/piolibs export PLATFORMIO_GLOBALLIB_DIR=/piolibs
bashio::log.info "Starting ESPHome dashboard..." bashio::log.info "Starting ESPHome dashboard..."
exec esphome dashboard /config/esphome --socket /var/run/esphome.sock --hassio exec esphome dashboard /config/esphome --socket /var/run/esphome.sock --ha-addon

View file

@ -661,7 +661,7 @@ def parse_args(argv):
"--open-ui", help="Open the dashboard UI in a browser.", action="store_true" "--open-ui", help="Open the dashboard UI in a browser.", action="store_true"
) )
parser_dashboard.add_argument( parser_dashboard.add_argument(
"--hassio", help=argparse.SUPPRESS, action="store_true" "--ha-addon", help=argparse.SUPPRESS, action="store_true"
) )
parser_dashboard.add_argument( parser_dashboard.add_argument(
"--socket", help="Make the dashboard serve under a unix socket", type=str "--socket", help="Make the dashboard serve under a unix socket", type=str

View file

@ -63,6 +63,7 @@ from esphome.cpp_types import ( # noqa
uint32, uint32,
uint64, uint64,
int32, int32,
int64,
const_char_ptr, const_char_ptr,
NAN, NAN,
esphome_ns, esphome_ns,
@ -81,4 +82,5 @@ from esphome.cpp_types import ( # noqa
InternalGPIOPin, InternalGPIOPin,
gpio_Flags, gpio_Flags,
EntityCategory, EntityCategory,
Parented,
) )

View file

@ -52,10 +52,10 @@ uint32_t IRAM_ATTR HOT AcDimmerDataStore::timer_intr(uint32_t now) {
this->gate_pin.digital_write(false); this->gate_pin.digital_write(false);
} }
if (time_since_zc < this->enable_time_us) if (time_since_zc < this->enable_time_us) {
// Next event is enable, return time until that event // Next event is enable, return time until that event
return this->enable_time_us - time_since_zc; return this->enable_time_us - time_since_zc;
else if (time_since_zc < disable_time_us) { } else if (time_since_zc < disable_time_us) {
// Next event is disable, return time until that event // Next event is disable, return time until that event
return this->disable_time_us - time_since_zc; return this->disable_time_us - time_since_zc;
} }
@ -74,9 +74,10 @@ uint32_t IRAM_ATTR HOT timer_interrupt() {
uint32_t min_dt_us = 1000; uint32_t min_dt_us = 1000;
uint32_t now = micros(); uint32_t now = micros();
for (auto *dimmer : all_dimmers) { for (auto *dimmer : all_dimmers) {
if (dimmer == nullptr) if (dimmer == nullptr) {
// no more dimmers // no more dimmers
break; break;
}
uint32_t res = dimmer->timer_intr(now); uint32_t res = dimmer->timer_intr(now);
if (res != 0 && res < min_dt_us) if (res != 0 && res < min_dt_us)
min_dt_us = res; min_dt_us = res;
@ -212,12 +213,13 @@ void AcDimmer::dump_config() {
LOG_PIN(" Zero-Cross Pin: ", this->zero_cross_pin_); LOG_PIN(" Zero-Cross Pin: ", this->zero_cross_pin_);
ESP_LOGCONFIG(TAG, " Min Power: %.1f%%", this->store_.min_power / 10.0f); ESP_LOGCONFIG(TAG, " Min Power: %.1f%%", this->store_.min_power / 10.0f);
ESP_LOGCONFIG(TAG, " Init with half cycle: %s", YESNO(this->init_with_half_cycle_)); ESP_LOGCONFIG(TAG, " Init with half cycle: %s", YESNO(this->init_with_half_cycle_));
if (method_ == DIM_METHOD_LEADING_PULSE) if (method_ == DIM_METHOD_LEADING_PULSE) {
ESP_LOGCONFIG(TAG, " Method: leading pulse"); ESP_LOGCONFIG(TAG, " Method: leading pulse");
else if (method_ == DIM_METHOD_LEADING) } else if (method_ == DIM_METHOD_LEADING) {
ESP_LOGCONFIG(TAG, " Method: leading"); ESP_LOGCONFIG(TAG, " Method: leading");
else } else {
ESP_LOGCONFIG(TAG, " Method: trailing"); ESP_LOGCONFIG(TAG, " Method: trailing");
}
LOG_FLOAT_OUTPUT(this); LOG_FLOAT_OUTPUT(this);
ESP_LOGV(TAG, " Estimated Frequency: %.3fHz", 1e6f / this->store_.cycle_time_us / 2); ESP_LOGV(TAG, " Estimated Frequency: %.3fHz", 1e6f / this->store_.cycle_time_us / 2);

View file

@ -13,7 +13,6 @@ class AdalightLightEffect : public light::AddressableLightEffect, public uart::U
public: public:
AdalightLightEffect(const std::string &name); AdalightLightEffect(const std::string &name);
public:
void start() override; void start() override;
void stop() override; void stop() override;
void apply(light::AddressableLight &it, const Color &current_color) override; void apply(light::AddressableLight &it, const Color &current_color) override;
@ -30,7 +29,6 @@ class AdalightLightEffect : public light::AddressableLightEffect, public uart::U
void blank_all_leds_(light::AddressableLight &it); void blank_all_leds_(light::AddressableLight &it);
Frame parse_frame_(light::AddressableLight &it); Frame parse_frame_(light::AddressableLight &it);
protected:
uint32_t last_ack_{0}; uint32_t last_ack_{0};
uint32_t last_byte_{0}; uint32_t last_byte_{0};
uint32_t last_reset_{0}; uint32_t last_reset_{0};

View file

@ -15,6 +15,11 @@ namespace esphome {
namespace adc { namespace adc {
static const char *const TAG = "adc"; static const char *const TAG = "adc";
// 13 bits for S3 / 12 bit for all other esp32 variants
// create a const to avoid the repated cast to enum
#ifdef USE_ESP32
static const adc_bits_width_t ADC_WIDTH_MAX_SOC_BITS = static_cast<adc_bits_width_t>(ADC_WIDTH_MAX - 1);
#endif
void ADCSensor::setup() { void ADCSensor::setup() {
ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str()); ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str());
@ -23,14 +28,14 @@ void ADCSensor::setup() {
#endif #endif
#ifdef USE_ESP32 #ifdef USE_ESP32
adc1_config_width(ADC_WIDTH_BIT_12); adc1_config_width(ADC_WIDTH_MAX_SOC_BITS);
if (!autorange_) { if (!autorange_) {
adc1_config_channel_atten(channel_, attenuation_); adc1_config_channel_atten(channel_, attenuation_);
} }
// load characteristics for each attenuation // load characteristics for each attenuation
for (int i = 0; i < (int) ADC_ATTEN_MAX; i++) { for (int i = 0; i < (int) ADC_ATTEN_MAX; i++) {
auto cal_value = esp_adc_cal_characterize(ADC_UNIT_1, (adc_atten_t) i, ADC_WIDTH_BIT_12, auto cal_value = esp_adc_cal_characterize(ADC_UNIT_1, (adc_atten_t) i, ADC_WIDTH_MAX_SOC_BITS,
1100, // default vref 1100, // default vref
&cal_characteristics_[i]); &cal_characteristics_[i]);
switch (cal_value) { switch (cal_value) {
@ -65,9 +70,9 @@ void ADCSensor::dump_config() {
#ifdef USE_ESP32 #ifdef USE_ESP32
LOG_PIN(" Pin: ", pin_); LOG_PIN(" Pin: ", pin_);
if (autorange_) if (autorange_) {
ESP_LOGCONFIG(TAG, " Attenuation: auto"); ESP_LOGCONFIG(TAG, " Attenuation: auto");
else } else {
switch (this->attenuation_) { switch (this->attenuation_) {
case ADC_ATTEN_DB_0: case ADC_ATTEN_DB_0:
ESP_LOGCONFIG(TAG, " Attenuation: 0db (max 1.1V)"); ESP_LOGCONFIG(TAG, " Attenuation: 0db (max 1.1V)");
@ -84,6 +89,7 @@ void ADCSensor::dump_config() {
default: // This is to satisfy the unused ADC_ATTEN_MAX default: // This is to satisfy the unused ADC_ATTEN_MAX
break; break;
} }
}
#endif // USE_ESP32 #endif // USE_ESP32
LOG_UPDATE_INTERVAL(this); LOG_UPDATE_INTERVAL(this);
} }

View file

@ -24,7 +24,7 @@ void AirthingsWaveMini::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt
case ESP_GATTC_SEARCH_CMPL_EVT: { case ESP_GATTC_SEARCH_CMPL_EVT: {
this->handle_ = 0; this->handle_ = 0;
auto chr = this->parent()->get_characteristic(service_uuid_, sensors_data_characteristic_uuid_); auto *chr = this->parent()->get_characteristic(service_uuid_, sensors_data_characteristic_uuid_);
if (chr == nullptr) { if (chr == nullptr) {
ESP_LOGW(TAG, "No sensor characteristic found at service %s char %s", service_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()); sensors_data_characteristic_uuid_.to_string().c_str());
@ -56,7 +56,7 @@ void AirthingsWaveMini::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt
} }
void AirthingsWaveMini::read_sensors_(uint8_t *raw_value, uint16_t value_len) { void AirthingsWaveMini::read_sensors_(uint8_t *raw_value, uint16_t value_len) {
auto value = (WaveMiniReadings *) raw_value; auto *value = (WaveMiniReadings *) raw_value;
if (sizeof(WaveMiniReadings) <= value_len) { if (sizeof(WaveMiniReadings) <= value_len) {
this->humidity_sensor_->publish_state(value->humidity / 100.0f); this->humidity_sensor_->publish_state(value->humidity / 100.0f);

View file

@ -24,7 +24,7 @@ void AirthingsWavePlus::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt
case ESP_GATTC_SEARCH_CMPL_EVT: { case ESP_GATTC_SEARCH_CMPL_EVT: {
this->handle_ = 0; this->handle_ = 0;
auto chr = this->parent()->get_characteristic(service_uuid_, sensors_data_characteristic_uuid_); auto *chr = this->parent()->get_characteristic(service_uuid_, sensors_data_characteristic_uuid_);
if (chr == nullptr) { if (chr == nullptr) {
ESP_LOGW(TAG, "No sensor characteristic found at service %s char %s", service_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()); sensors_data_characteristic_uuid_.to_string().c_str());
@ -56,7 +56,7 @@ void AirthingsWavePlus::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt
} }
void AirthingsWavePlus::read_sensors_(uint8_t *raw_value, uint16_t value_len) { void AirthingsWavePlus::read_sensors_(uint8_t *raw_value, uint16_t value_len) {
auto value = (WavePlusReadings *) raw_value; auto *value = (WavePlusReadings *) raw_value;
if (sizeof(WavePlusReadings) <= value_len) { if (sizeof(WavePlusReadings) <= value_len) {
ESP_LOGD(TAG, "version = %d", value->version); ESP_LOGD(TAG, "version = %d", value->version);

View file

@ -19,12 +19,14 @@ uint16_t crc_16(uint8_t *ptr, uint8_t length) {
//------------------------------ //------------------------------
while (length--) { while (length--) {
crc ^= *ptr++; crc ^= *ptr++;
for (i = 0; i < 8; i++) for (i = 0; i < 8; i++) {
if ((crc & 0x01) != 0) { if ((crc & 0x01) != 0) {
crc >>= 1; crc >>= 1;
crc ^= 0xA001; crc ^= 0xA001;
} else } else {
crc >>= 1; crc >>= 1;
}
}
} }
return crc; return crc;
} }

View file

@ -39,7 +39,7 @@ void Am43::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_i
break; break;
} }
case ESP_GATTC_SEARCH_CMPL_EVT: { case ESP_GATTC_SEARCH_CMPL_EVT: {
auto chr = this->parent_->get_characteristic(AM43_SERVICE_UUID, AM43_CHARACTERISTIC_UUID); auto *chr = this->parent_->get_characteristic(AM43_SERVICE_UUID, AM43_CHARACTERISTIC_UUID);
if (chr == nullptr) { if (chr == nullptr) {
if (this->parent_->get_characteristic(AM43_TUYA_SERVICE_UUID, AM43_TUYA_CHARACTERISTIC_UUID) != nullptr) { if (this->parent_->get_characteristic(AM43_TUYA_SERVICE_UUID, AM43_TUYA_CHARACTERISTIC_UUID) != nullptr) {
ESP_LOGE(TAG, "[%s] Detected a Tuya AM43 which is not supported, sorry.", ESP_LOGE(TAG, "[%s] Detected a Tuya AM43 which is not supported, sorry.",
@ -75,13 +75,14 @@ void Am43::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_i
if (this->current_sensor_ > 0) { if (this->current_sensor_ > 0) {
if (this->illuminance_ != nullptr) { if (this->illuminance_ != nullptr) {
auto packet = this->encoder_->get_light_level_request(); auto *packet = this->encoder_->get_light_level_request();
auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_,
packet->length, packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, packet->length, packet->data, ESP_GATT_WRITE_TYPE_NO_RSP,
ESP_GATT_AUTH_REQ_NONE); ESP_GATT_AUTH_REQ_NONE);
if (status) if (status) {
ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(),
status); status);
}
} }
this->current_sensor_ = 0; this->current_sensor_ = 0;
} }
@ -99,7 +100,7 @@ void Am43::update() {
} }
if (this->current_sensor_ == 0) { if (this->current_sensor_ == 0) {
if (this->battery_ != nullptr) { if (this->battery_ != nullptr) {
auto packet = this->encoder_->get_battery_level_request(); auto *packet = this->encoder_->get_battery_level_request();
auto status = auto status =
esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, packet->length, esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, packet->length,
packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);

View file

@ -5,7 +5,7 @@ from esphome.const import CONF_ID, CONF_PIN
CODEOWNERS = ["@buxtronix"] CODEOWNERS = ["@buxtronix"]
DEPENDENCIES = ["ble_client"] DEPENDENCIES = ["ble_client"]
AUTO_LOAD = ["am43"] AUTO_LOAD = ["am43", "sensor"]
CONF_INVERT_POSITION = "invert_position" CONF_INVERT_POSITION = "invert_position"

View file

@ -25,15 +25,16 @@ void Am43Component::setup() {
void Am43Component::loop() { 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 *packet = this->encoder_->get_send_pin_request(this->pin_);
auto status = auto status =
esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, packet->length, esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, packet->length,
packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
ESP_LOGI(TAG, "[%s] Logging into AM43", this->get_name().c_str()); ESP_LOGI(TAG, "[%s] Logging into AM43", this->get_name().c_str());
if (status) if (status) {
ESP_LOGW(TAG, "[%s] Error writing set_pin to device, error = %d", this->get_name().c_str(), status); ESP_LOGW(TAG, "[%s] Error writing set_pin to device, error = %d", this->get_name().c_str(), status);
else } else {
this->logged_in_ = true; this->logged_in_ = true;
}
} }
} }
@ -51,7 +52,7 @@ void Am43Component::control(const CoverCall &call) {
return; return;
} }
if (call.get_stop()) { if (call.get_stop()) {
auto packet = this->encoder_->get_stop_request(); auto *packet = this->encoder_->get_stop_request();
auto status = auto status =
esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, packet->length, esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, packet->length,
packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
@ -63,7 +64,7 @@ void Am43Component::control(const CoverCall &call) {
if (this->invert_position_) if (this->invert_position_)
pos = 1 - pos; pos = 1 - pos;
auto packet = this->encoder_->get_set_position_request(100 - (uint8_t)(pos * 100)); auto *packet = this->encoder_->get_set_position_request(100 - (uint8_t)(pos * 100));
auto status = auto status =
esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, packet->length, esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, packet->length,
packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
@ -80,7 +81,7 @@ void Am43Component::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
break; break;
} }
case ESP_GATTC_SEARCH_CMPL_EVT: { case ESP_GATTC_SEARCH_CMPL_EVT: {
auto chr = this->parent_->get_characteristic(AM43_SERVICE_UUID, AM43_CHARACTERISTIC_UUID); auto *chr = this->parent_->get_characteristic(AM43_SERVICE_UUID, AM43_CHARACTERISTIC_UUID);
if (chr == nullptr) { if (chr == nullptr) {
if (this->parent_->get_characteristic(AM43_TUYA_SERVICE_UUID, AM43_TUYA_CHARACTERISTIC_UUID) != nullptr) { if (this->parent_->get_characteristic(AM43_TUYA_SERVICE_UUID, AM43_TUYA_CHARACTERISTIC_UUID) != nullptr) {
ESP_LOGE(TAG, "[%s] Detected a Tuya AM43 which is not supported, sorry.", this->get_name().c_str()); ESP_LOGE(TAG, "[%s] Detected a Tuya AM43 which is not supported, sorry.", this->get_name().c_str());
@ -120,7 +121,7 @@ void Am43Component::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
if (this->decoder_->has_pin_response()) { if (this->decoder_->has_pin_response()) {
if (this->decoder_->pin_ok_) { if (this->decoder_->pin_ok_) {
ESP_LOGI(TAG, "[%s] AM43 pin accepted.", this->get_name().c_str()); ESP_LOGI(TAG, "[%s] AM43 pin accepted.", this->get_name().c_str());
auto packet = this->encoder_->get_position_request(); auto *packet = this->encoder_->get_position_request();
auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_,
packet->length, packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, packet->length, packet->data, ESP_GATT_WRITE_TYPE_NO_RSP,
ESP_GATT_AUTH_REQ_NONE); ESP_GATT_AUTH_REQ_NONE);

View file

@ -40,7 +40,7 @@ void Anova::control(const ClimateCall &call) {
ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status); ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status);
} }
if (call.get_target_temperature().has_value()) { if (call.get_target_temperature().has_value()) {
auto pkt = this->codec_->get_set_target_temp_request(*call.get_target_temperature()); auto *pkt = this->codec_->get_set_target_temp_request(*call.get_target_temperature());
auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, 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); pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
if (status) if (status)
@ -57,7 +57,7 @@ void Anova::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_
break; break;
} }
case ESP_GATTC_SEARCH_CMPL_EVT: { case ESP_GATTC_SEARCH_CMPL_EVT: {
auto chr = this->parent_->get_characteristic(ANOVA_SERVICE_UUID, ANOVA_CHARACTERISTIC_UUID); auto *chr = this->parent_->get_characteristic(ANOVA_SERVICE_UUID, ANOVA_CHARACTERISTIC_UUID);
if (chr == nullptr) { if (chr == nullptr) {
ESP_LOGW(TAG, "[%s] No control service found at device, not an Anova..?", this->get_name().c_str()); ESP_LOGW(TAG, "[%s] No control service found at device, not an Anova..?", this->get_name().c_str());
ESP_LOGW(TAG, "[%s] Note, this component does not currently support Anova Nano.", this->get_name().c_str()); ESP_LOGW(TAG, "[%s] Note, this component does not currently support Anova Nano.", this->get_name().c_str());
@ -114,9 +114,10 @@ void Anova::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_
auto status = auto status =
esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, pkt->length, 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); pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
if (status) if (status) {
ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(),
status); status);
}
} }
} }
break; break;
@ -133,7 +134,7 @@ void Anova::update() {
return; return;
if (this->current_request_ < 2) { if (this->current_request_ < 2) {
auto pkt = this->codec_->get_read_device_status_request(); auto *pkt = this->codec_->get_read_device_status_request();
if (this->current_request_ == 0) if (this->current_request_ == 0)
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_, auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_,

View file

@ -36,7 +36,7 @@ class Anova : public climate::Climate, public esphome::ble_client::BLEClientNode
traits.set_visual_temperature_step(0.1); traits.set_visual_temperature_step(0.1);
return traits; return traits;
} }
void set_unit_of_measurement(const char *); void set_unit_of_measurement(const char *unit);
protected: protected:
std::unique_ptr<AnovaCodec> codec_; std::unique_ptr<AnovaCodec> codec_;

View file

@ -225,9 +225,10 @@ void APDS9960::read_gesture_data_() {
uint8_t fifo_level; uint8_t fifo_level;
APDS9960_WARNING_CHECK(this->read_byte(0xAE, &fifo_level), "Reading FIFO level failed."); APDS9960_WARNING_CHECK(this->read_byte(0xAE, &fifo_level), "Reading FIFO level failed.");
if (fifo_level == 0) if (fifo_level == 0) {
// no data to process // no data to process
return; return;
}
APDS9960_WARNING_CHECK(fifo_level <= 32, "FIFO level has invalid value.") APDS9960_WARNING_CHECK(fifo_level <= 32, "FIFO level has invalid value.")

View file

@ -41,6 +41,7 @@ service APIConnection {
rpc number_command (NumberCommandRequest) returns (void) {} rpc number_command (NumberCommandRequest) returns (void) {}
rpc select_command (SelectCommandRequest) returns (void) {} rpc select_command (SelectCommandRequest) returns (void) {}
rpc button_command (ButtonCommandRequest) returns (void) {} rpc button_command (ButtonCommandRequest) returns (void) {}
rpc lock_command (LockCommandRequest) returns (void) {}
} }
@ -95,6 +96,9 @@ message HelloResponse {
// and only exists for debugging/logging purposes. // and only exists for debugging/logging purposes.
// For example "ESPHome v1.10.0 on ESP8266" // For example "ESPHome v1.10.0 on ESP8266"
string server_info = 3; string server_info = 3;
// The name of the server (App.get_name())
string name = 4;
} }
// Message sent at the beginning of each connection to authenticate the client // Message sent at the beginning of each connection to authenticate the client
@ -525,6 +529,7 @@ message ListEntitiesSwitchResponse {
bool assumed_state = 6; bool assumed_state = 6;
bool disabled_by_default = 7; bool disabled_by_default = 7;
EntityCategory entity_category = 8; EntityCategory entity_category = 8;
string device_class = 9;
} }
message SwitchStateResponse { message SwitchStateResponse {
option (id) = 26; option (id) = 26;
@ -953,6 +958,63 @@ message SelectCommandRequest {
string state = 2; string state = 2;
} }
// ==================== LOCK ====================
enum LockState {
LOCK_STATE_NONE = 0;
LOCK_STATE_LOCKED = 1;
LOCK_STATE_UNLOCKED = 2;
LOCK_STATE_JAMMED = 3;
LOCK_STATE_LOCKING = 4;
LOCK_STATE_UNLOCKING = 5;
}
enum LockCommand {
LOCK_UNLOCK = 0;
LOCK_LOCK = 1;
LOCK_OPEN = 2;
}
message ListEntitiesLockResponse {
option (id) = 58;
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_LOCK";
string object_id = 1;
fixed32 key = 2;
string name = 3;
string unique_id = 4;
string icon = 5;
bool disabled_by_default = 6;
EntityCategory entity_category = 7;
bool assumed_state = 8;
bool supports_open = 9;
bool requires_code = 10;
# Not yet implemented:
string code_format = 11;
}
message LockStateResponse {
option (id) = 59;
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_LOCK";
option (no_delay) = true;
fixed32 key = 1;
LockState state = 2;
}
message LockCommandRequest {
option (id) = 60;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_LOCK";
option (no_delay) = true;
fixed32 key = 1;
LockCommand command = 2;
# Not yet implemented:
bool has_code = 3;
string code = 4;
}
// ==================== BUTTON ==================== // ==================== BUTTON ====================
message ListEntitiesButtonResponse { message ListEntitiesButtonResponse {
option (id) = 61; option (id) = 61;
@ -977,3 +1039,4 @@ message ButtonCommandRequest {
fixed32 key = 1; fixed32 key = 1;
} }

View file

@ -255,7 +255,7 @@ void APIConnection::cover_command(const CoverCommandRequest &msg) {
// Shut-up about usage of deprecated speed_level_to_enum/speed_enum_to_level functions for a bit. // Shut-up about usage of deprecated speed_level_to_enum/speed_enum_to_level functions for a bit.
#pragma GCC diagnostic push #pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations" #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
bool APIConnection::send_fan_state(fan::FanState *fan) { bool APIConnection::send_fan_state(fan::Fan *fan) {
if (!this->state_subscription_) if (!this->state_subscription_)
return false; return false;
@ -273,7 +273,7 @@ bool APIConnection::send_fan_state(fan::FanState *fan) {
resp.direction = static_cast<enums::FanDirection>(fan->direction); resp.direction = static_cast<enums::FanDirection>(fan->direction);
return this->send_fan_state_response(resp); return this->send_fan_state_response(resp);
} }
bool APIConnection::send_fan_info(fan::FanState *fan) { bool APIConnection::send_fan_info(fan::Fan *fan) {
auto traits = fan->get_traits(); auto traits = fan->get_traits();
ListEntitiesFanResponse msg; ListEntitiesFanResponse msg;
msg.key = fan->get_object_id_hash(); msg.key = fan->get_object_id_hash();
@ -290,7 +290,7 @@ bool APIConnection::send_fan_info(fan::FanState *fan) {
return this->send_list_entities_fan_response(msg); return this->send_list_entities_fan_response(msg);
} }
void APIConnection::fan_command(const FanCommandRequest &msg) { void APIConnection::fan_command(const FanCommandRequest &msg) {
fan::FanState *fan = App.get_fan_by_key(msg.key); fan::Fan *fan = App.get_fan_by_key(msg.key);
if (fan == nullptr) if (fan == nullptr)
return; return;
@ -462,6 +462,7 @@ bool APIConnection::send_switch_info(switch_::Switch *a_switch) {
msg.assumed_state = a_switch->assumed_state(); msg.assumed_state = a_switch->assumed_state();
msg.disabled_by_default = a_switch->is_disabled_by_default(); msg.disabled_by_default = a_switch->is_disabled_by_default();
msg.entity_category = static_cast<enums::EntityCategory>(a_switch->get_entity_category()); msg.entity_category = static_cast<enums::EntityCategory>(a_switch->get_entity_category());
msg.device_class = a_switch->get_device_class();
return this->send_list_entities_switch_response(msg); return this->send_list_entities_switch_response(msg);
} }
void APIConnection::switch_command(const SwitchCommandRequest &msg) { void APIConnection::switch_command(const SwitchCommandRequest &msg) {
@ -469,10 +470,11 @@ void APIConnection::switch_command(const SwitchCommandRequest &msg) {
if (a_switch == nullptr) if (a_switch == nullptr)
return; return;
if (msg.state) if (msg.state) {
a_switch->turn_on(); a_switch->turn_on();
else } else {
a_switch->turn_off(); a_switch->turn_off();
}
} }
#endif #endif
@ -699,6 +701,49 @@ void APIConnection::button_command(const ButtonCommandRequest &msg) {
} }
#endif #endif
#ifdef USE_LOCK
bool APIConnection::send_lock_state(lock::Lock *a_lock, lock::LockState state) {
if (!this->state_subscription_)
return false;
LockStateResponse resp{};
resp.key = a_lock->get_object_id_hash();
resp.state = static_cast<enums::LockState>(state);
return this->send_lock_state_response(resp);
}
bool APIConnection::send_lock_info(lock::Lock *a_lock) {
ListEntitiesLockResponse msg;
msg.key = a_lock->get_object_id_hash();
msg.object_id = a_lock->get_object_id();
msg.name = a_lock->get_name();
msg.unique_id = get_default_unique_id("lock", a_lock);
msg.icon = a_lock->get_icon();
msg.assumed_state = a_lock->traits.get_assumed_state();
msg.disabled_by_default = a_lock->is_disabled_by_default();
msg.entity_category = static_cast<enums::EntityCategory>(a_lock->get_entity_category());
msg.supports_open = a_lock->traits.get_supports_open();
msg.requires_code = a_lock->traits.get_requires_code();
return this->send_list_entities_lock_response(msg);
}
void APIConnection::lock_command(const LockCommandRequest &msg) {
lock::Lock *a_lock = App.get_lock_by_key(msg.key);
if (a_lock == nullptr)
return;
switch (msg.command) {
case enums::LOCK_UNLOCK:
a_lock->unlock();
break;
case enums::LOCK_LOCK:
a_lock->lock();
break;
case enums::LOCK_OPEN:
a_lock->open();
break;
}
}
#endif
#ifdef USE_ESP32_CAMERA #ifdef USE_ESP32_CAMERA
void APIConnection::send_camera_state(std::shared_ptr<esp32_camera::CameraImage> image) { void APIConnection::send_camera_state(std::shared_ptr<esp32_camera::CameraImage> image) {
if (!this->state_subscription_) if (!this->state_subscription_)
@ -766,6 +811,8 @@ HelloResponse APIConnection::hello(const HelloRequest &msg) {
resp.api_version_major = 1; resp.api_version_major = 1;
resp.api_version_minor = 6; resp.api_version_minor = 6;
resp.server_info = App.get_name() + " (esphome v" ESPHOME_VERSION ")"; resp.server_info = App.get_name() + " (esphome v" ESPHOME_VERSION ")";
resp.name = App.get_name();
this->connection_state_ = ConnectionState::CONNECTED; this->connection_state_ = ConnectionState::CONNECTED;
return resp; return resp;
} }
@ -803,15 +850,16 @@ DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) {
resp.project_version = ESPHOME_PROJECT_VERSION; resp.project_version = ESPHOME_PROJECT_VERSION;
#endif #endif
#ifdef USE_WEBSERVER #ifdef USE_WEBSERVER
resp.webserver_port = WEBSERVER_PORT; resp.webserver_port = USE_WEBSERVER_PORT;
#endif #endif
return resp; return resp;
} }
void APIConnection::on_home_assistant_state_response(const HomeAssistantStateResponse &msg) { void APIConnection::on_home_assistant_state_response(const HomeAssistantStateResponse &msg) {
for (auto &it : this->parent_->get_state_subs()) for (auto &it : this->parent_->get_state_subs()) {
if (it.entity_id == msg.entity_id && it.attribute.value() == msg.attribute) { if (it.entity_id == msg.entity_id && it.attribute.value() == msg.attribute) {
it.callback(msg.state); it.callback(msg.state);
} }
}
} }
void APIConnection::execute_service(const ExecuteServiceRequest &msg) { void APIConnection::execute_service(const ExecuteServiceRequest &msg) {
bool found = false; bool found = false;

View file

@ -32,8 +32,8 @@ class APIConnection : public APIServerConnection {
void cover_command(const CoverCommandRequest &msg) override; void cover_command(const CoverCommandRequest &msg) override;
#endif #endif
#ifdef USE_FAN #ifdef USE_FAN
bool send_fan_state(fan::FanState *fan); bool send_fan_state(fan::Fan *fan);
bool send_fan_info(fan::FanState *fan); bool send_fan_info(fan::Fan *fan);
void fan_command(const FanCommandRequest &msg) override; void fan_command(const FanCommandRequest &msg) override;
#endif #endif
#ifdef USE_LIGHT #ifdef USE_LIGHT
@ -77,6 +77,11 @@ class APIConnection : public APIServerConnection {
#ifdef USE_BUTTON #ifdef USE_BUTTON
bool send_button_info(button::Button *button); bool send_button_info(button::Button *button);
void button_command(const ButtonCommandRequest &msg) override; void button_command(const ButtonCommandRequest &msg) override;
#endif
#ifdef USE_LOCK
bool send_lock_state(lock::Lock *a_lock, lock::LockState state);
bool send_lock_info(lock::Lock *a_lock);
void lock_command(const LockCommandRequest &msg) override;
#endif #endif
bool send_log_message(int level, const char *tag, const char *line); bool send_log_message(int level, const char *tag, const char *line);
void send_homeassistant_service_call(const HomeassistantServiceResponse &call) { void send_homeassistant_service_call(const HomeassistantServiceResponse &call) {

View file

@ -3,6 +3,7 @@
#include "esphome/core/log.h" #include "esphome/core/log.h"
#include "esphome/core/hal.h" #include "esphome/core/hal.h"
#include "esphome/core/helpers.h" #include "esphome/core/helpers.h"
#include "esphome/core/application.h"
#include "proto.h" #include "proto.h"
#include <cstring> #include <cstring>
@ -302,9 +303,16 @@ APIError APINoiseFrameHelper::state_action_() {
} }
if (state_ == State::SERVER_HELLO) { if (state_ == State::SERVER_HELLO) {
// send server hello // send server hello
uint8_t msg[1]; std::vector<uint8_t> msg;
msg[0] = 0x01; // chosen proto // chosen proto
aerr = write_frame_(msg, 1); msg.push_back(0x01);
// node name, terminated by null byte
const std::string &name = App.get_name();
const uint8_t *name_ptr = reinterpret_cast<const uint8_t *>(name.c_str());
msg.insert(msg.end(), name_ptr, name_ptr + name.size() + 1);
aerr = write_frame_(msg.data(), msg.size());
if (aerr != APIError::OK) if (aerr != APIError::OK)
return aerr; return aerr;

View file

@ -278,6 +278,36 @@ template<> const char *proto_enum_to_string<enums::NumberMode>(enums::NumberMode
return "UNKNOWN"; return "UNKNOWN";
} }
} }
template<> const char *proto_enum_to_string<enums::LockState>(enums::LockState value) {
switch (value) {
case enums::LOCK_STATE_NONE:
return "LOCK_STATE_NONE";
case enums::LOCK_STATE_LOCKED:
return "LOCK_STATE_LOCKED";
case enums::LOCK_STATE_UNLOCKED:
return "LOCK_STATE_UNLOCKED";
case enums::LOCK_STATE_JAMMED:
return "LOCK_STATE_JAMMED";
case enums::LOCK_STATE_LOCKING:
return "LOCK_STATE_LOCKING";
case enums::LOCK_STATE_UNLOCKING:
return "LOCK_STATE_UNLOCKING";
default:
return "UNKNOWN";
}
}
template<> const char *proto_enum_to_string<enums::LockCommand>(enums::LockCommand value) {
switch (value) {
case enums::LOCK_UNLOCK:
return "LOCK_UNLOCK";
case enums::LOCK_LOCK:
return "LOCK_LOCK";
case enums::LOCK_OPEN:
return "LOCK_OPEN";
default:
return "UNKNOWN";
}
}
bool HelloRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) { bool HelloRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) { switch (field_id) {
case 1: { case 1: {
@ -319,6 +349,10 @@ bool HelloResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value)
this->server_info = value.as_string(); this->server_info = value.as_string();
return true; return true;
} }
case 4: {
this->name = value.as_string();
return true;
}
default: default:
return false; return false;
} }
@ -327,6 +361,7 @@ void HelloResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_uint32(1, this->api_version_major); buffer.encode_uint32(1, this->api_version_major);
buffer.encode_uint32(2, this->api_version_minor); buffer.encode_uint32(2, this->api_version_minor);
buffer.encode_string(3, this->server_info); buffer.encode_string(3, this->server_info);
buffer.encode_string(4, this->name);
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void HelloResponse::dump_to(std::string &out) const { void HelloResponse::dump_to(std::string &out) const {
@ -345,6 +380,10 @@ void HelloResponse::dump_to(std::string &out) const {
out.append(" server_info: "); out.append(" server_info: ");
out.append("'").append(this->server_info).append("'"); out.append("'").append(this->server_info).append("'");
out.append("\n"); out.append("\n");
out.append(" name: ");
out.append("'").append(this->name).append("'");
out.append("\n");
out.append("}"); out.append("}");
} }
#endif #endif
@ -2138,6 +2177,10 @@ bool ListEntitiesSwitchResponse::decode_length(uint32_t field_id, ProtoLengthDel
this->icon = value.as_string(); this->icon = value.as_string();
return true; return true;
} }
case 9: {
this->device_class = value.as_string();
return true;
}
default: default:
return false; return false;
} }
@ -2161,6 +2204,7 @@ void ListEntitiesSwitchResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_bool(6, this->assumed_state); buffer.encode_bool(6, this->assumed_state);
buffer.encode_bool(7, this->disabled_by_default); buffer.encode_bool(7, this->disabled_by_default);
buffer.encode_enum<enums::EntityCategory>(8, this->entity_category); buffer.encode_enum<enums::EntityCategory>(8, this->entity_category);
buffer.encode_string(9, this->device_class);
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesSwitchResponse::dump_to(std::string &out) const { void ListEntitiesSwitchResponse::dump_to(std::string &out) const {
@ -2198,6 +2242,10 @@ void ListEntitiesSwitchResponse::dump_to(std::string &out) const {
out.append(" entity_category: "); out.append(" entity_category: ");
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category)); out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
out.append("\n"); out.append("\n");
out.append(" device_class: ");
out.append("'").append(this->device_class).append("'");
out.append("\n");
out.append("}"); out.append("}");
} }
#endif #endif
@ -4177,6 +4225,234 @@ void SelectCommandRequest::dump_to(std::string &out) const {
out.append("}"); out.append("}");
} }
#endif #endif
bool ListEntitiesLockResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 6: {
this->disabled_by_default = value.as_bool();
return true;
}
case 7: {
this->entity_category = value.as_enum<enums::EntityCategory>();
return true;
}
case 8: {
this->assumed_state = value.as_bool();
return true;
}
case 9: {
this->supports_open = value.as_bool();
return true;
}
case 10: {
this->requires_code = value.as_bool();
return true;
}
default:
return false;
}
}
bool ListEntitiesLockResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 1: {
this->object_id = value.as_string();
return true;
}
case 3: {
this->name = value.as_string();
return true;
}
case 4: {
this->unique_id = value.as_string();
return true;
}
case 5: {
this->icon = value.as_string();
return true;
}
case 11: {
this->code_format = value.as_string();
return true;
}
default:
return false;
}
}
bool ListEntitiesLockResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
switch (field_id) {
case 2: {
this->key = value.as_fixed32();
return true;
}
default:
return false;
}
}
void ListEntitiesLockResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(1, this->object_id);
buffer.encode_fixed32(2, this->key);
buffer.encode_string(3, this->name);
buffer.encode_string(4, this->unique_id);
buffer.encode_string(5, this->icon);
buffer.encode_bool(6, this->disabled_by_default);
buffer.encode_enum<enums::EntityCategory>(7, this->entity_category);
buffer.encode_bool(8, this->assumed_state);
buffer.encode_bool(9, this->supports_open);
buffer.encode_bool(10, this->requires_code);
buffer.encode_string(11, this->code_format);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesLockResponse::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64];
out.append("ListEntitiesLockResponse {\n");
out.append(" object_id: ");
out.append("'").append(this->object_id).append("'");
out.append("\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
out.append(buffer);
out.append("\n");
out.append(" name: ");
out.append("'").append(this->name).append("'");
out.append("\n");
out.append(" unique_id: ");
out.append("'").append(this->unique_id).append("'");
out.append("\n");
out.append(" icon: ");
out.append("'").append(this->icon).append("'");
out.append("\n");
out.append(" disabled_by_default: ");
out.append(YESNO(this->disabled_by_default));
out.append("\n");
out.append(" entity_category: ");
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
out.append("\n");
out.append(" assumed_state: ");
out.append(YESNO(this->assumed_state));
out.append("\n");
out.append(" supports_open: ");
out.append(YESNO(this->supports_open));
out.append("\n");
out.append(" requires_code: ");
out.append(YESNO(this->requires_code));
out.append("\n");
out.append(" code_format: ");
out.append("'").append(this->code_format).append("'");
out.append("\n");
out.append("}");
}
#endif
bool LockStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 2: {
this->state = value.as_enum<enums::LockState>();
return true;
}
default:
return false;
}
}
bool LockStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
switch (field_id) {
case 1: {
this->key = value.as_fixed32();
return true;
}
default:
return false;
}
}
void LockStateResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_fixed32(1, this->key);
buffer.encode_enum<enums::LockState>(2, this->state);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void LockStateResponse::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64];
out.append("LockStateResponse {\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
out.append(buffer);
out.append("\n");
out.append(" state: ");
out.append(proto_enum_to_string<enums::LockState>(this->state));
out.append("\n");
out.append("}");
}
#endif
bool LockCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 2: {
this->command = value.as_enum<enums::LockCommand>();
return true;
}
case 3: {
this->has_code = value.as_bool();
return true;
}
default:
return false;
}
}
bool LockCommandRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 4: {
this->code = value.as_string();
return true;
}
default:
return false;
}
}
bool LockCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
switch (field_id) {
case 1: {
this->key = value.as_fixed32();
return true;
}
default:
return false;
}
}
void LockCommandRequest::encode(ProtoWriteBuffer buffer) const {
buffer.encode_fixed32(1, this->key);
buffer.encode_enum<enums::LockCommand>(2, this->command);
buffer.encode_bool(3, this->has_code);
buffer.encode_string(4, this->code);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void LockCommandRequest::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64];
out.append("LockCommandRequest {\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
out.append(buffer);
out.append("\n");
out.append(" command: ");
out.append(proto_enum_to_string<enums::LockCommand>(this->command));
out.append("\n");
out.append(" has_code: ");
out.append(YESNO(this->has_code));
out.append("\n");
out.append(" code: ");
out.append("'").append(this->code).append("'");
out.append("\n");
out.append("}");
}
#endif
bool ListEntitiesButtonResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { bool ListEntitiesButtonResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) { switch (field_id) {
case 6: { case 6: {
@ -4239,7 +4515,7 @@ void ListEntitiesButtonResponse::encode(ProtoWriteBuffer buffer) const {
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesButtonResponse::dump_to(std::string &out) const { void ListEntitiesButtonResponse::dump_to(std::string &out) const {
char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("ListEntitiesButtonResponse {\n"); out.append("ListEntitiesButtonResponse {\n");
out.append(" object_id: "); out.append(" object_id: ");
out.append("'").append(this->object_id).append("'"); out.append("'").append(this->object_id).append("'");
@ -4289,7 +4565,7 @@ bool ButtonCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
void ButtonCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); } void ButtonCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void ButtonCommandRequest::dump_to(std::string &out) const { void ButtonCommandRequest::dump_to(std::string &out) const {
char buffer[64]; __attribute__((unused)) char buffer[64];
out.append("ButtonCommandRequest {\n"); out.append("ButtonCommandRequest {\n");
out.append(" key: "); out.append(" key: ");
sprintf(buffer, "%u", this->key); sprintf(buffer, "%u", this->key);

View file

@ -128,6 +128,19 @@ enum NumberMode : uint32_t {
NUMBER_MODE_BOX = 1, NUMBER_MODE_BOX = 1,
NUMBER_MODE_SLIDER = 2, NUMBER_MODE_SLIDER = 2,
}; };
enum LockState : uint32_t {
LOCK_STATE_NONE = 0,
LOCK_STATE_LOCKED = 1,
LOCK_STATE_UNLOCKED = 2,
LOCK_STATE_JAMMED = 3,
LOCK_STATE_LOCKING = 4,
LOCK_STATE_UNLOCKING = 5,
};
enum LockCommand : uint32_t {
LOCK_UNLOCK = 0,
LOCK_LOCK = 1,
LOCK_OPEN = 2,
};
} // namespace enums } // namespace enums
@ -147,6 +160,7 @@ class HelloResponse : public ProtoMessage {
uint32_t api_version_major{0}; uint32_t api_version_major{0};
uint32_t api_version_minor{0}; uint32_t api_version_minor{0};
std::string server_info{}; std::string server_info{};
std::string name{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -566,6 +580,7 @@ class ListEntitiesSwitchResponse : public ProtoMessage {
bool assumed_state{false}; bool assumed_state{false};
bool disabled_by_default{false}; bool disabled_by_default{false};
enums::EntityCategory entity_category{}; enums::EntityCategory entity_category{};
std::string device_class{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -1048,6 +1063,58 @@ class SelectCommandRequest : public ProtoMessage {
bool decode_32bit(uint32_t field_id, Proto32Bit value) override; bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
}; };
class ListEntitiesLockResponse : public ProtoMessage {
public:
std::string object_id{};
uint32_t key{0};
std::string name{};
std::string unique_id{};
std::string icon{};
bool disabled_by_default{false};
enums::EntityCategory entity_category{};
bool assumed_state{false};
bool supports_open{false};
bool requires_code{false};
std::string code_format{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class LockStateResponse : public ProtoMessage {
public:
uint32_t key{0};
enums::LockState state{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class LockCommandRequest : public ProtoMessage {
public:
uint32_t key{0};
enums::LockCommand command{};
bool has_code{false};
std::string code{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class ListEntitiesButtonResponse : public ProtoMessage { class ListEntitiesButtonResponse : public ProtoMessage {
public: public:
std::string object_id{}; std::string object_id{};

View file

@ -282,6 +282,24 @@ bool APIServerConnectionBase::send_select_state_response(const SelectStateRespon
#endif #endif
#ifdef USE_SELECT #ifdef USE_SELECT
#endif #endif
#ifdef USE_LOCK
bool APIServerConnectionBase::send_list_entities_lock_response(const ListEntitiesLockResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_lock_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesLockResponse>(msg, 58);
}
#endif
#ifdef USE_LOCK
bool APIServerConnectionBase::send_lock_state_response(const LockStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_lock_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<LockStateResponse>(msg, 59);
}
#endif
#ifdef USE_LOCK
#endif
#ifdef USE_BUTTON #ifdef USE_BUTTON
bool APIServerConnectionBase::send_list_entities_button_response(const ListEntitiesButtonResponse &msg) { bool APIServerConnectionBase::send_list_entities_button_response(const ListEntitiesButtonResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
@ -523,6 +541,17 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
ESP_LOGVV(TAG, "on_select_command_request: %s", msg.dump().c_str()); ESP_LOGVV(TAG, "on_select_command_request: %s", msg.dump().c_str());
#endif #endif
this->on_select_command_request(msg); this->on_select_command_request(msg);
#endif
break;
}
case 60: {
#ifdef USE_LOCK
LockCommandRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_lock_command_request: %s", msg.dump().c_str());
#endif
this->on_lock_command_request(msg);
#endif #endif
break; break;
} }
@ -771,6 +800,19 @@ void APIServerConnection::on_button_command_request(const ButtonCommandRequest &
this->button_command(msg); this->button_command(msg);
} }
#endif #endif
#ifdef USE_LOCK
void APIServerConnection::on_lock_command_request(const LockCommandRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->lock_command(msg);
}
#endif
} // namespace api } // namespace api
} // namespace esphome } // namespace esphome

View file

@ -130,6 +130,15 @@ class APIServerConnectionBase : public ProtoService {
#ifdef USE_SELECT #ifdef USE_SELECT
virtual void on_select_command_request(const SelectCommandRequest &value){}; virtual void on_select_command_request(const SelectCommandRequest &value){};
#endif #endif
#ifdef USE_LOCK
bool send_list_entities_lock_response(const ListEntitiesLockResponse &msg);
#endif
#ifdef USE_LOCK
bool send_lock_state_response(const LockStateResponse &msg);
#endif
#ifdef USE_LOCK
virtual void on_lock_command_request(const LockCommandRequest &value){};
#endif
#ifdef USE_BUTTON #ifdef USE_BUTTON
bool send_list_entities_button_response(const ListEntitiesButtonResponse &msg); bool send_list_entities_button_response(const ListEntitiesButtonResponse &msg);
#endif #endif
@ -180,6 +189,9 @@ class APIServerConnection : public APIServerConnectionBase {
#endif #endif
#ifdef USE_BUTTON #ifdef USE_BUTTON
virtual void button_command(const ButtonCommandRequest &msg) = 0; virtual void button_command(const ButtonCommandRequest &msg) = 0;
#endif
#ifdef USE_LOCK
virtual void lock_command(const LockCommandRequest &msg) = 0;
#endif #endif
protected: protected:
void on_hello_request(const HelloRequest &msg) override; void on_hello_request(const HelloRequest &msg) override;
@ -221,6 +233,9 @@ class APIServerConnection : public APIServerConnectionBase {
#ifdef USE_BUTTON #ifdef USE_BUTTON
void on_button_command_request(const ButtonCommandRequest &msg) override; void on_button_command_request(const ButtonCommandRequest &msg) override;
#endif #endif
#ifdef USE_LOCK
void on_lock_command_request(const LockCommandRequest &msg) override;
#endif
}; };
} // namespace api } // namespace api

View file

@ -24,7 +24,7 @@ static const char *const TAG = "api";
void APIServer::setup() { void APIServer::setup() {
ESP_LOGCONFIG(TAG, "Setting up Home Assistant API server..."); ESP_LOGCONFIG(TAG, "Setting up Home Assistant API server...");
this->setup_controller(); this->setup_controller();
socket_ = socket::socket(AF_INET, SOCK_STREAM, 0); socket_ = socket::socket_ip(SOCK_STREAM, 0);
if (socket_ == nullptr) { if (socket_ == nullptr) {
ESP_LOGW(TAG, "Could not create socket."); ESP_LOGW(TAG, "Could not create socket.");
this->mark_failed(); this->mark_failed();
@ -43,13 +43,16 @@ void APIServer::setup() {
return; return;
} }
struct sockaddr_in server; struct sockaddr_storage server;
memset(&server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_addr.s_addr = ESPHOME_INADDR_ANY;
server.sin_port = htons(this->port_);
err = socket_->bind((struct sockaddr *) &server, sizeof(server)); socklen_t sl = socket::set_sockaddr_any((struct sockaddr *) &server, sizeof(server), htons(this->port_));
if (sl == 0) {
ESP_LOGW(TAG, "Socket unable to set sockaddr: errno %d", errno);
this->mark_failed();
return;
}
err = socket_->bind((struct sockaddr *) &server, sl);
if (err != 0) { if (err != 0) {
ESP_LOGW(TAG, "Socket unable to bind: errno %d", errno); ESP_LOGW(TAG, "Socket unable to bind: errno %d", errno);
this->mark_failed(); this->mark_failed();
@ -80,9 +83,10 @@ void APIServer::setup() {
if (esp32_camera::global_esp32_camera != nullptr && !esp32_camera::global_esp32_camera->is_internal()) { if (esp32_camera::global_esp32_camera != nullptr && !esp32_camera::global_esp32_camera->is_internal()) {
esp32_camera::global_esp32_camera->add_image_callback( esp32_camera::global_esp32_camera->add_image_callback(
[this](const std::shared_ptr<esp32_camera::CameraImage> &image) { [this](const std::shared_ptr<esp32_camera::CameraImage> &image) {
for (auto &c : this->clients_) for (auto &c : this->clients_) {
if (!c->remove_) if (!c->remove_)
c->send_camera_state(image); c->send_camera_state(image);
}
}); });
} }
#endif #endif
@ -188,7 +192,7 @@ void APIServer::on_cover_update(cover::Cover *obj) {
#endif #endif
#ifdef USE_FAN #ifdef USE_FAN
void APIServer::on_fan_update(fan::FanState *obj) { void APIServer::on_fan_update(fan::Fan *obj) {
if (obj->is_internal()) if (obj->is_internal())
return; return;
for (auto &c : this->clients_) for (auto &c : this->clients_)
@ -259,6 +263,15 @@ void APIServer::on_select_update(select::Select *obj, const std::string &state)
} }
#endif #endif
#ifdef USE_LOCK
void APIServer::on_lock_update(lock::Lock *obj) {
if (obj->is_internal())
return;
for (auto &c : this->clients_)
c->send_lock_state(obj, obj->state);
}
#endif
float APIServer::get_setup_priority() const { return setup_priority::AFTER_WIFI; } float APIServer::get_setup_priority() const { return setup_priority::AFTER_WIFI; }
void APIServer::set_port(uint16_t port) { this->port_ = port; } void APIServer::set_port(uint16_t port) { this->port_ = port; }
APIServer *global_api_server = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) APIServer *global_api_server = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)

View file

@ -44,7 +44,7 @@ class APIServer : public Component, public Controller {
void on_cover_update(cover::Cover *obj) override; void on_cover_update(cover::Cover *obj) override;
#endif #endif
#ifdef USE_FAN #ifdef USE_FAN
void on_fan_update(fan::FanState *obj) override; void on_fan_update(fan::Fan *obj) override;
#endif #endif
#ifdef USE_LIGHT #ifdef USE_LIGHT
void on_light_update(light::LightState *obj) override; void on_light_update(light::LightState *obj) override;
@ -66,6 +66,9 @@ class APIServer : public Component, public Controller {
#endif #endif
#ifdef USE_SELECT #ifdef USE_SELECT
void on_select_update(select::Select *obj, const std::string &state) override; void on_select_update(select::Select *obj, const std::string &state) override;
#endif
#ifdef USE_LOCK
void on_lock_update(lock::Lock *obj) override;
#endif #endif
void send_homeassistant_service_call(const HomeassistantServiceResponse &call); void send_homeassistant_service_call(const HomeassistantServiceResponse &call);
void register_user_service(UserServiceDescriptor *descriptor) { this->user_services_.push_back(descriptor); } void register_user_service(UserServiceDescriptor *descriptor) { this->user_services_.push_back(descriptor); }

View file

@ -16,7 +16,7 @@ bool ListEntitiesIterator::on_binary_sensor(binary_sensor::BinarySensor *binary_
bool ListEntitiesIterator::on_cover(cover::Cover *cover) { return this->client_->send_cover_info(cover); } bool ListEntitiesIterator::on_cover(cover::Cover *cover) { return this->client_->send_cover_info(cover); }
#endif #endif
#ifdef USE_FAN #ifdef USE_FAN
bool ListEntitiesIterator::on_fan(fan::FanState *fan) { return this->client_->send_fan_info(fan); } bool ListEntitiesIterator::on_fan(fan::Fan *fan) { return this->client_->send_fan_info(fan); }
#endif #endif
#ifdef USE_LIGHT #ifdef USE_LIGHT
bool ListEntitiesIterator::on_light(light::LightState *light) { return this->client_->send_light_info(light); } bool ListEntitiesIterator::on_light(light::LightState *light) { return this->client_->send_light_info(light); }
@ -35,6 +35,9 @@ bool ListEntitiesIterator::on_text_sensor(text_sensor::TextSensor *text_sensor)
return this->client_->send_text_sensor_info(text_sensor); return this->client_->send_text_sensor_info(text_sensor);
} }
#endif #endif
#ifdef USE_LOCK
bool ListEntitiesIterator::on_lock(lock::Lock *a_lock) { return this->client_->send_lock_info(a_lock); }
#endif
bool ListEntitiesIterator::on_end() { return this->client_->send_list_info_done(); } bool ListEntitiesIterator::on_end() { return this->client_->send_list_info_done(); }
ListEntitiesIterator::ListEntitiesIterator(APIServer *server, APIConnection *client) ListEntitiesIterator::ListEntitiesIterator(APIServer *server, APIConnection *client)

View file

@ -19,7 +19,7 @@ class ListEntitiesIterator : public ComponentIterator {
bool on_cover(cover::Cover *cover) override; bool on_cover(cover::Cover *cover) override;
#endif #endif
#ifdef USE_FAN #ifdef USE_FAN
bool on_fan(fan::FanState *fan) override; bool on_fan(fan::Fan *fan) override;
#endif #endif
#ifdef USE_LIGHT #ifdef USE_LIGHT
bool on_light(light::LightState *light) override; bool on_light(light::LightState *light) override;
@ -48,6 +48,9 @@ class ListEntitiesIterator : public ComponentIterator {
#endif #endif
#ifdef USE_SELECT #ifdef USE_SELECT
bool on_select(select::Select *select) override; bool on_select(select::Select *select) override;
#endif
#ifdef USE_LOCK
bool on_lock(lock::Lock *a_lock) override;
#endif #endif
bool on_end() override; bool on_end() override;

View file

@ -55,17 +55,19 @@ class ProtoVarInt {
} }
int32_t as_sint32() const { int32_t as_sint32() const {
// with ZigZag encoding // with ZigZag encoding
if (this->value_ & 1) if (this->value_ & 1) {
return static_cast<int32_t>(~(this->value_ >> 1)); return static_cast<int32_t>(~(this->value_ >> 1));
else } else {
return static_cast<int32_t>(this->value_ >> 1); return static_cast<int32_t>(this->value_ >> 1);
}
} }
int64_t as_sint64() const { int64_t as_sint64() const {
// with ZigZag encoding // with ZigZag encoding
if (this->value_ & 1) if (this->value_ & 1) {
return static_cast<int64_t>(~(this->value_ >> 1)); return static_cast<int64_t>(~(this->value_ >> 1));
else } else {
return static_cast<int64_t>(this->value_ >> 1); return static_cast<int64_t>(this->value_ >> 1);
}
} }
void encode(std::vector<uint8_t> &out) { void encode(std::vector<uint8_t> &out) {
uint32_t val = this->value_; uint32_t val = this->value_;
@ -220,10 +222,11 @@ class ProtoWriteBuffer {
} }
void encode_sint32(uint32_t field_id, int32_t value, bool force = false) { void encode_sint32(uint32_t field_id, int32_t value, bool force = false) {
uint32_t uvalue; uint32_t uvalue;
if (value < 0) if (value < 0) {
uvalue = ~(value << 1); uvalue = ~(value << 1);
else } else {
uvalue = value << 1; uvalue = value << 1;
}
this->encode_uint32(field_id, uvalue, force); this->encode_uint32(field_id, uvalue, force);
} }
template<class C> void encode_message(uint32_t field_id, const C &value, bool force = false) { template<class C> void encode_message(uint32_t field_id, const C &value, bool force = false) {

View file

@ -14,7 +14,7 @@ bool InitialStateIterator::on_binary_sensor(binary_sensor::BinarySensor *binary_
bool InitialStateIterator::on_cover(cover::Cover *cover) { return this->client_->send_cover_state(cover); } bool InitialStateIterator::on_cover(cover::Cover *cover) { return this->client_->send_cover_state(cover); }
#endif #endif
#ifdef USE_FAN #ifdef USE_FAN
bool InitialStateIterator::on_fan(fan::FanState *fan) { return this->client_->send_fan_state(fan); } bool InitialStateIterator::on_fan(fan::Fan *fan) { return this->client_->send_fan_state(fan); }
#endif #endif
#ifdef USE_LIGHT #ifdef USE_LIGHT
bool InitialStateIterator::on_light(light::LightState *light) { return this->client_->send_light_state(light); } bool InitialStateIterator::on_light(light::LightState *light) { return this->client_->send_light_state(light); }
@ -47,6 +47,9 @@ bool InitialStateIterator::on_select(select::Select *select) {
return this->client_->send_select_state(select, select->state); return this->client_->send_select_state(select, select->state);
} }
#endif #endif
#ifdef USE_LOCK
bool InitialStateIterator::on_lock(lock::Lock *a_lock) { return this->client_->send_lock_state(a_lock, a_lock->state); }
#endif
InitialStateIterator::InitialStateIterator(APIServer *server, APIConnection *client) InitialStateIterator::InitialStateIterator(APIServer *server, APIConnection *client)
: ComponentIterator(server), client_(client) {} : ComponentIterator(server), client_(client) {}

View file

@ -20,7 +20,7 @@ class InitialStateIterator : public ComponentIterator {
bool on_cover(cover::Cover *cover) override; bool on_cover(cover::Cover *cover) override;
#endif #endif
#ifdef USE_FAN #ifdef USE_FAN
bool on_fan(fan::FanState *fan) override; bool on_fan(fan::Fan *fan) override;
#endif #endif
#ifdef USE_LIGHT #ifdef USE_LIGHT
bool on_light(light::LightState *light) override; bool on_light(light::LightState *light) override;
@ -45,6 +45,9 @@ class InitialStateIterator : public ComponentIterator {
#endif #endif
#ifdef USE_SELECT #ifdef USE_SELECT
bool on_select(select::Select *select) override; bool on_select(select::Select *select) override;
#endif
#ifdef USE_LOCK
bool on_lock(lock::Lock *a_lock) override;
#endif #endif
protected: protected:
APIConnection *client_; APIConnection *client_;

View file

@ -52,7 +52,7 @@ template<typename... Ts> class UserServiceBase : public UserServiceDescriptor {
protected: protected:
virtual void execute(Ts... x) = 0; virtual void execute(Ts... x) = 0;
template<int... S> void execute_(std::vector<ExecuteServiceArgument> args, seq<S...>) { template<int... S> void execute_(std::vector<ExecuteServiceArgument> args, seq<S...> type) {
this->execute((get_execute_arg_value<Ts>(args[S]))...); this->execute((get_execute_arg_value<Ts>(args[S]))...);
} }

View file

@ -212,6 +212,21 @@ void ComponentIterator::advance() {
} }
} }
break; break;
#endif
#ifdef USE_LOCK
case IteratorState::LOCK:
if (this->at_ >= App.get_locks().size()) {
advance_platform = true;
} else {
auto *a_lock = App.get_locks()[this->at_];
if (a_lock->is_internal()) {
success = true;
break;
} else {
success = this->on_lock(a_lock);
}
}
break;
#endif #endif
case IteratorState::MAX: case IteratorState::MAX:
if (this->on_end()) { if (this->on_end()) {

View file

@ -27,7 +27,7 @@ class ComponentIterator {
virtual bool on_cover(cover::Cover *cover) = 0; virtual bool on_cover(cover::Cover *cover) = 0;
#endif #endif
#ifdef USE_FAN #ifdef USE_FAN
virtual bool on_fan(fan::FanState *fan) = 0; virtual bool on_fan(fan::Fan *fan) = 0;
#endif #endif
#ifdef USE_LIGHT #ifdef USE_LIGHT
virtual bool on_light(light::LightState *light) = 0; virtual bool on_light(light::LightState *light) = 0;
@ -56,6 +56,9 @@ class ComponentIterator {
#endif #endif
#ifdef USE_SELECT #ifdef USE_SELECT
virtual bool on_select(select::Select *select) = 0; virtual bool on_select(select::Select *select) = 0;
#endif
#ifdef USE_LOCK
virtual bool on_lock(lock::Lock *a_lock) = 0;
#endif #endif
virtual bool on_end(); virtual bool on_end();
@ -99,6 +102,9 @@ class ComponentIterator {
#endif #endif
#ifdef USE_SELECT #ifdef USE_SELECT
SELECT, SELECT,
#endif
#ifdef USE_LOCK
LOCK,
#endif #endif
MAX, MAX,
} state_{IteratorState::NONE}; } state_{IteratorState::NONE};

View file

@ -58,10 +58,11 @@ void AS3935Component::loop() {
void AS3935Component::write_indoor(bool indoor) { void AS3935Component::write_indoor(bool indoor) {
ESP_LOGV(TAG, "Setting indoor to %d", indoor); ESP_LOGV(TAG, "Setting indoor to %d", indoor);
if (indoor) if (indoor) {
this->write_register(AFE_GAIN, GAIN_MASK, INDOOR, 1); this->write_register(AFE_GAIN, GAIN_MASK, INDOOR, 1);
else } else {
this->write_register(AFE_GAIN, GAIN_MASK, OUTDOOR, 1); this->write_register(AFE_GAIN, GAIN_MASK, OUTDOOR, 1);
}
} }
// REG0x01, bits[3:0], manufacturer default: 0010 (2). // REG0x01, bits[3:0], manufacturer default: 0010 (2).
// This setting determines the threshold for events that trigger the // This setting determines the threshold for events that trigger the

View file

@ -97,7 +97,7 @@ void BalluClimate::transmit_state() {
// Send code // Send code
auto transmit = this->transmitter_->transmit(); auto transmit = this->transmitter_->transmit();
auto data = transmit.get_data(); auto *data = transmit.get_data();
data->set_carrier_frequency(38000); data->set_carrier_frequency(38000);
@ -130,10 +130,10 @@ bool BalluClimate::on_receive(remote_base::RemoteReceiveData data) {
for (int i = 0; i < BALLU_STATE_LENGTH; i++) { for (int i = 0; i < BALLU_STATE_LENGTH; i++) {
// Read bit // Read bit
for (int j = 0; j < 8; j++) { for (int j = 0; j < 8; j++) {
if (data.expect_item(BALLU_BIT_MARK, BALLU_ONE_SPACE)) if (data.expect_item(BALLU_BIT_MARK, BALLU_ONE_SPACE)) {
remote_state[i] |= 1 << j; remote_state[i] |= 1 << j;
else if (!data.expect_item(BALLU_BIT_MARK, BALLU_ZERO_SPACE)) { } else if (!data.expect_item(BALLU_BIT_MARK, BALLU_ZERO_SPACE)) {
ESP_LOGV(TAG, "Byte %d bit %d fail", i, j); ESP_LOGV(TAG, "Byte %d bit %d fail", i, j);
return false; return false;
} }

View file

@ -21,12 +21,13 @@ void BangBangClimate::setup() {
restore->to_call(this).perform(); restore->to_call(this).perform();
} else { } else {
// restore from defaults, change_away handles those for us // restore from defaults, change_away handles those for us
if (supports_cool_ && supports_heat_) if (supports_cool_ && supports_heat_) {
this->mode = climate::CLIMATE_MODE_HEAT_COOL; this->mode = climate::CLIMATE_MODE_HEAT_COOL;
else if (supports_cool_) } else if (supports_cool_) {
this->mode = climate::CLIMATE_MODE_COOL; this->mode = climate::CLIMATE_MODE_COOL;
else if (supports_heat_) } else if (supports_heat_) {
this->mode = climate::CLIMATE_MODE_HEAT; this->mode = climate::CLIMATE_MODE_HEAT;
}
this->change_away_(false); this->change_away_(false);
} }
} }
@ -56,11 +57,12 @@ climate::ClimateTraits BangBangClimate::traits() {
if (supports_cool_ && supports_heat_) if (supports_cool_ && supports_heat_)
traits.add_supported_mode(climate::CLIMATE_MODE_HEAT_COOL); traits.add_supported_mode(climate::CLIMATE_MODE_HEAT_COOL);
traits.set_supports_two_point_target_temperature(true); traits.set_supports_two_point_target_temperature(true);
if (supports_away_) if (supports_away_) {
traits.set_supported_presets({ traits.set_supported_presets({
climate::CLIMATE_PRESET_HOME, climate::CLIMATE_PRESET_HOME,
climate::CLIMATE_PRESET_AWAY, climate::CLIMATE_PRESET_AWAY,
}); });
}
traits.set_supports_action(true); traits.set_supports_action(true);
return traits; return traits;
} }
@ -82,17 +84,19 @@ void BangBangClimate::compute_state_() {
if (too_cold) { if (too_cold) {
// too cold -> enable heating if possible and enabled, else idle // too cold -> enable heating if possible and enabled, else idle
if (this->supports_heat_ && if (this->supports_heat_ &&
(this->mode == climate::CLIMATE_MODE_HEAT_COOL || this->mode == climate::CLIMATE_MODE_HEAT)) (this->mode == climate::CLIMATE_MODE_HEAT_COOL || this->mode == climate::CLIMATE_MODE_HEAT)) {
target_action = climate::CLIMATE_ACTION_HEATING; target_action = climate::CLIMATE_ACTION_HEATING;
else } else {
target_action = climate::CLIMATE_ACTION_IDLE; target_action = climate::CLIMATE_ACTION_IDLE;
}
} else if (too_hot) { } else if (too_hot) {
// too hot -> enable cooling if possible and enabled, else idle // too hot -> enable cooling if possible and enabled, else idle
if (this->supports_cool_ && if (this->supports_cool_ &&
(this->mode == climate::CLIMATE_MODE_HEAT_COOL || this->mode == climate::CLIMATE_MODE_COOL)) (this->mode == climate::CLIMATE_MODE_HEAT_COOL || this->mode == climate::CLIMATE_MODE_COOL)) {
target_action = climate::CLIMATE_ACTION_COOLING; target_action = climate::CLIMATE_ACTION_COOLING;
else } else {
target_action = climate::CLIMATE_ACTION_IDLE; target_action = climate::CLIMATE_ACTION_IDLE;
}
} else { } else {
// neither too hot nor too cold -> in range // neither too hot nor too cold -> in range
if (this->supports_cool_ && this->supports_heat_ && this->mode == climate::CLIMATE_MODE_HEAT_COOL) { if (this->supports_cool_ && this->supports_heat_ && this->mode == climate::CLIMATE_MODE_HEAT_COOL) {
@ -107,9 +111,10 @@ void BangBangClimate::compute_state_() {
this->switch_to_action_(target_action); this->switch_to_action_(target_action);
} }
void BangBangClimate::switch_to_action_(climate::ClimateAction action) { void BangBangClimate::switch_to_action_(climate::ClimateAction action) {
if (action == this->action) if (action == this->action) {
// already in target mode // already in target mode
return; return;
}
if ((action == climate::CLIMATE_ACTION_OFF && this->action == climate::CLIMATE_ACTION_IDLE) || if ((action == climate::CLIMATE_ACTION_OFF && this->action == climate::CLIMATE_ACTION_IDLE) ||
(action == climate::CLIMATE_ACTION_IDLE && this->action == climate::CLIMATE_ACTION_OFF)) { (action == climate::CLIMATE_ACTION_IDLE && this->action == climate::CLIMATE_ACTION_OFF)) {

View file

@ -9,7 +9,7 @@ from esphome.const import (
) )
from .. import binary_ns from .. import binary_ns
BinaryFan = binary_ns.class_("BinaryFan", cg.Component) BinaryFan = binary_ns.class_("BinaryFan", fan.Fan, cg.Component)
CONFIG_SCHEMA = fan.FAN_SCHEMA.extend( CONFIG_SCHEMA = fan.FAN_SCHEMA.extend(
{ {
@ -24,9 +24,8 @@ CONFIG_SCHEMA = fan.FAN_SCHEMA.extend(
async def to_code(config): async def to_code(config):
var = cg.new_Pvariable(config[CONF_OUTPUT_ID]) var = cg.new_Pvariable(config[CONF_OUTPUT_ID])
await cg.register_component(var, config) await cg.register_component(var, config)
await fan.register_fan(var, config)
fan_ = await fan.create_fan_state(config)
cg.add(var.set_fan(fan_))
output_ = await cg.get_variable(config[CONF_OUTPUT]) output_ = await cg.get_variable(config[CONF_OUTPUT])
cg.add(var.set_output(output_)) cg.add(var.set_output(output_))

View file

@ -6,59 +6,35 @@ namespace binary {
static const char *const TAG = "binary.fan"; static const char *const TAG = "binary.fan";
void binary::BinaryFan::dump_config() {
ESP_LOGCONFIG(TAG, "Fan '%s':", this->fan_->get_name().c_str());
if (this->fan_->get_traits().supports_oscillation()) {
ESP_LOGCONFIG(TAG, " Oscillation: YES");
}
if (this->fan_->get_traits().supports_direction()) {
ESP_LOGCONFIG(TAG, " Direction: YES");
}
}
void BinaryFan::setup() { void BinaryFan::setup() {
auto traits = fan::FanTraits(this->oscillating_ != nullptr, false, this->direction_ != nullptr, 0); auto restore = this->restore_state_();
this->fan_->set_traits(traits); if (restore.has_value()) {
this->fan_->add_on_state_callback([this]() { this->next_update_ = true; }); restore->apply(*this);
} this->write_state_();
void BinaryFan::loop() {
if (!this->next_update_) {
return;
}
this->next_update_ = false;
{
bool enable = this->fan_->state;
if (enable)
this->output_->turn_on();
else
this->output_->turn_off();
ESP_LOGD(TAG, "Setting binary state: %s", ONOFF(enable));
}
if (this->oscillating_ != nullptr) {
bool enable = this->fan_->oscillating;
if (enable) {
this->oscillating_->turn_on();
} else {
this->oscillating_->turn_off();
}
ESP_LOGD(TAG, "Setting oscillation: %s", ONOFF(enable));
}
if (this->direction_ != nullptr) {
bool enable = this->fan_->direction == fan::FAN_DIRECTION_REVERSE;
if (enable) {
this->direction_->turn_on();
} else {
this->direction_->turn_off();
}
ESP_LOGD(TAG, "Setting reverse direction: %s", ONOFF(enable));
} }
} }
void BinaryFan::dump_config() { LOG_FAN("", "Binary Fan", this); }
fan::FanTraits BinaryFan::get_traits() {
return fan::FanTraits(this->oscillating_ != nullptr, false, this->direction_ != nullptr, 0);
}
void BinaryFan::control(const fan::FanCall &call) {
if (call.get_state().has_value())
this->state = *call.get_state();
if (call.get_oscillating().has_value())
this->oscillating = *call.get_oscillating();
if (call.get_direction().has_value())
this->direction = *call.get_direction();
// We need a higher priority than the FanState component to make sure that the traits are set this->write_state_();
// when that component sets itself up. this->publish_state();
float BinaryFan::get_setup_priority() const { return fan_->get_setup_priority() + 1.0f; } }
void BinaryFan::write_state_() {
this->output_->set_state(this->state);
if (this->oscillating_ != nullptr)
this->oscillating_->set_state(this->oscillating);
if (this->direction_ != nullptr)
this->direction_->set_state(this->direction == fan::FanDirection::REVERSE);
}
} // namespace binary } // namespace binary
} // namespace esphome } // namespace esphome

View file

@ -2,28 +2,29 @@
#include "esphome/core/component.h" #include "esphome/core/component.h"
#include "esphome/components/output/binary_output.h" #include "esphome/components/output/binary_output.h"
#include "esphome/components/fan/fan_state.h" #include "esphome/components/fan/fan.h"
namespace esphome { namespace esphome {
namespace binary { namespace binary {
class BinaryFan : public Component { class BinaryFan : public Component, public fan::Fan {
public: public:
void set_fan(fan::FanState *fan) { fan_ = fan; }
void set_output(output::BinaryOutput *output) { output_ = output; }
void setup() override; void setup() override;
void loop() override;
void dump_config() override; void dump_config() override;
float get_setup_priority() const override;
void set_output(output::BinaryOutput *output) { this->output_ = output; }
void set_oscillating(output::BinaryOutput *oscillating) { this->oscillating_ = oscillating; } void set_oscillating(output::BinaryOutput *oscillating) { this->oscillating_ = oscillating; }
void set_direction(output::BinaryOutput *direction) { this->direction_ = direction; } void set_direction(output::BinaryOutput *direction) { this->direction_ = direction; }
fan::FanTraits get_traits() override;
protected: protected:
fan::FanState *fan_; void control(const fan::FanCall &call) override;
void write_state_();
output::BinaryOutput *output_; output::BinaryOutput *output_;
output::BinaryOutput *oscillating_{nullptr}; output::BinaryOutput *oscillating_{nullptr};
output::BinaryOutput *direction_{nullptr}; output::BinaryOutput *direction_{nullptr};
bool next_update_{true};
}; };
} // namespace binary } // namespace binary

View file

@ -65,7 +65,7 @@ void BL0940::update() {
} }
void BL0940::setup() { void BL0940::setup() {
for (auto i : BL0940_INIT) { for (auto *i : BL0940_INIT) {
this->write_array(i, 6); this->write_array(i, 6);
delay(1); delay(1);
} }

View file

@ -167,7 +167,7 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es
break; break;
} }
case ESP_GATTC_REG_FOR_NOTIFY_EVT: { case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
auto descr = this->get_config_descriptor(param->reg_for_notify.handle); auto *descr = this->get_config_descriptor(param->reg_for_notify.handle);
if (descr == nullptr) { if (descr == nullptr) {
ESP_LOGW(TAG, "No descriptor found for notify of handle 0x%x", param->reg_for_notify.handle); ESP_LOGW(TAG, "No descriptor found for notify of handle 0x%x", param->reg_for_notify.handle);
break; break;
@ -252,16 +252,17 @@ float BLEClient::parse_char_value(uint8_t *value, uint16_t length) {
} }
BLEService *BLEClient::get_service(espbt::ESPBTUUID uuid) { BLEService *BLEClient::get_service(espbt::ESPBTUUID uuid) {
for (auto svc : this->services_) for (auto *svc : this->services_) {
if (svc->uuid == uuid) if (svc->uuid == uuid)
return svc; return svc;
}
return nullptr; return nullptr;
} }
BLEService *BLEClient::get_service(uint16_t uuid) { return this->get_service(espbt::ESPBTUUID::from_uint16(uuid)); } BLEService *BLEClient::get_service(uint16_t uuid) { return this->get_service(espbt::ESPBTUUID::from_uint16(uuid)); }
BLECharacteristic *BLEClient::get_characteristic(espbt::ESPBTUUID service, espbt::ESPBTUUID chr) { BLECharacteristic *BLEClient::get_characteristic(espbt::ESPBTUUID service, espbt::ESPBTUUID chr) {
auto svc = this->get_service(service); auto *svc = this->get_service(service);
if (svc == nullptr) if (svc == nullptr)
return nullptr; return nullptr;
return svc->get_characteristic(chr); return svc->get_characteristic(chr);
@ -272,19 +273,24 @@ BLECharacteristic *BLEClient::get_characteristic(uint16_t service, uint16_t chr)
} }
BLEDescriptor *BLEClient::get_config_descriptor(uint16_t handle) { BLEDescriptor *BLEClient::get_config_descriptor(uint16_t handle) {
for (auto &svc : this->services_) for (auto &svc : this->services_) {
for (auto &chr : svc->characteristics) for (auto &chr : svc->characteristics) {
if (chr->handle == handle) if (chr->handle == handle) {
for (auto &desc : chr->descriptors) for (auto &desc : chr->descriptors) {
if (desc->uuid == espbt::ESPBTUUID::from_uint16(0x2902)) if (desc->uuid == espbt::ESPBTUUID::from_uint16(0x2902))
return desc; return desc;
}
}
}
}
return nullptr; return nullptr;
} }
BLECharacteristic *BLEService::get_characteristic(espbt::ESPBTUUID uuid) { BLECharacteristic *BLEService::get_characteristic(espbt::ESPBTUUID uuid) {
for (auto &chr : this->characteristics) for (auto &chr : this->characteristics) {
if (chr->uuid == uuid) if (chr->uuid == uuid)
return chr; return chr;
}
return nullptr; return nullptr;
} }
@ -293,10 +299,10 @@ BLECharacteristic *BLEService::get_characteristic(uint16_t uuid) {
} }
BLEDescriptor *BLEClient::get_descriptor(espbt::ESPBTUUID service, espbt::ESPBTUUID chr, espbt::ESPBTUUID descr) { BLEDescriptor *BLEClient::get_descriptor(espbt::ESPBTUUID service, espbt::ESPBTUUID chr, espbt::ESPBTUUID descr) {
auto svc = this->get_service(service); auto *svc = this->get_service(service);
if (svc == nullptr) if (svc == nullptr)
return nullptr; return nullptr;
auto ch = svc->get_characteristic(chr); auto *ch = svc->get_characteristic(chr);
if (ch == nullptr) if (ch == nullptr)
return nullptr; return nullptr;
return ch->get_descriptor(descr); return ch->get_descriptor(descr);
@ -379,24 +385,29 @@ void BLECharacteristic::parse_descriptors() {
} }
BLEDescriptor *BLECharacteristic::get_descriptor(espbt::ESPBTUUID uuid) { BLEDescriptor *BLECharacteristic::get_descriptor(espbt::ESPBTUUID uuid) {
for (auto &desc : this->descriptors) for (auto &desc : this->descriptors) {
if (desc->uuid == uuid) if (desc->uuid == uuid)
return desc; return desc;
}
return nullptr; return nullptr;
} }
BLEDescriptor *BLECharacteristic::get_descriptor(uint16_t uuid) { BLEDescriptor *BLECharacteristic::get_descriptor(uint16_t uuid) {
return this->get_descriptor(espbt::ESPBTUUID::from_uint16(uuid)); return this->get_descriptor(espbt::ESPBTUUID::from_uint16(uuid));
} }
void BLECharacteristic::write_value(uint8_t *new_val, int16_t new_val_size) { void BLECharacteristic::write_value(uint8_t *new_val, int16_t new_val_size, esp_gatt_write_type_t write_type) {
auto client = this->service->client; auto *client = this->service->client;
auto status = esp_ble_gattc_write_char(client->gattc_if, client->conn_id, this->handle, new_val_size, new_val, auto status = esp_ble_gattc_write_char(client->gattc_if, client->conn_id, this->handle, new_val_size, new_val,
ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); write_type, ESP_GATT_AUTH_REQ_NONE);
if (status) { if (status) {
ESP_LOGW(TAG, "Error sending write value to BLE gattc server, status=%d", status); ESP_LOGW(TAG, "Error sending write value to BLE gattc server, status=%d", status);
} }
} }
void BLECharacteristic::write_value(uint8_t *new_val, int16_t new_val_size) {
write_value(new_val, new_val_size, ESP_GATT_WRITE_TYPE_NO_RSP);
}
} // namespace ble_client } // namespace ble_client
} // namespace esphome } // namespace esphome

View file

@ -60,6 +60,7 @@ class BLECharacteristic {
BLEDescriptor *get_descriptor(espbt::ESPBTUUID uuid); BLEDescriptor *get_descriptor(espbt::ESPBTUUID uuid);
BLEDescriptor *get_descriptor(uint16_t uuid); BLEDescriptor *get_descriptor(uint16_t uuid);
void write_value(uint8_t *new_val, int16_t new_val_size); void write_value(uint8_t *new_val, int16_t new_val_size);
void write_value(uint8_t *new_val, int16_t new_val_size, esp_gatt_write_type_t write_type);
BLEService *service; BLEService *service;
}; };
@ -126,9 +127,10 @@ class BLEClient : public espbt::ESPBTClient, public Component {
bool all_nodes_established_() { bool all_nodes_established_() {
if (this->state() != espbt::ClientState::ESTABLISHED) if (this->state() != espbt::ClientState::ESTABLISHED)
return false; return false;
for (auto &node : nodes_) for (auto &node : nodes_) {
if (node->node_state != espbt::ClientState::ESTABLISHED) if (node->node_state != espbt::ClientState::ESTABLISHED)
return false; return false;
}
return true; return true;
} }

View file

@ -1,13 +1,14 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import output, ble_client, esp32_ble_tracker from esphome.components import ble_client, esp32_ble_tracker, output
from esphome.const import CONF_ID, CONF_SERVICE_UUID from esphome.const import CONF_ID, CONF_SERVICE_UUID
from .. import ble_client_ns
from .. import ble_client_ns
DEPENDENCIES = ["ble_client"] DEPENDENCIES = ["ble_client"]
CONF_CHARACTERISTIC_UUID = "characteristic_uuid" CONF_CHARACTERISTIC_UUID = "characteristic_uuid"
CONF_REQUIRE_RESPONSE = "require_response"
BLEBinaryOutput = ble_client_ns.class_( BLEBinaryOutput = ble_client_ns.class_(
"BLEBinaryOutput", output.BinaryOutput, ble_client.BLEClientNode, cg.Component "BLEBinaryOutput", output.BinaryOutput, ble_client.BLEClientNode, cg.Component
@ -19,6 +20,7 @@ CONFIG_SCHEMA = cv.All(
cv.Required(CONF_ID): cv.declare_id(BLEBinaryOutput), cv.Required(CONF_ID): cv.declare_id(BLEBinaryOutput),
cv.Required(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid, cv.Required(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid,
cv.Required(CONF_CHARACTERISTIC_UUID): esp32_ble_tracker.bt_uuid, cv.Required(CONF_CHARACTERISTIC_UUID): esp32_ble_tracker.bt_uuid,
cv.Optional(CONF_REQUIRE_RESPONSE, default=False): cv.boolean,
} }
) )
.extend(cv.COMPONENT_SCHEMA) .extend(cv.COMPONENT_SCHEMA)
@ -61,7 +63,7 @@ def to_code(config):
config[CONF_CHARACTERISTIC_UUID] config[CONF_CHARACTERISTIC_UUID]
) )
cg.add(var.set_char_uuid128(uuid128)) cg.add(var.set_char_uuid128(uuid128))
cg.add(var.set_require_response(config[CONF_REQUIRE_RESPONSE]))
yield output.register_output(var, config) yield output.register_output(var, config)
yield ble_client.register_ble_node(var, config) yield ble_client.register_ble_node(var, config)
yield cg.register_component(var, config) yield cg.register_component(var, config)

View file

@ -32,7 +32,7 @@ void BLEBinaryOutput::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_i
break; break;
} }
auto chr = this->parent()->get_characteristic(this->service_uuid_, this->char_uuid_); auto *chr = this->parent()->get_characteristic(this->service_uuid_, this->char_uuid_);
if (chr == nullptr) { if (chr == nullptr) {
ESP_LOGW(TAG, "[%s] Characteristic not found.", this->char_uuid_.to_string().c_str()); ESP_LOGW(TAG, "[%s] Characteristic not found.", this->char_uuid_.to_string().c_str());
break; break;
@ -54,7 +54,7 @@ void BLEBinaryOutput::write_state(bool state) {
return; return;
} }
auto chr = this->parent()->get_characteristic(this->service_uuid_, this->char_uuid_); auto *chr = this->parent()->get_characteristic(this->service_uuid_, this->char_uuid_);
if (chr == nullptr) { if (chr == nullptr) {
ESP_LOGW(TAG, "[%s] Characteristic not found. State update can not be written.", ESP_LOGW(TAG, "[%s] Characteristic not found. State update can not be written.",
this->char_uuid_.to_string().c_str()); this->char_uuid_.to_string().c_str());
@ -63,7 +63,11 @@ void BLEBinaryOutput::write_state(bool state) {
uint8_t state_as_uint = (uint8_t) state; uint8_t state_as_uint = (uint8_t) state;
ESP_LOGV(TAG, "[%s] Write State: %d", this->char_uuid_.to_string().c_str(), state_as_uint); ESP_LOGV(TAG, "[%s] Write State: %d", this->char_uuid_.to_string().c_str(), state_as_uint);
chr->write_value(&state_as_uint, sizeof(state_as_uint)); if (this->require_response_) {
chr->write_value(&state_as_uint, sizeof(state_as_uint), ESP_GATT_WRITE_TYPE_RSP);
} else {
chr->write_value(&state_as_uint, sizeof(state_as_uint), ESP_GATT_WRITE_TYPE_NO_RSP);
}
} }
} // namespace ble_client } // namespace ble_client

View file

@ -25,9 +25,11 @@ class BLEBinaryOutput : public output::BinaryOutput, public BLEClientNode, publi
void set_char_uuid128(uint8_t *uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_raw(uuid); } void set_char_uuid128(uint8_t *uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_raw(uuid); }
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) override; esp_ble_gattc_cb_param_t *param) override;
void set_require_response(bool response) { this->require_response_ = response; }
protected: protected:
void write_state(bool state) override; void write_state(bool state) override;
bool require_response_;
espbt::ESPBTUUID service_uuid_; espbt::ESPBTUUID service_uuid_;
espbt::ESPBTUUID char_uuid_; espbt::ESPBTUUID char_uuid_;
espbt::ClientState client_state_; espbt::ClientState client_state_;

View file

@ -43,7 +43,7 @@ void BLESensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga
} }
case ESP_GATTC_SEARCH_CMPL_EVT: { case ESP_GATTC_SEARCH_CMPL_EVT: {
this->handle = 0; this->handle = 0;
auto chr = this->parent()->get_characteristic(this->service_uuid_, this->char_uuid_); auto *chr = this->parent()->get_characteristic(this->service_uuid_, this->char_uuid_);
if (chr == nullptr) { if (chr == nullptr) {
this->status_set_warning(); this->status_set_warning();
this->publish_state(NAN); this->publish_state(NAN);
@ -53,7 +53,7 @@ void BLESensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga
} }
this->handle = chr->handle; this->handle = chr->handle;
if (this->descr_uuid_.get_uuid().len > 0) { if (this->descr_uuid_.get_uuid().len > 0) {
auto descr = chr->get_descriptor(this->descr_uuid_); auto *descr = chr->get_descriptor(this->descr_uuid_);
if (descr == nullptr) { if (descr == nullptr) {
this->status_set_warning(); this->status_set_warning();
this->publish_state(NAN); this->publish_state(NAN);

View file

@ -1,7 +1,6 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import text_sensor, esp32_ble_tracker from esphome.components import text_sensor, esp32_ble_tracker
from esphome.const import CONF_ID
DEPENDENCIES = ["esp32_ble_tracker"] DEPENDENCIES = ["esp32_ble_tracker"]
@ -14,18 +13,13 @@ BLEScanner = ble_scanner_ns.class_(
) )
CONFIG_SCHEMA = cv.All( CONFIG_SCHEMA = cv.All(
text_sensor.TEXT_SENSOR_SCHEMA.extend( text_sensor.text_sensor_schema(klass=BLEScanner)
{
cv.GenerateID(): cv.declare_id(BLEScanner),
}
)
.extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA) .extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)
.extend(cv.COMPONENT_SCHEMA) .extend(cv.COMPONENT_SCHEMA)
) )
async def to_code(config): async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) var = await text_sensor.new_text_sensor(config)
await cg.register_component(var, config) await cg.register_component(var, config)
await esp32_ble_tracker.register_ble_device(var, config) await esp32_ble_tracker.register_ble_device(var, config)
await text_sensor.register_text_sensor(var, config)

View file

@ -214,9 +214,10 @@ void BME280Component::update() {
float BME280Component::read_temperature_(const uint8_t *data, int32_t *t_fine) { float BME280Component::read_temperature_(const uint8_t *data, int32_t *t_fine) {
int32_t adc = ((data[3] & 0xFF) << 16) | ((data[4] & 0xFF) << 8) | (data[5] & 0xFF); int32_t adc = ((data[3] & 0xFF) << 16) | ((data[4] & 0xFF) << 8) | (data[5] & 0xFF);
adc >>= 4; adc >>= 4;
if (adc == 0x80000) if (adc == 0x80000) {
// temperature was disabled // temperature was disabled
return NAN; return NAN;
}
const int32_t t1 = this->calibration_.t1; const int32_t t1 = this->calibration_.t1;
const int32_t t2 = this->calibration_.t2; const int32_t t2 = this->calibration_.t2;
@ -233,9 +234,10 @@ float BME280Component::read_temperature_(const uint8_t *data, int32_t *t_fine) {
float BME280Component::read_pressure_(const uint8_t *data, int32_t t_fine) { float BME280Component::read_pressure_(const uint8_t *data, int32_t t_fine) {
int32_t adc = ((data[0] & 0xFF) << 16) | ((data[1] & 0xFF) << 8) | (data[2] & 0xFF); int32_t adc = ((data[0] & 0xFF) << 16) | ((data[1] & 0xFF) << 8) | (data[2] & 0xFF);
adc >>= 4; adc >>= 4;
if (adc == 0x80000) if (adc == 0x80000) {
// pressure was disabled // pressure was disabled
return NAN; return NAN;
}
const int64_t p1 = this->calibration_.p1; const int64_t p1 = this->calibration_.p1;
const int64_t p2 = this->calibration_.p2; const int64_t p2 = this->calibration_.p2;
const int64_t p3 = this->calibration_.p3; const int64_t p3 = this->calibration_.p3;

View file

@ -95,7 +95,7 @@ void BME680Component::setup() {
this->calibration_.t3 = cal1[3]; this->calibration_.t3 = cal1[3];
this->calibration_.h1 = cal2[2] << 4 | (cal2[1] & 0x0F); this->calibration_.h1 = cal2[2] << 4 | (cal2[1] & 0x0F);
this->calibration_.h2 = cal2[0] << 4 | cal2[1]; this->calibration_.h2 = cal2[0] << 4 | cal2[1] >> 4;
this->calibration_.h3 = cal2[3]; this->calibration_.h3 = cal2[3];
this->calibration_.h4 = cal2[4]; this->calibration_.h4 = cal2[4];
this->calibration_.h5 = cal2[5]; this->calibration_.h5 = cal2[5];
@ -420,10 +420,11 @@ float BME680Component::calc_humidity_(uint16_t raw_humidity) {
calc_hum = var2 + (var3 + var4 * temp_comp) * var2 * var2; calc_hum = var2 + (var3 + var4 * temp_comp) * var2 * var2;
if (calc_hum > 100.0f) if (calc_hum > 100.0f) {
calc_hum = 100.0f; calc_hum = 100.0f;
else if (calc_hum < 0.0f) } else if (calc_hum < 0.0f) {
calc_hum = 0.0f; calc_hum = 0.0f;
}
return calc_hum; return calc_hum;
} }

View file

@ -1,7 +1,6 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import text_sensor from esphome.components import text_sensor
from esphome.const import CONF_ID, CONF_ICON
from . import BME680BSECComponent, CONF_BME680_BSEC_ID from . import BME680BSECComponent, CONF_BME680_BSEC_ID
DEPENDENCIES = ["bme680_bsec"] DEPENDENCIES = ["bme680_bsec"]
@ -14,11 +13,8 @@ TYPES = [CONF_IAQ_ACCURACY]
CONFIG_SCHEMA = cv.Schema( CONFIG_SCHEMA = cv.Schema(
{ {
cv.GenerateID(CONF_BME680_BSEC_ID): cv.use_id(BME680BSECComponent), cv.GenerateID(CONF_BME680_BSEC_ID): cv.use_id(BME680BSECComponent),
cv.Optional(CONF_IAQ_ACCURACY): text_sensor.TEXT_SENSOR_SCHEMA.extend( cv.Optional(CONF_IAQ_ACCURACY): text_sensor.text_sensor_schema(
{ icon=ICON_ACCURACY
cv.GenerateID(): cv.declare_id(text_sensor.TextSensor),
cv.Optional(CONF_ICON, default=ICON_ACCURACY): cv.icon,
}
), ),
} }
) )
@ -27,8 +23,7 @@ CONFIG_SCHEMA = cv.Schema(
async def setup_conf(config, key, hub): async def setup_conf(config, key, hub):
if key in config: if key in config:
conf = config[key] conf = config[key]
sens = cg.new_Pvariable(conf[CONF_ID]) sens = await text_sensor.new_text_sensor(conf)
await text_sensor.register_text_sensor(sens, conf)
cg.add(getattr(hub, f"set_{key}_text_sensor")(sens)) cg.add(getattr(hub, f"set_{key}_text_sensor")(sens))

View file

@ -161,9 +161,10 @@ float BMP280Component::read_temperature_(int32_t *t_fine) {
return NAN; return NAN;
int32_t adc = ((data[0] & 0xFF) << 16) | ((data[1] & 0xFF) << 8) | (data[2] & 0xFF); int32_t adc = ((data[0] & 0xFF) << 16) | ((data[1] & 0xFF) << 8) | (data[2] & 0xFF);
adc >>= 4; adc >>= 4;
if (adc == 0x80000) if (adc == 0x80000) {
// temperature was disabled // temperature was disabled
return NAN; return NAN;
}
const int32_t t1 = this->calibration_.t1; const int32_t t1 = this->calibration_.t1;
const int32_t t2 = this->calibration_.t2; const int32_t t2 = this->calibration_.t2;
@ -183,9 +184,10 @@ float BMP280Component::read_pressure_(int32_t t_fine) {
return NAN; return NAN;
int32_t adc = ((data[0] & 0xFF) << 16) | ((data[1] & 0xFF) << 8) | (data[2] & 0xFF); int32_t adc = ((data[0] & 0xFF) << 16) | ((data[1] & 0xFF) << 8) | (data[2] & 0xFF);
adc >>= 4; adc >>= 4;
if (adc == 0x80000) if (adc == 0x80000) {
// pressure was disabled // pressure was disabled
return NAN; return NAN;
}
const int64_t p1 = this->calibration_.p1; const int64_t p1 = this->calibration_.p1;
const int64_t p2 = this->calibration_.p2; const int64_t p2 = this->calibration_.p2;
const int64_t p3 = this->calibration_.p3; const int64_t p3 = this->calibration_.p3;

View file

@ -14,10 +14,14 @@ CONF_BIT_RATE = "bit_rate"
CONF_ON_FRAME = "on_frame" CONF_ON_FRAME = "on_frame"
def validate_id(id_value, id_ext): def validate_id(config):
if not id_ext: if CONF_CAN_ID in config:
if id_value > 0x7FF: id_value = config[CONF_CAN_ID]
raise cv.Invalid("Standard IDs must be 11 Bit (0x000-0x7ff / 0-2047)") id_ext = config[CONF_USE_EXTENDED_ID]
if not id_ext:
if id_value > 0x7FF:
raise cv.Invalid("Standard IDs must be 11 Bit (0x000-0x7ff / 0-2047)")
return config
def validate_raw_data(value): def validate_raw_data(value):
@ -67,23 +71,18 @@ CANBUS_SCHEMA = cv.Schema(
cv.Optional(CONF_ON_FRAME): automation.validate_automation( cv.Optional(CONF_ON_FRAME): automation.validate_automation(
{ {
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(CanbusTrigger), cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(CanbusTrigger),
cv.GenerateID(CONF_CAN_ID): cv.int_range(min=0, max=0x1FFFFFFF), cv.Required(CONF_CAN_ID): cv.int_range(min=0, max=0x1FFFFFFF),
cv.Optional(CONF_USE_EXTENDED_ID, default=False): cv.boolean, cv.Optional(CONF_USE_EXTENDED_ID, default=False): cv.boolean,
cv.Optional(CONF_ON_FRAME): automation.validate_automation( },
{ validate_id,
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(CanbusTrigger),
cv.GenerateID(CONF_CAN_ID): cv.int_range(min=0, max=0x1FFFFFFF),
cv.Optional(CONF_USE_EXTENDED_ID, default=False): cv.boolean,
}
),
}
), ),
} },
).extend(cv.COMPONENT_SCHEMA) ).extend(cv.COMPONENT_SCHEMA)
CANBUS_SCHEMA.add_extra(validate_id)
async def setup_canbus_core_(var, config): async def setup_canbus_core_(var, config):
validate_id(config[CONF_CAN_ID], config[CONF_USE_EXTENDED_ID])
await cg.register_component(var, config) await cg.register_component(var, config)
cg.add(var.set_can_id([config[CONF_CAN_ID]])) cg.add(var.set_can_id([config[CONF_CAN_ID]]))
cg.add(var.set_use_extended_id([config[CONF_USE_EXTENDED_ID]])) cg.add(var.set_use_extended_id([config[CONF_USE_EXTENDED_ID]]))
@ -92,7 +91,6 @@ async def setup_canbus_core_(var, config):
for conf in config.get(CONF_ON_FRAME, []): for conf in config.get(CONF_ON_FRAME, []):
can_id = conf[CONF_CAN_ID] can_id = conf[CONF_CAN_ID]
ext_id = conf[CONF_USE_EXTENDED_ID] ext_id = conf[CONF_USE_EXTENDED_ID]
validate_id(can_id, ext_id)
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var, can_id, ext_id) trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var, can_id, ext_id)
await cg.register_component(trigger, conf) await cg.register_component(trigger, conf)
await automation.build_automation( await automation.build_automation(
@ -117,11 +115,11 @@ async def register_canbus(var, config):
cv.Optional(CONF_USE_EXTENDED_ID, default=False): cv.boolean, cv.Optional(CONF_USE_EXTENDED_ID, default=False): cv.boolean,
cv.Required(CONF_DATA): cv.templatable(validate_raw_data), cv.Required(CONF_DATA): cv.templatable(validate_raw_data),
}, },
validate_id,
key=CONF_DATA, key=CONF_DATA,
), ),
) )
async def canbus_action_to_code(config, action_id, template_arg, args): async def canbus_action_to_code(config, action_id, template_arg, args):
validate_id(config[CONF_CAN_ID], config[CONF_USE_EXTENDED_ID])
var = cg.new_Pvariable(action_id, template_arg) var = cg.new_Pvariable(action_id, template_arg)
await cg.register_parented(var, config[CONF_CANBUS_ID]) await cg.register_parented(var, config[CONF_CANBUS_ID])

View file

@ -75,7 +75,7 @@ void Canbus::loop() {
} }
// fire all triggers // fire all triggers
for (auto trigger : this->triggers_) { for (auto *trigger : this->triggers_) {
if ((trigger->can_id_ == can_message.can_id) && (trigger->use_extended_id_ == can_message.use_extended_id)) { if ((trigger->can_id_ == can_message.can_id) && (trigger->use_extended_id_ == can_message.use_extended_id)) {
trigger->trigger(data); trigger->trigger(data);
} }

View file

@ -39,14 +39,15 @@ void CCS811Component::setup() {
// set MEAS_MODE (page 5) // set MEAS_MODE (page 5)
uint8_t meas_mode = 0; uint8_t meas_mode = 0;
uint32_t interval = this->get_update_interval(); uint32_t interval = this->get_update_interval();
if (interval >= 60 * 1000) if (interval >= 60 * 1000) {
meas_mode = 3 << 4; // sensor takes a reading every 60 seconds meas_mode = 3 << 4; // sensor takes a reading every 60 seconds
else if (interval >= 10 * 1000) } else if (interval >= 10 * 1000) {
meas_mode = 2 << 4; // sensor takes a reading every 10 seconds meas_mode = 2 << 4; // sensor takes a reading every 10 seconds
else if (interval >= 1 * 1000) } else if (interval >= 1 * 1000) {
meas_mode = 1 << 4; // sensor takes a reading every second meas_mode = 1 << 4; // sensor takes a reading every second
else } else {
meas_mode = 4 << 4; // sensor takes a reading every 250ms meas_mode = 4 << 4; // sensor takes a reading every 250ms
}
CHECKED_IO(this->write_byte(0x01, meas_mode)) CHECKED_IO(this->write_byte(0x01, meas_mode))

View file

@ -2,7 +2,6 @@ import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import i2c, sensor, text_sensor from esphome.components import i2c, sensor, text_sensor
from esphome.const import ( from esphome.const import (
CONF_ICON,
CONF_ID, CONF_ID,
ICON_RADIATOR, ICON_RADIATOR,
ICON_RESTART, ICON_RESTART,
@ -47,11 +46,8 @@ CONFIG_SCHEMA = (
device_class=DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS, device_class=DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS,
state_class=STATE_CLASS_MEASUREMENT, state_class=STATE_CLASS_MEASUREMENT,
), ),
cv.Optional(CONF_VERSION): text_sensor.TEXT_SENSOR_SCHEMA.extend( cv.Optional(CONF_VERSION): text_sensor.text_sensor_schema(
{ icon=ICON_RESTART
cv.GenerateID(): cv.declare_id(text_sensor.TextSensor),
cv.Optional(CONF_ICON, default=ICON_RESTART): cv.icon,
}
), ),
cv.Optional(CONF_BASELINE): cv.hex_uint16_t, cv.Optional(CONF_BASELINE): cv.hex_uint16_t,
cv.Optional(CONF_TEMPERATURE): cv.use_id(sensor.Sensor), cv.Optional(CONF_TEMPERATURE): cv.use_id(sensor.Sensor),
@ -74,8 +70,7 @@ async def to_code(config):
cg.add(var.set_tvoc(sens)) cg.add(var.set_tvoc(sens))
if CONF_VERSION in config: if CONF_VERSION in config:
sens = cg.new_Pvariable(config[CONF_VERSION][CONF_ID]) sens = await text_sensor.new_text_sensor(config[CONF_VERSION])
await text_sensor.register_text_sensor(sens, config[CONF_VERSION])
cg.add(var.set_version(sens)) cg.add(var.set_version(sens))
if CONF_BASELINE in config: if CONF_BASELINE in config:

View file

@ -6,8 +6,9 @@ from esphome.const import (
CONF_ID, CONF_ID,
) )
CODEOWNERS = ["@asoehlke"]
AUTO_LOAD = ["sensor", "voltage_sampler"] AUTO_LOAD = ["sensor", "voltage_sampler"]
CODEOWNERS = ["@asoehlke"]
MULTI_CONF = True
cd74hc4067_ns = cg.esphome_ns.namespace("cd74hc4067") cd74hc4067_ns = cg.esphome_ns.namespace("cd74hc4067")

View file

@ -65,7 +65,7 @@ class ClimateTraits {
ESPDEPRECATED("This method is deprecated, use set_supported_modes() instead", "v1.20") ESPDEPRECATED("This method is deprecated, use set_supported_modes() instead", "v1.20")
void set_supports_dry_mode(bool supports_dry_mode) { set_mode_support_(CLIMATE_MODE_DRY, supports_dry_mode); } void set_supports_dry_mode(bool supports_dry_mode) { set_mode_support_(CLIMATE_MODE_DRY, supports_dry_mode); }
bool supports_mode(ClimateMode mode) const { return supported_modes_.count(mode); } bool supports_mode(ClimateMode mode) const { return supported_modes_.count(mode); }
const std::set<ClimateMode> get_supported_modes() const { return supported_modes_; } std::set<ClimateMode> get_supported_modes() const { return supported_modes_; }
void set_supports_action(bool supports_action) { supports_action_ = supports_action; } void set_supports_action(bool supports_action) { supports_action_ = supports_action; }
bool get_supports_action() const { return supports_action_; } bool get_supports_action() const { return supports_action_; }
@ -93,7 +93,7 @@ class ClimateTraits {
void set_supports_fan_mode_diffuse(bool supported) { set_fan_mode_support_(CLIMATE_FAN_DIFFUSE, supported); } void set_supports_fan_mode_diffuse(bool supported) { set_fan_mode_support_(CLIMATE_FAN_DIFFUSE, supported); }
bool supports_fan_mode(ClimateFanMode fan_mode) const { return supported_fan_modes_.count(fan_mode); } bool supports_fan_mode(ClimateFanMode fan_mode) const { return supported_fan_modes_.count(fan_mode); }
bool get_supports_fan_modes() const { return !supported_fan_modes_.empty() || !supported_custom_fan_modes_.empty(); } bool get_supports_fan_modes() const { return !supported_fan_modes_.empty() || !supported_custom_fan_modes_.empty(); }
const std::set<ClimateFanMode> get_supported_fan_modes() const { return supported_fan_modes_; } std::set<ClimateFanMode> get_supported_fan_modes() const { return supported_fan_modes_; }
void set_supported_custom_fan_modes(std::set<std::string> supported_custom_fan_modes) { void set_supported_custom_fan_modes(std::set<std::string> supported_custom_fan_modes) {
supported_custom_fan_modes_ = std::move(supported_custom_fan_modes); supported_custom_fan_modes_ = std::move(supported_custom_fan_modes);
@ -141,7 +141,7 @@ class ClimateTraits {
} }
bool supports_swing_mode(ClimateSwingMode swing_mode) const { return supported_swing_modes_.count(swing_mode); } bool supports_swing_mode(ClimateSwingMode swing_mode) const { return supported_swing_modes_.count(swing_mode); }
bool get_supports_swing_modes() const { return !supported_swing_modes_.empty(); } bool get_supports_swing_modes() const { return !supported_swing_modes_.empty(); }
const std::set<ClimateSwingMode> get_supported_swing_modes() { return supported_swing_modes_; } std::set<ClimateSwingMode> get_supported_swing_modes() { return supported_swing_modes_; }
float get_visual_min_temperature() const { return visual_min_temperature_; } float get_visual_min_temperature() const { return visual_min_temperature_; }
void set_visual_min_temperature(float visual_min_temperature) { visual_min_temperature_ = visual_min_temperature; } void set_visual_min_temperature(float visual_min_temperature) { visual_min_temperature_ = visual_min_temperature; }

View file

@ -137,11 +137,11 @@ bool LgIrClimate::on_receive(remote_base::RemoteReceiveData data) {
this->swing_mode = this->swing_mode =
this->swing_mode == climate::CLIMATE_SWING_OFF ? climate::CLIMATE_SWING_VERTICAL : climate::CLIMATE_SWING_OFF; this->swing_mode == climate::CLIMATE_SWING_OFF ? climate::CLIMATE_SWING_VERTICAL : climate::CLIMATE_SWING_OFF;
} else { } else {
if ((remote_state & COMMAND_MASK) == COMMAND_AUTO) if ((remote_state & COMMAND_MASK) == COMMAND_AUTO) {
this->mode = climate::CLIMATE_MODE_HEAT_COOL; this->mode = climate::CLIMATE_MODE_HEAT_COOL;
else if ((remote_state & COMMAND_MASK) == COMMAND_DRY_FAN) } else if ((remote_state & COMMAND_MASK) == COMMAND_DRY_FAN) {
this->mode = climate::CLIMATE_MODE_DRY; this->mode = climate::CLIMATE_MODE_DRY;
else if ((remote_state & COMMAND_MASK) == COMMAND_HEAT) { } else if ((remote_state & COMMAND_MASK) == COMMAND_HEAT) {
this->mode = climate::CLIMATE_MODE_HEAT; this->mode = climate::CLIMATE_MODE_HEAT;
} else { } else {
this->mode = climate::CLIMATE_MODE_COOL; this->mode = climate::CLIMATE_MODE_COOL;
@ -156,14 +156,15 @@ bool LgIrClimate::on_receive(remote_base::RemoteReceiveData data) {
this->fan_mode = climate::CLIMATE_FAN_AUTO; this->fan_mode = climate::CLIMATE_FAN_AUTO;
} else if (this->mode == climate::CLIMATE_MODE_COOL || this->mode == climate::CLIMATE_MODE_HEAT || } else if (this->mode == climate::CLIMATE_MODE_COOL || this->mode == climate::CLIMATE_MODE_HEAT ||
this->mode == climate::CLIMATE_MODE_DRY) { this->mode == climate::CLIMATE_MODE_DRY) {
if ((remote_state & FAN_MASK) == FAN_AUTO) if ((remote_state & FAN_MASK) == FAN_AUTO) {
this->fan_mode = climate::CLIMATE_FAN_AUTO; this->fan_mode = climate::CLIMATE_FAN_AUTO;
else if ((remote_state & FAN_MASK) == FAN_MIN) } else if ((remote_state & FAN_MASK) == FAN_MIN) {
this->fan_mode = climate::CLIMATE_FAN_LOW; this->fan_mode = climate::CLIMATE_FAN_LOW;
else if ((remote_state & FAN_MASK) == FAN_MED) } else if ((remote_state & FAN_MASK) == FAN_MED) {
this->fan_mode = climate::CLIMATE_FAN_MEDIUM; this->fan_mode = climate::CLIMATE_FAN_MEDIUM;
else if ((remote_state & FAN_MASK) == FAN_MAX) } else if ((remote_state & FAN_MASK) == FAN_MAX) {
this->fan_mode = climate::CLIMATE_FAN_HIGH; this->fan_mode = climate::CLIMATE_FAN_HIGH;
}
} }
} }
this->publish_state(); this->publish_state();
@ -175,7 +176,7 @@ void LgIrClimate::transmit_(uint32_t value) {
ESP_LOGD(TAG, "Sending climate_lg_ir code: 0x%02X", value); ESP_LOGD(TAG, "Sending climate_lg_ir code: 0x%02X", value);
auto transmit = this->transmitter_->transmit(); auto transmit = this->transmitter_->transmit();
auto data = transmit.get_data(); auto *data = transmit.get_data();
data->set_carrier_frequency(38000); data->set_carrier_frequency(38000);
data->reserve(2 + BITS * 2u); data->reserve(2 + BITS * 2u);

View file

@ -104,7 +104,7 @@ void CoolixClimate::transmit_state() {
ESP_LOGV(TAG, "Sending coolix code: 0x%06X", remote_state); ESP_LOGV(TAG, "Sending coolix code: 0x%06X", remote_state);
auto transmit = this->transmitter_->transmit(); auto transmit = this->transmitter_->transmit();
auto data = transmit.get_data(); auto *data = transmit.get_data();
remote_base::CoolixProtocol().encode(data, remote_state); remote_base::CoolixProtocol().encode(data, remote_state);
transmit.perform(); transmit.perform();
} }
@ -125,34 +125,37 @@ bool CoolixClimate::on_coolix(climate::Climate *parent, remote_base::RemoteRecei
parent->swing_mode = parent->swing_mode =
parent->swing_mode == climate::CLIMATE_SWING_OFF ? climate::CLIMATE_SWING_VERTICAL : climate::CLIMATE_SWING_OFF; parent->swing_mode == climate::CLIMATE_SWING_OFF ? climate::CLIMATE_SWING_VERTICAL : climate::CLIMATE_SWING_OFF;
} else { } else {
if ((remote_state & COOLIX_MODE_MASK) == COOLIX_HEAT) if ((remote_state & COOLIX_MODE_MASK) == COOLIX_HEAT) {
parent->mode = climate::CLIMATE_MODE_HEAT; parent->mode = climate::CLIMATE_MODE_HEAT;
else if ((remote_state & COOLIX_MODE_MASK) == COOLIX_AUTO) } else if ((remote_state & COOLIX_MODE_MASK) == COOLIX_AUTO) {
parent->mode = climate::CLIMATE_MODE_HEAT_COOL; parent->mode = climate::CLIMATE_MODE_HEAT_COOL;
else if ((remote_state & COOLIX_MODE_MASK) == COOLIX_DRY_FAN) { } else if ((remote_state & COOLIX_MODE_MASK) == COOLIX_DRY_FAN) {
if ((remote_state & COOLIX_FAN_MASK) == COOLIX_FAN_MODE_AUTO_DRY) if ((remote_state & COOLIX_FAN_MASK) == COOLIX_FAN_MODE_AUTO_DRY) {
parent->mode = climate::CLIMATE_MODE_DRY; parent->mode = climate::CLIMATE_MODE_DRY;
else } else {
parent->mode = climate::CLIMATE_MODE_FAN_ONLY; parent->mode = climate::CLIMATE_MODE_FAN_ONLY;
}
} else } else
parent->mode = climate::CLIMATE_MODE_COOL; parent->mode = climate::CLIMATE_MODE_COOL;
// Fan Speed // Fan Speed
if ((remote_state & COOLIX_FAN_AUTO) == COOLIX_FAN_AUTO || parent->mode == climate::CLIMATE_MODE_HEAT_COOL || if ((remote_state & COOLIX_FAN_AUTO) == COOLIX_FAN_AUTO || parent->mode == climate::CLIMATE_MODE_HEAT_COOL ||
parent->mode == climate::CLIMATE_MODE_DRY) parent->mode == climate::CLIMATE_MODE_DRY) {
parent->fan_mode = climate::CLIMATE_FAN_AUTO; parent->fan_mode = climate::CLIMATE_FAN_AUTO;
else if ((remote_state & COOLIX_FAN_MIN) == COOLIX_FAN_MIN) } else if ((remote_state & COOLIX_FAN_MIN) == COOLIX_FAN_MIN) {
parent->fan_mode = climate::CLIMATE_FAN_LOW; parent->fan_mode = climate::CLIMATE_FAN_LOW;
else if ((remote_state & COOLIX_FAN_MED) == COOLIX_FAN_MED) } else if ((remote_state & COOLIX_FAN_MED) == COOLIX_FAN_MED) {
parent->fan_mode = climate::CLIMATE_FAN_MEDIUM; parent->fan_mode = climate::CLIMATE_FAN_MEDIUM;
else if ((remote_state & COOLIX_FAN_MAX) == COOLIX_FAN_MAX) } else if ((remote_state & COOLIX_FAN_MAX) == COOLIX_FAN_MAX) {
parent->fan_mode = climate::CLIMATE_FAN_HIGH; parent->fan_mode = climate::CLIMATE_FAN_HIGH;
}
// Temperature // Temperature
uint8_t temperature_code = remote_state & COOLIX_TEMP_MASK; uint8_t temperature_code = remote_state & COOLIX_TEMP_MASK;
for (uint8_t i = 0; i < COOLIX_TEMP_RANGE; i++) for (uint8_t i = 0; i < COOLIX_TEMP_RANGE; i++) {
if (COOLIX_TEMP_MAP[i] == temperature_code) if (COOLIX_TEMP_MAP[i] == temperature_code)
parent->target_temperature = i + COOLIX_TEMP_MIN; parent->target_temperature = i + COOLIX_TEMP_MIN;
}
} }
parent->publish_state(); parent->publish_state();

View file

@ -7,6 +7,7 @@ from esphome.const import (
CONF_ID, CONF_ID,
CONF_DEVICE_CLASS, CONF_DEVICE_CLASS,
CONF_STATE, CONF_STATE,
CONF_ON_OPEN,
CONF_POSITION, CONF_POSITION,
CONF_POSITION_COMMAND_TOPIC, CONF_POSITION_COMMAND_TOPIC,
CONF_POSITION_STATE_TOPIC, CONF_POSITION_STATE_TOPIC,
@ -74,7 +75,6 @@ CoverClosedTrigger = cover_ns.class_(
"CoverClosedTrigger", automation.Trigger.template() "CoverClosedTrigger", automation.Trigger.template()
) )
CONF_ON_OPEN = "on_open"
CONF_ON_CLOSED = "on_closed" CONF_ON_CLOSED = "on_closed"
COVER_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend( COVER_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(

View file

@ -49,8 +49,8 @@ def validate_config(config):
if current_gain == 0.0 or voltage_gain == 0.0: if current_gain == 0.0 or voltage_gain == 0.0:
raise cv.Invalid("The gains can't be zero") raise cv.Invalid("The gains can't be zero")
max_energy = (0.25 * 0.25 / 3600 / (2 ** -4)) / (voltage_gain * current_gain) max_energy = (0.25 * 0.25 / 3600 / (2**-4)) / (voltage_gain * current_gain)
min_energy = (0.25 * 0.25 / 3600 / (2 ** 18)) / (voltage_gain * current_gain) min_energy = (0.25 * 0.25 / 3600 / (2**18)) / (voltage_gain * current_gain)
mech_min_energy = (0.25 * 0.25 / 3600 / 7.8) / (voltage_gain * current_gain) mech_min_energy = (0.25 * 0.25 / 3600 / 7.8) / (voltage_gain * current_gain)
if pulse_energy < min_energy or pulse_energy > max_energy: if pulse_energy < min_energy or pulse_energy > max_energy:
raise cv.Invalid( raise cv.Invalid(

View file

@ -11,11 +11,7 @@ CONFIG_SCHEMA = cv.Schema(
cv.GenerateID(): cv.declare_id(CustomTextSensorConstructor), cv.GenerateID(): cv.declare_id(CustomTextSensorConstructor),
cv.Required(CONF_LAMBDA): cv.returning_lambda, cv.Required(CONF_LAMBDA): cv.returning_lambda,
cv.Required(CONF_TEXT_SENSORS): cv.ensure_list( cv.Required(CONF_TEXT_SENSORS): cv.ensure_list(
text_sensor.TEXT_SENSOR_SCHEMA.extend( text_sensor.text_sensor_schema()
{
cv.GenerateID(): cv.declare_id(text_sensor.TextSensor),
}
)
), ),
} }
) )

View file

@ -23,7 +23,7 @@ void DaikinClimate::transmit_state() {
} }
auto transmit = this->transmitter_->transmit(); auto transmit = this->transmitter_->transmit();
auto data = transmit.get_data(); auto *data = transmit.get_data();
data->set_carrier_frequency(DAIKIN_IR_FREQUENCY); data->set_carrier_frequency(DAIKIN_IR_FREQUENCY);
data->mark(DAIKIN_HEADER_MARK); data->mark(DAIKIN_HEADER_MARK);
@ -175,14 +175,15 @@ bool DaikinClimate::parse_state_frame_(const uint8_t frame[]) {
} }
uint8_t fan_mode = frame[8]; uint8_t fan_mode = frame[8];
uint8_t swing_mode = frame[9]; uint8_t swing_mode = frame[9];
if (fan_mode & 0xF && swing_mode & 0xF) if (fan_mode & 0xF && swing_mode & 0xF) {
this->swing_mode = climate::CLIMATE_SWING_BOTH; this->swing_mode = climate::CLIMATE_SWING_BOTH;
else if (fan_mode & 0xF) } else if (fan_mode & 0xF) {
this->swing_mode = climate::CLIMATE_SWING_VERTICAL; this->swing_mode = climate::CLIMATE_SWING_VERTICAL;
else if (swing_mode & 0xF) } else if (swing_mode & 0xF) {
this->swing_mode = climate::CLIMATE_SWING_HORIZONTAL; this->swing_mode = climate::CLIMATE_SWING_HORIZONTAL;
else } else {
this->swing_mode = climate::CLIMATE_SWING_OFF; this->swing_mode = climate::CLIMATE_SWING_OFF;
}
switch (fan_mode & 0xF0) { switch (fan_mode & 0xF0) {
case DAIKIN_FAN_1: case DAIKIN_FAN_1:
case DAIKIN_FAN_2: case DAIKIN_FAN_2:
@ -212,9 +213,9 @@ bool DaikinClimate::on_receive(remote_base::RemoteReceiveData data) {
for (uint8_t pos = 0; pos < DAIKIN_STATE_FRAME_SIZE; pos++) { for (uint8_t pos = 0; pos < DAIKIN_STATE_FRAME_SIZE; pos++) {
uint8_t byte = 0; uint8_t byte = 0;
for (int8_t bit = 0; bit < 8; bit++) { for (int8_t bit = 0; bit < 8; bit++) {
if (data.expect_item(DAIKIN_BIT_MARK, DAIKIN_ONE_SPACE)) if (data.expect_item(DAIKIN_BIT_MARK, DAIKIN_ONE_SPACE)) {
byte |= 1 << bit; byte |= 1 << bit;
else if (!data.expect_item(DAIKIN_BIT_MARK, DAIKIN_ZERO_SPACE)) { } else if (!data.expect_item(DAIKIN_BIT_MARK, DAIKIN_ZERO_SPACE)) {
return false; return false;
} }
} }

View file

@ -52,7 +52,7 @@ void DallasComponent::setup() {
this->found_sensors_.push_back(address); this->found_sensors_.push_back(address);
} }
for (auto sensor : this->sensors_) { for (auto *sensor : this->sensors_) {
if (sensor->get_index().has_value()) { if (sensor->get_index().has_value()) {
if (*sensor->get_index() >= this->found_sensors_.size()) { if (*sensor->get_index() >= this->found_sensors_.size()) {
this->status_set_error(); this->status_set_error();
@ -235,7 +235,7 @@ float DallasTemperatureSensor::get_temp_c() {
return temp / 128.0f; return temp / 128.0f;
} }
std::string DallasTemperatureSensor::unique_id() { return "dallas-" + str_upper_case(format_hex(this->address_)); } std::string DallasTemperatureSensor::unique_id() { return "dallas-" + str_lower_case(format_hex(this->address_)); }
} // namespace dallas } // namespace dallas
} // namespace esphome } // namespace esphome

View file

@ -145,9 +145,10 @@ uint64_t ESPOneWire::search() {
// read its complement // read its complement
bool cmp_id_bit = this->read_bit(); bool cmp_id_bit = this->read_bit();
if (id_bit && cmp_id_bit) if (id_bit && cmp_id_bit) {
// No devices participating in search // No devices participating in search
break; break;
}
bool branch; bool branch;
@ -170,12 +171,13 @@ uint64_t ESPOneWire::search() {
} }
} }
if (branch) if (branch) {
// set bit // set bit
this->rom_number8_()[rom_byte_number] |= rom_byte_mask; this->rom_number8_()[rom_byte_number] |= rom_byte_mask;
else } else {
// clear bit // clear bit
this->rom_number8_()[rom_byte_number] &= ~rom_byte_mask; this->rom_number8_()[rom_byte_number] &= ~rom_byte_mask;
}
// choose/announce branch // choose/announce branch
this->write_bit(branch); this->write_bit(branch);
@ -190,9 +192,10 @@ uint64_t ESPOneWire::search() {
if (id_bit_number >= 65) { if (id_bit_number >= 65) {
this->last_discrepancy_ = last_zero; this->last_discrepancy_ = last_zero;
if (this->last_discrepancy_ == 0) if (this->last_discrepancy_ == 0) {
// we're at root and have no choices left, so this was the last one. // we're at root and have no choices left, so this was the last one.
this->last_device_flag_ = true; this->last_device_flag_ = true;
}
search_result = true; search_result = true;
} }

View file

@ -1,7 +1,7 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import text_sensor from esphome.components import text_sensor
from esphome.const import CONF_ICON, CONF_ID, CONF_STATUS from esphome.const import CONF_STATUS
from . import DalyBmsComponent, CONF_BMS_DALY_ID from . import DalyBmsComponent, CONF_BMS_DALY_ID
ICON_CAR_BATTERY = "mdi:car-battery" ICON_CAR_BATTERY = "mdi:car-battery"
@ -14,11 +14,8 @@ CONFIG_SCHEMA = cv.All(
cv.Schema( cv.Schema(
{ {
cv.GenerateID(CONF_BMS_DALY_ID): cv.use_id(DalyBmsComponent), cv.GenerateID(CONF_BMS_DALY_ID): cv.use_id(DalyBmsComponent),
cv.Optional(CONF_STATUS): text_sensor.TEXT_SENSOR_SCHEMA.extend( cv.Optional(CONF_STATUS): text_sensor.text_sensor_schema(
{ icon=ICON_CAR_BATTERY
cv.GenerateID(): cv.declare_id(text_sensor.TextSensor),
cv.Optional(CONF_ICON, default=ICON_CAR_BATTERY): cv.icon,
}
), ),
} }
).extend(cv.COMPONENT_SCHEMA) ).extend(cv.COMPONENT_SCHEMA)
@ -28,8 +25,7 @@ CONFIG_SCHEMA = cv.All(
async def setup_conf(config, key, hub): async def setup_conf(config, key, hub):
if key in config: if key in config:
conf = config[key] conf = config[key]
sens = cg.new_Pvariable(conf[CONF_ID]) sens = await text_sensor.new_text_sensor(conf)
await text_sensor.register_text_sensor(sens, conf)
cg.add(getattr(hub, f"set_{key}_text_sensor")(sens)) cg.add(getattr(hub, f"set_{key}_text_sensor")(sens))

View file

@ -1,17 +1,42 @@
import esphome.config_validation as cv import esphome.config_validation as cv
import esphome.codegen as cg import esphome.codegen as cg
from esphome.const import CONF_ID from esphome.const import (
CONF_ID,
CONF_DEVICE,
CONF_FREE,
CONF_FRAGMENTATION,
CONF_BLOCK,
CONF_LOOP_TIME,
)
CODEOWNERS = ["@OttoWinter"] CODEOWNERS = ["@OttoWinter"]
DEPENDENCIES = ["logger"] DEPENDENCIES = ["logger"]
CONF_DEBUG_ID = "debug_id"
debug_ns = cg.esphome_ns.namespace("debug") debug_ns = cg.esphome_ns.namespace("debug")
DebugComponent = debug_ns.class_("DebugComponent", cg.Component) DebugComponent = debug_ns.class_("DebugComponent", cg.PollingComponent)
CONFIG_SCHEMA = cv.Schema( CONFIG_SCHEMA = cv.Schema(
{ {
cv.GenerateID(): cv.declare_id(DebugComponent), cv.GenerateID(): cv.declare_id(DebugComponent),
cv.Optional(CONF_DEVICE): cv.invalid(
"The 'device' option has been moved to the 'debug' text_sensor component"
),
cv.Optional(CONF_FREE): cv.invalid(
"The 'free' option has been moved to the 'debug' sensor component"
),
cv.Optional(CONF_BLOCK): cv.invalid(
"The 'block' option has been moved to the 'debug' sensor component"
),
cv.Optional(CONF_FRAGMENTATION): cv.invalid(
"The 'fragmentation' option has been moved to the 'debug' sensor component"
),
cv.Optional(CONF_LOOP_TIME): cv.invalid(
"The 'loop_time' option has been moved to the 'debug' sensor component"
),
} }
).extend(cv.COMPONENT_SCHEMA) ).extend(cv.polling_component_schema("60s"))
async def to_code(config): async def to_code(config):

View file

@ -1,21 +1,23 @@
#include "debug_component.h" #include "debug_component.h"
#include <algorithm>
#include "esphome/core/log.h" #include "esphome/core/log.h"
#include "esphome/core/hal.h"
#include "esphome/core/helpers.h" #include "esphome/core/helpers.h"
#include "esphome/core/defines.h"
#include "esphome/core/version.h" #include "esphome/core/version.h"
#ifdef USE_ESP_IDF #ifdef USE_ESP32
#include <esp_heap_caps.h> #include <esp_heap_caps.h>
#include <esp_system.h> #include <esp_system.h>
#endif
#ifdef USE_ESP32
#if ESP_IDF_VERSION_MAJOR >= 4 #if ESP_IDF_VERSION_MAJOR >= 4
#include <esp32/rom/rtc.h> #include <esp32/rom/rtc.h>
#else #else
#include <rom/rtc.h> #include <rom/rtc.h>
#endif #endif
#endif
#endif // USE_ESP32
#ifdef USE_ARDUINO #ifdef USE_ARDUINO
#include <Esp.h> #include <Esp.h>
@ -26,19 +28,40 @@ namespace debug {
static const char *const TAG = "debug"; static const char *const TAG = "debug";
static uint32_t get_free_heap() {
#if defined(USE_ESP8266)
return ESP.getFreeHeap(); // NOLINT(readability-static-accessed-through-instance)
#elif defined(USE_ESP32)
return heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
#endif
}
void DebugComponent::dump_config() { void DebugComponent::dump_config() {
std::string device_info;
device_info.reserve(256);
#ifndef ESPHOME_LOG_HAS_DEBUG #ifndef ESPHOME_LOG_HAS_DEBUG
ESP_LOGE(TAG, "Debug Component requires debug log level!"); ESP_LOGE(TAG, "Debug Component requires debug log level!");
this->status_set_error(); this->status_set_error();
return; return;
#endif #endif
ESP_LOGCONFIG(TAG, "Debug component:");
#ifdef USE_TEXT_SENSOR
LOG_TEXT_SENSOR(" ", "Device info", this->device_info_);
#endif // USE_TEXT_SENSOR
#ifdef USE_SENSOR
LOG_SENSOR(" ", "Free space on heap", this->free_sensor_);
LOG_SENSOR(" ", "Largest free heap block", this->block_sensor_);
#if defined(USE_ESP8266) && ARDUINO_VERSION_CODE >= VERSION_CODE(2, 5, 2)
LOG_SENSOR(" ", "Heap fragmentation", this->fragmentation_sensor_);
#endif // defined(USE_ESP8266) && ARDUINO_VERSION_CODE >= VERSION_CODE(2, 5, 2)
#endif // USE_SENSOR
ESP_LOGD(TAG, "ESPHome version %s", ESPHOME_VERSION); ESP_LOGD(TAG, "ESPHome version %s", ESPHOME_VERSION);
#ifdef USE_ARDUINO device_info += ESPHOME_VERSION;
this->free_heap_ = ESP.getFreeHeap(); // NOLINT(readability-static-accessed-through-instance)
#elif defined(USE_ESP_IDF) this->free_heap_ = get_free_heap();
this->free_heap_ = heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
#endif
ESP_LOGD(TAG, "Free Heap Size: %u bytes", this->free_heap_); ESP_LOGD(TAG, "Free Heap Size: %u bytes", this->free_heap_);
#ifdef USE_ARDUINO #ifdef USE_ARDUINO
@ -67,9 +90,12 @@ void DebugComponent::dump_config() {
default: default:
flash_mode = "UNKNOWN"; flash_mode = "UNKNOWN";
} }
// NOLINTNEXTLINE(readability-static-accessed-through-instance) ESP_LOGD(TAG, "Flash Chip: Size=%ukB Speed=%uMHz Mode=%s",
ESP_LOGD(TAG, "Flash Chip: Size=%ukB Speed=%uMHz Mode=%s", ESP.getFlashChipSize() / 1024, ESP.getFlashChipSize() / 1024, // NOLINT
ESP.getFlashChipSpeed() / 1000000, flash_mode); ESP.getFlashChipSpeed() / 1000000, flash_mode); // NOLINT
device_info += "|Flash: " + to_string(ESP.getFlashChipSize() / 1024) + // NOLINT
"kB Speed:" + to_string(ESP.getFlashChipSpeed() / 1000000) + "MHz Mode:"; // NOLINT
device_info += flash_mode;
#endif // USE_ARDUINO #endif // USE_ARDUINO
#ifdef USE_ESP32 #ifdef USE_ESP32
@ -104,10 +130,21 @@ void DebugComponent::dump_config() {
features += "Other:" + format_hex(info.features); features += "Other:" + format_hex(info.features);
ESP_LOGD(TAG, "Chip: Model=%s, Features=%s Cores=%u, Revision=%u", model, features.c_str(), info.cores, ESP_LOGD(TAG, "Chip: Model=%s, Features=%s Cores=%u, Revision=%u", model, features.c_str(), info.cores,
info.revision); info.revision);
device_info += "|Chip: ";
device_info += model;
device_info += " Features:";
device_info += features;
device_info += " Cores:" + to_string(info.cores);
device_info += " Revision:" + to_string(info.revision);
ESP_LOGD(TAG, "ESP-IDF Version: %s", esp_get_idf_version()); ESP_LOGD(TAG, "ESP-IDF Version: %s", esp_get_idf_version());
device_info += "|ESP-IDF: ";
device_info += esp_get_idf_version();
ESP_LOGD(TAG, "EFuse MAC: %s", get_mac_address_pretty().c_str()); std::string mac = get_mac_address_pretty();
ESP_LOGD(TAG, "EFuse MAC: %s", mac.c_str());
device_info += "|EFuse MAC: ";
device_info += mac;
const char *reset_reason; const char *reset_reason;
switch (rtc_get_reset_reason(0)) { switch (rtc_get_reset_reason(0)) {
@ -160,6 +197,8 @@ void DebugComponent::dump_config() {
reset_reason = "Unknown Reset Reason"; reset_reason = "Unknown Reset Reason";
} }
ESP_LOGD(TAG, "Reset Reason: %s", reset_reason); ESP_LOGD(TAG, "Reset Reason: %s", reset_reason);
device_info += "|Reset: ";
device_info += reset_reason;
const char *wakeup_reason; const char *wakeup_reason;
switch (rtc_get_wakeup_cause()) { switch (rtc_get_wakeup_cause()) {
@ -203,6 +242,8 @@ void DebugComponent::dump_config() {
wakeup_reason = "Unknown"; wakeup_reason = "Unknown";
} }
ESP_LOGD(TAG, "Wakeup Reason: %s", wakeup_reason); ESP_LOGD(TAG, "Wakeup Reason: %s", wakeup_reason);
device_info += "|Wakeup: ";
device_info += wakeup_reason;
#endif #endif
#if defined(USE_ESP8266) && !defined(CLANG_TIDY) #if defined(USE_ESP8266) && !defined(CLANG_TIDY)
@ -214,20 +255,81 @@ void DebugComponent::dump_config() {
ESP_LOGD(TAG, "Flash Chip ID=0x%08X", ESP.getFlashChipId()); ESP_LOGD(TAG, "Flash Chip ID=0x%08X", ESP.getFlashChipId());
ESP_LOGD(TAG, "Reset Reason: %s", ESP.getResetReason().c_str()); ESP_LOGD(TAG, "Reset Reason: %s", ESP.getResetReason().c_str());
ESP_LOGD(TAG, "Reset Info: %s", ESP.getResetInfo().c_str()); ESP_LOGD(TAG, "Reset Info: %s", ESP.getResetInfo().c_str());
device_info += "|Chip: 0x" + format_hex(ESP.getChipId());
device_info += "|SDK: ";
device_info += ESP.getSdkVersion();
device_info += "|Core: ";
device_info += ESP.getCoreVersion().c_str();
device_info += "|Boot: ";
device_info += to_string(ESP.getBootVersion());
device_info += "|Mode: " + to_string(ESP.getBootMode());
device_info += "|CPU: " + to_string(ESP.getCpuFreqMHz());
device_info += "|Flash: 0x" + format_hex(ESP.getFlashChipId());
device_info += "|Reset: ";
device_info += ESP.getResetReason().c_str();
device_info += "|";
device_info += ESP.getResetInfo().c_str();
#endif #endif
#ifdef USE_TEXT_SENSOR
if (this->device_info_ != nullptr) {
if (device_info.length() > 255)
device_info.resize(255);
this->device_info_->publish_state(device_info);
}
#endif // USE_TEXT_SENSOR
} }
void DebugComponent::loop() { void DebugComponent::loop() {
#ifdef USE_ARDUINO // log when free heap space has halved
uint32_t new_free_heap = ESP.getFreeHeap(); // NOLINT(readability-static-accessed-through-instance) uint32_t new_free_heap = get_free_heap();
#elif defined(USE_ESP_IDF)
uint32_t new_free_heap = heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
#endif
if (new_free_heap < this->free_heap_ / 2) { if (new_free_heap < this->free_heap_ / 2) {
this->free_heap_ = new_free_heap; this->free_heap_ = new_free_heap;
ESP_LOGD(TAG, "Free Heap Size: %u bytes", this->free_heap_); ESP_LOGD(TAG, "Free Heap Size: %u bytes", this->free_heap_);
this->status_momentary_warning("heap", 1000); this->status_momentary_warning("heap", 1000);
} }
#ifdef USE_SENSOR
// calculate loop time - from last call to this one
if (this->loop_time_sensor_ != nullptr) {
uint32_t now = millis();
uint32_t loop_time = now - this->last_loop_timetag_;
this->max_loop_time_ = std::max(this->max_loop_time_, loop_time);
this->last_loop_timetag_ = now;
}
#endif // USE_SENSOR
} }
void DebugComponent::update() {
#ifdef USE_SENSOR
if (this->free_sensor_ != nullptr) {
this->free_sensor_->publish_state(get_free_heap());
}
if (this->block_sensor_ != nullptr) {
#if defined(USE_ESP8266)
// NOLINTNEXTLINE(readability-static-accessed-through-instance)
this->block_sensor_->publish_state(ESP.getMaxFreeBlockSize());
#elif defined(USE_ESP32)
this->block_sensor_->publish_state(heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL));
#endif
}
#if defined(USE_ESP8266) && ARDUINO_VERSION_CODE >= VERSION_CODE(2, 5, 2)
if (this->fragmentation_sensor_ != nullptr) {
// NOLINTNEXTLINE(readability-static-accessed-through-instance)
this->fragmentation_sensor_->publish_state(ESP.getHeapFragmentation());
}
#endif
if (this->loop_time_sensor_ != nullptr) {
this->loop_time_sensor_->publish_state(this->max_loop_time_);
this->max_loop_time_ = 0;
}
#endif // USE_SENSOR
}
float DebugComponent::get_setup_priority() const { return setup_priority::LATE; } float DebugComponent::get_setup_priority() const { return setup_priority::LATE; }
} // namespace debug } // namespace debug

View file

@ -1,18 +1,56 @@
#pragma once #pragma once
#include "esphome/core/component.h" #include "esphome/core/component.h"
#include "esphome/core/defines.h"
#include "esphome/core/macros.h"
#include "esphome/core/helpers.h"
#ifdef USE_SENSOR
#include "esphome/components/sensor/sensor.h"
#endif
#ifdef USE_TEXT_SENSOR
#include "esphome/components/text_sensor/text_sensor.h"
#endif
namespace esphome { namespace esphome {
namespace debug { namespace debug {
class DebugComponent : public Component { class DebugComponent : public PollingComponent {
public: public:
void loop() override; void loop() override;
void update() override;
float get_setup_priority() const override; float get_setup_priority() const override;
void dump_config() override; void dump_config() override;
#ifdef USE_TEXT_SENSOR
void set_device_info_sensor(text_sensor::TextSensor *device_info) { device_info_ = device_info; }
#endif // USE_TEXT_SENSOR
#ifdef USE_SENSOR
void set_free_sensor(sensor::Sensor *free_sensor) { free_sensor_ = free_sensor; }
void set_block_sensor(sensor::Sensor *block_sensor) { block_sensor_ = block_sensor; }
#if defined(USE_ESP8266) && ARDUINO_VERSION_CODE >= VERSION_CODE(2, 5, 2)
void set_fragmentation_sensor(sensor::Sensor *fragmentation_sensor) { fragmentation_sensor_ = fragmentation_sensor; }
#endif
void set_loop_time_sensor(sensor::Sensor *loop_time_sensor) { loop_time_sensor_ = loop_time_sensor; }
#endif // USE_SENSOR
protected: protected:
uint32_t free_heap_{}; uint32_t free_heap_{};
#ifdef USE_SENSOR
uint32_t last_loop_timetag_{0};
uint32_t max_loop_time_{0};
sensor::Sensor *free_sensor_{nullptr};
sensor::Sensor *block_sensor_{nullptr};
#if defined(USE_ESP8266) && ARDUINO_VERSION_CODE >= VERSION_CODE(2, 5, 2)
sensor::Sensor *fragmentation_sensor_{nullptr};
#endif
sensor::Sensor *loop_time_sensor_{nullptr};
#endif // USE_SENSOR
#ifdef USE_TEXT_SENSOR
text_sensor::TextSensor *device_info_{nullptr};
#endif // USE_TEXT_SENSOR
}; };
} // namespace debug } // namespace debug

View file

@ -0,0 +1,49 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor
from esphome.const import (
CONF_FREE,
CONF_FRAGMENTATION,
CONF_BLOCK,
CONF_LOOP_TIME,
UNIT_MILLISECOND,
UNIT_PERCENT,
UNIT_BYTES,
ICON_COUNTER,
ICON_TIMER,
)
from . import CONF_DEBUG_ID, DebugComponent
DEPENDENCIES = ["debug"]
CONFIG_SCHEMA = {
cv.GenerateID(CONF_DEBUG_ID): cv.use_id(DebugComponent),
cv.Optional(CONF_FREE): sensor.sensor_schema(UNIT_BYTES, ICON_COUNTER, 0),
cv.Optional(CONF_BLOCK): sensor.sensor_schema(UNIT_BYTES, ICON_COUNTER, 0),
cv.Optional(CONF_FRAGMENTATION): cv.All(
cv.only_on_esp8266,
cv.require_framework_version(esp8266_arduino=cv.Version(2, 5, 2)),
sensor.sensor_schema(UNIT_PERCENT, ICON_COUNTER, 1),
),
cv.Optional(CONF_LOOP_TIME): sensor.sensor_schema(UNIT_MILLISECOND, ICON_TIMER, 0),
}
async def to_code(config):
debug_component = await cg.get_variable(config[CONF_DEBUG_ID])
if CONF_FREE in config:
sens = await sensor.new_sensor(config[CONF_FREE])
cg.add(debug_component.set_free_sensor(sens))
if CONF_BLOCK in config:
sens = await sensor.new_sensor(config[CONF_BLOCK])
cg.add(debug_component.set_block_sensor(sens))
if CONF_FRAGMENTATION in config:
sens = await sensor.new_sensor(config[CONF_FRAGMENTATION])
cg.add(debug_component.set_fragmentation_sensor(sens))
if CONF_LOOP_TIME in config:
sens = await sensor.new_sensor(config[CONF_LOOP_TIME])
cg.add(debug_component.set_loop_time_sensor(sens))

View file

@ -0,0 +1,24 @@
from esphome.components import text_sensor
import esphome.config_validation as cv
import esphome.codegen as cg
from esphome.const import CONF_DEVICE
from . import CONF_DEBUG_ID, DebugComponent
DEPENDENCIES = ["debug"]
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(CONF_DEBUG_ID): cv.use_id(DebugComponent),
cv.Optional(CONF_DEVICE): text_sensor.text_sensor_schema(),
}
)
async def to_code(config):
debug_component = await cg.get_variable(config[CONF_DEBUG_ID])
if CONF_DEVICE in config:
sens = await text_sensor.new_text_sensor(config[CONF_DEVICE])
cg.add(debug_component.set_device_info_sensor(sens))

View file

@ -37,12 +37,10 @@ from esphome.const import (
DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_TEMPERATURE,
ICON_BLUETOOTH, ICON_BLUETOOTH,
ICON_BLUR, ICON_BLUR,
ICON_EMPTY,
ICON_THERMOMETER, ICON_THERMOMETER,
STATE_CLASS_MEASUREMENT, STATE_CLASS_MEASUREMENT,
STATE_CLASS_TOTAL_INCREASING, STATE_CLASS_TOTAL_INCREASING,
UNIT_CELSIUS, UNIT_CELSIUS,
UNIT_EMPTY,
UNIT_PERCENT, UNIT_PERCENT,
UNIT_WATT_HOURS, UNIT_WATT_HOURS,
) )
@ -67,7 +65,7 @@ DemoClimate = demo_ns.class_("DemoClimate", climate.Climate, cg.Component)
DemoClimateType = demo_ns.enum("DemoClimateType", is_class=True) DemoClimateType = demo_ns.enum("DemoClimateType", is_class=True)
DemoCover = demo_ns.class_("DemoCover", cover.Cover, cg.Component) DemoCover = demo_ns.class_("DemoCover", cover.Cover, cg.Component)
DemoCoverType = demo_ns.enum("DemoCoverType", is_class=True) DemoCoverType = demo_ns.enum("DemoCoverType", is_class=True)
DemoFan = demo_ns.class_("DemoFan", cg.Component) DemoFan = demo_ns.class_("DemoFan", fan.Fan, cg.Component)
DemoFanType = demo_ns.enum("DemoFanType", is_class=True) DemoFanType = demo_ns.enum("DemoFanType", is_class=True)
DemoLight = demo_ns.class_("DemoLight", light.LightOutput, cg.Component) DemoLight = demo_ns.class_("DemoLight", light.LightOutput, cg.Component)
DemoLightType = demo_ns.enum("DemoLightType", is_class=True) DemoLightType = demo_ns.enum("DemoLightType", is_class=True)
@ -339,7 +337,7 @@ CONFIG_SCHEMA = cv.Schema(
}, },
], ],
): [ ): [
sensor.sensor_schema(UNIT_EMPTY, ICON_EMPTY, 0) sensor.sensor_schema(accuracy_decimals=0)
.extend(cv.polling_component_schema("60s")) .extend(cv.polling_component_schema("60s"))
.extend( .extend(
{ {
@ -378,12 +376,8 @@ CONFIG_SCHEMA = cv.Schema(
}, },
], ],
): [ ): [
text_sensor.TEXT_SENSOR_SCHEMA.extend( text_sensor.text_sensor_schema(klass=DemoTextSensor).extend(
cv.polling_component_schema("60s") cv.polling_component_schema("60s")
).extend(
{
cv.GenerateID(): cv.declare_id(DemoTextSensor),
}
) )
], ],
} }
@ -411,8 +405,7 @@ async def to_code(config):
for conf in config[CONF_FANS]: for conf in config[CONF_FANS]:
var = cg.new_Pvariable(conf[CONF_OUTPUT_ID]) var = cg.new_Pvariable(conf[CONF_OUTPUT_ID])
await cg.register_component(var, conf) await cg.register_component(var, conf)
fan_ = await fan.create_fan_state(conf) await fan.register_fan(var, conf)
cg.add(var.set_fan(fan_))
cg.add(var.set_type(conf[CONF_TYPE])) cg.add(var.set_type(conf[CONF_TYPE]))
for conf in config[CONF_LIGHTS]: for conf in config[CONF_LIGHTS]:
@ -444,6 +437,5 @@ async def to_code(config):
await switch.register_switch(var, conf) await switch.register_switch(var, conf)
for conf in config[CONF_TEXT_SENSORS]: for conf in config[CONF_TEXT_SENSORS]:
var = cg.new_Pvariable(conf[CONF_ID]) var = await text_sensor.new_text_sensor(conf)
await cg.register_component(var, conf) await cg.register_component(var, conf)
await text_sensor.register_text_sensor(var, conf)

View file

@ -1,7 +1,7 @@
#pragma once #pragma once
#include "esphome/core/component.h" #include "esphome/core/component.h"
#include "esphome/components/fan/fan_state.h" #include "esphome/components/fan/fan.h"
namespace esphome { namespace esphome {
namespace demo { namespace demo {
@ -13,11 +13,10 @@ enum class DemoFanType {
TYPE_4, TYPE_4,
}; };
class DemoFan : public Component { class DemoFan : public fan::Fan, public Component {
public: public:
void set_type(DemoFanType type) { type_ = type; } void set_type(DemoFanType type) { type_ = type; }
void set_fan(fan::FanState *fan) { fan_ = fan; } fan::FanTraits get_traits() override {
void setup() override {
fan::FanTraits traits{}; fan::FanTraits traits{};
// oscillation // oscillation
@ -43,10 +42,23 @@ class DemoFan : public Component {
break; break;
} }
this->fan_->set_traits(traits); return traits;
}
protected:
void control(const fan::FanCall &call) override {
if (call.get_state().has_value())
this->state = *call.get_state();
if (call.get_oscillating().has_value())
this->oscillating = *call.get_oscillating();
if (call.get_speed().has_value())
this->speed = *call.get_speed();
if (call.get_direction().has_value())
this->direction = *call.get_direction();
this->publish_state();
} }
fan::FanState *fan_;
DemoFanType type_; DemoFanType type_;
}; };

View file

@ -114,10 +114,11 @@ bool HOT IRAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, bool r
// Wait for rising edge // Wait for rising edge
while (!this->pin_->digital_read()) { while (!this->pin_->digital_read()) {
if (micros() - start_time > 90) { if (micros() - start_time > 90) {
if (i < 0) if (i < 0) {
error_code = 1; error_code = 1;
else } else {
error_code = 2; error_code = 2;
}
break; break;
} }
} }
@ -130,10 +131,11 @@ bool HOT IRAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, bool r
// Wait for falling edge // Wait for falling edge
while (this->pin_->digital_read()) { while (this->pin_->digital_read()) {
if ((end_time = micros()) - start_time > 90) { if ((end_time = micros()) - start_time > 90) {
if (i < 0) if (i < 0) {
error_code = 3; error_code = 3;
else } else {
error_code = 4; error_code = 4;
}
break; break;
} }
} }

View file

@ -176,9 +176,10 @@ void DisplayBuffer::print(int x, int y, Font *font, Color color, TextAlign align
ESP_LOGW(TAG, "Encountered character without representation in font: '%c'", text[i]); ESP_LOGW(TAG, "Encountered character without representation in font: '%c'", text[i]);
if (!font->get_glyphs().empty()) { if (!font->get_glyphs().empty()) {
uint8_t glyph_width = font->get_glyphs()[0].glyph_data_->width; uint8_t glyph_width = font->get_glyphs()[0].glyph_data_->width;
for (int glyph_x = 0; glyph_x < glyph_width; glyph_x++) for (int glyph_x = 0; glyph_x < glyph_width; glyph_x++) {
for (int glyph_y = 0; glyph_y < height; glyph_y++) for (int glyph_y = 0; glyph_y < height; glyph_y++)
this->draw_pixel_at(glyph_x + x_at, glyph_y + y_start, color); this->draw_pixel_at(glyph_x + x_at, glyph_y + y_start, color);
}
x_at += glyph_width; x_at += glyph_width;
} }
@ -233,6 +234,14 @@ void DisplayBuffer::image(int x, int y, Image *image, Color color_on, Color colo
} }
} }
break; break;
case IMAGE_TYPE_TRANSPARENT_BINARY:
for (int img_x = 0; img_x < image->get_width(); img_x++) {
for (int img_y = 0; img_y < image->get_height(); img_y++) {
if (image->get_pixel(img_x, img_y))
this->draw_pixel_at(x + img_x, y + img_y, color_on);
}
}
break;
} }
} }
@ -243,6 +252,12 @@ void DisplayBuffer::legend(int x, int y, graph::Graph *graph, Color color_on) {
} }
#endif // USE_GRAPH #endif // USE_GRAPH
#ifdef USE_QR_CODE
void DisplayBuffer::qr_code(int x, int y, qr_code::QrCode *qr_code, Color color_on, int scale) {
qr_code->draw(this, x, y, color_on, scale);
}
#endif // USE_QR_CODE
void DisplayBuffer::get_text_bounds(int x, int y, const char *text, Font *font, TextAlign align, int *x1, int *y1, void DisplayBuffer::get_text_bounds(int x, int y, const char *text, Font *font, TextAlign align, int *x1, int *y1,
int *width, int *height) { int *width, int *height) {
int x_offset, baseline; int x_offset, baseline;
@ -417,10 +432,11 @@ int Font::match_next_glyph(const char *str, int *match_length) {
int hi = this->glyphs_.size() - 1; int hi = this->glyphs_.size() - 1;
while (lo != hi) { while (lo != hi) {
int mid = (lo + hi + 1) / 2; int mid = (lo + hi + 1) / 2;
if (this->glyphs_[mid].compare_to(str)) if (this->glyphs_[mid].compare_to(str)) {
lo = mid; lo = mid;
else } else {
hi = mid - 1; hi = mid - 1;
}
} }
*match_length = this->glyphs_[lo].match_length(str); *match_length = this->glyphs_[lo].match_length(str);
if (*match_length <= 0) if (*match_length <= 0)
@ -446,10 +462,11 @@ void Font::measure(const char *str, int *width, int *x_offset, int *baseline, in
} }
const Glyph &glyph = this->glyphs_[glyph_n]; const Glyph &glyph = this->glyphs_[glyph_n];
if (!has_char) if (!has_char) {
min_x = glyph.glyph_data_->offset_x; min_x = glyph.glyph_data_->offset_x;
else } else {
min_x = std::min(min_x, x + glyph.glyph_data_->offset_x); min_x = std::min(min_x, x + glyph.glyph_data_->offset_x);
}
x += glyph.glyph_data_->width + glyph.glyph_data_->offset_x; x += glyph.glyph_data_->width + glyph.glyph_data_->offset_x;
i += match_length; i += match_length;

View file

@ -14,6 +14,10 @@
#include "esphome/components/graph/graph.h" #include "esphome/components/graph/graph.h"
#endif #endif
#ifdef USE_QR_CODE
#include "esphome/components/qr_code/qr_code.h"
#endif
namespace esphome { namespace esphome {
namespace display { namespace display {
@ -73,7 +77,12 @@ extern const Color COLOR_OFF;
/// Turn the pixel ON. /// Turn the pixel ON.
extern const Color COLOR_ON; extern const Color COLOR_ON;
enum ImageType { IMAGE_TYPE_BINARY = 0, IMAGE_TYPE_GRAYSCALE = 1, IMAGE_TYPE_RGB24 = 2 }; enum ImageType {
IMAGE_TYPE_BINARY = 0,
IMAGE_TYPE_GRAYSCALE = 1,
IMAGE_TYPE_RGB24 = 2,
IMAGE_TYPE_TRANSPARENT_BINARY = 3,
};
enum DisplayRotation { enum DisplayRotation {
DISPLAY_ROTATION_0_DEGREES = 0, DISPLAY_ROTATION_0_DEGREES = 0,
@ -302,6 +311,17 @@ class DisplayBuffer {
void legend(int x, int y, graph::Graph *graph, Color color_on = COLOR_ON); void legend(int x, int y, graph::Graph *graph, Color color_on = COLOR_ON);
#endif // USE_GRAPH #endif // USE_GRAPH
#ifdef USE_QR_CODE
/** Draw the `qr_code` with the top-left corner at [x,y] to the screen.
*
* @param x The x coordinate of the upper left corner.
* @param y The y coordinate of the upper left corner.
* @param qr_code The qr_code to draw
* @param color_on The color to replace in binary images for the on bits.
*/
void qr_code(int x, int y, qr_code::QrCode *qr_code, Color color_on = COLOR_ON, int scale = 1);
#endif
/** Get the text bounds of the given string. /** Get the text bounds of the given string.
* *
* @param x The x coordinate to place the string at, can be 0 if only interested in dimensions. * @param x The x coordinate to place the string at, can be 0 if only interested in dimensions.
@ -336,15 +356,15 @@ class DisplayBuffer {
// Internal method to set display auto clearing. // Internal method to set display auto clearing.
void set_auto_clear(bool auto_clear_enabled) { this->auto_clear_enabled_ = auto_clear_enabled; } void set_auto_clear(bool auto_clear_enabled) { this->auto_clear_enabled_ = auto_clear_enabled; }
virtual int get_height_internal() = 0;
virtual int get_width_internal() = 0;
DisplayRotation get_rotation() const { return this->rotation_; }
protected: protected:
void vprintf_(int x, int y, Font *font, Color color, TextAlign align, const char *format, va_list arg); void vprintf_(int x, int y, Font *font, Color color, TextAlign align, const char *format, va_list arg);
virtual void draw_absolute_pixel_internal(int x, int y, Color color) = 0; virtual void draw_absolute_pixel_internal(int x, int y, Color color) = 0;
virtual int get_height_internal() = 0;
virtual int get_width_internal() = 0;
void init_internal_(uint32_t buffer_length); void init_internal_(uint32_t buffer_length);
void do_update_(); void do_update_();

View file

@ -1,9 +1,7 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import text_sensor from esphome.components import text_sensor
from esphome.const import (
CONF_ID,
)
from . import Dsmr, CONF_DSMR_ID from . import Dsmr, CONF_DSMR_ID
AUTO_LOAD = ["dsmr"] AUTO_LOAD = ["dsmr"]
@ -11,71 +9,19 @@ AUTO_LOAD = ["dsmr"]
CONFIG_SCHEMA = cv.Schema( CONFIG_SCHEMA = cv.Schema(
{ {
cv.GenerateID(CONF_DSMR_ID): cv.use_id(Dsmr), cv.GenerateID(CONF_DSMR_ID): cv.use_id(Dsmr),
cv.Optional("identification"): text_sensor.TEXT_SENSOR_SCHEMA.extend( cv.Optional("identification"): text_sensor.text_sensor_schema(),
{ cv.Optional("p1_version"): text_sensor.text_sensor_schema(),
cv.GenerateID(): cv.declare_id(text_sensor.TextSensor), cv.Optional("p1_version_be"): text_sensor.text_sensor_schema(),
} cv.Optional("timestamp"): text_sensor.text_sensor_schema(),
), cv.Optional("electricity_tariff"): text_sensor.text_sensor_schema(),
cv.Optional("p1_version"): text_sensor.TEXT_SENSOR_SCHEMA.extend( cv.Optional("electricity_failure_log"): text_sensor.text_sensor_schema(),
{ cv.Optional("message_short"): text_sensor.text_sensor_schema(),
cv.GenerateID(): cv.declare_id(text_sensor.TextSensor), cv.Optional("message_long"): text_sensor.text_sensor_schema(),
} cv.Optional("gas_equipment_id"): text_sensor.text_sensor_schema(),
), cv.Optional("thermal_equipment_id"): text_sensor.text_sensor_schema(),
cv.Optional("p1_version_be"): text_sensor.TEXT_SENSOR_SCHEMA.extend( cv.Optional("water_equipment_id"): text_sensor.text_sensor_schema(),
{ cv.Optional("sub_equipment_id"): text_sensor.text_sensor_schema(),
cv.GenerateID(): cv.declare_id(text_sensor.TextSensor), cv.Optional("gas_delivered_text"): text_sensor.text_sensor_schema(),
}
),
cv.Optional("timestamp"): text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(text_sensor.TextSensor),
}
),
cv.Optional("electricity_tariff"): text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(text_sensor.TextSensor),
}
),
cv.Optional("electricity_failure_log"): text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(text_sensor.TextSensor),
}
),
cv.Optional("message_short"): text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(text_sensor.TextSensor),
}
),
cv.Optional("message_long"): text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(text_sensor.TextSensor),
}
),
cv.Optional("gas_equipment_id"): text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(text_sensor.TextSensor),
}
),
cv.Optional("thermal_equipment_id"): text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(text_sensor.TextSensor),
}
),
cv.Optional("water_equipment_id"): text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(text_sensor.TextSensor),
}
),
cv.Optional("sub_equipment_id"): text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(text_sensor.TextSensor),
}
),
cv.Optional("gas_delivered_text"): text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(text_sensor.TextSensor),
}
),
} }
).extend(cv.COMPONENT_SCHEMA) ).extend(cv.COMPONENT_SCHEMA)
@ -89,8 +35,7 @@ async def to_code(config):
continue continue
id = conf.get("id") id = conf.get("id")
if id and id.type == text_sensor.TextSensor: if id and id.type == text_sensor.TextSensor:
var = cg.new_Pvariable(conf[CONF_ID]) var = await text_sensor.new_text_sensor(conf)
await text_sensor.register_text_sensor(var, conf)
cg.add(getattr(hub, f"set_{key}")(var)) cg.add(getattr(hub, f"set_{key}")(var))
text_sensors.append(f"F({key})") text_sensors.append(f"F({key})")

View file

@ -97,7 +97,7 @@ bool E131Component::process_(int universe, const E131Packet &packet) {
ESP_LOGV(TAG, "Received E1.31 packet for %d universe, with %d bytes", universe, packet.count); ESP_LOGV(TAG, "Received E1.31 packet for %d universe, with %d bytes", universe, packet.count);
for (auto light_effect : light_effects_) { for (auto *light_effect : light_effects_) {
handled = light_effect->process_(universe, packet) || handled; handled = light_effect->process_(universe, packet) || handled;
} }

View file

@ -33,11 +33,9 @@ class E131Component : public esphome::Component {
void loop() override; void loop() override;
float get_setup_priority() const override { return setup_priority::AFTER_WIFI; } float get_setup_priority() const override { return setup_priority::AFTER_WIFI; }
public:
void add_effect(E131AddressableLightEffect *light_effect); void add_effect(E131AddressableLightEffect *light_effect);
void remove_effect(E131AddressableLightEffect *light_effect); void remove_effect(E131AddressableLightEffect *light_effect);
public:
void set_method(E131ListenMethod listen_method) { this->listen_method_ = listen_method; } void set_method(E131ListenMethod listen_method) { this->listen_method_ = listen_method; }
protected: protected:
@ -47,7 +45,6 @@ class E131Component : public esphome::Component {
void join_(int universe); void join_(int universe);
void leave_(int universe); void leave_(int universe);
protected:
E131ListenMethod listen_method_{E131_MULTICAST}; E131ListenMethod listen_method_{E131_MULTICAST};
std::unique_ptr<UDP> udp_; std::unique_ptr<UDP> udp_;
std::set<E131AddressableLightEffect *> light_effects_; std::set<E131AddressableLightEffect *> light_effects_;

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