mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 15:12:06 +00:00 
			
		
		
		
	Merge branch 'dev' into bump-2022.2.0b1
This commit is contained in:
		
							
								
								
									
										10
									
								
								.clang-tidy
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								.clang-tidy
									
									
									
									
									
								
							| @@ -68,8 +68,6 @@ Checks: >- | ||||
|   -modernize-use-nodiscard, | ||||
|   -mpi-*, | ||||
|   -objc-*, | ||||
|   -readability-braces-around-statements, | ||||
|   -readability-const-return-type, | ||||
|   -readability-convert-member-functions-to-static, | ||||
|   -readability-else-after-return, | ||||
|   -readability-function-cognitive-complexity, | ||||
| @@ -77,10 +75,6 @@ Checks: >- | ||||
|   -readability-isolate-declaration, | ||||
|   -readability-magic-numbers, | ||||
|   -readability-make-member-function-const, | ||||
|   -readability-named-parameter, | ||||
|   -readability-qualified-auto, | ||||
|   -readability-redundant-access-specifiers, | ||||
|   -readability-redundant-member-init, | ||||
|   -readability-redundant-string-init, | ||||
|   -readability-uppercase-literal-suffix, | ||||
|   -readability-use-anyofallof, | ||||
| @@ -114,6 +108,8 @@ CheckOptions: | ||||
|     value:           'make_unique' | ||||
|   - key:             modernize-make-unique.MakeSmartPtrFunctionHeader | ||||
|     value:           'esphome/core/helpers.h' | ||||
|   - key:             readability-braces-around-statements.ShortStatementLines | ||||
|     value:           2 | ||||
|   - key:             readability-identifier-naming.LocalVariableCase | ||||
|     value:           'lower_case' | ||||
|   - key:             readability-identifier-naming.ClassCase | ||||
| @@ -160,3 +156,5 @@ CheckOptions: | ||||
|     value:           'lower_case' | ||||
|   - key:             readability-identifier-naming.VirtualMethodSuffix | ||||
|     value:           '' | ||||
|   - key:             readability-qualified-auto.AddConstToQualified | ||||
|     value:           0 | ||||
|   | ||||
							
								
								
									
										4
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							| @@ -137,7 +137,7 @@ jobs: | ||||
|           --build-type "${{ matrix.build_type }}" \ | ||||
|           manifest | ||||
|  | ||||
|   deploy-hassio-repo: | ||||
|   deploy-ha-addon-repo: | ||||
|     if: github.repository == 'esphome/esphome' && github.event_name == 'release' | ||||
|     runs-on: ubuntu-latest | ||||
|     needs: [deploy-docker] | ||||
| @@ -150,5 +150,5 @@ jobs: | ||||
|             -u ":$TOKEN" \ | ||||
|             -X POST \ | ||||
|             -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\"}}" | ||||
|   | ||||
| @@ -3,4 +3,4 @@ ports: | ||||
|   onOpen: open-preview | ||||
| tasks: | ||||
| - 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 | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
| # See https://pre-commit.com/hooks.html for more hooks | ||||
| repos: | ||||
|   - repo: https://github.com/ambv/black | ||||
|     rev: 20.8b1 | ||||
|     rev: 22.1.0 | ||||
|     hooks: | ||||
|     - id: black | ||||
|       args: | ||||
| @@ -10,7 +10,7 @@ repos: | ||||
|         - --quiet | ||||
|       files: ^((esphome|script|tests)/.+)?[^/]+\.py$ | ||||
|   - repo: https://gitlab.com/pycqa/flake8 | ||||
|     rev: 3.8.4 | ||||
|     rev: 4.0.1 | ||||
|     hooks: | ||||
|       - id: flake8 | ||||
|         additional_dependencies: | ||||
|   | ||||
							
								
								
									
										12
									
								
								CODEOWNERS
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								CODEOWNERS
									
									
									
									
									
								
							| @@ -54,6 +54,7 @@ esphome/components/dfplayer/* @glmnet | ||||
| esphome/components/dht/* @OttoWinter | ||||
| esphome/components/ds1307/* @badbadc0ffee | ||||
| esphome/components/dsmr/* @glmnet @zuidwijk | ||||
| esphome/components/ektf2232/* @jesserockz | ||||
| esphome/components/esp32/* @esphome/core | ||||
| esphome/components/esp32_ble/* @jesserockz | ||||
| esphome/components/esp32_ble_server/* @jesserockz | ||||
| @@ -88,9 +89,12 @@ esphome/components/json/* @OttoWinter | ||||
| esphome/components/kalman_combinator/* @Cat-Ion | ||||
| esphome/components/ledc/* @OttoWinter | ||||
| esphome/components/light/* @esphome/core | ||||
| esphome/components/lilygo_t5_47/touchscreen/* @jesserockz | ||||
| esphome/components/lock/* @esphome/core | ||||
| esphome/components/logger/* @esphome/core | ||||
| esphome/components/ltr390/* @sjtrny | ||||
| esphome/components/max7219digit/* @rspaargaren | ||||
| esphome/components/max9611/* @mckaymatthew | ||||
| esphome/components/mcp23008/* @jesserockz | ||||
| esphome/components/mcp23017/* @jesserockz | ||||
| esphome/components/mcp23s08/* @SenexCrenshaw @jesserockz | ||||
| @@ -107,10 +111,12 @@ esphome/components/mdns/* @esphome/core | ||||
| esphome/components/midea/* @dudanov | ||||
| esphome/components/midea_ir/* @dudanov | ||||
| esphome/components/mitsubishi/* @RubyBailey | ||||
| esphome/components/mlx90393/* @functionpointer | ||||
| esphome/components/modbus_controller/* @martgras | ||||
| esphome/components/modbus_controller/binary_sensor/* @martgras | ||||
| esphome/components/modbus_controller/number/* @martgras | ||||
| esphome/components/modbus_controller/output/* @martgras | ||||
| esphome/components/modbus_controller/select/* @martgras @stegm | ||||
| esphome/components/modbus_controller/sensor/* @martgras | ||||
| esphome/components/modbus_controller/switch/* @martgras | ||||
| esphome/components/modbus_controller/text_sensor/* @martgras | ||||
| @@ -136,6 +142,9 @@ esphome/components/preferences/* @esphome/core | ||||
| esphome/components/psram/* @esphome/core | ||||
| esphome/components/pulse_meter/* @stevebaxter | ||||
| 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_i2c/* @glmnet | ||||
| esphome/components/rc522_spi/* @glmnet | ||||
| @@ -187,6 +196,7 @@ esphome/components/tmp102/* @timsavage | ||||
| esphome/components/tmp117/* @Azimath | ||||
| esphome/components/tof10120/* @wstrzalka | ||||
| esphome/components/toshiba/* @kbx81 | ||||
| esphome/components/touchscreen/* @jesserockz | ||||
| esphome/components/tsl2591/* @wjcarpenter | ||||
| esphome/components/tuya/binary_sensor/* @jesserockz | ||||
| esphome/components/tuya/climate/* @jesserockz | ||||
| @@ -197,8 +207,10 @@ esphome/components/tuya/text_sensor/* @dentra | ||||
| esphome/components/uart/* @esphome/core | ||||
| esphome/components/ultrasonic/* @OttoWinter | ||||
| esphome/components/version/* @esphome/core | ||||
| esphome/components/wake_on_lan/* @willwill2will54 | ||||
| esphome/components/web_server_base/* @OttoWinter | ||||
| esphome/components/whirlpool/* @glmnet | ||||
| esphome/components/xiaomi_lywsd03mmc/* @ahpohl | ||||
| esphome/components/xiaomi_mhoc303/* @drug123 | ||||
| esphome/components/xiaomi_mhoc401/* @vevsvevs | ||||
| esphome/components/xpt2046/* @numo68 | ||||
|   | ||||
| @@ -4,4 +4,5 @@ include requirements.txt | ||||
| include esphome/dashboard/templates/*.html | ||||
| recursive-include esphome/dashboard/static *.ico *.js *.css *.woff* LICENSE | ||||
| recursive-include esphome *.cpp *.h *.tcc | ||||
| recursive-include esphome *.py.script | ||||
| recursive-include esphome LICENSE.txt | ||||
|   | ||||
| @@ -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/aarch64:5.2.3 AS base-hassio-arm64 | ||||
| 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-20211220-slim AS base-docker-arm64 | ||||
| FROM debian:bullseye-20211220-slim AS base-docker-armv7 | ||||
| FROM debian:bullseye-20220125-slim AS base-docker-amd64 | ||||
| FROM debian:bullseye-20220125-slim AS base-docker-arm64 | ||||
| FROM debian:bullseye-20220125-slim AS base-docker-armv7 | ||||
|  | ||||
| # Use TARGETARCH/TARGETVARIANT defined by docker | ||||
| # 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-pip=20.3.4-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 \ | ||||
|         iputils-ping=3:20210202-1 \ | ||||
|         git=1:2.30.2-1 \ | ||||
| @@ -102,7 +102,7 @@ RUN \ | ||||
| ARG BUILD_VERSION=dev | ||||
|  | ||||
| # Copy root filesystem | ||||
| COPY docker/hassio-rootfs/ / | ||||
| COPY docker/ha-addon-rootfs/ / | ||||
|  | ||||
| # First install requirements to leverage caching when requirements don't change | ||||
| COPY requirements.txt requirements_optional.txt docker/platformio_install_deps.py platformio.ini / | ||||
|   | ||||
| @@ -10,7 +10,7 @@ server { | ||||
|     ssl_certificate_key /ssl/%%keyfile%%; | ||||
| 
 | ||||
|     # 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. | ||||
|     # https://rageagainstshell.com/2016/11/redirect-http-to-https-on-the-same-port-in-nginx/ | ||||
| @@ -4,7 +4,7 @@ server { | ||||
|     include /etc/nginx/includes/server_params.conf; | ||||
|     include /etc/nginx/includes/proxy_params.conf; | ||||
|     # Clear Hass.io Ingress header | ||||
|     proxy_set_header X-Hassio-Ingress ""; | ||||
|     proxy_set_header X-HA-Ingress ""; | ||||
| 
 | ||||
|     location / { | ||||
|         proxy_pass http://esphome; | ||||
| @@ -3,8 +3,8 @@ server { | ||||
| 
 | ||||
|     include /etc/nginx/includes/server_params.conf; | ||||
|     include /etc/nginx/includes/proxy_params.conf; | ||||
|     # Set Hass.io Ingress header | ||||
|     proxy_set_header X-Hassio-Ingress "YES"; | ||||
|     # Set Home Assistant Ingress header | ||||
|     proxy_set_header X-HA-Ingress "YES"; | ||||
| 
 | ||||
|     location / { | ||||
|         # Only allow from Hass.io supervisor | ||||
| @@ -4,7 +4,7 @@ | ||||
| # Runs the ESPHome dashboard | ||||
| # ============================================================================== | ||||
| 
 | ||||
| export ESPHOME_IS_HASSIO=true | ||||
| export ESPHOME_IS_HA_ADDON=true | ||||
| 
 | ||||
| if bashio::config.true 'leave_front_door_open'; then | ||||
|     export DISABLE_HA_AUTHENTICATION=true | ||||
| @@ -32,4 +32,4 @@ export PLATFORMIO_CACHE_DIR="${pio_cache_base}/cache" | ||||
| export PLATFORMIO_GLOBALLIB_DIR=/piolibs | ||||
| 
 | ||||
| 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 | ||||
| @@ -661,7 +661,7 @@ def parse_args(argv): | ||||
|         "--open-ui", help="Open the dashboard UI in a browser.", action="store_true" | ||||
|     ) | ||||
|     parser_dashboard.add_argument( | ||||
|         "--hassio", help=argparse.SUPPRESS, action="store_true" | ||||
|         "--ha-addon", help=argparse.SUPPRESS, action="store_true" | ||||
|     ) | ||||
|     parser_dashboard.add_argument( | ||||
|         "--socket", help="Make the dashboard serve under a unix socket", type=str | ||||
|   | ||||
| @@ -63,6 +63,7 @@ from esphome.cpp_types import (  # noqa | ||||
|     uint32, | ||||
|     uint64, | ||||
|     int32, | ||||
|     int64, | ||||
|     const_char_ptr, | ||||
|     NAN, | ||||
|     esphome_ns, | ||||
| @@ -81,4 +82,5 @@ from esphome.cpp_types import (  # noqa | ||||
|     InternalGPIOPin, | ||||
|     gpio_Flags, | ||||
|     EntityCategory, | ||||
|     Parented, | ||||
| ) | ||||
|   | ||||
| @@ -52,10 +52,10 @@ uint32_t IRAM_ATTR HOT AcDimmerDataStore::timer_intr(uint32_t now) { | ||||
|     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 | ||||
|     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 | ||||
|     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 now = micros(); | ||||
|   for (auto *dimmer : all_dimmers) { | ||||
|     if (dimmer == nullptr) | ||||
|     if (dimmer == nullptr) { | ||||
|       // no more dimmers | ||||
|       break; | ||||
|     } | ||||
|     uint32_t res = dimmer->timer_intr(now); | ||||
|     if (res != 0 && res < min_dt_us) | ||||
|       min_dt_us = res; | ||||
| @@ -212,12 +213,13 @@ void AcDimmer::dump_config() { | ||||
|   LOG_PIN("  Zero-Cross Pin: ", this->zero_cross_pin_); | ||||
|   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_)); | ||||
|   if (method_ == DIM_METHOD_LEADING_PULSE) | ||||
|   if (method_ == DIM_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"); | ||||
|   else | ||||
|   } else { | ||||
|     ESP_LOGCONFIG(TAG, "   Method: trailing"); | ||||
|   } | ||||
|  | ||||
|   LOG_FLOAT_OUTPUT(this); | ||||
|   ESP_LOGV(TAG, "  Estimated Frequency: %.3fHz", 1e6f / this->store_.cycle_time_us / 2); | ||||
|   | ||||
| @@ -13,7 +13,6 @@ class AdalightLightEffect : public light::AddressableLightEffect, public uart::U | ||||
|  public: | ||||
|   AdalightLightEffect(const std::string &name); | ||||
|  | ||||
|  public: | ||||
|   void start() override; | ||||
|   void stop() override; | ||||
|   void apply(light::AddressableLight &it, const Color ¤t_color) override; | ||||
| @@ -30,7 +29,6 @@ class AdalightLightEffect : public light::AddressableLightEffect, public uart::U | ||||
|   void blank_all_leds_(light::AddressableLight &it); | ||||
|   Frame parse_frame_(light::AddressableLight &it); | ||||
|  | ||||
|  protected: | ||||
|   uint32_t last_ack_{0}; | ||||
|   uint32_t last_byte_{0}; | ||||
|   uint32_t last_reset_{0}; | ||||
|   | ||||
| @@ -15,6 +15,11 @@ namespace esphome { | ||||
| namespace 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() { | ||||
|   ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str()); | ||||
| @@ -23,14 +28,14 @@ void ADCSensor::setup() { | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_ESP32 | ||||
|   adc1_config_width(ADC_WIDTH_BIT_12); | ||||
|   adc1_config_width(ADC_WIDTH_MAX_SOC_BITS); | ||||
|   if (!autorange_) { | ||||
|     adc1_config_channel_atten(channel_, attenuation_); | ||||
|   } | ||||
|  | ||||
|   // load characteristics for each attenuation | ||||
|   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 | ||||
|                                               &cal_characteristics_[i]); | ||||
|     switch (cal_value) { | ||||
| @@ -65,9 +70,9 @@ void ADCSensor::dump_config() { | ||||
|  | ||||
| #ifdef USE_ESP32 | ||||
|   LOG_PIN("  Pin: ", pin_); | ||||
|   if (autorange_) | ||||
|   if (autorange_) { | ||||
|     ESP_LOGCONFIG(TAG, " Attenuation: auto"); | ||||
|   else | ||||
|   } else { | ||||
|     switch (this->attenuation_) { | ||||
|       case ADC_ATTEN_DB_0: | ||||
|         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 | ||||
|         break; | ||||
|     } | ||||
|   } | ||||
| #endif  // USE_ESP32 | ||||
|   LOG_UPDATE_INTERVAL(this); | ||||
| } | ||||
|   | ||||
| @@ -24,7 +24,7 @@ void AirthingsWaveMini::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt | ||||
|  | ||||
|     case ESP_GATTC_SEARCH_CMPL_EVT: { | ||||
|       this->handle_ = 0; | ||||
|       auto chr = this->parent()->get_characteristic(service_uuid_, sensors_data_characteristic_uuid_); | ||||
|       auto *chr = this->parent()->get_characteristic(service_uuid_, sensors_data_characteristic_uuid_); | ||||
|       if (chr == nullptr) { | ||||
|         ESP_LOGW(TAG, "No sensor characteristic found at service %s char %s", service_uuid_.to_string().c_str(), | ||||
|                  sensors_data_characteristic_uuid_.to_string().c_str()); | ||||
| @@ -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) { | ||||
|   auto value = (WaveMiniReadings *) raw_value; | ||||
|   auto *value = (WaveMiniReadings *) raw_value; | ||||
|  | ||||
|   if (sizeof(WaveMiniReadings) <= value_len) { | ||||
|     this->humidity_sensor_->publish_state(value->humidity / 100.0f); | ||||
|   | ||||
| @@ -24,7 +24,7 @@ void AirthingsWavePlus::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt | ||||
|  | ||||
|     case ESP_GATTC_SEARCH_CMPL_EVT: { | ||||
|       this->handle_ = 0; | ||||
|       auto chr = this->parent()->get_characteristic(service_uuid_, sensors_data_characteristic_uuid_); | ||||
|       auto *chr = this->parent()->get_characteristic(service_uuid_, sensors_data_characteristic_uuid_); | ||||
|       if (chr == nullptr) { | ||||
|         ESP_LOGW(TAG, "No sensor characteristic found at service %s char %s", service_uuid_.to_string().c_str(), | ||||
|                  sensors_data_characteristic_uuid_.to_string().c_str()); | ||||
| @@ -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) { | ||||
|   auto value = (WavePlusReadings *) raw_value; | ||||
|   auto *value = (WavePlusReadings *) raw_value; | ||||
|  | ||||
|   if (sizeof(WavePlusReadings) <= value_len) { | ||||
|     ESP_LOGD(TAG, "version = %d", value->version); | ||||
|   | ||||
| @@ -19,12 +19,14 @@ uint16_t crc_16(uint8_t *ptr, uint8_t length) { | ||||
|   //------------------------------ | ||||
|   while (length--) { | ||||
|     crc ^= *ptr++; | ||||
|     for (i = 0; i < 8; i++) | ||||
|     for (i = 0; i < 8; i++) { | ||||
|       if ((crc & 0x01) != 0) { | ||||
|         crc >>= 1; | ||||
|         crc ^= 0xA001; | ||||
|       } else | ||||
|       } else { | ||||
|         crc >>= 1; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   return crc; | ||||
| } | ||||
|   | ||||
| @@ -39,7 +39,7 @@ void Am43::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_i | ||||
|       break; | ||||
|     } | ||||
|     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 (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.", | ||||
| @@ -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->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_, | ||||
|                                                  packet->length, packet->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(), | ||||
|                      status); | ||||
|           } | ||||
|         } | ||||
|         this->current_sensor_ = 0; | ||||
|       } | ||||
| @@ -99,7 +100,7 @@ void Am43::update() { | ||||
|   } | ||||
|   if (this->current_sensor_ == 0) { | ||||
|     if (this->battery_ != nullptr) { | ||||
|       auto packet = this->encoder_->get_battery_level_request(); | ||||
|       auto *packet = this->encoder_->get_battery_level_request(); | ||||
|       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, ESP_GATT_AUTH_REQ_NONE); | ||||
|   | ||||
| @@ -5,7 +5,7 @@ from esphome.const import CONF_ID, CONF_PIN | ||||
|  | ||||
| CODEOWNERS = ["@buxtronix"] | ||||
| DEPENDENCIES = ["ble_client"] | ||||
| AUTO_LOAD = ["am43"] | ||||
| AUTO_LOAD = ["am43", "sensor"] | ||||
|  | ||||
| CONF_INVERT_POSITION = "invert_position" | ||||
|  | ||||
|   | ||||
| @@ -25,15 +25,16 @@ void Am43Component::setup() { | ||||
|  | ||||
| void Am43Component::loop() { | ||||
|   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 = | ||||
|         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); | ||||
|     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); | ||||
|     else | ||||
|     } else { | ||||
|       this->logged_in_ = true; | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| @@ -51,7 +52,7 @@ void Am43Component::control(const CoverCall &call) { | ||||
|     return; | ||||
|   } | ||||
|   if (call.get_stop()) { | ||||
|     auto packet = this->encoder_->get_stop_request(); | ||||
|     auto *packet = this->encoder_->get_stop_request(); | ||||
|     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, ESP_GATT_AUTH_REQ_NONE); | ||||
| @@ -63,7 +64,7 @@ void Am43Component::control(const CoverCall &call) { | ||||
|  | ||||
|     if (this->invert_position_) | ||||
|       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 = | ||||
|         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); | ||||
| @@ -80,7 +81,7 @@ void Am43Component::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ | ||||
|       break; | ||||
|     } | ||||
|     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 (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()); | ||||
| @@ -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_->pin_ok_) { | ||||
|           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_, | ||||
|                                                  packet->length, packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, | ||||
|                                                  ESP_GATT_AUTH_REQ_NONE); | ||||
|   | ||||
| @@ -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); | ||||
|   } | ||||
|   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_, | ||||
|                                            pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); | ||||
|     if (status) | ||||
| @@ -57,7 +57,7 @@ void Anova::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_ | ||||
|       break; | ||||
|     } | ||||
|     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) { | ||||
|         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()); | ||||
| @@ -114,9 +114,10 @@ void Anova::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_ | ||||
|           auto status = | ||||
|               esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, pkt->length, | ||||
|                                        pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); | ||||
|           if (status) | ||||
|           if (status) { | ||||
|             ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), | ||||
|                      status); | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|       break; | ||||
| @@ -133,7 +134,7 @@ void Anova::update() { | ||||
|     return; | ||||
|  | ||||
|   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) | ||||
|       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_, | ||||
|   | ||||
| @@ -36,7 +36,7 @@ class Anova : public climate::Climate, public esphome::ble_client::BLEClientNode | ||||
|     traits.set_visual_temperature_step(0.1); | ||||
|     return traits; | ||||
|   } | ||||
|   void set_unit_of_measurement(const char *); | ||||
|   void set_unit_of_measurement(const char *unit); | ||||
|  | ||||
|  protected: | ||||
|   std::unique_ptr<AnovaCodec> codec_; | ||||
|   | ||||
| @@ -225,9 +225,10 @@ void APDS9960::read_gesture_data_() { | ||||
|  | ||||
|   uint8_t fifo_level; | ||||
|   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 | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   APDS9960_WARNING_CHECK(fifo_level <= 32, "FIFO level has invalid value.") | ||||
|  | ||||
|   | ||||
| @@ -41,6 +41,7 @@ service APIConnection { | ||||
|   rpc number_command (NumberCommandRequest) returns (void) {} | ||||
|   rpc select_command (SelectCommandRequest) 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. | ||||
|   // For example "ESPHome v1.10.0 on ESP8266" | ||||
|   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 | ||||
| @@ -525,6 +529,7 @@ message ListEntitiesSwitchResponse { | ||||
|   bool assumed_state = 6; | ||||
|   bool disabled_by_default = 7; | ||||
|   EntityCategory entity_category = 8; | ||||
|   string device_class = 9; | ||||
| } | ||||
| message SwitchStateResponse { | ||||
|   option (id) = 26; | ||||
| @@ -953,6 +958,63 @@ message SelectCommandRequest { | ||||
|   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 ==================== | ||||
| message ListEntitiesButtonResponse { | ||||
|   option (id) = 61; | ||||
| @@ -977,3 +1039,4 @@ message ButtonCommandRequest { | ||||
|  | ||||
|   fixed32 key = 1; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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. | ||||
| #pragma GCC diagnostic push | ||||
| #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_) | ||||
|     return false; | ||||
|  | ||||
| @@ -273,7 +273,7 @@ bool APIConnection::send_fan_state(fan::FanState *fan) { | ||||
|     resp.direction = static_cast<enums::FanDirection>(fan->direction); | ||||
|   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(); | ||||
|   ListEntitiesFanResponse msg; | ||||
|   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); | ||||
| } | ||||
| 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) | ||||
|     return; | ||||
|  | ||||
| @@ -462,6 +462,7 @@ bool APIConnection::send_switch_info(switch_::Switch *a_switch) { | ||||
|   msg.assumed_state = a_switch->assumed_state(); | ||||
|   msg.disabled_by_default = a_switch->is_disabled_by_default(); | ||||
|   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); | ||||
| } | ||||
| void APIConnection::switch_command(const SwitchCommandRequest &msg) { | ||||
| @@ -469,10 +470,11 @@ void APIConnection::switch_command(const SwitchCommandRequest &msg) { | ||||
|   if (a_switch == nullptr) | ||||
|     return; | ||||
|  | ||||
|   if (msg.state) | ||||
|   if (msg.state) { | ||||
|     a_switch->turn_on(); | ||||
|   else | ||||
|   } else { | ||||
|     a_switch->turn_off(); | ||||
|   } | ||||
| } | ||||
| #endif | ||||
|  | ||||
| @@ -699,6 +701,49 @@ void APIConnection::button_command(const ButtonCommandRequest &msg) { | ||||
| } | ||||
| #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 | ||||
| void APIConnection::send_camera_state(std::shared_ptr<esp32_camera::CameraImage> image) { | ||||
|   if (!this->state_subscription_) | ||||
| @@ -766,6 +811,8 @@ HelloResponse APIConnection::hello(const HelloRequest &msg) { | ||||
|   resp.api_version_major = 1; | ||||
|   resp.api_version_minor = 6; | ||||
|   resp.server_info = App.get_name() + " (esphome v" ESPHOME_VERSION ")"; | ||||
|   resp.name = App.get_name(); | ||||
|  | ||||
|   this->connection_state_ = ConnectionState::CONNECTED; | ||||
|   return resp; | ||||
| } | ||||
| @@ -803,15 +850,16 @@ DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) { | ||||
|   resp.project_version = ESPHOME_PROJECT_VERSION; | ||||
| #endif | ||||
| #ifdef USE_WEBSERVER | ||||
|   resp.webserver_port = WEBSERVER_PORT; | ||||
|   resp.webserver_port = USE_WEBSERVER_PORT; | ||||
| #endif | ||||
|   return resp; | ||||
| } | ||||
| 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) { | ||||
|       it.callback(msg.state); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| void APIConnection::execute_service(const ExecuteServiceRequest &msg) { | ||||
|   bool found = false; | ||||
|   | ||||
| @@ -32,8 +32,8 @@ class APIConnection : public APIServerConnection { | ||||
|   void cover_command(const CoverCommandRequest &msg) override; | ||||
| #endif | ||||
| #ifdef USE_FAN | ||||
|   bool send_fan_state(fan::FanState *fan); | ||||
|   bool send_fan_info(fan::FanState *fan); | ||||
|   bool send_fan_state(fan::Fan *fan); | ||||
|   bool send_fan_info(fan::Fan *fan); | ||||
|   void fan_command(const FanCommandRequest &msg) override; | ||||
| #endif | ||||
| #ifdef USE_LIGHT | ||||
| @@ -77,6 +77,11 @@ class APIConnection : public APIServerConnection { | ||||
| #ifdef USE_BUTTON | ||||
|   bool send_button_info(button::Button *button); | ||||
|   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 | ||||
|   bool send_log_message(int level, const char *tag, const char *line); | ||||
|   void send_homeassistant_service_call(const HomeassistantServiceResponse &call) { | ||||
|   | ||||
| @@ -3,6 +3,7 @@ | ||||
| #include "esphome/core/log.h" | ||||
| #include "esphome/core/hal.h" | ||||
| #include "esphome/core/helpers.h" | ||||
| #include "esphome/core/application.h" | ||||
| #include "proto.h" | ||||
| #include <cstring> | ||||
|  | ||||
| @@ -302,9 +303,16 @@ APIError APINoiseFrameHelper::state_action_() { | ||||
|   } | ||||
|   if (state_ == State::SERVER_HELLO) { | ||||
|     // send server hello | ||||
|     uint8_t msg[1]; | ||||
|     msg[0] = 0x01;  // chosen proto | ||||
|     aerr = write_frame_(msg, 1); | ||||
|     std::vector<uint8_t> msg; | ||||
|     // chosen proto | ||||
|     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) | ||||
|       return aerr; | ||||
|  | ||||
|   | ||||
| @@ -278,6 +278,36 @@ template<> const char *proto_enum_to_string<enums::NumberMode>(enums::NumberMode | ||||
|       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) { | ||||
|   switch (field_id) { | ||||
|     case 1: { | ||||
| @@ -319,6 +349,10 @@ bool HelloResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) | ||||
|       this->server_info = value.as_string(); | ||||
|       return true; | ||||
|     } | ||||
|     case 4: { | ||||
|       this->name = value.as_string(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| @@ -327,6 +361,7 @@ void HelloResponse::encode(ProtoWriteBuffer buffer) const { | ||||
|   buffer.encode_uint32(1, this->api_version_major); | ||||
|   buffer.encode_uint32(2, this->api_version_minor); | ||||
|   buffer.encode_string(3, this->server_info); | ||||
|   buffer.encode_string(4, this->name); | ||||
| } | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
| 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("'").append(this->server_info).append("'"); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  name: "); | ||||
|   out.append("'").append(this->name).append("'"); | ||||
|   out.append("\n"); | ||||
|   out.append("}"); | ||||
| } | ||||
| #endif | ||||
| @@ -2138,6 +2177,10 @@ bool ListEntitiesSwitchResponse::decode_length(uint32_t field_id, ProtoLengthDel | ||||
|       this->icon = value.as_string(); | ||||
|       return true; | ||||
|     } | ||||
|     case 9: { | ||||
|       this->device_class = value.as_string(); | ||||
|       return true; | ||||
|     } | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| @@ -2161,6 +2204,7 @@ void ListEntitiesSwitchResponse::encode(ProtoWriteBuffer buffer) const { | ||||
|   buffer.encode_bool(6, this->assumed_state); | ||||
|   buffer.encode_bool(7, this->disabled_by_default); | ||||
|   buffer.encode_enum<enums::EntityCategory>(8, this->entity_category); | ||||
|   buffer.encode_string(9, this->device_class); | ||||
| } | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
| 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(proto_enum_to_string<enums::EntityCategory>(this->entity_category)); | ||||
|   out.append("\n"); | ||||
|  | ||||
|   out.append("  device_class: "); | ||||
|   out.append("'").append(this->device_class).append("'"); | ||||
|   out.append("\n"); | ||||
|   out.append("}"); | ||||
| } | ||||
| #endif | ||||
| @@ -4177,6 +4225,234 @@ void SelectCommandRequest::dump_to(std::string &out) const { | ||||
|   out.append("}"); | ||||
| } | ||||
| #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) { | ||||
|   switch (field_id) { | ||||
|     case 6: { | ||||
| @@ -4239,7 +4515,7 @@ void ListEntitiesButtonResponse::encode(ProtoWriteBuffer buffer) const { | ||||
| } | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
| void ListEntitiesButtonResponse::dump_to(std::string &out) const { | ||||
|   char buffer[64]; | ||||
|   __attribute__((unused)) char buffer[64]; | ||||
|   out.append("ListEntitiesButtonResponse {\n"); | ||||
|   out.append("  object_id: "); | ||||
|   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); } | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
| void ButtonCommandRequest::dump_to(std::string &out) const { | ||||
|   char buffer[64]; | ||||
|   __attribute__((unused)) char buffer[64]; | ||||
|   out.append("ButtonCommandRequest {\n"); | ||||
|   out.append("  key: "); | ||||
|   sprintf(buffer, "%u", this->key); | ||||
|   | ||||
| @@ -128,6 +128,19 @@ enum NumberMode : uint32_t { | ||||
|   NUMBER_MODE_BOX = 1, | ||||
|   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 | ||||
|  | ||||
| @@ -147,6 +160,7 @@ class HelloResponse : public ProtoMessage { | ||||
|   uint32_t api_version_major{0}; | ||||
|   uint32_t api_version_minor{0}; | ||||
|   std::string server_info{}; | ||||
|   std::string name{}; | ||||
|   void encode(ProtoWriteBuffer buffer) const override; | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
|   void dump_to(std::string &out) const override; | ||||
| @@ -566,6 +580,7 @@ class ListEntitiesSwitchResponse : public ProtoMessage { | ||||
|   bool assumed_state{false}; | ||||
|   bool disabled_by_default{false}; | ||||
|   enums::EntityCategory entity_category{}; | ||||
|   std::string device_class{}; | ||||
|   void encode(ProtoWriteBuffer buffer) const override; | ||||
| #ifdef HAS_PROTO_MESSAGE_DUMP | ||||
|   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_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 { | ||||
|  public: | ||||
|   std::string object_id{}; | ||||
|   | ||||
| @@ -282,6 +282,24 @@ bool APIServerConnectionBase::send_select_state_response(const SelectStateRespon | ||||
| #endif | ||||
| #ifdef USE_SELECT | ||||
| #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 | ||||
| bool APIServerConnectionBase::send_list_entities_button_response(const ListEntitiesButtonResponse &msg) { | ||||
| #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()); | ||||
| #endif | ||||
|       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 | ||||
|       break; | ||||
|     } | ||||
| @@ -771,6 +800,19 @@ void APIServerConnection::on_button_command_request(const ButtonCommandRequest & | ||||
|   this->button_command(msg); | ||||
| } | ||||
| #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 esphome | ||||
|   | ||||
| @@ -130,6 +130,15 @@ class APIServerConnectionBase : public ProtoService { | ||||
| #ifdef USE_SELECT | ||||
|   virtual void on_select_command_request(const SelectCommandRequest &value){}; | ||||
| #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 | ||||
|   bool send_list_entities_button_response(const ListEntitiesButtonResponse &msg); | ||||
| #endif | ||||
| @@ -180,6 +189,9 @@ class APIServerConnection : public APIServerConnectionBase { | ||||
| #endif | ||||
| #ifdef USE_BUTTON | ||||
|   virtual void button_command(const ButtonCommandRequest &msg) = 0; | ||||
| #endif | ||||
| #ifdef USE_LOCK | ||||
|   virtual void lock_command(const LockCommandRequest &msg) = 0; | ||||
| #endif | ||||
|  protected: | ||||
|   void on_hello_request(const HelloRequest &msg) override; | ||||
| @@ -221,6 +233,9 @@ class APIServerConnection : public APIServerConnectionBase { | ||||
| #ifdef USE_BUTTON | ||||
|   void on_button_command_request(const ButtonCommandRequest &msg) override; | ||||
| #endif | ||||
| #ifdef USE_LOCK | ||||
|   void on_lock_command_request(const LockCommandRequest &msg) override; | ||||
| #endif | ||||
| }; | ||||
|  | ||||
| }  // namespace api | ||||
|   | ||||
| @@ -24,7 +24,7 @@ static const char *const TAG = "api"; | ||||
| void APIServer::setup() { | ||||
|   ESP_LOGCONFIG(TAG, "Setting up Home Assistant API server..."); | ||||
|   this->setup_controller(); | ||||
|   socket_ = socket::socket(AF_INET, SOCK_STREAM, 0); | ||||
|   socket_ = socket::socket_ip(SOCK_STREAM, 0); | ||||
|   if (socket_ == nullptr) { | ||||
|     ESP_LOGW(TAG, "Could not create socket."); | ||||
|     this->mark_failed(); | ||||
| @@ -43,13 +43,16 @@ void APIServer::setup() { | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   struct sockaddr_in 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_); | ||||
|   struct sockaddr_storage server; | ||||
|  | ||||
|   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) { | ||||
|     ESP_LOGW(TAG, "Socket unable to bind: errno %d", errno); | ||||
|     this->mark_failed(); | ||||
| @@ -80,9 +83,10 @@ void APIServer::setup() { | ||||
|   if (esp32_camera::global_esp32_camera != nullptr && !esp32_camera::global_esp32_camera->is_internal()) { | ||||
|     esp32_camera::global_esp32_camera->add_image_callback( | ||||
|         [this](const std::shared_ptr<esp32_camera::CameraImage> &image) { | ||||
|           for (auto &c : this->clients_) | ||||
|           for (auto &c : this->clients_) { | ||||
|             if (!c->remove_) | ||||
|               c->send_camera_state(image); | ||||
|           } | ||||
|         }); | ||||
|   } | ||||
| #endif | ||||
| @@ -188,7 +192,7 @@ void APIServer::on_cover_update(cover::Cover *obj) { | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_FAN | ||||
| void APIServer::on_fan_update(fan::FanState *obj) { | ||||
| void APIServer::on_fan_update(fan::Fan *obj) { | ||||
|   if (obj->is_internal()) | ||||
|     return; | ||||
|   for (auto &c : this->clients_) | ||||
| @@ -259,6 +263,15 @@ void APIServer::on_select_update(select::Select *obj, const std::string &state) | ||||
| } | ||||
| #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; } | ||||
| void APIServer::set_port(uint16_t port) { this->port_ = port; } | ||||
| APIServer *global_api_server = nullptr;  // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) | ||||
|   | ||||
| @@ -44,7 +44,7 @@ class APIServer : public Component, public Controller { | ||||
|   void on_cover_update(cover::Cover *obj) override; | ||||
| #endif | ||||
| #ifdef USE_FAN | ||||
|   void on_fan_update(fan::FanState *obj) override; | ||||
|   void on_fan_update(fan::Fan *obj) override; | ||||
| #endif | ||||
| #ifdef USE_LIGHT | ||||
|   void on_light_update(light::LightState *obj) override; | ||||
| @@ -66,6 +66,9 @@ class APIServer : public Component, public Controller { | ||||
| #endif | ||||
| #ifdef USE_SELECT | ||||
|   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 | ||||
|   void send_homeassistant_service_call(const HomeassistantServiceResponse &call); | ||||
|   void register_user_service(UserServiceDescriptor *descriptor) { this->user_services_.push_back(descriptor); } | ||||
|   | ||||
| @@ -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); } | ||||
| #endif | ||||
| #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 | ||||
| #ifdef USE_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); | ||||
| } | ||||
| #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(); } | ||||
| ListEntitiesIterator::ListEntitiesIterator(APIServer *server, APIConnection *client) | ||||
|   | ||||
| @@ -19,7 +19,7 @@ class ListEntitiesIterator : public ComponentIterator { | ||||
|   bool on_cover(cover::Cover *cover) override; | ||||
| #endif | ||||
| #ifdef USE_FAN | ||||
|   bool on_fan(fan::FanState *fan) override; | ||||
|   bool on_fan(fan::Fan *fan) override; | ||||
| #endif | ||||
| #ifdef USE_LIGHT | ||||
|   bool on_light(light::LightState *light) override; | ||||
| @@ -48,6 +48,9 @@ class ListEntitiesIterator : public ComponentIterator { | ||||
| #endif | ||||
| #ifdef USE_SELECT | ||||
|   bool on_select(select::Select *select) override; | ||||
| #endif | ||||
| #ifdef USE_LOCK | ||||
|   bool on_lock(lock::Lock *a_lock) override; | ||||
| #endif | ||||
|   bool on_end() override; | ||||
|  | ||||
|   | ||||
| @@ -55,17 +55,19 @@ class ProtoVarInt { | ||||
|   } | ||||
|   int32_t as_sint32() const { | ||||
|     // with ZigZag encoding | ||||
|     if (this->value_ & 1) | ||||
|     if (this->value_ & 1) { | ||||
|       return static_cast<int32_t>(~(this->value_ >> 1)); | ||||
|     else | ||||
|     } else { | ||||
|       return static_cast<int32_t>(this->value_ >> 1); | ||||
|     } | ||||
|   } | ||||
|   int64_t as_sint64() const { | ||||
|     // with ZigZag encoding | ||||
|     if (this->value_ & 1) | ||||
|     if (this->value_ & 1) { | ||||
|       return static_cast<int64_t>(~(this->value_ >> 1)); | ||||
|     else | ||||
|     } else { | ||||
|       return static_cast<int64_t>(this->value_ >> 1); | ||||
|     } | ||||
|   } | ||||
|   void encode(std::vector<uint8_t> &out) { | ||||
|     uint32_t val = this->value_; | ||||
| @@ -220,10 +222,11 @@ class ProtoWriteBuffer { | ||||
|   } | ||||
|   void encode_sint32(uint32_t field_id, int32_t value, bool force = false) { | ||||
|     uint32_t uvalue; | ||||
|     if (value < 0) | ||||
|     if (value < 0) { | ||||
|       uvalue = ~(value << 1); | ||||
|     else | ||||
|     } else { | ||||
|       uvalue = value << 1; | ||||
|     } | ||||
|     this->encode_uint32(field_id, uvalue, force); | ||||
|   } | ||||
|   template<class C> void encode_message(uint32_t field_id, const C &value, bool force = false) { | ||||
|   | ||||
| @@ -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); } | ||||
| #endif | ||||
| #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 | ||||
| #ifdef USE_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); | ||||
| } | ||||
| #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) | ||||
|     : ComponentIterator(server), client_(client) {} | ||||
|  | ||||
|   | ||||
| @@ -20,7 +20,7 @@ class InitialStateIterator : public ComponentIterator { | ||||
|   bool on_cover(cover::Cover *cover) override; | ||||
| #endif | ||||
| #ifdef USE_FAN | ||||
|   bool on_fan(fan::FanState *fan) override; | ||||
|   bool on_fan(fan::Fan *fan) override; | ||||
| #endif | ||||
| #ifdef USE_LIGHT | ||||
|   bool on_light(light::LightState *light) override; | ||||
| @@ -45,6 +45,9 @@ class InitialStateIterator : public ComponentIterator { | ||||
| #endif | ||||
| #ifdef USE_SELECT | ||||
|   bool on_select(select::Select *select) override; | ||||
| #endif | ||||
| #ifdef USE_LOCK | ||||
|   bool on_lock(lock::Lock *a_lock) override; | ||||
| #endif | ||||
|  protected: | ||||
|   APIConnection *client_; | ||||
|   | ||||
| @@ -52,7 +52,7 @@ template<typename... Ts> class UserServiceBase : public UserServiceDescriptor { | ||||
|  | ||||
|  protected: | ||||
|   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]))...); | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -212,6 +212,21 @@ void ComponentIterator::advance() { | ||||
|         } | ||||
|       } | ||||
|       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 | ||||
|     case IteratorState::MAX: | ||||
|       if (this->on_end()) { | ||||
|   | ||||
| @@ -27,7 +27,7 @@ class ComponentIterator { | ||||
|   virtual bool on_cover(cover::Cover *cover) = 0; | ||||
| #endif | ||||
| #ifdef USE_FAN | ||||
|   virtual bool on_fan(fan::FanState *fan) = 0; | ||||
|   virtual bool on_fan(fan::Fan *fan) = 0; | ||||
| #endif | ||||
| #ifdef USE_LIGHT | ||||
|   virtual bool on_light(light::LightState *light) = 0; | ||||
| @@ -56,6 +56,9 @@ class ComponentIterator { | ||||
| #endif | ||||
| #ifdef USE_SELECT | ||||
|   virtual bool on_select(select::Select *select) = 0; | ||||
| #endif | ||||
| #ifdef USE_LOCK | ||||
|   virtual bool on_lock(lock::Lock *a_lock) = 0; | ||||
| #endif | ||||
|   virtual bool on_end(); | ||||
|  | ||||
| @@ -99,6 +102,9 @@ class ComponentIterator { | ||||
| #endif | ||||
| #ifdef USE_SELECT | ||||
|     SELECT, | ||||
| #endif | ||||
| #ifdef USE_LOCK | ||||
|     LOCK, | ||||
| #endif | ||||
|     MAX, | ||||
|   } state_{IteratorState::NONE}; | ||||
|   | ||||
| @@ -58,10 +58,11 @@ void AS3935Component::loop() { | ||||
|  | ||||
| void AS3935Component::write_indoor(bool indoor) { | ||||
|   ESP_LOGV(TAG, "Setting indoor to %d", indoor); | ||||
|   if (indoor) | ||||
|   if (indoor) { | ||||
|     this->write_register(AFE_GAIN, GAIN_MASK, INDOOR, 1); | ||||
|   else | ||||
|   } else { | ||||
|     this->write_register(AFE_GAIN, GAIN_MASK, OUTDOOR, 1); | ||||
|   } | ||||
| } | ||||
| // REG0x01, bits[3:0], manufacturer default: 0010 (2). | ||||
| // This setting determines the threshold for events that trigger the | ||||
|   | ||||
| @@ -97,7 +97,7 @@ void BalluClimate::transmit_state() { | ||||
|  | ||||
|   // Send code | ||||
|   auto transmit = this->transmitter_->transmit(); | ||||
|   auto data = transmit.get_data(); | ||||
|   auto *data = transmit.get_data(); | ||||
|  | ||||
|   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++) { | ||||
|     // Read bit | ||||
|     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; | ||||
|  | ||||
|       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); | ||||
|         return false; | ||||
|       } | ||||
|   | ||||
| @@ -21,12 +21,13 @@ void BangBangClimate::setup() { | ||||
|     restore->to_call(this).perform(); | ||||
|   } else { | ||||
|     // 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; | ||||
|     else if (supports_cool_) | ||||
|     } else if (supports_cool_) { | ||||
|       this->mode = climate::CLIMATE_MODE_COOL; | ||||
|     else if (supports_heat_) | ||||
|     } else if (supports_heat_) { | ||||
|       this->mode = climate::CLIMATE_MODE_HEAT; | ||||
|     } | ||||
|     this->change_away_(false); | ||||
|   } | ||||
| } | ||||
| @@ -56,11 +57,12 @@ climate::ClimateTraits BangBangClimate::traits() { | ||||
|   if (supports_cool_ && supports_heat_) | ||||
|     traits.add_supported_mode(climate::CLIMATE_MODE_HEAT_COOL); | ||||
|   traits.set_supports_two_point_target_temperature(true); | ||||
|   if (supports_away_) | ||||
|   if (supports_away_) { | ||||
|     traits.set_supported_presets({ | ||||
|         climate::CLIMATE_PRESET_HOME, | ||||
|         climate::CLIMATE_PRESET_AWAY, | ||||
|     }); | ||||
|   } | ||||
|   traits.set_supports_action(true); | ||||
|   return traits; | ||||
| } | ||||
| @@ -82,17 +84,19 @@ void BangBangClimate::compute_state_() { | ||||
|   if (too_cold) { | ||||
|     // too cold -> enable heating if possible and enabled, else idle | ||||
|     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; | ||||
|     else | ||||
|     } else { | ||||
|       target_action = climate::CLIMATE_ACTION_IDLE; | ||||
|     } | ||||
|   } else if (too_hot) { | ||||
|     // too hot -> enable cooling if possible and enabled, else idle | ||||
|     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; | ||||
|     else | ||||
|     } else { | ||||
|       target_action = climate::CLIMATE_ACTION_IDLE; | ||||
|     } | ||||
|   } else { | ||||
|     // neither too hot nor too cold -> in range | ||||
|     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); | ||||
| } | ||||
| void BangBangClimate::switch_to_action_(climate::ClimateAction action) { | ||||
|   if (action == this->action) | ||||
|   if (action == this->action) { | ||||
|     // already in target mode | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   if ((action == climate::CLIMATE_ACTION_OFF && this->action == climate::CLIMATE_ACTION_IDLE) || | ||||
|       (action == climate::CLIMATE_ACTION_IDLE && this->action == climate::CLIMATE_ACTION_OFF)) { | ||||
|   | ||||
| @@ -9,7 +9,7 @@ from esphome.const import ( | ||||
| ) | ||||
| 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( | ||||
|     { | ||||
| @@ -24,9 +24,8 @@ CONFIG_SCHEMA = fan.FAN_SCHEMA.extend( | ||||
| async def to_code(config): | ||||
|     var = cg.new_Pvariable(config[CONF_OUTPUT_ID]) | ||||
|     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]) | ||||
|     cg.add(var.set_output(output_)) | ||||
|  | ||||
|   | ||||
| @@ -6,59 +6,35 @@ namespace binary { | ||||
|  | ||||
| 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() { | ||||
|   auto traits = fan::FanTraits(this->oscillating_ != nullptr, false, this->direction_ != nullptr, 0); | ||||
|   this->fan_->set_traits(traits); | ||||
|   this->fan_->add_on_state_callback([this]() { this->next_update_ = true; }); | ||||
| } | ||||
| 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)); | ||||
|   auto restore = this->restore_state_(); | ||||
|   if (restore.has_value()) { | ||||
|     restore->apply(*this); | ||||
|     this->write_state_(); | ||||
|   } | ||||
| } | ||||
| 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 | ||||
| // when that component sets itself up. | ||||
| float BinaryFan::get_setup_priority() const { return fan_->get_setup_priority() + 1.0f; } | ||||
|   this->write_state_(); | ||||
|   this->publish_state(); | ||||
| } | ||||
| 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 esphome | ||||
|   | ||||
| @@ -2,28 +2,29 @@ | ||||
|  | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/components/output/binary_output.h" | ||||
| #include "esphome/components/fan/fan_state.h" | ||||
| #include "esphome/components/fan/fan.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace binary { | ||||
|  | ||||
| class BinaryFan : public Component { | ||||
| class BinaryFan : public Component, public fan::Fan { | ||||
|  public: | ||||
|   void set_fan(fan::FanState *fan) { fan_ = fan; } | ||||
|   void set_output(output::BinaryOutput *output) { output_ = output; } | ||||
|   void setup() override; | ||||
|   void loop() 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_direction(output::BinaryOutput *direction) { this->direction_ = direction; } | ||||
|  | ||||
|   fan::FanTraits get_traits() override; | ||||
|  | ||||
|  protected: | ||||
|   fan::FanState *fan_; | ||||
|   void control(const fan::FanCall &call) override; | ||||
|   void write_state_(); | ||||
|  | ||||
|   output::BinaryOutput *output_; | ||||
|   output::BinaryOutput *oscillating_{nullptr}; | ||||
|   output::BinaryOutput *direction_{nullptr}; | ||||
|   bool next_update_{true}; | ||||
| }; | ||||
|  | ||||
| }  // namespace binary | ||||
|   | ||||
| @@ -65,7 +65,7 @@ void BL0940::update() { | ||||
| } | ||||
|  | ||||
| void BL0940::setup() { | ||||
|   for (auto i : BL0940_INIT) { | ||||
|   for (auto *i : BL0940_INIT) { | ||||
|     this->write_array(i, 6); | ||||
|     delay(1); | ||||
|   } | ||||
|   | ||||
| @@ -167,7 +167,7 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es | ||||
|       break; | ||||
|     } | ||||
|     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) { | ||||
|         ESP_LOGW(TAG, "No descriptor found for notify of handle 0x%x", param->reg_for_notify.handle); | ||||
|         break; | ||||
| @@ -252,16 +252,17 @@ float BLEClient::parse_char_value(uint8_t *value, uint16_t length) { | ||||
| } | ||||
|  | ||||
| BLEService *BLEClient::get_service(espbt::ESPBTUUID uuid) { | ||||
|   for (auto svc : this->services_) | ||||
|   for (auto *svc : this->services_) { | ||||
|     if (svc->uuid == uuid) | ||||
|       return svc; | ||||
|   } | ||||
|   return nullptr; | ||||
| } | ||||
|  | ||||
| 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) { | ||||
|   auto svc = this->get_service(service); | ||||
|   auto *svc = this->get_service(service); | ||||
|   if (svc == nullptr) | ||||
|     return nullptr; | ||||
|   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) { | ||||
|   for (auto &svc : this->services_) | ||||
|     for (auto &chr : svc->characteristics) | ||||
|       if (chr->handle == handle) | ||||
|         for (auto &desc : chr->descriptors) | ||||
|   for (auto &svc : this->services_) { | ||||
|     for (auto &chr : svc->characteristics) { | ||||
|       if (chr->handle == handle) { | ||||
|         for (auto &desc : chr->descriptors) { | ||||
|           if (desc->uuid == espbt::ESPBTUUID::from_uint16(0x2902)) | ||||
|             return desc; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   return nullptr; | ||||
| } | ||||
|  | ||||
| BLECharacteristic *BLEService::get_characteristic(espbt::ESPBTUUID uuid) { | ||||
|   for (auto &chr : this->characteristics) | ||||
|   for (auto &chr : this->characteristics) { | ||||
|     if (chr->uuid == uuid) | ||||
|       return chr; | ||||
|   } | ||||
|   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) { | ||||
|   auto svc = this->get_service(service); | ||||
|   auto *svc = this->get_service(service); | ||||
|   if (svc == nullptr) | ||||
|     return nullptr; | ||||
|   auto ch = svc->get_characteristic(chr); | ||||
|   auto *ch = svc->get_characteristic(chr); | ||||
|   if (ch == nullptr) | ||||
|     return nullptr; | ||||
|   return ch->get_descriptor(descr); | ||||
| @@ -379,24 +385,29 @@ void BLECharacteristic::parse_descriptors() { | ||||
| } | ||||
|  | ||||
| BLEDescriptor *BLECharacteristic::get_descriptor(espbt::ESPBTUUID uuid) { | ||||
|   for (auto &desc : this->descriptors) | ||||
|   for (auto &desc : this->descriptors) { | ||||
|     if (desc->uuid == uuid) | ||||
|       return desc; | ||||
|   } | ||||
|   return nullptr; | ||||
| } | ||||
| BLEDescriptor *BLECharacteristic::get_descriptor(uint16_t uuid) { | ||||
|   return this->get_descriptor(espbt::ESPBTUUID::from_uint16(uuid)); | ||||
| } | ||||
|  | ||||
| void BLECharacteristic::write_value(uint8_t *new_val, int16_t new_val_size) { | ||||
|   auto client = this->service->client; | ||||
| 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 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) { | ||||
|     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 esphome | ||||
|  | ||||
|   | ||||
| @@ -60,6 +60,7 @@ class BLECharacteristic { | ||||
|   BLEDescriptor *get_descriptor(espbt::ESPBTUUID 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, esp_gatt_write_type_t write_type); | ||||
|   BLEService *service; | ||||
| }; | ||||
|  | ||||
| @@ -126,9 +127,10 @@ class BLEClient : public espbt::ESPBTClient, public Component { | ||||
|   bool all_nodes_established_() { | ||||
|     if (this->state() != espbt::ClientState::ESTABLISHED) | ||||
|       return false; | ||||
|     for (auto &node : nodes_) | ||||
|     for (auto &node : nodes_) { | ||||
|       if (node->node_state != espbt::ClientState::ESTABLISHED) | ||||
|         return false; | ||||
|     } | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -1,13 +1,14 @@ | ||||
| import esphome.codegen as cg | ||||
| 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 .. import ble_client_ns | ||||
|  | ||||
| from .. import ble_client_ns | ||||
|  | ||||
| DEPENDENCIES = ["ble_client"] | ||||
|  | ||||
| CONF_CHARACTERISTIC_UUID = "characteristic_uuid" | ||||
| CONF_REQUIRE_RESPONSE = "require_response" | ||||
|  | ||||
| BLEBinaryOutput = ble_client_ns.class_( | ||||
|     "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_SERVICE_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) | ||||
| @@ -61,7 +63,7 @@ def to_code(config): | ||||
|             config[CONF_CHARACTERISTIC_UUID] | ||||
|         ) | ||||
|         cg.add(var.set_char_uuid128(uuid128)) | ||||
|  | ||||
|     cg.add(var.set_require_response(config[CONF_REQUIRE_RESPONSE])) | ||||
|     yield output.register_output(var, config) | ||||
|     yield ble_client.register_ble_node(var, config) | ||||
|     yield cg.register_component(var, config) | ||||
|   | ||||
| @@ -32,7 +32,7 @@ void BLEBinaryOutput::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_i | ||||
|         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) { | ||||
|         ESP_LOGW(TAG, "[%s] Characteristic not found.", this->char_uuid_.to_string().c_str()); | ||||
|         break; | ||||
| @@ -54,7 +54,7 @@ void BLEBinaryOutput::write_state(bool state) { | ||||
|     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) { | ||||
|     ESP_LOGW(TAG, "[%s] Characteristic not found.  State update can not be written.", | ||||
|              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; | ||||
|   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 | ||||
|   | ||||
| @@ -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 gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, | ||||
|                            esp_ble_gattc_cb_param_t *param) override; | ||||
|   void set_require_response(bool response) { this->require_response_ = response; } | ||||
|  | ||||
|  protected: | ||||
|   void write_state(bool state) override; | ||||
|   bool require_response_; | ||||
|   espbt::ESPBTUUID service_uuid_; | ||||
|   espbt::ESPBTUUID char_uuid_; | ||||
|   espbt::ClientState client_state_; | ||||
|   | ||||
| @@ -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: { | ||||
|       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) { | ||||
|         this->status_set_warning(); | ||||
|         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; | ||||
|       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) { | ||||
|           this->status_set_warning(); | ||||
|           this->publish_state(NAN); | ||||
|   | ||||
| @@ -1,7 +1,6 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome.components import text_sensor, esp32_ble_tracker | ||||
| from esphome.const import CONF_ID | ||||
|  | ||||
| DEPENDENCIES = ["esp32_ble_tracker"] | ||||
|  | ||||
| @@ -14,18 +13,13 @@ BLEScanner = ble_scanner_ns.class_( | ||||
| ) | ||||
|  | ||||
| CONFIG_SCHEMA = cv.All( | ||||
|     text_sensor.TEXT_SENSOR_SCHEMA.extend( | ||||
|         { | ||||
|             cv.GenerateID(): cv.declare_id(BLEScanner), | ||||
|         } | ||||
|     ) | ||||
|     text_sensor.text_sensor_schema(klass=BLEScanner) | ||||
|     .extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA) | ||||
|     .extend(cv.COMPONENT_SCHEMA) | ||||
| ) | ||||
|  | ||||
|  | ||||
| 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 esp32_ble_tracker.register_ble_device(var, config) | ||||
|     await text_sensor.register_text_sensor(var, config) | ||||
|   | ||||
| @@ -214,9 +214,10 @@ void BME280Component::update() { | ||||
| 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); | ||||
|   adc >>= 4; | ||||
|   if (adc == 0x80000) | ||||
|   if (adc == 0x80000) { | ||||
|     // temperature was disabled | ||||
|     return NAN; | ||||
|   } | ||||
|  | ||||
|   const int32_t t1 = this->calibration_.t1; | ||||
|   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) { | ||||
|   int32_t adc = ((data[0] & 0xFF) << 16) | ((data[1] & 0xFF) << 8) | (data[2] & 0xFF); | ||||
|   adc >>= 4; | ||||
|   if (adc == 0x80000) | ||||
|   if (adc == 0x80000) { | ||||
|     // pressure was disabled | ||||
|     return NAN; | ||||
|   } | ||||
|   const int64_t p1 = this->calibration_.p1; | ||||
|   const int64_t p2 = this->calibration_.p2; | ||||
|   const int64_t p3 = this->calibration_.p3; | ||||
|   | ||||
| @@ -95,7 +95,7 @@ void BME680Component::setup() { | ||||
|   this->calibration_.t3 = cal1[3]; | ||||
|  | ||||
|   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_.h4 = cal2[4]; | ||||
|   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; | ||||
|  | ||||
|   if (calc_hum > 100.0f) | ||||
|   if (calc_hum > 100.0f) { | ||||
|     calc_hum = 100.0f; | ||||
|   else if (calc_hum < 0.0f) | ||||
|   } else if (calc_hum < 0.0f) { | ||||
|     calc_hum = 0.0f; | ||||
|   } | ||||
|  | ||||
|   return calc_hum; | ||||
| } | ||||
|   | ||||
| @@ -1,7 +1,6 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome.components import text_sensor | ||||
| from esphome.const import CONF_ID, CONF_ICON | ||||
| from . import BME680BSECComponent, CONF_BME680_BSEC_ID | ||||
|  | ||||
| DEPENDENCIES = ["bme680_bsec"] | ||||
| @@ -14,11 +13,8 @@ TYPES = [CONF_IAQ_ACCURACY] | ||||
| CONFIG_SCHEMA = cv.Schema( | ||||
|     { | ||||
|         cv.GenerateID(CONF_BME680_BSEC_ID): cv.use_id(BME680BSECComponent), | ||||
|         cv.Optional(CONF_IAQ_ACCURACY): text_sensor.TEXT_SENSOR_SCHEMA.extend( | ||||
|             { | ||||
|                 cv.GenerateID(): cv.declare_id(text_sensor.TextSensor), | ||||
|                 cv.Optional(CONF_ICON, default=ICON_ACCURACY): cv.icon, | ||||
|             } | ||||
|         cv.Optional(CONF_IAQ_ACCURACY): text_sensor.text_sensor_schema( | ||||
|             icon=ICON_ACCURACY | ||||
|         ), | ||||
|     } | ||||
| ) | ||||
| @@ -27,8 +23,7 @@ CONFIG_SCHEMA = cv.Schema( | ||||
| async def setup_conf(config, key, hub): | ||||
|     if key in config: | ||||
|         conf = config[key] | ||||
|         sens = cg.new_Pvariable(conf[CONF_ID]) | ||||
|         await text_sensor.register_text_sensor(sens, conf) | ||||
|         sens = await text_sensor.new_text_sensor(conf) | ||||
|         cg.add(getattr(hub, f"set_{key}_text_sensor")(sens)) | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -161,9 +161,10 @@ float BMP280Component::read_temperature_(int32_t *t_fine) { | ||||
|     return NAN; | ||||
|   int32_t adc = ((data[0] & 0xFF) << 16) | ((data[1] & 0xFF) << 8) | (data[2] & 0xFF); | ||||
|   adc >>= 4; | ||||
|   if (adc == 0x80000) | ||||
|   if (adc == 0x80000) { | ||||
|     // temperature was disabled | ||||
|     return NAN; | ||||
|   } | ||||
|  | ||||
|   const int32_t t1 = this->calibration_.t1; | ||||
|   const int32_t t2 = this->calibration_.t2; | ||||
| @@ -183,9 +184,10 @@ float BMP280Component::read_pressure_(int32_t t_fine) { | ||||
|     return NAN; | ||||
|   int32_t adc = ((data[0] & 0xFF) << 16) | ((data[1] & 0xFF) << 8) | (data[2] & 0xFF); | ||||
|   adc >>= 4; | ||||
|   if (adc == 0x80000) | ||||
|   if (adc == 0x80000) { | ||||
|     // pressure was disabled | ||||
|     return NAN; | ||||
|   } | ||||
|   const int64_t p1 = this->calibration_.p1; | ||||
|   const int64_t p2 = this->calibration_.p2; | ||||
|   const int64_t p3 = this->calibration_.p3; | ||||
|   | ||||
| @@ -14,10 +14,14 @@ CONF_BIT_RATE = "bit_rate" | ||||
| CONF_ON_FRAME = "on_frame" | ||||
|  | ||||
|  | ||||
| def validate_id(id_value, id_ext): | ||||
|     if not id_ext: | ||||
|         if id_value > 0x7FF: | ||||
|             raise cv.Invalid("Standard IDs must be 11 Bit (0x000-0x7ff / 0-2047)") | ||||
| def validate_id(config): | ||||
|     if CONF_CAN_ID in config: | ||||
|         id_value = config[CONF_CAN_ID] | ||||
|         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): | ||||
| @@ -67,23 +71,18 @@ CANBUS_SCHEMA = cv.Schema( | ||||
|         cv.Optional(CONF_ON_FRAME): automation.validate_automation( | ||||
|             { | ||||
|                 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_ON_FRAME): automation.validate_automation( | ||||
|                     { | ||||
|                         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, | ||||
|                     } | ||||
|                 ), | ||||
|             } | ||||
|             }, | ||||
|             validate_id, | ||||
|         ), | ||||
|     } | ||||
|     }, | ||||
| ).extend(cv.COMPONENT_SCHEMA) | ||||
|  | ||||
| CANBUS_SCHEMA.add_extra(validate_id) | ||||
|  | ||||
|  | ||||
| async def setup_canbus_core_(var, config): | ||||
|     validate_id(config[CONF_CAN_ID], config[CONF_USE_EXTENDED_ID]) | ||||
|     await cg.register_component(var, config) | ||||
|     cg.add(var.set_can_id([config[CONF_CAN_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, []): | ||||
|         can_id = conf[CONF_CAN_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) | ||||
|         await cg.register_component(trigger, conf) | ||||
|         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.Required(CONF_DATA): cv.templatable(validate_raw_data), | ||||
|         }, | ||||
|         validate_id, | ||||
|         key=CONF_DATA, | ||||
|     ), | ||||
| ) | ||||
| 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) | ||||
|     await cg.register_parented(var, config[CONF_CANBUS_ID]) | ||||
|  | ||||
|   | ||||
| @@ -75,7 +75,7 @@ void Canbus::loop() { | ||||
|     } | ||||
|  | ||||
|     // 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)) { | ||||
|         trigger->trigger(data); | ||||
|       } | ||||
|   | ||||
| @@ -39,14 +39,15 @@ void CCS811Component::setup() { | ||||
|   // set MEAS_MODE (page 5) | ||||
|   uint8_t meas_mode = 0; | ||||
|   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 | ||||
|   else if (interval >= 10 * 1000) | ||||
|   } else if (interval >= 10 * 1000) { | ||||
|     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 | ||||
|   else | ||||
|   } else { | ||||
|     meas_mode = 4 << 4;  // sensor takes a reading every 250ms | ||||
|   } | ||||
|  | ||||
|   CHECKED_IO(this->write_byte(0x01, meas_mode)) | ||||
|  | ||||
|   | ||||
| @@ -2,7 +2,6 @@ import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome.components import i2c, sensor, text_sensor | ||||
| from esphome.const import ( | ||||
|     CONF_ICON, | ||||
|     CONF_ID, | ||||
|     ICON_RADIATOR, | ||||
|     ICON_RESTART, | ||||
| @@ -47,11 +46,8 @@ CONFIG_SCHEMA = ( | ||||
|                 device_class=DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS, | ||||
|                 state_class=STATE_CLASS_MEASUREMENT, | ||||
|             ), | ||||
|             cv.Optional(CONF_VERSION): text_sensor.TEXT_SENSOR_SCHEMA.extend( | ||||
|                 { | ||||
|                     cv.GenerateID(): cv.declare_id(text_sensor.TextSensor), | ||||
|                     cv.Optional(CONF_ICON, default=ICON_RESTART): cv.icon, | ||||
|                 } | ||||
|             cv.Optional(CONF_VERSION): text_sensor.text_sensor_schema( | ||||
|                 icon=ICON_RESTART | ||||
|             ), | ||||
|             cv.Optional(CONF_BASELINE): cv.hex_uint16_t, | ||||
|             cv.Optional(CONF_TEMPERATURE): cv.use_id(sensor.Sensor), | ||||
| @@ -74,8 +70,7 @@ async def to_code(config): | ||||
|     cg.add(var.set_tvoc(sens)) | ||||
|  | ||||
|     if CONF_VERSION in config: | ||||
|         sens = cg.new_Pvariable(config[CONF_VERSION][CONF_ID]) | ||||
|         await text_sensor.register_text_sensor(sens, config[CONF_VERSION]) | ||||
|         sens = await text_sensor.new_text_sensor(config[CONF_VERSION]) | ||||
|         cg.add(var.set_version(sens)) | ||||
|  | ||||
|     if CONF_BASELINE in config: | ||||
|   | ||||
| @@ -6,8 +6,9 @@ from esphome.const import ( | ||||
|     CONF_ID, | ||||
| ) | ||||
|  | ||||
| CODEOWNERS = ["@asoehlke"] | ||||
| AUTO_LOAD = ["sensor", "voltage_sampler"] | ||||
| CODEOWNERS = ["@asoehlke"] | ||||
| MULTI_CONF = True | ||||
|  | ||||
| cd74hc4067_ns = cg.esphome_ns.namespace("cd74hc4067") | ||||
|  | ||||
|   | ||||
| @@ -65,7 +65,7 @@ class ClimateTraits { | ||||
|   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); } | ||||
|   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; } | ||||
|   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); } | ||||
|   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(); } | ||||
|   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) { | ||||
|     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 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_; } | ||||
|   void set_visual_min_temperature(float visual_min_temperature) { visual_min_temperature_ = visual_min_temperature; } | ||||
|   | ||||
| @@ -137,11 +137,11 @@ bool LgIrClimate::on_receive(remote_base::RemoteReceiveData data) { | ||||
|     this->swing_mode = | ||||
|         this->swing_mode == climate::CLIMATE_SWING_OFF ? climate::CLIMATE_SWING_VERTICAL : climate::CLIMATE_SWING_OFF; | ||||
|   } else { | ||||
|     if ((remote_state & COMMAND_MASK) == COMMAND_AUTO) | ||||
|     if ((remote_state & COMMAND_MASK) == COMMAND_AUTO) { | ||||
|       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; | ||||
|     else if ((remote_state & COMMAND_MASK) == COMMAND_HEAT) { | ||||
|     } else if ((remote_state & COMMAND_MASK) == COMMAND_HEAT) { | ||||
|       this->mode = climate::CLIMATE_MODE_HEAT; | ||||
|     } else { | ||||
|       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; | ||||
|     } else if (this->mode == climate::CLIMATE_MODE_COOL || this->mode == climate::CLIMATE_MODE_HEAT || | ||||
|                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; | ||||
|       else if ((remote_state & FAN_MASK) == FAN_MIN) | ||||
|       } else if ((remote_state & FAN_MASK) == FAN_MIN) { | ||||
|         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; | ||||
|       else if ((remote_state & FAN_MASK) == FAN_MAX) | ||||
|       } else if ((remote_state & FAN_MASK) == FAN_MAX) { | ||||
|         this->fan_mode = climate::CLIMATE_FAN_HIGH; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   this->publish_state(); | ||||
| @@ -175,7 +176,7 @@ void LgIrClimate::transmit_(uint32_t value) { | ||||
|   ESP_LOGD(TAG, "Sending climate_lg_ir code: 0x%02X", value); | ||||
|  | ||||
|   auto transmit = this->transmitter_->transmit(); | ||||
|   auto data = transmit.get_data(); | ||||
|   auto *data = transmit.get_data(); | ||||
|  | ||||
|   data->set_carrier_frequency(38000); | ||||
|   data->reserve(2 + BITS * 2u); | ||||
|   | ||||
| @@ -104,7 +104,7 @@ void CoolixClimate::transmit_state() { | ||||
|   ESP_LOGV(TAG, "Sending coolix code: 0x%06X", remote_state); | ||||
|  | ||||
|   auto transmit = this->transmitter_->transmit(); | ||||
|   auto data = transmit.get_data(); | ||||
|   auto *data = transmit.get_data(); | ||||
|   remote_base::CoolixProtocol().encode(data, remote_state); | ||||
|   transmit.perform(); | ||||
| } | ||||
| @@ -125,34 +125,37 @@ bool CoolixClimate::on_coolix(climate::Climate *parent, remote_base::RemoteRecei | ||||
|     parent->swing_mode = | ||||
|         parent->swing_mode == climate::CLIMATE_SWING_OFF ? climate::CLIMATE_SWING_VERTICAL : climate::CLIMATE_SWING_OFF; | ||||
|   } else { | ||||
|     if ((remote_state & COOLIX_MODE_MASK) == COOLIX_HEAT) | ||||
|     if ((remote_state & COOLIX_MODE_MASK) == COOLIX_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; | ||||
|     else if ((remote_state & COOLIX_MODE_MASK) == COOLIX_DRY_FAN) { | ||||
|       if ((remote_state & COOLIX_FAN_MASK) == COOLIX_FAN_MODE_AUTO_DRY) | ||||
|     } else if ((remote_state & COOLIX_MODE_MASK) == COOLIX_DRY_FAN) { | ||||
|       if ((remote_state & COOLIX_FAN_MASK) == COOLIX_FAN_MODE_AUTO_DRY) { | ||||
|         parent->mode = climate::CLIMATE_MODE_DRY; | ||||
|       else | ||||
|       } else { | ||||
|         parent->mode = climate::CLIMATE_MODE_FAN_ONLY; | ||||
|       } | ||||
|     } else | ||||
|       parent->mode = climate::CLIMATE_MODE_COOL; | ||||
|  | ||||
|     // Fan Speed | ||||
|     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; | ||||
|     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; | ||||
|     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; | ||||
|     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; | ||||
|     } | ||||
|  | ||||
|     // Temperature | ||||
|     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) | ||||
|         parent->target_temperature = i + COOLIX_TEMP_MIN; | ||||
|     } | ||||
|   } | ||||
|   parent->publish_state(); | ||||
|  | ||||
|   | ||||
| @@ -7,6 +7,7 @@ from esphome.const import ( | ||||
|     CONF_ID, | ||||
|     CONF_DEVICE_CLASS, | ||||
|     CONF_STATE, | ||||
|     CONF_ON_OPEN, | ||||
|     CONF_POSITION, | ||||
|     CONF_POSITION_COMMAND_TOPIC, | ||||
|     CONF_POSITION_STATE_TOPIC, | ||||
| @@ -74,7 +75,6 @@ CoverClosedTrigger = cover_ns.class_( | ||||
|     "CoverClosedTrigger", automation.Trigger.template() | ||||
| ) | ||||
|  | ||||
| CONF_ON_OPEN = "on_open" | ||||
| CONF_ON_CLOSED = "on_closed" | ||||
|  | ||||
| COVER_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend( | ||||
|   | ||||
| @@ -49,8 +49,8 @@ def validate_config(config): | ||||
|     if current_gain == 0.0 or voltage_gain == 0.0: | ||||
|         raise cv.Invalid("The gains can't be zero") | ||||
|  | ||||
|     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) | ||||
|     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) | ||||
|     mech_min_energy = (0.25 * 0.25 / 3600 / 7.8) / (voltage_gain * current_gain) | ||||
|     if pulse_energy < min_energy or pulse_energy > max_energy: | ||||
|         raise cv.Invalid( | ||||
|   | ||||
| @@ -11,11 +11,7 @@ CONFIG_SCHEMA = cv.Schema( | ||||
|         cv.GenerateID(): cv.declare_id(CustomTextSensorConstructor), | ||||
|         cv.Required(CONF_LAMBDA): cv.returning_lambda, | ||||
|         cv.Required(CONF_TEXT_SENSORS): cv.ensure_list( | ||||
|             text_sensor.TEXT_SENSOR_SCHEMA.extend( | ||||
|                 { | ||||
|                     cv.GenerateID(): cv.declare_id(text_sensor.TextSensor), | ||||
|                 } | ||||
|             ) | ||||
|             text_sensor.text_sensor_schema() | ||||
|         ), | ||||
|     } | ||||
| ) | ||||
|   | ||||
| @@ -23,7 +23,7 @@ void DaikinClimate::transmit_state() { | ||||
|   } | ||||
|  | ||||
|   auto transmit = this->transmitter_->transmit(); | ||||
|   auto data = transmit.get_data(); | ||||
|   auto *data = transmit.get_data(); | ||||
|   data->set_carrier_frequency(DAIKIN_IR_FREQUENCY); | ||||
|  | ||||
|   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 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; | ||||
|   else if (fan_mode & 0xF) | ||||
|   } else if (fan_mode & 0xF) { | ||||
|     this->swing_mode = climate::CLIMATE_SWING_VERTICAL; | ||||
|   else if (swing_mode & 0xF) | ||||
|   } else if (swing_mode & 0xF) { | ||||
|     this->swing_mode = climate::CLIMATE_SWING_HORIZONTAL; | ||||
|   else | ||||
|   } else { | ||||
|     this->swing_mode = climate::CLIMATE_SWING_OFF; | ||||
|   } | ||||
|   switch (fan_mode & 0xF0) { | ||||
|     case DAIKIN_FAN_1: | ||||
|     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++) { | ||||
|     uint8_t byte = 0; | ||||
|     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; | ||||
|       else if (!data.expect_item(DAIKIN_BIT_MARK, DAIKIN_ZERO_SPACE)) { | ||||
|       } else if (!data.expect_item(DAIKIN_BIT_MARK, DAIKIN_ZERO_SPACE)) { | ||||
|         return false; | ||||
|       } | ||||
|     } | ||||
|   | ||||
| @@ -52,7 +52,7 @@ void DallasComponent::setup() { | ||||
|     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() >= this->found_sensors_.size()) { | ||||
|         this->status_set_error(); | ||||
| @@ -235,7 +235,7 @@ float DallasTemperatureSensor::get_temp_c() { | ||||
|  | ||||
|   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 esphome | ||||
|   | ||||
| @@ -145,9 +145,10 @@ uint64_t ESPOneWire::search() { | ||||
|     // read its complement | ||||
|     bool cmp_id_bit = this->read_bit(); | ||||
|  | ||||
|     if (id_bit && cmp_id_bit) | ||||
|     if (id_bit && cmp_id_bit) { | ||||
|       // No devices participating in search | ||||
|       break; | ||||
|     } | ||||
|  | ||||
|     bool branch; | ||||
|  | ||||
| @@ -170,12 +171,13 @@ uint64_t ESPOneWire::search() { | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     if (branch) | ||||
|     if (branch) { | ||||
|       // set bit | ||||
|       this->rom_number8_()[rom_byte_number] |= rom_byte_mask; | ||||
|     else | ||||
|     } else { | ||||
|       // clear bit | ||||
|       this->rom_number8_()[rom_byte_number] &= ~rom_byte_mask; | ||||
|     } | ||||
|  | ||||
|     // choose/announce branch | ||||
|     this->write_bit(branch); | ||||
| @@ -190,9 +192,10 @@ uint64_t ESPOneWire::search() { | ||||
|  | ||||
|   if (id_bit_number >= 65) { | ||||
|     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. | ||||
|       this->last_device_flag_ = true; | ||||
|     } | ||||
|     search_result = true; | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| 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 | ||||
|  | ||||
| ICON_CAR_BATTERY = "mdi:car-battery" | ||||
| @@ -14,11 +14,8 @@ CONFIG_SCHEMA = cv.All( | ||||
|     cv.Schema( | ||||
|         { | ||||
|             cv.GenerateID(CONF_BMS_DALY_ID): cv.use_id(DalyBmsComponent), | ||||
|             cv.Optional(CONF_STATUS): text_sensor.TEXT_SENSOR_SCHEMA.extend( | ||||
|                 { | ||||
|                     cv.GenerateID(): cv.declare_id(text_sensor.TextSensor), | ||||
|                     cv.Optional(CONF_ICON, default=ICON_CAR_BATTERY): cv.icon, | ||||
|                 } | ||||
|             cv.Optional(CONF_STATUS): text_sensor.text_sensor_schema( | ||||
|                 icon=ICON_CAR_BATTERY | ||||
|             ), | ||||
|         } | ||||
|     ).extend(cv.COMPONENT_SCHEMA) | ||||
| @@ -28,8 +25,7 @@ CONFIG_SCHEMA = cv.All( | ||||
| async def setup_conf(config, key, hub): | ||||
|     if key in config: | ||||
|         conf = config[key] | ||||
|         sens = cg.new_Pvariable(conf[CONF_ID]) | ||||
|         await text_sensor.register_text_sensor(sens, conf) | ||||
|         sens = await text_sensor.new_text_sensor(conf) | ||||
|         cg.add(getattr(hub, f"set_{key}_text_sensor")(sens)) | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -1,17 +1,42 @@ | ||||
| import esphome.config_validation as cv | ||||
| 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"] | ||||
| DEPENDENCIES = ["logger"] | ||||
|  | ||||
| CONF_DEBUG_ID = "debug_id" | ||||
| 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( | ||||
|     { | ||||
|         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): | ||||
|   | ||||
| @@ -1,21 +1,23 @@ | ||||
| #include "debug_component.h" | ||||
|  | ||||
| #include <algorithm> | ||||
| #include "esphome/core/log.h" | ||||
| #include "esphome/core/hal.h" | ||||
| #include "esphome/core/helpers.h" | ||||
| #include "esphome/core/defines.h" | ||||
| #include "esphome/core/version.h" | ||||
|  | ||||
| #ifdef USE_ESP_IDF | ||||
| #ifdef USE_ESP32 | ||||
|  | ||||
| #include <esp_heap_caps.h> | ||||
| #include <esp_system.h> | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_ESP32 | ||||
| #if ESP_IDF_VERSION_MAJOR >= 4 | ||||
| #include <esp32/rom/rtc.h> | ||||
| #else | ||||
| #include <rom/rtc.h> | ||||
| #endif | ||||
| #endif | ||||
|  | ||||
| #endif  // USE_ESP32 | ||||
|  | ||||
| #ifdef USE_ARDUINO | ||||
| #include <Esp.h> | ||||
| @@ -26,19 +28,40 @@ namespace 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() { | ||||
|   std::string device_info; | ||||
|   device_info.reserve(256); | ||||
|  | ||||
| #ifndef ESPHOME_LOG_HAS_DEBUG | ||||
|   ESP_LOGE(TAG, "Debug Component requires debug log level!"); | ||||
|   this->status_set_error(); | ||||
|   return; | ||||
| #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); | ||||
| #ifdef USE_ARDUINO | ||||
|   this->free_heap_ = ESP.getFreeHeap();  // NOLINT(readability-static-accessed-through-instance) | ||||
| #elif defined(USE_ESP_IDF) | ||||
|   this->free_heap_ = heap_caps_get_free_size(MALLOC_CAP_INTERNAL); | ||||
| #endif | ||||
|   device_info += ESPHOME_VERSION; | ||||
|  | ||||
|   this->free_heap_ = get_free_heap(); | ||||
|   ESP_LOGD(TAG, "Free Heap Size: %u bytes", this->free_heap_); | ||||
|  | ||||
| #ifdef USE_ARDUINO | ||||
| @@ -67,9 +90,12 @@ void DebugComponent::dump_config() { | ||||
|     default: | ||||
|       flash_mode = "UNKNOWN"; | ||||
|   } | ||||
|   // NOLINTNEXTLINE(readability-static-accessed-through-instance) | ||||
|   ESP_LOGD(TAG, "Flash Chip: Size=%ukB Speed=%uMHz Mode=%s", ESP.getFlashChipSize() / 1024, | ||||
|            ESP.getFlashChipSpeed() / 1000000, flash_mode); | ||||
|   ESP_LOGD(TAG, "Flash Chip: Size=%ukB Speed=%uMHz Mode=%s", | ||||
|            ESP.getFlashChipSize() / 1024,                                                   // NOLINT | ||||
|            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 | ||||
|  | ||||
| #ifdef USE_ESP32 | ||||
| @@ -104,10 +130,21 @@ void DebugComponent::dump_config() { | ||||
|     features += "Other:" + format_hex(info.features); | ||||
|   ESP_LOGD(TAG, "Chip: Model=%s, Features=%s Cores=%u, Revision=%u", model, features.c_str(), info.cores, | ||||
|            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()); | ||||
|   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; | ||||
|   switch (rtc_get_reset_reason(0)) { | ||||
| @@ -160,6 +197,8 @@ void DebugComponent::dump_config() { | ||||
|       reset_reason = "Unknown Reset Reason"; | ||||
|   } | ||||
|   ESP_LOGD(TAG, "Reset Reason: %s", reset_reason); | ||||
|   device_info += "|Reset: "; | ||||
|   device_info += reset_reason; | ||||
|  | ||||
|   const char *wakeup_reason; | ||||
|   switch (rtc_get_wakeup_cause()) { | ||||
| @@ -203,6 +242,8 @@ void DebugComponent::dump_config() { | ||||
|       wakeup_reason = "Unknown"; | ||||
|   } | ||||
|   ESP_LOGD(TAG, "Wakeup Reason: %s", wakeup_reason); | ||||
|   device_info += "|Wakeup: "; | ||||
|   device_info += wakeup_reason; | ||||
| #endif | ||||
|  | ||||
| #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, "Reset Reason: %s", ESP.getResetReason().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 | ||||
|  | ||||
| #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() { | ||||
| #ifdef USE_ARDUINO | ||||
|   uint32_t new_free_heap = ESP.getFreeHeap();  // NOLINT(readability-static-accessed-through-instance) | ||||
| #elif defined(USE_ESP_IDF) | ||||
|   uint32_t new_free_heap = heap_caps_get_free_size(MALLOC_CAP_INTERNAL); | ||||
| #endif | ||||
|   // log when free heap space has halved | ||||
|   uint32_t new_free_heap = get_free_heap(); | ||||
|   if (new_free_heap < this->free_heap_ / 2) { | ||||
|     this->free_heap_ = new_free_heap; | ||||
|     ESP_LOGD(TAG, "Free Heap Size: %u bytes", this->free_heap_); | ||||
|     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; } | ||||
|  | ||||
| }  // namespace debug | ||||
|   | ||||
| @@ -1,18 +1,56 @@ | ||||
| #pragma once | ||||
|  | ||||
| #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 debug { | ||||
|  | ||||
| class DebugComponent : public Component { | ||||
| class DebugComponent : public PollingComponent { | ||||
|  public: | ||||
|   void loop() override; | ||||
|   void update() override; | ||||
|   float get_setup_priority() const 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: | ||||
|   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 | ||||
|   | ||||
							
								
								
									
										49
									
								
								esphome/components/debug/sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								esphome/components/debug/sensor.py
									
									
									
									
									
										Normal 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)) | ||||
							
								
								
									
										24
									
								
								esphome/components/debug/text_sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								esphome/components/debug/text_sensor.py
									
									
									
									
									
										Normal 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)) | ||||
| @@ -37,12 +37,10 @@ from esphome.const import ( | ||||
|     DEVICE_CLASS_TEMPERATURE, | ||||
|     ICON_BLUETOOTH, | ||||
|     ICON_BLUR, | ||||
|     ICON_EMPTY, | ||||
|     ICON_THERMOMETER, | ||||
|     STATE_CLASS_MEASUREMENT, | ||||
|     STATE_CLASS_TOTAL_INCREASING, | ||||
|     UNIT_CELSIUS, | ||||
|     UNIT_EMPTY, | ||||
|     UNIT_PERCENT, | ||||
|     UNIT_WATT_HOURS, | ||||
| ) | ||||
| @@ -67,7 +65,7 @@ DemoClimate = demo_ns.class_("DemoClimate", climate.Climate, cg.Component) | ||||
| DemoClimateType = demo_ns.enum("DemoClimateType", is_class=True) | ||||
| DemoCover = demo_ns.class_("DemoCover", cover.Cover, cg.Component) | ||||
| 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) | ||||
| DemoLight = demo_ns.class_("DemoLight", light.LightOutput, cg.Component) | ||||
| 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( | ||||
|                 { | ||||
| @@ -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") | ||||
|             ).extend( | ||||
|                 { | ||||
|                     cv.GenerateID(): cv.declare_id(DemoTextSensor), | ||||
|                 } | ||||
|             ) | ||||
|         ], | ||||
|     } | ||||
| @@ -411,8 +405,7 @@ async def to_code(config): | ||||
|     for conf in config[CONF_FANS]: | ||||
|         var = cg.new_Pvariable(conf[CONF_OUTPUT_ID]) | ||||
|         await cg.register_component(var, conf) | ||||
|         fan_ = await fan.create_fan_state(conf) | ||||
|         cg.add(var.set_fan(fan_)) | ||||
|         await fan.register_fan(var, conf) | ||||
|         cg.add(var.set_type(conf[CONF_TYPE])) | ||||
|  | ||||
|     for conf in config[CONF_LIGHTS]: | ||||
| @@ -444,6 +437,5 @@ async def to_code(config): | ||||
|         await switch.register_switch(var, conf) | ||||
|  | ||||
|     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 text_sensor.register_text_sensor(var, conf) | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/components/fan/fan_state.h" | ||||
| #include "esphome/components/fan/fan.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace demo { | ||||
| @@ -13,11 +13,10 @@ enum class DemoFanType { | ||||
|   TYPE_4, | ||||
| }; | ||||
|  | ||||
| class DemoFan : public Component { | ||||
| class DemoFan : public fan::Fan, public Component { | ||||
|  public: | ||||
|   void set_type(DemoFanType type) { type_ = type; } | ||||
|   void set_fan(fan::FanState *fan) { fan_ = fan; } | ||||
|   void setup() override { | ||||
|   fan::FanTraits get_traits() override { | ||||
|     fan::FanTraits traits{}; | ||||
|  | ||||
|     // oscillation | ||||
| @@ -43,10 +42,23 @@ class DemoFan : public Component { | ||||
|         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_; | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -114,10 +114,11 @@ bool HOT IRAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, bool r | ||||
|       // Wait for rising edge | ||||
|       while (!this->pin_->digital_read()) { | ||||
|         if (micros() - start_time > 90) { | ||||
|           if (i < 0) | ||||
|           if (i < 0) { | ||||
|             error_code = 1; | ||||
|           else | ||||
|           } else { | ||||
|             error_code = 2; | ||||
|           } | ||||
|           break; | ||||
|         } | ||||
|       } | ||||
| @@ -130,10 +131,11 @@ bool HOT IRAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, bool r | ||||
|       // Wait for falling edge | ||||
|       while (this->pin_->digital_read()) { | ||||
|         if ((end_time = micros()) - start_time > 90) { | ||||
|           if (i < 0) | ||||
|           if (i < 0) { | ||||
|             error_code = 3; | ||||
|           else | ||||
|           } else { | ||||
|             error_code = 4; | ||||
|           } | ||||
|           break; | ||||
|         } | ||||
|       } | ||||
|   | ||||
| @@ -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]); | ||||
|       if (!font->get_glyphs().empty()) { | ||||
|         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++) | ||||
|             this->draw_pixel_at(glyph_x + x_at, glyph_y + y_start, color); | ||||
|         } | ||||
|         x_at += glyph_width; | ||||
|       } | ||||
|  | ||||
| @@ -233,6 +234,14 @@ void DisplayBuffer::image(int x, int y, Image *image, Color color_on, Color colo | ||||
|         } | ||||
|       } | ||||
|       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 | ||||
|  | ||||
| #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, | ||||
|                                     int *width, int *height) { | ||||
|   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; | ||||
|   while (lo != hi) { | ||||
|     int mid = (lo + hi + 1) / 2; | ||||
|     if (this->glyphs_[mid].compare_to(str)) | ||||
|     if (this->glyphs_[mid].compare_to(str)) { | ||||
|       lo = mid; | ||||
|     else | ||||
|     } else { | ||||
|       hi = mid - 1; | ||||
|     } | ||||
|   } | ||||
|   *match_length = this->glyphs_[lo].match_length(str); | ||||
|   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]; | ||||
|     if (!has_char) | ||||
|     if (!has_char) { | ||||
|       min_x = glyph.glyph_data_->offset_x; | ||||
|     else | ||||
|     } else { | ||||
|       min_x = std::min(min_x, x + glyph.glyph_data_->offset_x); | ||||
|     } | ||||
|     x += glyph.glyph_data_->width + glyph.glyph_data_->offset_x; | ||||
|  | ||||
|     i += match_length; | ||||
|   | ||||
| @@ -14,6 +14,10 @@ | ||||
| #include "esphome/components/graph/graph.h" | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_QR_CODE | ||||
| #include "esphome/components/qr_code/qr_code.h" | ||||
| #endif | ||||
|  | ||||
| namespace esphome { | ||||
| namespace display { | ||||
|  | ||||
| @@ -73,7 +77,12 @@ extern const Color COLOR_OFF; | ||||
| /// Turn the pixel 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 { | ||||
|   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); | ||||
| #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. | ||||
|    * | ||||
|    * @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. | ||||
|   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: | ||||
|   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 int get_height_internal() = 0; | ||||
|  | ||||
|   virtual int get_width_internal() = 0; | ||||
|  | ||||
|   void init_internal_(uint32_t buffer_length); | ||||
|  | ||||
|   void do_update_(); | ||||
|   | ||||
| @@ -1,9 +1,7 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome.components import text_sensor | ||||
| from esphome.const import ( | ||||
|     CONF_ID, | ||||
| ) | ||||
|  | ||||
| from . import Dsmr, CONF_DSMR_ID | ||||
|  | ||||
| AUTO_LOAD = ["dsmr"] | ||||
| @@ -11,71 +9,19 @@ AUTO_LOAD = ["dsmr"] | ||||
| CONFIG_SCHEMA = cv.Schema( | ||||
|     { | ||||
|         cv.GenerateID(CONF_DSMR_ID): cv.use_id(Dsmr), | ||||
|         cv.Optional("identification"): text_sensor.TEXT_SENSOR_SCHEMA.extend( | ||||
|             { | ||||
|                 cv.GenerateID(): cv.declare_id(text_sensor.TextSensor), | ||||
|             } | ||||
|         ), | ||||
|         cv.Optional("p1_version"): text_sensor.TEXT_SENSOR_SCHEMA.extend( | ||||
|             { | ||||
|                 cv.GenerateID(): cv.declare_id(text_sensor.TextSensor), | ||||
|             } | ||||
|         ), | ||||
|         cv.Optional("p1_version_be"): text_sensor.TEXT_SENSOR_SCHEMA.extend( | ||||
|             { | ||||
|                 cv.GenerateID(): cv.declare_id(text_sensor.TextSensor), | ||||
|             } | ||||
|         ), | ||||
|         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), | ||||
|             } | ||||
|         ), | ||||
|         cv.Optional("identification"): text_sensor.text_sensor_schema(), | ||||
|         cv.Optional("p1_version"): text_sensor.text_sensor_schema(), | ||||
|         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("electricity_failure_log"): text_sensor.text_sensor_schema(), | ||||
|         cv.Optional("message_short"): text_sensor.text_sensor_schema(), | ||||
|         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("water_equipment_id"): text_sensor.text_sensor_schema(), | ||||
|         cv.Optional("sub_equipment_id"): text_sensor.text_sensor_schema(), | ||||
|         cv.Optional("gas_delivered_text"): text_sensor.text_sensor_schema(), | ||||
|     } | ||||
| ).extend(cv.COMPONENT_SCHEMA) | ||||
|  | ||||
| @@ -89,8 +35,7 @@ async def to_code(config): | ||||
|             continue | ||||
|         id = conf.get("id") | ||||
|         if id and id.type == text_sensor.TextSensor: | ||||
|             var = cg.new_Pvariable(conf[CONF_ID]) | ||||
|             await text_sensor.register_text_sensor(var, conf) | ||||
|             var = await text_sensor.new_text_sensor(conf) | ||||
|             cg.add(getattr(hub, f"set_{key}")(var)) | ||||
|             text_sensors.append(f"F({key})") | ||||
|  | ||||
|   | ||||
| @@ -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); | ||||
|  | ||||
|   for (auto light_effect : light_effects_) { | ||||
|   for (auto *light_effect : light_effects_) { | ||||
|     handled = light_effect->process_(universe, packet) || handled; | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -33,11 +33,9 @@ class E131Component : public esphome::Component { | ||||
|   void loop() override; | ||||
|   float get_setup_priority() const override { return setup_priority::AFTER_WIFI; } | ||||
|  | ||||
|  public: | ||||
|   void add_effect(E131AddressableLightEffect *light_effect); | ||||
|   void remove_effect(E131AddressableLightEffect *light_effect); | ||||
|  | ||||
|  public: | ||||
|   void set_method(E131ListenMethod listen_method) { this->listen_method_ = listen_method; } | ||||
|  | ||||
|  protected: | ||||
| @@ -47,7 +45,6 @@ class E131Component : public esphome::Component { | ||||
|   void join_(int universe); | ||||
|   void leave_(int universe); | ||||
|  | ||||
|  protected: | ||||
|   E131ListenMethod listen_method_{E131_MULTICAST}; | ||||
|   std::unique_ptr<UDP> udp_; | ||||
|   std::set<E131AddressableLightEffect *> light_effects_; | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user