From a269098a0ed81733f1db780c9fb87aaa8eadd22a Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Sat, 9 Jan 2021 07:51:18 +1300 Subject: [PATCH 001/111] Bump version to v1.17.0-dev --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 24a3573fea..8ab739768f 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,7 +1,7 @@ """Constants used by esphome.""" MAJOR_VERSION = 1 -MINOR_VERSION = 16 +MINOR_VERSION = 17 PATCH_VERSION = '0-dev' __short_version__ = f'{MAJOR_VERSION}.{MINOR_VERSION}' __version__ = f'{__short_version__}.{PATCH_VERSION}' From 93e35a53edb3d59b0263bdfb4812f3decf720b45 Mon Sep 17 00:00:00 2001 From: Alex <33379584+alexyao2015@users.noreply.github.com> Date: Fri, 8 Jan 2021 13:11:42 -0600 Subject: [PATCH 002/111] fix safe_mode (#1421) * Deprioritize automations Ensures safe mode is loaded before any automations are ran * Fix lint --- esphome/core_config.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/esphome/core_config.py b/esphome/core_config.py index f167eb8d8b..f1bb18eef5 100644 --- a/esphome/core_config.py +++ b/esphome/core_config.py @@ -209,11 +209,8 @@ def _esp8266_add_lwip_type(): cg.add_build_flag('-DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH') -@coroutine_with_priority(100.0) -def to_code(config): - cg.add_global(cg.global_ns.namespace('esphome').using) - cg.add(cg.App.pre_setup(config[CONF_NAME], cg.RawExpression('__DATE__ ", " __TIME__'))) - +@coroutine_with_priority(30.0) +def _add_automations(config): for conf in config.get(CONF_ON_BOOT, []): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], conf.get(CONF_PRIORITY)) yield cg.register_component(trigger, conf) @@ -229,6 +226,14 @@ def to_code(config): yield cg.register_component(trigger, conf) yield automation.build_automation(trigger, [], conf) + +@coroutine_with_priority(100.0) +def to_code(config): + cg.add_global(cg.global_ns.namespace('esphome').using) + cg.add(cg.App.pre_setup(config[CONF_NAME], cg.RawExpression('__DATE__ ", " __TIME__'))) + + CORE.add_job(_add_automations, config) + # Set LWIP build constants for ESP8266 if CORE.is_esp8266: CORE.add_job(_esp8266_add_lwip_type) From 699696e8d1de396c115e2362736429aa90bdb26f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=B6sch?= Date: Fri, 8 Jan 2021 23:40:22 +0100 Subject: [PATCH 003/111] DS1307 real time clock component (#1441) * initial support for DS1307 real time clock * add simple test, make sync functions public * cleanup lint * add sync to/from rtc actions * changes action names * Update esphome/components/ds1307/ds1307.cpp Co-authored-by: Guillermo Ruffino * Update esphome/components/ds1307/time.py Co-authored-by: Guillermo Ruffino * fix suggested change Co-authored-by: Guillermo Ruffino --- CODEOWNERS | 1 + esphome/components/ds1307/__init__.py | 0 esphome/components/ds1307/ds1307.cpp | 104 ++++++++++++++++++++++++++ esphome/components/ds1307/ds1307.h | 69 +++++++++++++++++ esphome/components/ds1307/time.py | 44 +++++++++++ tests/test1.yaml | 14 ++++ 6 files changed, 232 insertions(+) create mode 100644 esphome/components/ds1307/__init__.py create mode 100644 esphome/components/ds1307/ds1307.cpp create mode 100644 esphome/components/ds1307/ds1307.h create mode 100644 esphome/components/ds1307/time.py diff --git a/CODEOWNERS b/CODEOWNERS index 9cf1d30e19..95729bae8d 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -29,6 +29,7 @@ esphome/components/ct_clamp/* @jesserockz esphome/components/debug/* @OttoWinter esphome/components/dfplayer/* @glmnet esphome/components/dht/* @OttoWinter +esphome/components/ds1307/* @badbadc0ffee esphome/components/exposure_notifications/* @OttoWinter esphome/components/ezo/* @ssieb esphome/components/fastled_base/* @OttoWinter diff --git a/esphome/components/ds1307/__init__.py b/esphome/components/ds1307/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/ds1307/ds1307.cpp b/esphome/components/ds1307/ds1307.cpp new file mode 100644 index 0000000000..990767ddd4 --- /dev/null +++ b/esphome/components/ds1307/ds1307.cpp @@ -0,0 +1,104 @@ +#include "ds1307.h" +#include "esphome/core/log.h" + +// Datasheet: +// - https://datasheets.maximintegrated.com/en/ds/DS1307.pdf + +namespace esphome { +namespace ds1307 { + +static const char *TAG = "ds1307"; + +void DS1307Component::setup() { + ESP_LOGCONFIG(TAG, "Setting up DS1307..."); + if (!this->read_rtc_()) { + this->mark_failed(); + } + this->set_interval(15 * 60 * 1000, [&]() { this->read(); }); +} + +void DS1307Component::dump_config() { + ESP_LOGCONFIG(TAG, "DS1307:"); + LOG_I2C_DEVICE(this); + if (this->is_failed()) { + ESP_LOGE(TAG, "Communication with DS1307 failed!"); + } + ESP_LOGCONFIG(TAG, " Timezone: '%s'", this->timezone_.c_str()); +} + +float DS1307Component::get_setup_priority() const { return setup_priority::DATA; } + +void DS1307Component::read() { + if (!this->read_rtc_()) { + return; + } + if (ds1307_.reg.ch) { + ESP_LOGW(TAG, "RTC halted, not syncing to system clock."); + return; + } + time::ESPTime rtc_time{.second = uint8_t(ds1307_.reg.second + 10 * ds1307_.reg.second_10), + .minute = uint8_t(ds1307_.reg.minute + 10u * ds1307_.reg.minute_10), + .hour = uint8_t(ds1307_.reg.hour + 10u * ds1307_.reg.hour_10), + .day_of_week = uint8_t(ds1307_.reg.weekday), + .day_of_month = uint8_t(ds1307_.reg.day + 10u * ds1307_.reg.day_10), + .day_of_year = 1, // ignored by recalc_timestamp_utc(false) + .month = uint8_t(ds1307_.reg.month + 10u * ds1307_.reg.month_10), + .year = uint16_t(ds1307_.reg.year + 10u * ds1307_.reg.year_10 + 2000)}; + rtc_time.recalc_timestamp_utc(false); + if (!rtc_time.is_valid()) { + ESP_LOGE(TAG, "Invalid RTC time, not syncing to system clock."); + return; + } + time::RealTimeClock::synchronize_epoch_(rtc_time.timestamp); +} + +void DS1307Component::write() { + auto now = time::RealTimeClock::utcnow(); + if (!now.is_valid()) { + ESP_LOGE(TAG, "Invalid system time, not syncing to RTC."); + return; + } + ds1307_.reg.year = (now.year - 2000) % 10; + ds1307_.reg.year_10 = (now.year - 2000) / 10 % 10; + ds1307_.reg.month = now.month % 10; + ds1307_.reg.month_10 = now.month / 10; + ds1307_.reg.day = now.day_of_month % 10; + ds1307_.reg.day_10 = now.day_of_month / 10; + ds1307_.reg.weekday = now.day_of_week; + ds1307_.reg.hour = now.hour % 10; + ds1307_.reg.hour_10 = now.hour / 10; + ds1307_.reg.minute = now.minute % 10; + ds1307_.reg.minute_10 = now.minute / 10; + ds1307_.reg.second = now.second % 10; + ds1307_.reg.second_10 = now.second / 10; + ds1307_.reg.ch = false; + + this->write_rtc_(); +} + +bool DS1307Component::read_rtc_() { + if (!this->read_bytes(0, this->ds1307_.raw, sizeof(this->ds1307_.raw))) { + ESP_LOGE(TAG, "Can't read I2C data."); + return false; + } + ESP_LOGD(TAG, "Read %0u%0u:%0u%0u:%0u%0u 20%0u%0u-%0u%0u-%0u%0u CH:%s RS:%0u SQWE:%s OUT:%s", ds1307_.reg.hour_10, + ds1307_.reg.hour, ds1307_.reg.minute_10, ds1307_.reg.minute, ds1307_.reg.second_10, ds1307_.reg.second, + ds1307_.reg.year_10, ds1307_.reg.year, ds1307_.reg.month_10, ds1307_.reg.month, ds1307_.reg.day_10, + ds1307_.reg.day, ONOFF(ds1307_.reg.ch), ds1307_.reg.rs, ONOFF(ds1307_.reg.sqwe), ONOFF(ds1307_.reg.out)); + + return true; +} + +bool DS1307Component::write_rtc_() { + if (!this->write_bytes(0, this->ds1307_.raw, sizeof(this->ds1307_.raw))) { + ESP_LOGE(TAG, "Can't write I2C data."); + return false; + } + ESP_LOGD(TAG, "Write %0u%0u:%0u%0u:%0u%0u 20%0u%0u-%0u%0u-%0u%0u CH:%s RS:%0u SQWE:%s OUT:%s", ds1307_.reg.hour_10, + ds1307_.reg.hour, ds1307_.reg.minute_10, ds1307_.reg.minute, ds1307_.reg.second_10, ds1307_.reg.second, + ds1307_.reg.year_10, ds1307_.reg.year, ds1307_.reg.month_10, ds1307_.reg.month, ds1307_.reg.day_10, + ds1307_.reg.day, ONOFF(ds1307_.reg.ch), ds1307_.reg.rs, ONOFF(ds1307_.reg.sqwe), ONOFF(ds1307_.reg.out)); + return true; +} +} // namespace ds1307 +} // namespace esphome diff --git a/esphome/components/ds1307/ds1307.h b/esphome/components/ds1307/ds1307.h new file mode 100644 index 0000000000..1f732a7c7d --- /dev/null +++ b/esphome/components/ds1307/ds1307.h @@ -0,0 +1,69 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/i2c/i2c.h" +#include "esphome/components/time/real_time_clock.h" + +namespace esphome { +namespace ds1307 { + +class DS1307Component : public time::RealTimeClock, public i2c::I2CDevice { + public: + void setup() override; + void dump_config() override; + float get_setup_priority() const override; + void read(); + void write(); + + protected: + bool read_rtc_(); + bool write_rtc_(); + union DS1307Reg { + struct { + uint8_t second : 4; + uint8_t second_10 : 3; + bool ch : 1; + + uint8_t minute : 4; + uint8_t minute_10 : 3; + uint8_t unused_1 : 1; + + uint8_t hour : 4; + uint8_t hour_10 : 2; + uint8_t unused_2 : 2; + + uint8_t weekday : 3; + uint8_t unused_3 : 5; + + uint8_t day : 4; + uint8_t day_10 : 2; + uint8_t unused_4 : 2; + + uint8_t month : 4; + uint8_t month_10 : 1; + uint8_t unused_5 : 3; + + uint8_t year : 4; + uint8_t year_10 : 4; + + uint8_t rs : 2; + uint8_t unused_6 : 2; + bool sqwe : 1; + uint8_t unused_7 : 2; + bool out : 1; + } reg; + mutable uint8_t raw[sizeof(reg)]; + } ds1307_; +}; + +template class WriteAction : public Action, public Parented { + public: + void play(Ts... x) override { this->parent_->write(); } +}; + +template class ReadAction : public Action, public Parented { + public: + void play(Ts... x) override { this->parent_->read(); } +}; +} // namespace ds1307 +} // namespace esphome diff --git a/esphome/components/ds1307/time.py b/esphome/components/ds1307/time.py new file mode 100644 index 0000000000..c381fb7b2a --- /dev/null +++ b/esphome/components/ds1307/time.py @@ -0,0 +1,44 @@ +import esphome.config_validation as cv +import esphome.codegen as cg +from esphome import automation +from esphome.components import i2c, time +from esphome.const import CONF_ID + + +CODEOWNERS = ['@badbadc0ffee'] +DEPENDENCIES = ['i2c'] +ds1307_ns = cg.esphome_ns.namespace('ds1307') +DS1307Component = ds1307_ns.class_('DS1307Component', time.RealTimeClock, i2c.I2CDevice) +WriteAction = ds1307_ns.class_('WriteAction', automation.Action) +ReadAction = ds1307_ns.class_('ReadAction', automation.Action) + + +CONFIG_SCHEMA = time.TIME_SCHEMA.extend({ + cv.GenerateID(): cv.declare_id(DS1307Component), +}).extend(i2c.i2c_device_schema(0x68)) + + +@automation.register_action('ds1307.write', WriteAction, cv.Schema({ + cv.GenerateID(): cv.use_id(DS1307Component), +})) +def ds1307_write_to_code(config, action_id, template_arg, args): + var = cg.new_Pvariable(action_id, template_arg) + yield cg.register_parented(var, config[CONF_ID]) + yield var + + +@automation.register_action('ds1307.read', ReadAction, automation.maybe_simple_id({ + cv.GenerateID(): cv.use_id(DS1307Component), +})) +def ds1307_read_to_code(config, action_id, template_arg, args): + var = cg.new_Pvariable(action_id, template_arg) + yield cg.register_parented(var, config[CONF_ID]) + yield var + + +def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + + yield cg.register_component(var, config) + yield i2c.register_i2c_device(var, config) + yield time.register_time(var, config) diff --git a/tests/test1.yaml b/tests/test1.yaml index 0bccc3f57a..58fc7d4bb2 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -1837,6 +1837,20 @@ time: then: - lambda: 'ESP_LOGD("main", "time");' - platform: gps + on_time: + seconds: 0 + minutes: /15 + then: + ds1307.write: + id: ds1307_time + - platform: ds1307 + id: ds1307_time + on_time: + seconds: 0 + then: + ds1307.read + + cover: - platform: template From 5df398ec31f6e710ac87c0fe8d625a767c54c0b9 Mon Sep 17 00:00:00 2001 From: Dan Jackson Date: Sat, 9 Jan 2021 20:53:12 -0800 Subject: [PATCH 004/111] Add encode_uint32 method (#1427) --- esphome/components/api/proto.cpp | 3 +-- esphome/components/max31855/max31855.cpp | 2 +- esphome/components/rdm6300/rdm6300.cpp | 3 +-- esphome/components/tuya/tuya.cpp | 3 +-- esphome/components/xiaomi_ble/xiaomi_ble.cpp | 3 +-- esphome/core/helpers.cpp | 4 ++++ esphome/core/helpers.h | 2 ++ 7 files changed, 11 insertions(+), 9 deletions(-) diff --git a/esphome/components/api/proto.cpp b/esphome/components/api/proto.cpp index 3d2f669f54..4bd22af769 100644 --- a/esphome/components/api/proto.cpp +++ b/esphome/components/api/proto.cpp @@ -62,8 +62,7 @@ void ProtoMessage::decode(const uint8_t *buffer, size_t length) { error = true; break; } - uint32_t val = (uint32_t(buffer[i]) << 0) | (uint32_t(buffer[i + 1]) << 8) | (uint32_t(buffer[i + 2]) << 16) | - (uint32_t(buffer[i + 3]) << 24); + uint32_t val = encode_uint32(buffer[i + 3], buffer[i + 2], buffer[i + 1], buffer[i]); if (!this->decode_32bit(field_id, Proto32Bit(val))) { ESP_LOGV(TAG, "Cannot decode 32-bit field %u with value %u!", field_id, val); } diff --git a/esphome/components/max31855/max31855.cpp b/esphome/components/max31855/max31855.cpp index 7a0dc2427c..868cd4f16a 100644 --- a/esphome/components/max31855/max31855.cpp +++ b/esphome/components/max31855/max31855.cpp @@ -41,7 +41,7 @@ void MAX31855Sensor::read_data_() { this->read_array(data, 4); this->disable(); - const uint32_t mem = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3] << 0; + const uint32_t mem = encode_uint32(data[0], data[1], data[2], data[3]); // Verify we got data if (mem != 0xFFFFFFFF) { diff --git a/esphome/components/rdm6300/rdm6300.cpp b/esphome/components/rdm6300/rdm6300.cpp index 6c6f0c0311..2d2ba61824 100644 --- a/esphome/components/rdm6300/rdm6300.cpp +++ b/esphome/components/rdm6300/rdm6300.cpp @@ -46,8 +46,7 @@ void rdm6300::RDM6300Component::loop() { } else { // Valid data this->status_clear_warning(); - const uint32_t result = (uint32_t(this->buffer_[1]) << 24) | (uint32_t(this->buffer_[2]) << 16) | - (uint32_t(this->buffer_[3]) << 8) | this->buffer_[4]; + const uint32_t result = encode_uint32(this->buffer_[1], this->buffer_[2], this->buffer_[3], this->buffer_[4]); bool report = result != last_id_; for (auto *card : this->cards_) { if (card->process(result)) { diff --git a/esphome/components/tuya/tuya.cpp b/esphome/components/tuya/tuya.cpp index feaab9b0ed..2fd4b27937 100644 --- a/esphome/components/tuya/tuya.cpp +++ b/esphome/components/tuya/tuya.cpp @@ -281,8 +281,7 @@ void Tuya::handle_datapoint_(const uint8_t *buffer, size_t len) { case TuyaDatapointType::INTEGER: if (data_len != 4) return; - datapoint.value_uint = - (uint32_t(data[0]) << 24) | (uint32_t(data[1]) << 16) | (uint32_t(data[2]) << 8) | (uint32_t(data[3]) << 0); + datapoint.value_uint = encode_uint32(data[0], data[1], data[2], data[3]); break; case TuyaDatapointType::ENUM: if (data_len != 1) diff --git a/esphome/components/xiaomi_ble/xiaomi_ble.cpp b/esphome/components/xiaomi_ble/xiaomi_ble.cpp index 202620c06d..75af1e4b7c 100644 --- a/esphome/components/xiaomi_ble/xiaomi_ble.cpp +++ b/esphome/components/xiaomi_ble/xiaomi_ble.cpp @@ -70,8 +70,7 @@ bool parse_xiaomi_value(uint8_t value_type, const uint8_t *data, uint8_t value_l } // idle time since last motion, 4 byte, 32-bit unsigned integer, 1 min else if ((value_type == 0x17) && (value_length == 4)) { - const uint32_t idle_time = - uint32_t(data[0]) | (uint32_t(data[1]) << 8) | (uint32_t(data[2]) << 16) | (uint32_t(data[2]) << 24); + const uint32_t idle_time = encode_uint32(data[3], data[2], data[1], data[0]); result.idle_time = idle_time / 60.0f; result.has_motion = (idle_time) ? false : true; } else { diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index 38b80d85fb..f0e0c65f66 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -299,6 +299,10 @@ std::array decode_uint16(uint16_t value) { return {msb, lsb}; } +uint32_t encode_uint32(uint8_t msb, uint8_t byte2, uint8_t byte3, uint8_t lsb) { + return (uint32_t(msb) << 24) | (uint32_t(byte2) << 16) | (uint32_t(byte3) << 8) | uint32_t(lsb); +} + std::string hexencode(const uint8_t *data, uint32_t len) { char buf[20]; std::string res; diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index 0c660bdc8e..40e53e601e 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -132,6 +132,8 @@ uint32_t reverse_bits_32(uint32_t x); uint16_t encode_uint16(uint8_t msb, uint8_t lsb); /// Decode a 16-bit unsigned integer into an array of two values: most significant byte, least significant byte. std::array decode_uint16(uint16_t value); +/// Encode a 32-bit unsigned integer given four bytes in MSB -> LSB order +uint32_t encode_uint32(uint8_t msb, uint8_t byte2, uint8_t byte3, uint8_t lsb); /*** * An interrupt helper class. From 02dc49c272a3a7ba6160b679c20911d8e92fe3b9 Mon Sep 17 00:00:00 2001 From: mknjc Date: Sun, 10 Jan 2021 20:05:53 +0100 Subject: [PATCH 005/111] Rotary Encoder: Don't call callbacks in the isr (#1456) --- .../rotary_encoder/rotary_encoder.cpp | 51 ++++++++++++++++++- .../rotary_encoder/rotary_encoder.h | 13 +++-- 2 files changed, 58 insertions(+), 6 deletions(-) diff --git a/esphome/components/rotary_encoder/rotary_encoder.cpp b/esphome/components/rotary_encoder/rotary_encoder.cpp index 873aaf1971..0398489dca 100644 --- a/esphome/components/rotary_encoder/rotary_encoder.cpp +++ b/esphome/components/rotary_encoder/rotary_encoder.cpp @@ -90,16 +90,34 @@ void ICACHE_RAM_ATTR HOT RotaryEncoderSensorStore::gpio_intr(RotaryEncoderSensor if (arg->pin_b->digital_read()) input_state |= STATE_PIN_B_HIGH; + int8_t rotation_dir = 0; uint16_t new_state = STATE_LOOKUP_TABLE[input_state]; if ((new_state & arg->resolution & STATE_HAS_INCREMENTED) != 0) { if (arg->counter < arg->max_value) arg->counter++; - arg->on_clockwise_callback_.call(); + rotation_dir = 1; } if ((new_state & arg->resolution & STATE_HAS_DECREMENTED) != 0) { if (arg->counter > arg->min_value) arg->counter--; - arg->on_anticlockwise_callback_.call(); + rotation_dir = -1; + } + + if (rotation_dir != 0) { + auto first_zero = std::find(arg->rotation_events.begin(), arg->rotation_events.end(), 0); // find first zero + if (first_zero == arg->rotation_events.begin() // are we at the start (first event this loop iteration) + || std::signbit(*std::prev(first_zero)) != + std::signbit(rotation_dir) // or is the last stored event the wrong direction + || *std::prev(first_zero) == std::numeric_limits::lowest() // or the last event slot is full (negative) + || *std::prev(first_zero) == std::numeric_limits::max()) { // or the last event slot is full (positive) + if (first_zero != arg->rotation_events.end()) { // we have a free rotation slot + *first_zero += rotation_dir; // store the rotation into a new slot + } else { + arg->rotation_events_overflow = true; + } + } else { + *std::prev(first_zero) += rotation_dir; // store the rotation into the previous slot + } } arg->state = new_state; @@ -137,6 +155,35 @@ void RotaryEncoderSensor::dump_config() { } } void RotaryEncoderSensor::loop() { + std::array rotation_events; + bool rotation_events_overflow; + ets_intr_lock(); + rotation_events = this->store_.rotation_events; + rotation_events_overflow = this->store_.rotation_events_overflow; + + this->store_.rotation_events.fill(0); + this->store_.rotation_events_overflow = false; + ets_intr_unlock(); + + if (rotation_events_overflow) { + ESP_LOGW(TAG, "Captured more rotation events than expected"); + } + + for (auto events : rotation_events) { + if (events == 0) // we are at the end of the recorded events + break; + + if (events > 0) { + while (events--) { + this->on_clockwise_callback_.call(); + } + } else { + while (events++) { + this->on_anticlockwise_callback_.call(); + } + } + } + if (this->pin_i_ != nullptr && this->pin_i_->digital_read()) { this->store_.counter = 0; } diff --git a/esphome/components/rotary_encoder/rotary_encoder.h b/esphome/components/rotary_encoder/rotary_encoder.h index e066188f22..000350d66c 100644 --- a/esphome/components/rotary_encoder/rotary_encoder.h +++ b/esphome/components/rotary_encoder/rotary_encoder.h @@ -1,5 +1,7 @@ #pragma once +#include + #include "esphome/core/component.h" #include "esphome/core/esphal.h" #include "esphome/core/automation.h" @@ -27,8 +29,8 @@ struct RotaryEncoderSensorStore { int32_t last_read{0}; uint8_t state{0}; - CallbackManager on_clockwise_callback_; - CallbackManager on_anticlockwise_callback_; + std::array rotation_events{}; + bool rotation_events_overflow{false}; static void gpio_intr(RotaryEncoderSensorStore *arg); }; @@ -66,11 +68,11 @@ class RotaryEncoderSensor : public sensor::Sensor, public Component { float get_setup_priority() const override; void add_on_clockwise_callback(std::function callback) { - this->store_.on_clockwise_callback_.add(std::move(callback)); + this->on_clockwise_callback_.add(std::move(callback)); } void add_on_anticlockwise_callback(std::function callback) { - this->store_.on_anticlockwise_callback_.add(std::move(callback)); + this->on_anticlockwise_callback_.add(std::move(callback)); } protected: @@ -79,6 +81,9 @@ class RotaryEncoderSensor : public sensor::Sensor, public Component { GPIOPin *pin_i_{nullptr}; /// Index pin, if this is not nullptr, the counter will reset to 0 once this pin is HIGH. RotaryEncoderSensorStore store_{}; + + CallbackManager on_clockwise_callback_; + CallbackManager on_anticlockwise_callback_; }; template class RotaryEncoderSetValueAction : public Action { From 96ab6b51b89282c4c8f5b890440c27e75cddb5ba Mon Sep 17 00:00:00 2001 From: mknjc Date: Mon, 11 Jan 2021 14:46:21 +0100 Subject: [PATCH 006/111] API: copy the data to send into the tcp internal buffer (#1455) Without the flag lwip only holds a reference to the supplied buffers and the reference must be valid until the tcp ack is received. This can't be guaranteed for stack allocated buffers --- esphome/components/api/api_connection.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 1956f3119d..431be5b4dc 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -676,8 +676,10 @@ bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) } } - this->client_->add(reinterpret_cast(header.data()), header.size()); - this->client_->add(reinterpret_cast(buffer.get_buffer()->data()), buffer.get_buffer()->size()); + this->client_->add(reinterpret_cast(header.data()), header.size(), + ASYNC_WRITE_FLAG_COPY | ASYNC_WRITE_FLAG_MORE); + this->client_->add(reinterpret_cast(buffer.get_buffer()->data()), buffer.get_buffer()->size(), + ASYNC_WRITE_FLAG_COPY); bool ret = this->client_->send(); return ret; } From 86385a1c19cbb11dd062d251884c58b0c3bc81d1 Mon Sep 17 00:00:00 2001 From: Guillermo Ruffino Date: Mon, 11 Jan 2021 11:33:43 -0300 Subject: [PATCH 007/111] Revert esptool to 2.8 (#1460) Fixes https://github.com/esphome/issues/issues/1702 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 6ed3b1819f..78aa0442b9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,5 +10,5 @@ pytz==2020.5 pyserial==3.5 ifaddr==0.1.7 platformio==5.0.4 -esptool==3.0 +esptool==2.8 click==7.1.2 From 3c34b539b071f75a0294b805d70bbf403ffd7901 Mon Sep 17 00:00:00 2001 From: mmanza <40872469+mmanza@users.noreply.github.com> Date: Tue, 12 Jan 2021 09:51:38 -0300 Subject: [PATCH 008/111] Whirlpool ac (#1467) * Checksum calc change * first checksum change for MODEL_DG11J1_3A --- esphome/components/whirlpool/whirlpool.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/whirlpool/whirlpool.cpp b/esphome/components/whirlpool/whirlpool.cpp index d8db3b7353..eba08d5bbe 100644 --- a/esphome/components/whirlpool/whirlpool.cpp +++ b/esphome/components/whirlpool/whirlpool.cpp @@ -105,7 +105,7 @@ void WhirlpoolClimate::transmit_state() { } // Checksum - for (uint8_t i = 2; i < 12; i++) + for (uint8_t i = 2; i < 13; i++) remote_state[13] ^= remote_state[i]; for (uint8_t i = 14; i < 20; i++) remote_state[20] ^= remote_state[i]; @@ -184,7 +184,7 @@ bool WhirlpoolClimate::on_receive(remote_base::RemoteReceiveData data) { uint8_t checksum13 = 0; uint8_t checksum20 = 0; // Calculate checksum and compare with signal value. - for (uint8_t i = 2; i < 12; i++) + for (uint8_t i = 2; i < 13; i++) checksum13 ^= remote_state[i]; for (uint8_t i = 14; i < 20; i++) checksum20 ^= remote_state[i]; From 400819175d88c46d4f7d941bfaa7586dfb50da6c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Jan 2021 10:12:25 -0300 Subject: [PATCH 009/111] Bump pytest-mock from 3.3.1 to 3.5.1 (#1458) Bumps [pytest-mock](https://github.com/pytest-dev/pytest-mock) from 3.3.1 to 3.5.1. - [Release notes](https://github.com/pytest-dev/pytest-mock/releases) - [Changelog](https://github.com/pytest-dev/pytest-mock/blob/master/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest-mock/compare/v3.3.1...v3.5.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements_test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements_test.txt b/requirements_test.txt index 3733d025fa..864dfe1a72 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -7,6 +7,6 @@ pexpect==4.8.0 # Unit tests pytest==6.2.1 pytest-cov==2.10.1 -pytest-mock==3.3.1 +pytest-mock==3.5.1 asyncmock==0.4.2 hypothesis==5.21.0 From fbc1b3e316590776dc87dc90e1a98bb781517fde Mon Sep 17 00:00:00 2001 From: Guillermo Ruffino Date: Tue, 12 Jan 2021 10:13:53 -0300 Subject: [PATCH 010/111] Add rc522 i2c (#1432) * split to spi and i2c * fix binary_sensor * i2c comms ready * fix rc522_spi binary sensor compat * lint * lint * add test and codeowners * fix refactor --- CODEOWNERS | 2 + esphome/components/rc522/__init__.py | 38 + esphome/components/rc522/binary_sensor.py | 43 + esphome/components/rc522/rc522.cpp | 758 +++++++++++++++++ esphome/components/rc522/rc522.h | 284 +++++++ esphome/components/rc522_i2c/__init__.py | 22 + esphome/components/rc522_i2c/rc522_i2c.cpp | 99 +++ esphome/components/rc522_i2c/rc522_i2c.h | 42 + esphome/components/rc522_spi/__init__.py | 34 +- esphome/components/rc522_spi/binary_sensor.py | 43 +- esphome/components/rc522_spi/rc522_spi.cpp | 762 +----------------- esphome/components/rc522_spi/rc522_spi.h | 282 +------ tests/test1.yaml | 42 +- 13 files changed, 1359 insertions(+), 1092 deletions(-) create mode 100644 esphome/components/rc522/__init__.py create mode 100644 esphome/components/rc522/binary_sensor.py create mode 100644 esphome/components/rc522/rc522.cpp create mode 100644 esphome/components/rc522/rc522.h create mode 100644 esphome/components/rc522_i2c/__init__.py create mode 100644 esphome/components/rc522_i2c/rc522_i2c.cpp create mode 100644 esphome/components/rc522_i2c/rc522_i2c.h diff --git a/CODEOWNERS b/CODEOWNERS index 95729bae8d..fb287b1cc9 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -55,6 +55,8 @@ esphome/components/pn532/* @OttoWinter @jesserockz esphome/components/pn532_i2c/* @OttoWinter @jesserockz esphome/components/pn532_spi/* @OttoWinter @jesserockz esphome/components/power_supply/* @esphome/core +esphome/components/rc522/* @glmnet +esphome/components/rc522_i2c/* @glmnet esphome/components/rc522_spi/* @glmnet esphome/components/restart/* @esphome/core esphome/components/rf_bridge/* @jesserockz diff --git a/esphome/components/rc522/__init__.py b/esphome/components/rc522/__init__.py new file mode 100644 index 0000000000..7b4df37ce2 --- /dev/null +++ b/esphome/components/rc522/__init__.py @@ -0,0 +1,38 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome import automation, pins +from esphome.components import i2c +from esphome.const import CONF_ON_TAG, CONF_TRIGGER_ID, CONF_RESET_PIN +from esphome.core import coroutine + +CODEOWNERS = ['@glmnet'] +AUTO_LOAD = ['binary_sensor'] +MULTI_CONF = True + +CONF_RC522_ID = 'rc522_id' + +rc522_ns = cg.esphome_ns.namespace('rc522') +RC522 = rc522_ns.class_('RC522', cg.PollingComponent, i2c.I2CDevice) +RC522Trigger = rc522_ns.class_('RC522Trigger', automation.Trigger.template(cg.std_string)) + +RC522_SCHEMA = cv.Schema({ + cv.GenerateID(): cv.declare_id(RC522), + cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, + cv.Optional(CONF_ON_TAG): automation.validate_automation({ + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(RC522Trigger), + }), +}).extend(cv.polling_component_schema('1s')) + + +@coroutine +def setup_rc522(var, config): + yield cg.register_component(var, config) + + if CONF_RESET_PIN in config: + reset = yield cg.gpio_pin_expression(config[CONF_RESET_PIN]) + cg.add(var.set_reset_pin(reset)) + + for conf in config.get(CONF_ON_TAG, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID]) + cg.add(var.register_trigger(trigger)) + yield automation.build_automation(trigger, [(cg.std_string, 'x')], conf) diff --git a/esphome/components/rc522/binary_sensor.py b/esphome/components/rc522/binary_sensor.py new file mode 100644 index 0000000000..675db2f130 --- /dev/null +++ b/esphome/components/rc522/binary_sensor.py @@ -0,0 +1,43 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import binary_sensor +from esphome.const import CONF_UID, CONF_ID +from esphome.core import HexInt, coroutine +from . import rc522_ns, RC522, CONF_RC522_ID + +DEPENDENCIES = ['rc522'] + + +def validate_uid(value): + value = cv.string_strict(value) + for x in value.split('-'): + if len(x) != 2: + raise cv.Invalid("Each part (separated by '-') of the UID must be two characters " + "long.") + try: + x = int(x, 16) + except ValueError as err: + raise cv.Invalid("Valid characters for parts of a UID are 0123456789ABCDEF.") from err + if x < 0 or x > 255: + raise cv.Invalid("Valid values for UID parts (separated by '-') are 00 to FF") + return value + + +RC522BinarySensor = rc522_ns.class_('RC522BinarySensor', binary_sensor.BinarySensor) + +CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({ + cv.GenerateID(): cv.declare_id(RC522BinarySensor), + cv.GenerateID(CONF_RC522_ID): cv.use_id(RC522), + cv.Required(CONF_UID): validate_uid, +}) + + +@coroutine +def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + yield binary_sensor.register_binary_sensor(var, config) + + hub = yield cg.get_variable(config[CONF_RC522_ID]) + cg.add(hub.register_tag(var)) + addr = [HexInt(int(x, 16)) for x in config[CONF_UID].split('-')] + cg.add(var.set_uid(addr)) diff --git a/esphome/components/rc522/rc522.cpp b/esphome/components/rc522/rc522.cpp new file mode 100644 index 0000000000..ff8d18e3ea --- /dev/null +++ b/esphome/components/rc522/rc522.cpp @@ -0,0 +1,758 @@ +#include "rc522.h" +#include "esphome/core/log.h" + +// Based on: +// - https://github.com/miguelbalboa/rfid + +namespace esphome { +namespace rc522 { + +static const char *TAG = "rc522"; + +static const uint8_t RESET_COUNT = 5; + +void format_uid(char *buf, const uint8_t *uid, uint8_t uid_length) { + int offset = 0; + for (uint8_t i = 0; i < uid_length; i++) { + const char *format = "%02X"; + if (i + 1 < uid_length) + format = "%02X-"; + offset += sprintf(buf + offset, format, uid[i]); + } +} + +void RC522::setup() { + initialize_pending_ = true; + // Pull device out of power down / reset state. + + // First set the resetPowerDownPin as digital input, to check the MFRC522 power down mode. + if (reset_pin_ != nullptr) { + reset_pin_->pin_mode(INPUT); + + if (reset_pin_->digital_read() == LOW) { // The MFRC522 chip is in power down mode. + ESP_LOGV(TAG, "Power down mode detected. Hard resetting..."); + reset_pin_->pin_mode(OUTPUT); // Now set the resetPowerDownPin as digital output. + reset_pin_->digital_write(LOW); // Make sure we have a clean LOW state. + delayMicroseconds(2); // 8.8.1 Reset timing requirements says about 100ns. Let us be generous: 2μsl + reset_pin_->digital_write(HIGH); // Exit power down mode. This triggers a hard reset. + // Section 8.8.2 in the datasheet says the oscillator start-up time is the start up time of the crystal + 37,74μs. + // Let us be generous: 50ms. + reset_timeout_ = millis(); + return; + } + } + + // Setup a soft reset + reset_count_ = RESET_COUNT; + reset_timeout_ = millis(); +} + +void RC522::initialize_() { + // Per originall code, wait 50 ms + if (millis() - reset_timeout_ < 50) + return; + + // Reset baud rates + ESP_LOGV(TAG, "Initialize"); + + pcd_write_register(TX_MODE_REG, 0x00); + pcd_write_register(RX_MODE_REG, 0x00); + // Reset ModWidthReg + pcd_write_register(MOD_WIDTH_REG, 0x26); + + // When communicating with a PICC we need a timeout if something goes wrong. + // f_timer = 13.56 MHz / (2*TPreScaler+1) where TPreScaler = [TPrescaler_Hi:TPrescaler_Lo]. + // TPrescaler_Hi are the four low bits in TModeReg. TPrescaler_Lo is TPrescalerReg. + pcd_write_register(T_MODE_REG, 0x80); // TAuto=1; timer starts automatically at the end of the transmission in all + // communication modes at all speeds + + // TPreScaler = TModeReg[3..0]:TPrescalerReg, ie 0x0A9 = 169 => f_timer=40kHz, ie a timer period of 25μs. + pcd_write_register(T_PRESCALER_REG, 0xA9); + pcd_write_register(T_RELOAD_REG_H, 0x03); // Reload timer with 0x3E8 = 1000, ie 25ms before timeout. + pcd_write_register(T_RELOAD_REG_L, 0xE8); + + // Default 0x00. Force a 100 % ASK modulation independent of the ModGsPReg register setting + pcd_write_register(TX_ASK_REG, 0x40); + pcd_write_register(MODE_REG, 0x3D); // Default 0x3F. Set the preset value for the CRC coprocessor for the CalcCRC + // command to 0x6363 (ISO 14443-3 part 6.2.4) + pcd_antenna_on_(); // Enable the antenna driver pins TX1 and TX2 (they were disabled by the reset) + + initialize_pending_ = false; +} + +void RC522::dump_config() { + ESP_LOGCONFIG(TAG, "RC522:"); + switch (this->error_code_) { + case NONE: + break; + case RESET_FAILED: + ESP_LOGE(TAG, "Reset command failed!"); + break; + } + + LOG_PIN(" RESET Pin: ", this->reset_pin_); + + LOG_UPDATE_INTERVAL(this); + + for (auto *child : this->binary_sensors_) { + LOG_BINARY_SENSOR(" ", "Tag", child); + } +} + +void RC522::loop() { + // First check reset is needed + if (reset_count_ > 0) { + pcd_reset_(); + return; + } + if (initialize_pending_) { + initialize_(); + return; + } + + if (millis() - update_wait_ < this->update_interval_) + return; + + auto status = picc_is_new_card_present_(); + + static StatusCode LAST_STATUS = StatusCode::STATUS_OK; + + if (status != LAST_STATUS) { + ESP_LOGD(TAG, "Status is now: %d", status); + LAST_STATUS = status; + } + + if (status == STATUS_ERROR) // No card + { + // ESP_LOGE(TAG, "Error"); + // mark_failed(); + return; + } + + if (status != STATUS_OK) // We can receive STATUS_TIMEOUT when no card, or unexpected status. + return; + + // Try process card + if (!picc_read_card_serial_()) { + ESP_LOGW(TAG, "Requesting tag read failed!"); + return; + }; + + if (uid_.size < 4) { + return; + ESP_LOGW(TAG, "Read serial size: %d", uid_.size); + } + + update_wait_ = millis(); + + bool report = true; + // 1. Go through all triggers + for (auto *trigger : this->triggers_) + trigger->process(uid_.uiduint8_t, uid_.size); + + // 2. Find a binary sensor + for (auto *tag : this->binary_sensors_) { + if (tag->process(uid_.uiduint8_t, uid_.size)) { + // 2.1 if found, do not dump + report = false; + } + } + + if (report) { + char buf[32]; + format_uid(buf, uid_.uiduint8_t, uid_.size); + ESP_LOGD(TAG, "Found new tag '%s'", buf); + } +} + +void RC522::update() { + for (auto *obj : this->binary_sensors_) + obj->on_scan_end(); +} + +/** + * Performs a soft reset on the MFRC522 chip and waits for it to be ready again. + */ +void RC522::pcd_reset_() { + // The datasheet does not mention how long the SoftRest command takes to complete. + // But the MFRC522 might have been in soft power-down mode (triggered by bit 4 of CommandReg) + // Section 8.8.2 in the datasheet says the oscillator start-up time is the start up time of the crystal + 37,74μs. Let + // us be generous: 50ms. + + if (millis() - reset_timeout_ < 50) + return; + + if (reset_count_ == RESET_COUNT) { + ESP_LOGV(TAG, "Soft reset..."); + // Issue the SoftReset command. + pcd_write_register(COMMAND_REG, PCD_SOFT_RESET); + } + + // Expect the PowerDown bit in CommandReg to be cleared (max 3x50ms) + if ((pcd_read_register(COMMAND_REG) & (1 << 4)) == 0) { + reset_count_ = 0; + ESP_LOGI(TAG, "Device online."); + // Wait for initialize + reset_timeout_ = millis(); + return; + } + + if (--reset_count_ == 0) { + ESP_LOGE(TAG, "Unable to reset RC522."); + mark_failed(); + } +} + +/** + * Turns the antenna on by enabling pins TX1 and TX2. + * After a reset these pins are disabled. + */ +void RC522::pcd_antenna_on_() { + uint8_t value = pcd_read_register(TX_CONTROL_REG); + if ((value & 0x03) != 0x03) { + pcd_write_register(TX_CONTROL_REG, value | 0x03); + } +} + +/** + * Transmits a REQuest command, Type A. Invites PICCs in state IDLE to go to READY and prepare for anticollision or + * selection. 7 bit frame. Beware: When two PICCs are in the field at the same time I often get STATUS_TIMEOUT - + * probably due do bad antenna design. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +RC522::StatusCode RC522::picc_request_a_( + uint8_t *buffer_atqa, ///< The buffer to store the ATQA (Answer to request) in + uint8_t *buffer_size ///< Buffer size, at least two uint8_ts. Also number of uint8_ts returned if STATUS_OK. +) { + return picc_reqa_or_wupa_(PICC_CMD_REQA, buffer_atqa, buffer_size); +} + +/** + * Transmits REQA or WUPA commands. + * Beware: When two PICCs are in the field at the same time I often get STATUS_TIMEOUT - probably due do bad antenna + * design. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +RC522::StatusCode RC522::picc_reqa_or_wupa_( + uint8_t command, ///< The command to send - PICC_CMD_REQA or PICC_CMD_WUPA + uint8_t *buffer_atqa, ///< The buffer to store the ATQA (Answer to request) in + uint8_t *buffer_size ///< Buffer size, at least two uint8_ts. Also number of uint8_ts returned if STATUS_OK. +) { + uint8_t valid_bits; + RC522::StatusCode status; + + if (buffer_atqa == nullptr || *buffer_size < 2) { // The ATQA response is 2 uint8_ts long. + return STATUS_NO_ROOM; + } + pcd_clear_register_bit_mask_(COLL_REG, 0x80); // ValuesAfterColl=1 => Bits received after collision are cleared. + valid_bits = 7; // For REQA and WUPA we need the short frame format - transmit only 7 bits of the last (and only) + // uint8_t. TxLastBits = BitFramingReg[2..0] + status = pcd_transceive_data_(&command, 1, buffer_atqa, buffer_size, &valid_bits); + if (status != STATUS_OK) + return status; + if (*buffer_size != 2 || valid_bits != 0) { // ATQA must be exactly 16 bits. + ESP_LOGVV(TAG, "picc_reqa_or_wupa_() -> STATUS_ERROR"); + return STATUS_ERROR; + } + + return STATUS_OK; +} + +/** + * Sets the bits given in mask in register reg. + */ +void RC522::pcd_set_register_bit_mask_(PcdRegister reg, ///< The register to update. One of the PCD_Register enums. + uint8_t mask ///< The bits to set. +) { + uint8_t tmp = pcd_read_register(reg); + pcd_write_register(reg, tmp | mask); // set bit mask +} + +/** + * Clears the bits given in mask from register reg. + */ +void RC522::pcd_clear_register_bit_mask_(PcdRegister reg, ///< The register to update. One of the PCD_Register enums. + uint8_t mask ///< The bits to clear. +) { + uint8_t tmp = pcd_read_register(reg); + pcd_write_register(reg, tmp & (~mask)); // clear bit mask +} + +/** + * Executes the Transceive command. + * CRC validation can only be done if backData and backLen are specified. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +RC522::StatusCode RC522::pcd_transceive_data_( + uint8_t *send_data, ///< Pointer to the data to transfer to the FIFO. + uint8_t send_len, ///< Number of uint8_ts to transfer to the FIFO. + uint8_t *back_data, ///< nullptr or pointer to buffer if data should be read back after executing the command. + uint8_t *back_len, ///< In: Max number of uint8_ts to write to *backData. Out: The number of uint8_ts returned. + uint8_t + *valid_bits, ///< In/Out: The number of valid bits in the last uint8_t. 0 for 8 valid bits. Default nullptr. + uint8_t rx_align, ///< In: Defines the bit position in backData[0] for the first bit received. Default 0. + bool check_crc ///< In: True => The last two uint8_ts of the response is assumed to be a CRC_A that must be + ///< validated. +) { + uint8_t wait_i_rq = 0x30; // RxIRq and IdleIRq + auto ret = pcd_communicate_with_picc_(PCD_TRANSCEIVE, wait_i_rq, send_data, send_len, back_data, back_len, valid_bits, + rx_align, check_crc); + + if (ret == STATUS_OK && *back_len == 5) + ESP_LOGVV(TAG, "pcd_transceive_data_(..., %d, ) -> %d [%x, %x, %x, %x, %x]", send_len, ret, back_data[0], + back_data[1], back_data[2], back_data[3], back_data[4]); + else + ESP_LOGVV(TAG, "pcd_transceive_data_(..., %d, ... ) -> %d", send_len, ret); + return ret; +} + +/** + * Transfers data to the MFRC522 FIFO, executes a command, waits for completion and transfers data back from the FIFO. + * CRC validation can only be done if backData and backLen are specified. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +RC522::StatusCode RC522::pcd_communicate_with_picc_( + uint8_t command, ///< The command to execute. One of the PCD_Command enums. + uint8_t wait_i_rq, ///< The bits in the ComIrqReg register that signals successful completion of the command. + uint8_t *send_data, ///< Pointer to the data to transfer to the FIFO. + uint8_t send_len, ///< Number of uint8_ts to transfer to the FIFO. + uint8_t *back_data, ///< nullptr or pointer to buffer if data should be read back after executing the command. + uint8_t *back_len, ///< In: Max number of uint8_ts to write to *backData. Out: The number of uint8_ts returned. + uint8_t *valid_bits, ///< In/Out: The number of valid bits in the last uint8_t. 0 for 8 valid bits. + uint8_t rx_align, ///< In: Defines the bit position in backData[0] for the first bit received. Default 0. + bool check_crc ///< In: True => The last two uint8_ts of the response is assumed to be a CRC_A that must be + ///< validated. +) { + ESP_LOGVV(TAG, "pcd_communicate_with_picc_(%d, %d,... %d)", command, wait_i_rq, check_crc); + + // Prepare values for BitFramingReg + uint8_t tx_last_bits = valid_bits ? *valid_bits : 0; + uint8_t bit_framing = + (rx_align << 4) + tx_last_bits; // RxAlign = BitFramingReg[6..4]. TxLastBits = BitFramingReg[2..0] + + pcd_write_register(COMMAND_REG, PCD_IDLE); // Stop any active command. + pcd_write_register(COM_IRQ_REG, 0x7F); // Clear all seven interrupt request bits + pcd_write_register(FIFO_LEVEL_REG, 0x80); // FlushBuffer = 1, FIFO initialization + pcd_write_register(FIFO_DATA_REG, send_len, send_data); // Write sendData to the FIFO + pcd_write_register(BIT_FRAMING_REG, bit_framing); // Bit adjustments + pcd_write_register(COMMAND_REG, command); // Execute the command + if (command == PCD_TRANSCEIVE) { + pcd_set_register_bit_mask_(BIT_FRAMING_REG, 0x80); // StartSend=1, transmission of data starts + } + + // Wait for the command to complete. + // In PCD_Init() we set the TAuto flag in TModeReg. This means the timer automatically starts when the PCD stops + // transmitting. Each iteration of the do-while-loop takes 17.86μs. + // TODO check/modify for other architectures than Arduino Uno 16bit + uint16_t i; + for (i = 4; i > 0; i--) { + uint8_t n = pcd_read_register( + COM_IRQ_REG); // ComIrqReg[7..0] bits are: Set1 TxIRq RxIRq IdleIRq HiAlertIRq LoAlertIRq ErrIRq TimerIRq + if (n & wait_i_rq) { // One of the interrupts that signal success has been set. + break; + } + if (n & 0x01) { // Timer interrupt - nothing received in 25ms + return STATUS_TIMEOUT; + } + } + // 35.7ms and nothing happend. Communication with the MFRC522 might be down. + if (i == 0) { + return STATUS_TIMEOUT; + } + + // Stop now if any errors except collisions were detected. + uint8_t error_reg_value = pcd_read_register( + ERROR_REG); // ErrorReg[7..0] bits are: WrErr TempErr reserved BufferOvfl CollErr CRCErr ParityErr ProtocolErr + if (error_reg_value & 0x13) { // BufferOvfl ParityErr ProtocolErr + return STATUS_ERROR; + } + + uint8_t valid_bits_local = 0; + + // If the caller wants data back, get it from the MFRC522. + if (back_data && back_len) { + uint8_t n = pcd_read_register(FIFO_LEVEL_REG); // Number of uint8_ts in the FIFO + if (n > *back_len) { + return STATUS_NO_ROOM; + } + *back_len = n; // Number of uint8_ts returned + pcd_read_register(FIFO_DATA_REG, n, back_data, rx_align); // Get received data from FIFO + valid_bits_local = + pcd_read_register(CONTROL_REG) & 0x07; // RxLastBits[2:0] indicates the number of valid bits in the last + // received uint8_t. If this value is 000b, the whole uint8_t is valid. + if (valid_bits) { + *valid_bits = valid_bits_local; + } + } + + // Tell about collisions + if (error_reg_value & 0x08) { // CollErr + return STATUS_COLLISION; + } + + // Perform CRC_A validation if requested. + if (back_data && back_len && check_crc) { + // In this case a MIFARE Classic NAK is not OK. + if (*back_len == 1 && valid_bits_local == 4) { + return STATUS_MIFARE_NACK; + } + // We need at least the CRC_A value and all 8 bits of the last uint8_t must be received. + if (*back_len < 2 || valid_bits_local != 0) { + return STATUS_CRC_WRONG; + } + // Verify CRC_A - do our own calculation and store the control in controlBuffer. + uint8_t control_buffer[2]; + RC522::StatusCode status = pcd_calculate_crc_(&back_data[0], *back_len - 2, &control_buffer[0]); + if (status != STATUS_OK) { + return status; + } + if ((back_data[*back_len - 2] != control_buffer[0]) || (back_data[*back_len - 1] != control_buffer[1])) { + return STATUS_CRC_WRONG; + } + } + + return STATUS_OK; +} + +/** + * Use the CRC coprocessor in the MFRC522 to calculate a CRC_A. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ + +RC522::StatusCode RC522::pcd_calculate_crc_( + uint8_t *data, ///< In: Pointer to the data to transfer to the FIFO for CRC calculation. + uint8_t length, ///< In: The number of uint8_ts to transfer. + uint8_t *result ///< Out: Pointer to result buffer. Result is written to result[0..1], low uint8_t first. +) { + ESP_LOGVV(TAG, "pcd_calculate_crc_(..., %d, ...)", length); + pcd_write_register(COMMAND_REG, PCD_IDLE); // Stop any active command. + pcd_write_register(DIV_IRQ_REG, 0x04); // Clear the CRCIRq interrupt request bit + pcd_write_register(FIFO_LEVEL_REG, 0x80); // FlushBuffer = 1, FIFO initialization + pcd_write_register(FIFO_DATA_REG, length, data); // Write data to the FIFO + pcd_write_register(COMMAND_REG, PCD_CALC_CRC); // Start the calculation + + // Wait for the CRC calculation to complete. Each iteration of the while-loop takes 17.73μs. + // TODO check/modify for other architectures than Arduino Uno 16bit + + // Wait for the CRC calculation to complete. Each iteration of the while-loop takes 17.73us. + for (uint16_t i = 5000; i > 0; i--) { + // DivIrqReg[7..0] bits are: Set2 reserved reserved MfinActIRq reserved CRCIRq reserved reserved + uint8_t n = pcd_read_register(DIV_IRQ_REG); + if (n & 0x04) { // CRCIRq bit set - calculation done + pcd_write_register(COMMAND_REG, PCD_IDLE); // Stop calculating CRC for new content in the FIFO. + // Transfer the result from the registers to the result buffer + result[0] = pcd_read_register(CRC_RESULT_REG_L); + result[1] = pcd_read_register(CRC_RESULT_REG_H); + + ESP_LOGVV(TAG, "pcd_calculate_crc_() STATUS_OK"); + return STATUS_OK; + } + } + ESP_LOGVV(TAG, "pcd_calculate_crc_() TIMEOUT"); + // 89ms passed and nothing happend. Communication with the MFRC522 might be down. + return STATUS_TIMEOUT; +} +/** + * Returns STATUS_OK if a PICC responds to PICC_CMD_REQA. + * Only "new" cards in state IDLE are invited. Sleeping cards in state HALT are ignored. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ + +RC522::StatusCode RC522::picc_is_new_card_present_() { + uint8_t buffer_atqa[2]; + uint8_t buffer_size = sizeof(buffer_atqa); + + // Reset baud rates + pcd_write_register(TX_MODE_REG, 0x00); + pcd_write_register(RX_MODE_REG, 0x00); + // Reset ModWidthReg + pcd_write_register(MOD_WIDTH_REG, 0x26); + + auto result = picc_request_a_(buffer_atqa, &buffer_size); + + ESP_LOGV(TAG, "picc_is_new_card_present_() -> %d", result); + return result; +} + +/** + * Simple wrapper around PICC_Select. + * Returns true if a UID could be read. + * Remember to call PICC_IsNewCardPresent(), PICC_RequestA() or PICC_WakeupA() first. + * The read UID is available in the class variable uid. + * + * @return bool + */ +bool RC522::picc_read_card_serial_() { + RC522::StatusCode result = picc_select_(&this->uid_); + ESP_LOGVV(TAG, "picc_select_(...) -> %d", result); + return (result == STATUS_OK); +} + +/** + * Transmits SELECT/ANTICOLLISION commands to select a single PICC. + * Before calling this function the PICCs must be placed in the READY(*) state by calling PICC_RequestA() or + * PICC_WakeupA(). On success: + * - The chosen PICC is in state ACTIVE(*) and all other PICCs have returned to state IDLE/HALT. (Figure 7 of the + * ISO/IEC 14443-3 draft.) + * - The UID size and value of the chosen PICC is returned in *uid along with the SAK. + * + * A PICC UID consists of 4, 7 or 10 uint8_ts. + * Only 4 uint8_ts can be specified in a SELECT command, so for the longer UIDs two or three iterations are used: + * UID size Number of UID uint8_ts Cascade levels Example of PICC + * ======== =================== ============== =============== + * single 4 1 MIFARE Classic + * double 7 2 MIFARE Ultralight + * triple 10 3 Not currently in use? + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +RC522::StatusCode RC522::picc_select_( + Uid *uid, ///< Pointer to Uid struct. Normally output, but can also be used to supply a known UID. + uint8_t valid_bits ///< The number of known UID bits supplied in *uid. Normally 0. If set you must also supply + ///< uid->size. +) { + bool uid_complete; + bool select_done; + bool use_cascade_tag; + uint8_t cascade_level = 1; + RC522::StatusCode result; + uint8_t count; + uint8_t check_bit; + uint8_t index; + uint8_t uid_index; // The first index in uid->uiduint8_t[] that is used in the current Cascade Level. + int8_t current_level_known_bits; // The number of known UID bits in the current Cascade Level. + uint8_t buffer[9]; // The SELECT/ANTICOLLISION commands uses a 7 uint8_t standard frame + 2 uint8_ts CRC_A + uint8_t buffer_used; // The number of uint8_ts used in the buffer, ie the number of uint8_ts to transfer to the FIFO. + uint8_t rx_align; // Used in BitFramingReg. Defines the bit position for the first bit received. + uint8_t tx_last_bits; // Used in BitFramingReg. The number of valid bits in the last transmitted uint8_t. + uint8_t *response_buffer; + uint8_t response_length; + + // Description of buffer structure: + // uint8_t 0: SEL Indicates the Cascade Level: PICC_CMD_SEL_CL1, PICC_CMD_SEL_CL2 or PICC_CMD_SEL_CL3 + // uint8_t 1: NVB Number of Valid Bits (in complete command, not just the UID): High nibble: complete + // uint8_ts, + // Low nibble: Extra bits. uint8_t 2: UID-data or CT See explanation below. CT means Cascade Tag. uint8_t + // 3: UID-data uint8_t 4: UID-data uint8_t 5: UID-data uint8_t 6: BCC Block Check Character - XOR of + // uint8_ts 2-5 uint8_t 7: CRC_A uint8_t 8: CRC_A The BCC and CRC_A are only transmitted if we know all the UID bits + // of the current Cascade Level. + // + // Description of uint8_ts 2-5: (Section 6.5.4 of the ISO/IEC 14443-3 draft: UID contents and cascade levels) + // UID size Cascade level uint8_t2 uint8_t3 uint8_t4 uint8_t5 + // ======== ============= ===== ===== ===== ===== + // 4 uint8_ts 1 uid0 uid1 uid2 uid3 + // 7 uint8_ts 1 CT uid0 uid1 uid2 + // 2 uid3 uid4 uid5 uid6 + // 10 uint8_ts 1 CT uid0 uid1 uid2 + // 2 CT uid3 uid4 uid5 + // 3 uid6 uid7 uid8 uid9 + + // Sanity checks + if (valid_bits > 80) { + return STATUS_INVALID; + } + + ESP_LOGVV(TAG, "picc_select_(&, %d)", valid_bits); + + // Prepare MFRC522 + pcd_clear_register_bit_mask_(COLL_REG, 0x80); // ValuesAfterColl=1 => Bits received after collision are cleared. + + // Repeat Cascade Level loop until we have a complete UID. + uid_complete = false; + while (!uid_complete) { + // Set the Cascade Level in the SEL uint8_t, find out if we need to use the Cascade Tag in uint8_t 2. + switch (cascade_level) { + case 1: + buffer[0] = PICC_CMD_SEL_CL1; + uid_index = 0; + use_cascade_tag = valid_bits && uid->size > 4; // When we know that the UID has more than 4 uint8_ts + break; + + case 2: + buffer[0] = PICC_CMD_SEL_CL2; + uid_index = 3; + use_cascade_tag = valid_bits && uid->size > 7; // When we know that the UID has more than 7 uint8_ts + break; + + case 3: + buffer[0] = PICC_CMD_SEL_CL3; + uid_index = 6; + use_cascade_tag = false; // Never used in CL3. + break; + + default: + return STATUS_INTERNAL_ERROR; + break; + } + + // How many UID bits are known in this Cascade Level? + current_level_known_bits = valid_bits - (8 * uid_index); + if (current_level_known_bits < 0) { + current_level_known_bits = 0; + } + // Copy the known bits from uid->uiduint8_t[] to buffer[] + index = 2; // destination index in buffer[] + if (use_cascade_tag) { + buffer[index++] = PICC_CMD_CT; + } + uint8_t uint8_ts_to_copy = current_level_known_bits / 8 + + (current_level_known_bits % 8 + ? 1 + : 0); // The number of uint8_ts needed to represent the known bits for this level. + if (uint8_ts_to_copy) { + uint8_t maxuint8_ts = + use_cascade_tag ? 3 : 4; // Max 4 uint8_ts in each Cascade Level. Only 3 left if we use the Cascade Tag + if (uint8_ts_to_copy > maxuint8_ts) { + uint8_ts_to_copy = maxuint8_ts; + } + for (count = 0; count < uint8_ts_to_copy; count++) { + buffer[index++] = uid->uiduint8_t[uid_index + count]; + } + } + // Now that the data has been copied we need to include the 8 bits in CT in currentLevelKnownBits + if (use_cascade_tag) { + current_level_known_bits += 8; + } + + // Repeat anti collision loop until we can transmit all UID bits + BCC and receive a SAK - max 32 iterations. + select_done = false; + while (!select_done) { + // Find out how many bits and uint8_ts to send and receive. + if (current_level_known_bits >= 32) { // All UID bits in this Cascade Level are known. This is a SELECT. + + if (response_length < 4) { + ESP_LOGW(TAG, "Not enough data received."); + return STATUS_INVALID; + } + + // Serial.print(F("SELECT: currentLevelKnownBits=")); Serial.println(currentLevelKnownBits, DEC); + buffer[1] = 0x70; // NVB - Number of Valid Bits: Seven whole uint8_ts + // Calculate BCC - Block Check Character + buffer[6] = buffer[2] ^ buffer[3] ^ buffer[4] ^ buffer[5]; + // Calculate CRC_A + result = pcd_calculate_crc_(buffer, 7, &buffer[7]); + if (result != STATUS_OK) { + return result; + } + tx_last_bits = 0; // 0 => All 8 bits are valid. + buffer_used = 9; + // Store response in the last 3 uint8_ts of buffer (BCC and CRC_A - not needed after tx) + response_buffer = &buffer[6]; + response_length = 3; + } else { // This is an ANTICOLLISION. + // Serial.print(F("ANTICOLLISION: currentLevelKnownBits=")); Serial.println(currentLevelKnownBits, DEC); + tx_last_bits = current_level_known_bits % 8; + count = current_level_known_bits / 8; // Number of whole uint8_ts in the UID part. + index = 2 + count; // Number of whole uint8_ts: SEL + NVB + UIDs + buffer[1] = (index << 4) + tx_last_bits; // NVB - Number of Valid Bits + buffer_used = index + (tx_last_bits ? 1 : 0); + // Store response in the unused part of buffer + response_buffer = &buffer[index]; + response_length = sizeof(buffer) - index; + } + + // Set bit adjustments + rx_align = tx_last_bits; // Having a separate variable is overkill. But it makes the next line easier to read. + pcd_write_register( + BIT_FRAMING_REG, + (rx_align << 4) + tx_last_bits); // RxAlign = BitFramingReg[6..4]. TxLastBits = BitFramingReg[2..0] + + // Transmit the buffer and receive the response. + result = pcd_transceive_data_(buffer, buffer_used, response_buffer, &response_length, &tx_last_bits, rx_align); + if (result == STATUS_COLLISION) { // More than one PICC in the field => collision. + uint8_t value_of_coll_reg = pcd_read_register( + COLL_REG); // CollReg[7..0] bits are: ValuesAfterColl reserved CollPosNotValid CollPos[4:0] + if (value_of_coll_reg & 0x20) { // CollPosNotValid + return STATUS_COLLISION; // Without a valid collision position we cannot continue + } + uint8_t collision_pos = value_of_coll_reg & 0x1F; // Values 0-31, 0 means bit 32. + if (collision_pos == 0) { + collision_pos = 32; + } + if (collision_pos <= current_level_known_bits) { // No progress - should not happen + return STATUS_INTERNAL_ERROR; + } + // Choose the PICC with the bit set. + current_level_known_bits = collision_pos; + count = current_level_known_bits % 8; // The bit to modify + check_bit = (current_level_known_bits - 1) % 8; + index = 1 + (current_level_known_bits / 8) + (count ? 1 : 0); // First uint8_t is index 0. + if (response_length > 2) // Note: Otherwise buffer[index] might be not initialized + buffer[index] |= (1 << check_bit); + } else if (result != STATUS_OK) { + return result; + } else { // STATUS_OK + if (current_level_known_bits >= 32) { // This was a SELECT. + select_done = true; // No more anticollision + // We continue below outside the while. + } else { // This was an ANTICOLLISION. + // We now have all 32 bits of the UID in this Cascade Level + current_level_known_bits = 32; + // Run loop again to do the SELECT. + } + } + } // End of while (!selectDone) + + // We do not check the CBB - it was constructed by us above. + + // Copy the found UID uint8_ts from buffer[] to uid->uiduint8_t[] + index = (buffer[2] == PICC_CMD_CT) ? 3 : 2; // source index in buffer[] + uint8_ts_to_copy = (buffer[2] == PICC_CMD_CT) ? 3 : 4; + for (count = 0; count < uint8_ts_to_copy; count++) { + uid->uiduint8_t[uid_index + count] = buffer[index++]; + } + + // Check response SAK (Select Acknowledge) + if (response_length != 3 || tx_last_bits != 0) { // SAK must be exactly 24 bits (1 uint8_t + CRC_A). + return STATUS_ERROR; + } + // Verify CRC_A - do our own calculation and store the control in buffer[2..3] - those uint8_ts are not needed + // anymore. + result = pcd_calculate_crc_(response_buffer, 1, &buffer[2]); + if (result != STATUS_OK) { + return result; + } + if ((buffer[2] != response_buffer[1]) || (buffer[3] != response_buffer[2])) { + return STATUS_CRC_WRONG; + } + if (response_buffer[0] & 0x04) { // Cascade bit set - UID not complete yes + cascade_level++; + } else { + uid_complete = true; + uid->sak = response_buffer[0]; + } + } // End of while (!uidComplete) + + // Set correct uid->size + uid->size = 3 * cascade_level + 1; + + return STATUS_OK; +} + +bool RC522BinarySensor::process(const uint8_t *data, uint8_t len) { + if (len != this->uid_.size()) + return false; + + for (uint8_t i = 0; i < len; i++) { + if (data[i] != this->uid_[i]) + return false; + } + + this->publish_state(true); + this->found_ = true; + return true; +} +void RC522Trigger::process(const uint8_t *uid, uint8_t uid_length) { + char buf[32]; + format_uid(buf, uid, uid_length); + this->trigger(std::string(buf)); +} + +} // namespace rc522 +} // namespace esphome diff --git a/esphome/components/rc522/rc522.h b/esphome/components/rc522/rc522.h new file mode 100644 index 0000000000..cabcf8db0b --- /dev/null +++ b/esphome/components/rc522/rc522.h @@ -0,0 +1,284 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/core/automation.h" +#include "esphome/components/binary_sensor/binary_sensor.h" + +namespace esphome { +namespace rc522 { + +class RC522BinarySensor; +class RC522Trigger; +class RC522 : public PollingComponent { + public: + void setup() override; + + void dump_config() override; + + void update() override; + float get_setup_priority() const override { return setup_priority::DATA; }; + + void loop() override; + + void register_tag(RC522BinarySensor *tag) { this->binary_sensors_.push_back(tag); } + void register_trigger(RC522Trigger *trig) { this->triggers_.push_back(trig); } + + void set_reset_pin(GPIOPin *reset) { this->reset_pin_ = reset; } + + protected: + enum PcdRegister : uint8_t { + // Page 0: Command and status + // 0x00 // reserved for future use + COMMAND_REG = 0x01 << 1, // starts and stops command execution + COM_I_EN_REG = 0x02 << 1, // enable and disable interrupt request control bits + DIV_I_EN_REG = 0x03 << 1, // enable and disable interrupt request control bits + COM_IRQ_REG = 0x04 << 1, // interrupt request bits + DIV_IRQ_REG = 0x05 << 1, // interrupt request bits + ERROR_REG = 0x06 << 1, // error bits showing the error status of the last command executed + STATUS1_REG = 0x07 << 1, // communication status bits + STATUS2_REG = 0x08 << 1, // receiver and transmitter status bits + FIFO_DATA_REG = 0x09 << 1, // input and output of 64 uint8_t FIFO buffer + FIFO_LEVEL_REG = 0x0A << 1, // number of uint8_ts stored in the FIFO buffer + WATER_LEVEL_REG = 0x0B << 1, // level for FIFO underflow and overflow warning + CONTROL_REG = 0x0C << 1, // miscellaneous control registers + BIT_FRAMING_REG = 0x0D << 1, // adjustments for bit-oriented frames + COLL_REG = 0x0E << 1, // bit position of the first bit-collision detected on the RF interface + // 0x0F // reserved for future use + + // Page 1: Command + // 0x10 // reserved for future use + MODE_REG = 0x11 << 1, // defines general modes for transmitting and receiving + TX_MODE_REG = 0x12 << 1, // defines transmission data rate and framing + RX_MODE_REG = 0x13 << 1, // defines reception data rate and framing + TX_CONTROL_REG = 0x14 << 1, // controls the logical behavior of the antenna driver pins TX1 and TX2 + TX_ASK_REG = 0x15 << 1, // controls the setting of the transmission modulation + TX_SEL_REG = 0x16 << 1, // selects the internal sources for the antenna driver + RX_SEL_REG = 0x17 << 1, // selects internal receiver settings + RX_THRESHOLD_REG = 0x18 << 1, // selects thresholds for the bit decoder + DEMOD_REG = 0x19 << 1, // defines demodulator settings + // 0x1A // reserved for future use + // 0x1B // reserved for future use + MF_TX_REG = 0x1C << 1, // controls some MIFARE communication transmit parameters + MF_RX_REG = 0x1D << 1, // controls some MIFARE communication receive parameters + // 0x1E // reserved for future use + SERIAL_SPEED_REG = 0x1F << 1, // selects the speed of the serial UART interface + + // Page 2: Configuration + // 0x20 // reserved for future use + CRC_RESULT_REG_H = 0x21 << 1, // shows the MSB and LSB values of the CRC calculation + CRC_RESULT_REG_L = 0x22 << 1, + // 0x23 // reserved for future use + MOD_WIDTH_REG = 0x24 << 1, // controls the ModWidth setting? + // 0x25 // reserved for future use + RF_CFG_REG = 0x26 << 1, // configures the receiver gain + GS_N_REG = 0x27 << 1, // selects the conductance of the antenna driver pins TX1 and TX2 for modulation + CW_GS_P_REG = 0x28 << 1, // defines the conductance of the p-driver output during periods of no modulation + MOD_GS_P_REG = 0x29 << 1, // defines the conductance of the p-driver output during periods of modulation + T_MODE_REG = 0x2A << 1, // defines settings for the internal timer + T_PRESCALER_REG = 0x2B << 1, // the lower 8 bits of the TPrescaler value. The 4 high bits are in TModeReg. + T_RELOAD_REG_H = 0x2C << 1, // defines the 16-bit timer reload value + T_RELOAD_REG_L = 0x2D << 1, + T_COUNTER_VALUE_REG_H = 0x2E << 1, // shows the 16-bit timer value + T_COUNTER_VALUE_REG_L = 0x2F << 1, + + // Page 3: Test Registers + // 0x30 // reserved for future use + TEST_SEL1_REG = 0x31 << 1, // general test signal configuration + TEST_SEL2_REG = 0x32 << 1, // general test signal configuration + TEST_PIN_EN_REG = 0x33 << 1, // enables pin output driver on pins D1 to D7 + TEST_PIN_VALUE_REG = 0x34 << 1, // defines the values for D1 to D7 when it is used as an I/O bus + TEST_BUS_REG = 0x35 << 1, // shows the status of the internal test bus + AUTO_TEST_REG = 0x36 << 1, // controls the digital self-test + VERSION_REG = 0x37 << 1, // shows the software version + ANALOG_TEST_REG = 0x38 << 1, // controls the pins AUX1 and AUX2 + TEST_DA_C1_REG = 0x39 << 1, // defines the test value for TestDAC1 + TEST_DA_C2_REG = 0x3A << 1, // defines the test value for TestDAC2 + TEST_ADC_REG = 0x3B << 1 // shows the value of ADC I and Q channels + // 0x3C // reserved for production tests + // 0x3D // reserved for production tests + // 0x3E // reserved for production tests + // 0x3F // reserved for production tests + }; + + // MFRC522 commands. Described in chapter 10 of the datasheet. + enum PcdCommand : uint8_t { + PCD_IDLE = 0x00, // no action, cancels current command execution + PCD_MEM = 0x01, // stores 25 uint8_ts into the internal buffer + PCD_GENERATE_RANDOM_ID = 0x02, // generates a 10-uint8_t random ID number + PCD_CALC_CRC = 0x03, // activates the CRC coprocessor or performs a self-test + PCD_TRANSMIT = 0x04, // transmits data from the FIFO buffer + PCD_NO_CMD_CHANGE = 0x07, // no command change, can be used to modify the CommandReg register bits without + // affecting the command, for example, the PowerDown bit + PCD_RECEIVE = 0x08, // activates the receiver circuits + PCD_TRANSCEIVE = + 0x0C, // transmits data from FIFO buffer to antenna and automatically activates the receiver after transmission + PCD_MF_AUTHENT = 0x0E, // performs the MIFARE standard authentication as a reader + PCD_SOFT_RESET = 0x0F // resets the MFRC522 + }; + + // Commands sent to the PICC. + enum PiccCommand : uint8_t { + // The commands used by the PCD to manage communication with several PICCs (ISO 14443-3, Type A, section 6.4) + PICC_CMD_REQA = 0x26, // REQuest command, Type A. Invites PICCs in state IDLE to go to READY and prepare for + // anticollision or selection. 7 bit frame. + PICC_CMD_WUPA = 0x52, // Wake-UP command, Type A. Invites PICCs in state IDLE and HALT to go to READY(*) and + // prepare for anticollision or selection. 7 bit frame. + PICC_CMD_CT = 0x88, // Cascade Tag. Not really a command, but used during anti collision. + PICC_CMD_SEL_CL1 = 0x93, // Anti collision/Select, Cascade Level 1 + PICC_CMD_SEL_CL2 = 0x95, // Anti collision/Select, Cascade Level 2 + PICC_CMD_SEL_CL3 = 0x97, // Anti collision/Select, Cascade Level 3 + PICC_CMD_HLTA = 0x50, // HaLT command, Type A. Instructs an ACTIVE PICC to go to state HALT. + PICC_CMD_RATS = 0xE0, // Request command for Answer To Reset. + // The commands used for MIFARE Classic (from http://www.mouser.com/ds/2/302/MF1S503x-89574.pdf, Section 9) + // Use PCD_MFAuthent to authenticate access to a sector, then use these commands to read/write/modify the blocks on + // the sector. + // The read/write commands can also be used for MIFARE Ultralight. + PICC_CMD_MF_AUTH_KEY_A = 0x60, // Perform authentication with Key A + PICC_CMD_MF_AUTH_KEY_B = 0x61, // Perform authentication with Key B + PICC_CMD_MF_READ = + 0x30, // Reads one 16 uint8_t block from the authenticated sector of the PICC. Also used for MIFARE Ultralight. + PICC_CMD_MF_WRITE = 0xA0, // Writes one 16 uint8_t block to the authenticated sector of the PICC. Called + // "COMPATIBILITY WRITE" for MIFARE Ultralight. + PICC_CMD_MF_DECREMENT = + 0xC0, // Decrements the contents of a block and stores the result in the internal data register. + PICC_CMD_MF_INCREMENT = + 0xC1, // Increments the contents of a block and stores the result in the internal data register. + PICC_CMD_MF_RESTORE = 0xC2, // Reads the contents of a block into the internal data register. + PICC_CMD_MF_TRANSFER = 0xB0, // Writes the contents of the internal data register to a block. + // The commands used for MIFARE Ultralight (from http://www.nxp.com/documents/data_sheet/MF0ICU1.pdf, Section 8.6) + // The PICC_CMD_MF_READ and PICC_CMD_MF_WRITE can also be used for MIFARE Ultralight. + PICC_CMD_UL_WRITE = 0xA2 // Writes one 4 uint8_t page to the PICC. + }; + + // Return codes from the functions in this class. Remember to update GetStatusCodeName() if you add more. + // last value set to 0xff, then compiler uses less ram, it seems some optimisations are triggered + enum StatusCode : uint8_t { + STATUS_OK, // Success + STATUS_ERROR, // Error in communication + STATUS_COLLISION, // Collission detected + STATUS_TIMEOUT, // Timeout in communication. + STATUS_NO_ROOM, // A buffer is not big enough. + STATUS_INTERNAL_ERROR, // Internal error in the code. Should not happen ;-) + STATUS_INVALID, // Invalid argument. + STATUS_CRC_WRONG, // The CRC_A does not match + STATUS_MIFARE_NACK = 0xff // A MIFARE PICC responded with NAK. + }; + + // A struct used for passing the UID of a PICC. + using Uid = struct { + uint8_t size; // Number of uint8_ts in the UID. 4, 7 or 10. + uint8_t uiduint8_t[10]; + uint8_t sak; // The SAK (Select acknowledge) uint8_t returned from the PICC after successful selection. + }; + + Uid uid_; + uint32_t update_wait_{0}; + + void pcd_reset_(); + void initialize_(); + void pcd_antenna_on_(); + virtual uint8_t pcd_read_register(PcdRegister reg ///< The register to read from. One of the PCD_Register enums. + ) = 0; + + /** + * Reads a number of uint8_ts from the specified register in the MFRC522 chip. + * The interface is described in the datasheet section 8.1.2. + */ + virtual void pcd_read_register(PcdRegister reg, ///< The register to read from. One of the PCD_Register enums. + uint8_t count, ///< The number of uint8_ts to read + uint8_t *values, ///< uint8_t array to store the values in. + uint8_t rx_align ///< Only bit positions rxAlign..7 in values[0] are updated. + ) = 0; + virtual void pcd_write_register(PcdRegister reg, ///< The register to write to. One of the PCD_Register enums. + uint8_t value ///< The value to write. + ) = 0; + + /** + * Writes a number of uint8_ts to the specified register in the MFRC522 chip. + * The interface is described in the datasheet section 8.1.2. + */ + virtual void pcd_write_register(PcdRegister reg, ///< The register to write to. One of the PCD_Register enums. + uint8_t count, ///< The number of uint8_ts to write to the register + uint8_t *values ///< The values to write. uint8_t array. + ) = 0; + + StatusCode picc_request_a_( + uint8_t *buffer_atqa, ///< The buffer to store the ATQA (Answer to request) in + uint8_t *buffer_size ///< Buffer size, at least two uint8_ts. Also number of uint8_ts returned if STATUS_OK. + ); + StatusCode picc_reqa_or_wupa_( + uint8_t command, ///< The command to send - PICC_CMD_REQA or PICC_CMD_WUPA + uint8_t *buffer_atqa, ///< The buffer to store the ATQA (Answer to request) in + uint8_t *buffer_size ///< Buffer size, at least two uint8_ts. Also number of uint8_ts returned if STATUS_OK. + ); + void pcd_set_register_bit_mask_(PcdRegister reg, ///< The register to update. One of the PCD_Register enums. + uint8_t mask ///< The bits to set. + ); + void pcd_clear_register_bit_mask_(PcdRegister reg, ///< The register to update. One of the PCD_Register enums. + uint8_t mask ///< The bits to clear. + ); + + StatusCode pcd_transceive_data_(uint8_t *send_data, uint8_t send_len, uint8_t *back_data, uint8_t *back_len, + uint8_t *valid_bits = nullptr, uint8_t rx_align = 0, bool check_crc = false); + StatusCode pcd_communicate_with_picc_(uint8_t command, uint8_t wait_i_rq, uint8_t *send_data, uint8_t send_len, + uint8_t *back_data = nullptr, uint8_t *back_len = nullptr, + uint8_t *valid_bits = nullptr, uint8_t rx_align = 0, bool check_crc = false); + StatusCode pcd_calculate_crc_( + uint8_t *data, ///< In: Pointer to the data to transfer to the FIFO for CRC calculation. + uint8_t length, ///< In: The number of uint8_ts to transfer. + uint8_t *result ///< Out: Pointer to result buffer. Result is written to result[0..1], low uint8_t first. + ); + RC522::StatusCode picc_is_new_card_present_(); + bool picc_read_card_serial_(); + StatusCode picc_select_( + Uid *uid, ///< Pointer to Uid struct. Normally output, but can also be used to supply a known UID. + uint8_t valid_bits = 0 ///< The number of known UID bits supplied in *uid. Normally 0. If set you must also + ///< supply uid->size. + ); + + /** Read a data frame from the RC522 and return the result as a vector. + * + * Note that is_ready needs to be checked first before requesting this method. + * + * On failure, an empty vector is returned. + */ + std::vector r_c522_read_data_(); + + GPIOPin *reset_pin_{nullptr}; + uint8_t reset_count_{0}; + uint32_t reset_timeout_{0}; + bool initialize_pending_{false}; + std::vector binary_sensors_; + std::vector triggers_; + + enum RC522Error { + NONE = 0, + RESET_FAILED, + } error_code_{NONE}; +}; + +class RC522BinarySensor : public binary_sensor::BinarySensor { + public: + void set_uid(const std::vector &uid) { uid_ = uid; } + + bool process(const uint8_t *data, uint8_t len); + + void on_scan_end() { + if (!this->found_) { + this->publish_state(false); + } + this->found_ = false; + } + + protected: + std::vector uid_; + bool found_{false}; +}; + +class RC522Trigger : public Trigger { + public: + void process(const uint8_t *uid, uint8_t uid_length); +}; + +} // namespace rc522 +} // namespace esphome diff --git a/esphome/components/rc522_i2c/__init__.py b/esphome/components/rc522_i2c/__init__.py new file mode 100644 index 0000000000..c5bb72ee53 --- /dev/null +++ b/esphome/components/rc522_i2c/__init__.py @@ -0,0 +1,22 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import i2c, rc522 +from esphome.const import CONF_ID + +CODEOWNERS = ['@glmnet'] +DEPENDENCIES = ['i2c'] +AUTO_LOAD = ['rc522'] + + +rc522_i2c_ns = cg.esphome_ns.namespace('rc522_i2c') +RC522I2C = rc522_i2c_ns.class_('RC522I2C', rc522.RC522, i2c.I2CDevice) + +CONFIG_SCHEMA = cv.All(rc522.RC522_SCHEMA.extend({ + cv.GenerateID(): cv.declare_id(RC522I2C), +}).extend(i2c.i2c_device_schema(0x2c))) + + +def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + yield rc522.setup_rc522(var, config) + yield i2c.register_i2c_device(var, config) diff --git a/esphome/components/rc522_i2c/rc522_i2c.cpp b/esphome/components/rc522_i2c/rc522_i2c.cpp new file mode 100644 index 0000000000..8248e79b50 --- /dev/null +++ b/esphome/components/rc522_i2c/rc522_i2c.cpp @@ -0,0 +1,99 @@ +#include "rc522_i2c.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace rc522_i2c { + +static const char *TAG = "rc522_i2c"; + +void RC522I2C::dump_config() { + RC522::dump_config(); + LOG_I2C_DEVICE(this); +} + +/** + * Reads a uint8_t from the specified register in the MFRC522 chip. + * The interface is described in the datasheet section 8.1.2. + */ +uint8_t RC522I2C::pcd_read_register(PcdRegister reg ///< The register to read from. One of the PCD_Register enums. +) { + uint8_t value; + read_byte(reg >> 1, &value); + ESP_LOGVV(TAG, "read_register_(%x) -> %x", reg, value); + return value; +} + +/** + * Reads a number of uint8_ts from the specified register in the MFRC522 chip. + * The interface is described in the datasheet section 8.1.2. + */ +void RC522I2C::pcd_read_register(PcdRegister reg, ///< The register to read from. One of the PCD_Register enums. + uint8_t count, ///< The number of uint8_ts to read + uint8_t *values, ///< uint8_t array to store the values in. + uint8_t rx_align ///< Only bit positions rxAlign..7 in values[0] are updated. +) { + if (count == 0) { + return; + } + + std::string buf; + buf = "Rx"; + char cstrb[20]; + + uint8_t b = values[0]; + read_bytes(reg >> 1, values, count); + + if (rx_align) // Only update bit positions rxAlign..7 in values[0] + { + // Create bit mask for bit positions rxAlign..7 + uint8_t mask = 0xFF << rx_align; + // Apply mask to both current value of values[0] and the new data in values array. + values[0] = (b & ~mask) | (values[0] & mask); + } +} + +void RC522I2C::pcd_write_register(PcdRegister reg, ///< The register to write to. One of the PCD_Register enums. + uint8_t value ///< The value to write. +) { + this->write_byte(reg >> 1, value); +} + +/** + * Writes a number of uint8_ts to the specified register in the MFRC522 chip. + * The interface is described in the datasheet section 8.1.2. + */ +void RC522I2C::pcd_write_register(PcdRegister reg, ///< The register to write to. One of the PCD_Register enums. + uint8_t count, ///< The number of uint8_ts to write to the register + uint8_t *values ///< The values to write. uint8_t array. +) { + write_bytes(reg >> 1, values, count); +} + +// bool RC522I2C::write_data(const std::vector &data) { +// return this->write_bytes_raw(data.data(), data.size()); } + +// bool RC522I2C::read_data(std::vector &data, uint8_t len) { +// delay(5); + +// std::vector ready; +// ready.resize(1); +// uint32_t start_time = millis(); +// while (true) { +// if (this->read_bytes_raw(ready.data(), 1)) { +// if (ready[0] == 0x01) +// break; +// } + +// if (millis() - start_time > 100) { +// ESP_LOGV(TAG, "Timed out waiting for readiness from RC522!"); +// return false; +// } +// } + +// data.resize(len + 1); +// this->read_bytes_raw(data.data(), len + 1); +// return true; +// } + +} // namespace rc522_i2c +} // namespace esphome diff --git a/esphome/components/rc522_i2c/rc522_i2c.h b/esphome/components/rc522_i2c/rc522_i2c.h new file mode 100644 index 0000000000..8d8b0a0716 --- /dev/null +++ b/esphome/components/rc522_i2c/rc522_i2c.h @@ -0,0 +1,42 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/rc522/rc522.h" +#include "esphome/components/i2c/i2c.h" + +namespace esphome { +namespace rc522_i2c { + +class RC522I2C : public rc522::RC522, public i2c::I2CDevice { + public: + void dump_config() override; + + protected: + uint8_t pcd_read_register(PcdRegister reg ///< The register to read from. One of the PCD_Register enums. + ) override; + + /** + * Reads a number of uint8_ts from the specified register in the MFRC522 chip. + * The interface is described in the datasheet section 8.1.2. + */ + void pcd_read_register(PcdRegister reg, ///< The register to read from. One of the PCD_Register enums. + uint8_t count, ///< The number of uint8_ts to read + uint8_t *values, ///< uint8_t array to store the values in. + uint8_t rx_align ///< Only bit positions rxAlign..7 in values[0] are updated. + ) override; + void pcd_write_register(PcdRegister reg, ///< The register to write to. One of the PCD_Register enums. + uint8_t value ///< The value to write. + ) override; + + /** + * Writes a number of uint8_ts to the specified register in the MFRC522 chip. + * The interface is described in the datasheet section 8.1.2. + */ + void pcd_write_register(PcdRegister reg, ///< The register to write to. One of the PCD_Register enums. + uint8_t count, ///< The number of uint8_ts to write to the register + uint8_t *values ///< The values to write. uint8_t array. + ) override; +}; + +} // namespace rc522_i2c +} // namespace esphome diff --git a/esphome/components/rc522_spi/__init__.py b/esphome/components/rc522_spi/__init__.py index 043d083c4c..37f78b66d0 100644 --- a/esphome/components/rc522_spi/__init__.py +++ b/esphome/components/rc522_spi/__init__.py @@ -1,39 +1,21 @@ import esphome.codegen as cg import esphome.config_validation as cv -from esphome import automation, pins -from esphome.components import spi -from esphome.const import CONF_ID, CONF_ON_TAG, CONF_TRIGGER_ID, CONF_RESET_PIN, CONF_CS_PIN +from esphome.components import spi, rc522 +from esphome.const import CONF_ID CODEOWNERS = ['@glmnet'] DEPENDENCIES = ['spi'] -AUTO_LOAD = ['binary_sensor'] -MULTI_CONF = True - +AUTO_LOAD = ['rc522'] rc522_spi_ns = cg.esphome_ns.namespace('rc522_spi') -RC522 = rc522_spi_ns.class_('RC522', cg.PollingComponent, spi.SPIDevice) -RC522Trigger = rc522_spi_ns.class_('RC522Trigger', automation.Trigger.template(cg.std_string)) +RC522Spi = rc522_spi_ns.class_('RC522Spi', rc522.RC522, spi.SPIDevice) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(RC522), - cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, - cv.Required(CONF_CS_PIN): pins.gpio_output_pin_schema, - cv.Optional(CONF_ON_TAG): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(RC522Trigger), - }), -}).extend(cv.polling_component_schema('1s')).extend(spi.spi_device_schema()) +CONFIG_SCHEMA = cv.All(rc522.RC522_SCHEMA.extend({ + cv.GenerateID(): cv.declare_id(RC522Spi), +}).extend(spi.spi_device_schema(cs_pin_required=True))) def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) - yield cg.register_component(var, config) + yield rc522.setup_rc522(var, config) yield spi.register_spi_device(var, config) - - if CONF_RESET_PIN in config: - reset = yield cg.gpio_pin_expression(config[CONF_RESET_PIN]) - cg.add(var.set_reset_pin(reset)) - - for conf in config.get(CONF_ON_TAG, []): - trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID]) - cg.add(var.register_trigger(trigger)) - yield automation.build_automation(trigger, [(cg.std_string, 'x')], conf) diff --git a/esphome/components/rc522_spi/binary_sensor.py b/esphome/components/rc522_spi/binary_sensor.py index d58d62c797..1f0eafe6af 100644 --- a/esphome/components/rc522_spi/binary_sensor.py +++ b/esphome/components/rc522_spi/binary_sensor.py @@ -1,44 +1,9 @@ -import esphome.codegen as cg -import esphome.config_validation as cv -from esphome.components import binary_sensor -from esphome.const import CONF_UID, CONF_ID -from esphome.core import HexInt -from . import rc522_spi_ns, RC522 +import esphome.components.rc522.binary_sensor as rc522_binary_sensor -DEPENDENCIES = ['rc522_spi'] +DEPENDENCIES = ['rc522'] -CONF_RC522_ID = 'rc522_id' - - -def validate_uid(value): - value = cv.string_strict(value) - for x in value.split('-'): - if len(x) != 2: - raise cv.Invalid("Each part (separated by '-') of the UID must be two characters " - "long.") - try: - x = int(x, 16) - except ValueError as err: - raise cv.Invalid("Valid characters for parts of a UID are 0123456789ABCDEF.") from err - if x < 0 or x > 255: - raise cv.Invalid("Valid values for UID parts (separated by '-') are 00 to FF") - return value - - -RC522BinarySensor = rc522_spi_ns.class_('RC522BinarySensor', binary_sensor.BinarySensor) - -CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(RC522BinarySensor), - cv.GenerateID(CONF_RC522_ID): cv.use_id(RC522), - cv.Required(CONF_UID): validate_uid, -}) +CONFIG_SCHEMA = rc522_binary_sensor.CONFIG_SCHEMA def to_code(config): - var = cg.new_Pvariable(config[CONF_ID]) - yield binary_sensor.register_binary_sensor(var, config) - - hub = yield cg.get_variable(config[CONF_RC522_ID]) - cg.add(hub.register_tag(var)) - addr = [HexInt(int(x, 16)) for x in config[CONF_UID].split('-')] - cg.add(var.set_uid(addr)) + yield rc522_binary_sensor.to_code(config) diff --git a/esphome/components/rc522_spi/rc522_spi.cpp b/esphome/components/rc522_spi/rc522_spi.cpp index b332e50c53..61236393e4 100644 --- a/esphome/components/rc522_spi/rc522_spi.cpp +++ b/esphome/components/rc522_spi/rc522_spi.cpp @@ -9,218 +9,30 @@ namespace rc522_spi { static const char *TAG = "rc522_spi"; -static const uint8_t RESET_COUNT = 5; +void RC522Spi::setup() { + ESP_LOGI(TAG, "SPI Setup"); + this->spi_setup(); -void format_uid(char *buf, const uint8_t *uid, uint8_t uid_length) { - int offset = 0; - for (uint8_t i = 0; i < uid_length; i++) { - const char *format = "%02X"; - if (i + 1 < uid_length) - format = "%02X-"; - offset += sprintf(buf + offset, format, uid[i]); - } + RC522::setup(); } -void RC522::setup() { - spi_setup(); - initialize_pending_ = true; - // Pull device out of power down / reset state. - - // First set the resetPowerDownPin as digital input, to check the MFRC522 power down mode. - if (reset_pin_ != nullptr) { - reset_pin_->pin_mode(INPUT); - - if (reset_pin_->digital_read() == LOW) { // The MFRC522 chip is in power down mode. - ESP_LOGV(TAG, "Power down mode detected. Hard resetting..."); - reset_pin_->pin_mode(OUTPUT); // Now set the resetPowerDownPin as digital output. - reset_pin_->digital_write(LOW); // Make sure we have a clean LOW state. - delayMicroseconds(2); // 8.8.1 Reset timing requirements says about 100ns. Let us be generous: 2μsl - reset_pin_->digital_write(HIGH); // Exit power down mode. This triggers a hard reset. - // Section 8.8.2 in the datasheet says the oscillator start-up time is the start up time of the crystal + 37,74μs. - // Let us be generous: 50ms. - reset_timeout_ = millis(); - return; - } - } - - // Setup a soft reset - reset_count_ = RESET_COUNT; - reset_timeout_ = millis(); -} - -void RC522::initialize_() { - // Per originall code, wait 50 ms - if (millis() - reset_timeout_ < 50) - return; - - // Reset baud rates - ESP_LOGV(TAG, "Initialize"); - - pcd_write_register_(TX_MODE_REG, 0x00); - pcd_write_register_(RX_MODE_REG, 0x00); - // Reset ModWidthReg - pcd_write_register_(MOD_WIDTH_REG, 0x26); - - // When communicating with a PICC we need a timeout if something goes wrong. - // f_timer = 13.56 MHz / (2*TPreScaler+1) where TPreScaler = [TPrescaler_Hi:TPrescaler_Lo]. - // TPrescaler_Hi are the four low bits in TModeReg. TPrescaler_Lo is TPrescalerReg. - pcd_write_register_(T_MODE_REG, 0x80); // TAuto=1; timer starts automatically at the end of the transmission in all - // communication modes at all speeds - - // TPreScaler = TModeReg[3..0]:TPrescalerReg, ie 0x0A9 = 169 => f_timer=40kHz, ie a timer period of 25μs. - pcd_write_register_(T_PRESCALER_REG, 0xA9); - pcd_write_register_(T_RELOAD_REG_H, 0x03); // Reload timer with 0x3E8 = 1000, ie 25ms before timeout. - pcd_write_register_(T_RELOAD_REG_L, 0xE8); - - // Default 0x00. Force a 100 % ASK modulation independent of the ModGsPReg register setting - pcd_write_register_(TX_ASK_REG, 0x40); - pcd_write_register_(MODE_REG, 0x3D); // Default 0x3F. Set the preset value for the CRC coprocessor for the CalcCRC - // command to 0x6363 (ISO 14443-3 part 6.2.4) - pcd_antenna_on_(); // Enable the antenna driver pins TX1 and TX2 (they were disabled by the reset) - - initialize_pending_ = false; -} - -void RC522::dump_config() { - ESP_LOGCONFIG(TAG, "RC522:"); - switch (this->error_code_) { - case NONE: - break; - case RESET_FAILED: - ESP_LOGE(TAG, "Reset command failed!"); - break; - } - +void RC522Spi::dump_config() { + RC522::dump_config(); LOG_PIN(" CS Pin: ", this->cs_); - LOG_PIN(" RESET Pin: ", this->reset_pin_); - - LOG_UPDATE_INTERVAL(this); - - for (auto *child : this->binary_sensors_) { - LOG_BINARY_SENSOR(" ", "Tag", child); - } -} - -void RC522::loop() { - // First check reset is needed - if (reset_count_ > 0) { - pcd_reset_(); - return; - } - if (initialize_pending_) { - initialize_(); - return; - } - - if (millis() - update_wait_ < this->update_interval_) - return; - - auto status = picc_is_new_card_present_(); - - if (status == STATUS_ERROR) // No card - { - ESP_LOGE(TAG, "Error"); - // mark_failed(); - return; - } - - if (status != STATUS_OK) // We can receive STATUS_TIMEOUT when no card, or unexpected status. - return; - - // Try process card - if (!picc_read_card_serial_()) { - ESP_LOGW(TAG, "Requesting tag read failed!"); - return; - }; - - if (uid_.size < 4) { - return; - ESP_LOGW(TAG, "Read serial size: %d", uid_.size); - } - - update_wait_ = millis(); - - bool report = true; - // 1. Go through all triggers - for (auto *trigger : this->triggers_) - trigger->process(uid_.uiduint8_t, uid_.size); - - // 2. Find a binary sensor - for (auto *tag : this->binary_sensors_) { - if (tag->process(uid_.uiduint8_t, uid_.size)) { - // 2.1 if found, do not dump - report = false; - } - } - - if (report) { - char buf[32]; - format_uid(buf, uid_.uiduint8_t, uid_.size); - ESP_LOGD(TAG, "Found new tag '%s'", buf); - } -} - -void RC522::update() { - for (auto *obj : this->binary_sensors_) - obj->on_scan_end(); -} - -/** - * Performs a soft reset on the MFRC522 chip and waits for it to be ready again. - */ -void RC522::pcd_reset_() { - // The datasheet does not mention how long the SoftRest command takes to complete. - // But the MFRC522 might have been in soft power-down mode (triggered by bit 4 of CommandReg) - // Section 8.8.2 in the datasheet says the oscillator start-up time is the start up time of the crystal + 37,74μs. Let - // us be generous: 50ms. - - if (millis() - reset_timeout_ < 50) - return; - - if (reset_count_ == RESET_COUNT) { - ESP_LOGV(TAG, "Soft reset..."); - // Issue the SoftReset command. - pcd_write_register_(COMMAND_REG, PCD_SOFT_RESET); - } - - // Expect the PowerDown bit in CommandReg to be cleared (max 3x50ms) - if ((pcd_read_register_(COMMAND_REG) & (1 << 4)) == 0) { - reset_count_ = 0; - ESP_LOGI(TAG, "Device online."); - // Wait for initialize - reset_timeout_ = millis(); - return; - } - - if (--reset_count_ == 0) { - ESP_LOGE(TAG, "Unable to reset RC522."); - mark_failed(); - } -} - -/** - * Turns the antenna on by enabling pins TX1 and TX2. - * After a reset these pins are disabled. - */ -void RC522::pcd_antenna_on_() { - uint8_t value = pcd_read_register_(TX_CONTROL_REG); - if ((value & 0x03) != 0x03) { - pcd_write_register_(TX_CONTROL_REG, value | 0x03); - } } /** * Reads a uint8_t from the specified register in the MFRC522 chip. * The interface is described in the datasheet section 8.1.2. */ -uint8_t RC522::pcd_read_register_(PcdRegister reg ///< The register to read from. One of the PCD_Register enums. +uint8_t RC522Spi::pcd_read_register(PcdRegister reg ///< The register to read from. One of the PCD_Register enums. ) { uint8_t value; enable(); transfer_byte(0x80 | reg); value = read_byte(); disable(); - ESP_LOGVV(TAG, "read_register_(%x) -> %x", reg, value); + ESP_LOGV(TAG, "read_register_(%x) -> %x", reg, value); return value; } @@ -228,10 +40,10 @@ uint8_t RC522::pcd_read_register_(PcdRegister reg ///< The register to read fro * Reads a number of uint8_ts from the specified register in the MFRC522 chip. * The interface is described in the datasheet section 8.1.2. */ -void RC522::pcd_read_register_(PcdRegister reg, ///< The register to read from. One of the PCD_Register enums. - uint8_t count, ///< The number of uint8_ts to read - uint8_t *values, ///< uint8_t array to store the values in. - uint8_t rx_align ///< Only bit positions rxAlign..7 in values[0] are updated. +void RC522Spi::pcd_read_register(PcdRegister reg, ///< The register to read from. One of the PCD_Register enums. + uint8_t count, ///< The number of uint8_ts to read + uint8_t *values, ///< uint8_t array to store the values in. + uint8_t rx_align ///< Only bit positions rxAlign..7 in values[0] are updated. ) { std::string buf; buf = "Rx"; @@ -278,8 +90,8 @@ void RC522::pcd_read_register_(PcdRegister reg, ///< The register to read from. disable(); } -void RC522::pcd_write_register_(PcdRegister reg, ///< The register to write to. One of the PCD_Register enums. - uint8_t value ///< The value to write. +void RC522Spi::pcd_write_register(PcdRegister reg, ///< The register to write to. One of the PCD_Register enums. + uint8_t value ///< The value to write. ) { enable(); // MSB == 0 is for writing. LSB is not used in address. Datasheet section 8.1.2.3. @@ -292,9 +104,9 @@ void RC522::pcd_write_register_(PcdRegister reg, ///< The register to write to. * Writes a number of uint8_ts to the specified register in the MFRC522 chip. * The interface is described in the datasheet section 8.1.2. */ -void RC522::pcd_write_register_(PcdRegister reg, ///< The register to write to. One of the PCD_Register enums. - uint8_t count, ///< The number of uint8_ts to write to the register - uint8_t *values ///< The values to write. uint8_t array. +void RC522Spi::pcd_write_register(PcdRegister reg, ///< The register to write to. One of the PCD_Register enums. + uint8_t count, ///< The number of uint8_ts to write to the register + uint8_t *values ///< The values to write. uint8_t array. ) { std::string buf; buf = "Tx"; @@ -313,545 +125,5 @@ void RC522::pcd_write_register_(PcdRegister reg, ///< The register to write to. ESP_LOGVV(TAG, "write_register_(%x, %d) -> %s", reg, count, buf.c_str()); } -/** - * Transmits a REQuest command, Type A. Invites PICCs in state IDLE to go to READY and prepare for anticollision or - * selection. 7 bit frame. Beware: When two PICCs are in the field at the same time I often get STATUS_TIMEOUT - - * probably due do bad antenna design. - * - * @return STATUS_OK on success, STATUS_??? otherwise. - */ -RC522::StatusCode RC522::picc_request_a_( - uint8_t *buffer_atqa, ///< The buffer to store the ATQA (Answer to request) in - uint8_t *buffer_size ///< Buffer size, at least two uint8_ts. Also number of uint8_ts returned if STATUS_OK. -) { - return picc_reqa_or_wupa_(PICC_CMD_REQA, buffer_atqa, buffer_size); -} - -/** - * Transmits REQA or WUPA commands. - * Beware: When two PICCs are in the field at the same time I often get STATUS_TIMEOUT - probably due do bad antenna - * design. - * - * @return STATUS_OK on success, STATUS_??? otherwise. - */ -RC522::StatusCode RC522::picc_reqa_or_wupa_( - uint8_t command, ///< The command to send - PICC_CMD_REQA or PICC_CMD_WUPA - uint8_t *buffer_atqa, ///< The buffer to store the ATQA (Answer to request) in - uint8_t *buffer_size ///< Buffer size, at least two uint8_ts. Also number of uint8_ts returned if STATUS_OK. -) { - uint8_t valid_bits; - RC522::StatusCode status; - - if (buffer_atqa == nullptr || *buffer_size < 2) { // The ATQA response is 2 uint8_ts long. - return STATUS_NO_ROOM; - } - pcd_clear_register_bit_mask_(COLL_REG, 0x80); // ValuesAfterColl=1 => Bits received after collision are cleared. - valid_bits = 7; // For REQA and WUPA we need the short frame format - transmit only 7 bits of the last (and only) - // uint8_t. TxLastBits = BitFramingReg[2..0] - status = pcd_transceive_data_(&command, 1, buffer_atqa, buffer_size, &valid_bits); - if (status != STATUS_OK) - return status; - if (*buffer_size != 2 || valid_bits != 0) { // ATQA must be exactly 16 bits. - ESP_LOGVV(TAG, "picc_reqa_or_wupa_() -> STATUS_ERROR"); - return STATUS_ERROR; - } - - return STATUS_OK; -} - -/** - * Sets the bits given in mask in register reg. - */ -void RC522::pcd_set_register_bit_mask_(PcdRegister reg, ///< The register to update. One of the PCD_Register enums. - uint8_t mask ///< The bits to set. -) { - uint8_t tmp = pcd_read_register_(reg); - pcd_write_register_(reg, tmp | mask); // set bit mask -} - -/** - * Clears the bits given in mask from register reg. - */ -void RC522::pcd_clear_register_bit_mask_(PcdRegister reg, ///< The register to update. One of the PCD_Register enums. - uint8_t mask ///< The bits to clear. -) { - uint8_t tmp = pcd_read_register_(reg); - pcd_write_register_(reg, tmp & (~mask)); // clear bit mask -} - -/** - * Executes the Transceive command. - * CRC validation can only be done if backData and backLen are specified. - * - * @return STATUS_OK on success, STATUS_??? otherwise. - */ -RC522::StatusCode RC522::pcd_transceive_data_( - uint8_t *send_data, ///< Pointer to the data to transfer to the FIFO. - uint8_t send_len, ///< Number of uint8_ts to transfer to the FIFO. - uint8_t *back_data, ///< nullptr or pointer to buffer if data should be read back after executing the command. - uint8_t *back_len, ///< In: Max number of uint8_ts to write to *backData. Out: The number of uint8_ts returned. - uint8_t - *valid_bits, ///< In/Out: The number of valid bits in the last uint8_t. 0 for 8 valid bits. Default nullptr. - uint8_t rx_align, ///< In: Defines the bit position in backData[0] for the first bit received. Default 0. - bool check_crc ///< In: True => The last two uint8_ts of the response is assumed to be a CRC_A that must be - ///< validated. -) { - uint8_t wait_i_rq = 0x30; // RxIRq and IdleIRq - auto ret = pcd_communicate_with_picc_(PCD_TRANSCEIVE, wait_i_rq, send_data, send_len, back_data, back_len, valid_bits, - rx_align, check_crc); - - if (ret == STATUS_OK && *back_len == 5) - ESP_LOGVV(TAG, "pcd_transceive_data_(..., %d, ) -> %d [%x, %x, %x, %x, %x]", send_len, ret, back_data[0], - back_data[1], back_data[2], back_data[3], back_data[4]); - else - ESP_LOGVV(TAG, "pcd_transceive_data_(..., %d, ... ) -> %d", send_len, ret); - return ret; -} - -/** - * Transfers data to the MFRC522 FIFO, executes a command, waits for completion and transfers data back from the FIFO. - * CRC validation can only be done if backData and backLen are specified. - * - * @return STATUS_OK on success, STATUS_??? otherwise. - */ -RC522::StatusCode RC522::pcd_communicate_with_picc_( - uint8_t command, ///< The command to execute. One of the PCD_Command enums. - uint8_t wait_i_rq, ///< The bits in the ComIrqReg register that signals successful completion of the command. - uint8_t *send_data, ///< Pointer to the data to transfer to the FIFO. - uint8_t send_len, ///< Number of uint8_ts to transfer to the FIFO. - uint8_t *back_data, ///< nullptr or pointer to buffer if data should be read back after executing the command. - uint8_t *back_len, ///< In: Max number of uint8_ts to write to *backData. Out: The number of uint8_ts returned. - uint8_t *valid_bits, ///< In/Out: The number of valid bits in the last uint8_t. 0 for 8 valid bits. - uint8_t rx_align, ///< In: Defines the bit position in backData[0] for the first bit received. Default 0. - bool check_crc ///< In: True => The last two uint8_ts of the response is assumed to be a CRC_A that must be - ///< validated. -) { - ESP_LOGVV(TAG, "pcd_communicate_with_picc_(%d, %d,... %d)", command, wait_i_rq, check_crc); - - // Prepare values for BitFramingReg - uint8_t tx_last_bits = valid_bits ? *valid_bits : 0; - uint8_t bit_framing = - (rx_align << 4) + tx_last_bits; // RxAlign = BitFramingReg[6..4]. TxLastBits = BitFramingReg[2..0] - - pcd_write_register_(COMMAND_REG, PCD_IDLE); // Stop any active command. - pcd_write_register_(COM_IRQ_REG, 0x7F); // Clear all seven interrupt request bits - pcd_write_register_(FIFO_LEVEL_REG, 0x80); // FlushBuffer = 1, FIFO initialization - pcd_write_register_(FIFO_DATA_REG, send_len, send_data); // Write sendData to the FIFO - pcd_write_register_(BIT_FRAMING_REG, bit_framing); // Bit adjustments - pcd_write_register_(COMMAND_REG, command); // Execute the command - if (command == PCD_TRANSCEIVE) { - pcd_set_register_bit_mask_(BIT_FRAMING_REG, 0x80); // StartSend=1, transmission of data starts - } - - // Wait for the command to complete. - // In PCD_Init() we set the TAuto flag in TModeReg. This means the timer automatically starts when the PCD stops - // transmitting. Each iteration of the do-while-loop takes 17.86μs. - // TODO check/modify for other architectures than Arduino Uno 16bit - uint16_t i; - for (i = 2000; i > 0; i--) { - uint8_t n = pcd_read_register_( - COM_IRQ_REG); // ComIrqReg[7..0] bits are: Set1 TxIRq RxIRq IdleIRq HiAlertIRq LoAlertIRq ErrIRq TimerIRq - if (n & wait_i_rq) { // One of the interrupts that signal success has been set. - break; - } - if (n & 0x01) { // Timer interrupt - nothing received in 25ms - return STATUS_TIMEOUT; - } - } - // 35.7ms and nothing happend. Communication with the MFRC522 might be down. - if (i == 0) { - return STATUS_TIMEOUT; - } - - // Stop now if any errors except collisions were detected. - uint8_t error_reg_value = pcd_read_register_( - ERROR_REG); // ErrorReg[7..0] bits are: WrErr TempErr reserved BufferOvfl CollErr CRCErr ParityErr ProtocolErr - if (error_reg_value & 0x13) { // BufferOvfl ParityErr ProtocolErr - return STATUS_ERROR; - } - - uint8_t valid_bits_local = 0; - - // If the caller wants data back, get it from the MFRC522. - if (back_data && back_len) { - uint8_t n = pcd_read_register_(FIFO_LEVEL_REG); // Number of uint8_ts in the FIFO - if (n > *back_len) { - return STATUS_NO_ROOM; - } - *back_len = n; // Number of uint8_ts returned - pcd_read_register_(FIFO_DATA_REG, n, back_data, rx_align); // Get received data from FIFO - valid_bits_local = - pcd_read_register_(CONTROL_REG) & 0x07; // RxLastBits[2:0] indicates the number of valid bits in the last - // received uint8_t. If this value is 000b, the whole uint8_t is valid. - if (valid_bits) { - *valid_bits = valid_bits_local; - } - } - - // Tell about collisions - if (error_reg_value & 0x08) { // CollErr - return STATUS_COLLISION; - } - - // Perform CRC_A validation if requested. - if (back_data && back_len && check_crc) { - // In this case a MIFARE Classic NAK is not OK. - if (*back_len == 1 && valid_bits_local == 4) { - return STATUS_MIFARE_NACK; - } - // We need at least the CRC_A value and all 8 bits of the last uint8_t must be received. - if (*back_len < 2 || valid_bits_local != 0) { - return STATUS_CRC_WRONG; - } - // Verify CRC_A - do our own calculation and store the control in controlBuffer. - uint8_t control_buffer[2]; - RC522::StatusCode status = pcd_calculate_crc_(&back_data[0], *back_len - 2, &control_buffer[0]); - if (status != STATUS_OK) { - return status; - } - if ((back_data[*back_len - 2] != control_buffer[0]) || (back_data[*back_len - 1] != control_buffer[1])) { - return STATUS_CRC_WRONG; - } - } - - return STATUS_OK; -} - -/** - * Use the CRC coprocessor in the MFRC522 to calculate a CRC_A. - * - * @return STATUS_OK on success, STATUS_??? otherwise. - */ - -RC522::StatusCode RC522::pcd_calculate_crc_( - uint8_t *data, ///< In: Pointer to the data to transfer to the FIFO for CRC calculation. - uint8_t length, ///< In: The number of uint8_ts to transfer. - uint8_t *result ///< Out: Pointer to result buffer. Result is written to result[0..1], low uint8_t first. -) { - ESP_LOGVV(TAG, "pcd_calculate_crc_(..., %d, ...)", length); - pcd_write_register_(COMMAND_REG, PCD_IDLE); // Stop any active command. - pcd_write_register_(DIV_IRQ_REG, 0x04); // Clear the CRCIRq interrupt request bit - pcd_write_register_(FIFO_LEVEL_REG, 0x80); // FlushBuffer = 1, FIFO initialization - pcd_write_register_(FIFO_DATA_REG, length, data); // Write data to the FIFO - pcd_write_register_(COMMAND_REG, PCD_CALC_CRC); // Start the calculation - - // Wait for the CRC calculation to complete. Each iteration of the while-loop takes 17.73μs. - // TODO check/modify for other architectures than Arduino Uno 16bit - - // Wait for the CRC calculation to complete. Each iteration of the while-loop takes 17.73us. - for (uint16_t i = 5000; i > 0; i--) { - // DivIrqReg[7..0] bits are: Set2 reserved reserved MfinActIRq reserved CRCIRq reserved reserved - uint8_t n = pcd_read_register_(DIV_IRQ_REG); - if (n & 0x04) { // CRCIRq bit set - calculation done - pcd_write_register_(COMMAND_REG, PCD_IDLE); // Stop calculating CRC for new content in the FIFO. - // Transfer the result from the registers to the result buffer - result[0] = pcd_read_register_(CRC_RESULT_REG_L); - result[1] = pcd_read_register_(CRC_RESULT_REG_H); - - ESP_LOGVV(TAG, "pcd_calculate_crc_() STATUS_OK"); - return STATUS_OK; - } - } - ESP_LOGVV(TAG, "pcd_calculate_crc_() TIMEOUT"); - // 89ms passed and nothing happend. Communication with the MFRC522 might be down. - return STATUS_TIMEOUT; -} -/** - * Returns STATUS_OK if a PICC responds to PICC_CMD_REQA. - * Only "new" cards in state IDLE are invited. Sleeping cards in state HALT are ignored. - * - * @return STATUS_OK on success, STATUS_??? otherwise. - */ - -RC522::StatusCode RC522::picc_is_new_card_present_() { - uint8_t buffer_atqa[2]; - uint8_t buffer_size = sizeof(buffer_atqa); - - // Reset baud rates - pcd_write_register_(TX_MODE_REG, 0x00); - pcd_write_register_(RX_MODE_REG, 0x00); - // Reset ModWidthReg - pcd_write_register_(MOD_WIDTH_REG, 0x26); - - auto result = picc_request_a_(buffer_atqa, &buffer_size); - - ESP_LOGV(TAG, "picc_is_new_card_present_() -> %d", result); - return result; -} - -/** - * Simple wrapper around PICC_Select. - * Returns true if a UID could be read. - * Remember to call PICC_IsNewCardPresent(), PICC_RequestA() or PICC_WakeupA() first. - * The read UID is available in the class variable uid. - * - * @return bool - */ -bool RC522::picc_read_card_serial_() { - RC522::StatusCode result = picc_select_(&this->uid_); - ESP_LOGVV(TAG, "picc_select_(...) -> %d", result); - return (result == STATUS_OK); -} - -/** - * Transmits SELECT/ANTICOLLISION commands to select a single PICC. - * Before calling this function the PICCs must be placed in the READY(*) state by calling PICC_RequestA() or - * PICC_WakeupA(). On success: - * - The chosen PICC is in state ACTIVE(*) and all other PICCs have returned to state IDLE/HALT. (Figure 7 of the - * ISO/IEC 14443-3 draft.) - * - The UID size and value of the chosen PICC is returned in *uid along with the SAK. - * - * A PICC UID consists of 4, 7 or 10 uint8_ts. - * Only 4 uint8_ts can be specified in a SELECT command, so for the longer UIDs two or three iterations are used: - * UID size Number of UID uint8_ts Cascade levels Example of PICC - * ======== =================== ============== =============== - * single 4 1 MIFARE Classic - * double 7 2 MIFARE Ultralight - * triple 10 3 Not currently in use? - * - * @return STATUS_OK on success, STATUS_??? otherwise. - */ -RC522::StatusCode RC522::picc_select_( - Uid *uid, ///< Pointer to Uid struct. Normally output, but can also be used to supply a known UID. - uint8_t valid_bits ///< The number of known UID bits supplied in *uid. Normally 0. If set you must also supply - ///< uid->size. -) { - bool uid_complete; - bool select_done; - bool use_cascade_tag; - uint8_t cascade_level = 1; - RC522::StatusCode result; - uint8_t count; - uint8_t check_bit; - uint8_t index; - uint8_t uid_index; // The first index in uid->uiduint8_t[] that is used in the current Cascade Level. - int8_t current_level_known_bits; // The number of known UID bits in the current Cascade Level. - uint8_t buffer[9]; // The SELECT/ANTICOLLISION commands uses a 7 uint8_t standard frame + 2 uint8_ts CRC_A - uint8_t buffer_used; // The number of uint8_ts used in the buffer, ie the number of uint8_ts to transfer to the FIFO. - uint8_t rx_align; // Used in BitFramingReg. Defines the bit position for the first bit received. - uint8_t tx_last_bits; // Used in BitFramingReg. The number of valid bits in the last transmitted uint8_t. - uint8_t *response_buffer; - uint8_t response_length; - - // Description of buffer structure: - // uint8_t 0: SEL Indicates the Cascade Level: PICC_CMD_SEL_CL1, PICC_CMD_SEL_CL2 or PICC_CMD_SEL_CL3 - // uint8_t 1: NVB Number of Valid Bits (in complete command, not just the UID): High nibble: complete - // uint8_ts, - // Low nibble: Extra bits. uint8_t 2: UID-data or CT See explanation below. CT means Cascade Tag. uint8_t - // 3: UID-data uint8_t 4: UID-data uint8_t 5: UID-data uint8_t 6: BCC Block Check Character - XOR of - // uint8_ts 2-5 uint8_t 7: CRC_A uint8_t 8: CRC_A The BCC and CRC_A are only transmitted if we know all the UID bits - // of the current Cascade Level. - // - // Description of uint8_ts 2-5: (Section 6.5.4 of the ISO/IEC 14443-3 draft: UID contents and cascade levels) - // UID size Cascade level uint8_t2 uint8_t3 uint8_t4 uint8_t5 - // ======== ============= ===== ===== ===== ===== - // 4 uint8_ts 1 uid0 uid1 uid2 uid3 - // 7 uint8_ts 1 CT uid0 uid1 uid2 - // 2 uid3 uid4 uid5 uid6 - // 10 uint8_ts 1 CT uid0 uid1 uid2 - // 2 CT uid3 uid4 uid5 - // 3 uid6 uid7 uid8 uid9 - - // Sanity checks - if (valid_bits > 80) { - return STATUS_INVALID; - } - - ESP_LOGVV(TAG, "picc_select_(&, %d)", valid_bits); - - // Prepare MFRC522 - pcd_clear_register_bit_mask_(COLL_REG, 0x80); // ValuesAfterColl=1 => Bits received after collision are cleared. - - // Repeat Cascade Level loop until we have a complete UID. - uid_complete = false; - while (!uid_complete) { - // Set the Cascade Level in the SEL uint8_t, find out if we need to use the Cascade Tag in uint8_t 2. - switch (cascade_level) { - case 1: - buffer[0] = PICC_CMD_SEL_CL1; - uid_index = 0; - use_cascade_tag = valid_bits && uid->size > 4; // When we know that the UID has more than 4 uint8_ts - break; - - case 2: - buffer[0] = PICC_CMD_SEL_CL2; - uid_index = 3; - use_cascade_tag = valid_bits && uid->size > 7; // When we know that the UID has more than 7 uint8_ts - break; - - case 3: - buffer[0] = PICC_CMD_SEL_CL3; - uid_index = 6; - use_cascade_tag = false; // Never used in CL3. - break; - - default: - return STATUS_INTERNAL_ERROR; - break; - } - - // How many UID bits are known in this Cascade Level? - current_level_known_bits = valid_bits - (8 * uid_index); - if (current_level_known_bits < 0) { - current_level_known_bits = 0; - } - // Copy the known bits from uid->uiduint8_t[] to buffer[] - index = 2; // destination index in buffer[] - if (use_cascade_tag) { - buffer[index++] = PICC_CMD_CT; - } - uint8_t uint8_ts_to_copy = current_level_known_bits / 8 + - (current_level_known_bits % 8 - ? 1 - : 0); // The number of uint8_ts needed to represent the known bits for this level. - if (uint8_ts_to_copy) { - uint8_t maxuint8_ts = - use_cascade_tag ? 3 : 4; // Max 4 uint8_ts in each Cascade Level. Only 3 left if we use the Cascade Tag - if (uint8_ts_to_copy > maxuint8_ts) { - uint8_ts_to_copy = maxuint8_ts; - } - for (count = 0; count < uint8_ts_to_copy; count++) { - buffer[index++] = uid->uiduint8_t[uid_index + count]; - } - } - // Now that the data has been copied we need to include the 8 bits in CT in currentLevelKnownBits - if (use_cascade_tag) { - current_level_known_bits += 8; - } - - // Repeat anti collision loop until we can transmit all UID bits + BCC and receive a SAK - max 32 iterations. - select_done = false; - while (!select_done) { - // Find out how many bits and uint8_ts to send and receive. - if (current_level_known_bits >= 32) { // All UID bits in this Cascade Level are known. This is a SELECT. - - if (response_length < 4) { - ESP_LOGW(TAG, "Not enough data received."); - return STATUS_INVALID; - } - - // Serial.print(F("SELECT: currentLevelKnownBits=")); Serial.println(currentLevelKnownBits, DEC); - buffer[1] = 0x70; // NVB - Number of Valid Bits: Seven whole uint8_ts - // Calculate BCC - Block Check Character - buffer[6] = buffer[2] ^ buffer[3] ^ buffer[4] ^ buffer[5]; - // Calculate CRC_A - result = pcd_calculate_crc_(buffer, 7, &buffer[7]); - if (result != STATUS_OK) { - return result; - } - tx_last_bits = 0; // 0 => All 8 bits are valid. - buffer_used = 9; - // Store response in the last 3 uint8_ts of buffer (BCC and CRC_A - not needed after tx) - response_buffer = &buffer[6]; - response_length = 3; - } else { // This is an ANTICOLLISION. - // Serial.print(F("ANTICOLLISION: currentLevelKnownBits=")); Serial.println(currentLevelKnownBits, DEC); - tx_last_bits = current_level_known_bits % 8; - count = current_level_known_bits / 8; // Number of whole uint8_ts in the UID part. - index = 2 + count; // Number of whole uint8_ts: SEL + NVB + UIDs - buffer[1] = (index << 4) + tx_last_bits; // NVB - Number of Valid Bits - buffer_used = index + (tx_last_bits ? 1 : 0); - // Store response in the unused part of buffer - response_buffer = &buffer[index]; - response_length = sizeof(buffer) - index; - } - - // Set bit adjustments - rx_align = tx_last_bits; // Having a separate variable is overkill. But it makes the next line easier to read. - pcd_write_register_( - BIT_FRAMING_REG, - (rx_align << 4) + tx_last_bits); // RxAlign = BitFramingReg[6..4]. TxLastBits = BitFramingReg[2..0] - - // Transmit the buffer and receive the response. - result = pcd_transceive_data_(buffer, buffer_used, response_buffer, &response_length, &tx_last_bits, rx_align); - if (result == STATUS_COLLISION) { // More than one PICC in the field => collision. - uint8_t value_of_coll_reg = pcd_read_register_( - COLL_REG); // CollReg[7..0] bits are: ValuesAfterColl reserved CollPosNotValid CollPos[4:0] - if (value_of_coll_reg & 0x20) { // CollPosNotValid - return STATUS_COLLISION; // Without a valid collision position we cannot continue - } - uint8_t collision_pos = value_of_coll_reg & 0x1F; // Values 0-31, 0 means bit 32. - if (collision_pos == 0) { - collision_pos = 32; - } - if (collision_pos <= current_level_known_bits) { // No progress - should not happen - return STATUS_INTERNAL_ERROR; - } - // Choose the PICC with the bit set. - current_level_known_bits = collision_pos; - count = current_level_known_bits % 8; // The bit to modify - check_bit = (current_level_known_bits - 1) % 8; - index = 1 + (current_level_known_bits / 8) + (count ? 1 : 0); // First uint8_t is index 0. - if (response_length > 2) // Note: Otherwise buffer[index] might be not initialized - buffer[index] |= (1 << check_bit); - } else if (result != STATUS_OK) { - return result; - } else { // STATUS_OK - if (current_level_known_bits >= 32) { // This was a SELECT. - select_done = true; // No more anticollision - // We continue below outside the while. - } else { // This was an ANTICOLLISION. - // We now have all 32 bits of the UID in this Cascade Level - current_level_known_bits = 32; - // Run loop again to do the SELECT. - } - } - } // End of while (!selectDone) - - // We do not check the CBB - it was constructed by us above. - - // Copy the found UID uint8_ts from buffer[] to uid->uiduint8_t[] - index = (buffer[2] == PICC_CMD_CT) ? 3 : 2; // source index in buffer[] - uint8_ts_to_copy = (buffer[2] == PICC_CMD_CT) ? 3 : 4; - for (count = 0; count < uint8_ts_to_copy; count++) { - uid->uiduint8_t[uid_index + count] = buffer[index++]; - } - - // Check response SAK (Select Acknowledge) - if (response_length != 3 || tx_last_bits != 0) { // SAK must be exactly 24 bits (1 uint8_t + CRC_A). - return STATUS_ERROR; - } - // Verify CRC_A - do our own calculation and store the control in buffer[2..3] - those uint8_ts are not needed - // anymore. - result = pcd_calculate_crc_(response_buffer, 1, &buffer[2]); - if (result != STATUS_OK) { - return result; - } - if ((buffer[2] != response_buffer[1]) || (buffer[3] != response_buffer[2])) { - return STATUS_CRC_WRONG; - } - if (response_buffer[0] & 0x04) { // Cascade bit set - UID not complete yes - cascade_level++; - } else { - uid_complete = true; - uid->sak = response_buffer[0]; - } - } // End of while (!uidComplete) - - // Set correct uid->size - uid->size = 3 * cascade_level + 1; - - return STATUS_OK; -} - -bool RC522BinarySensor::process(const uint8_t *data, uint8_t len) { - if (len != this->uid_.size()) - return false; - - for (uint8_t i = 0; i < len; i++) { - if (data[i] != this->uid_[i]) - return false; - } - - this->publish_state(true); - this->found_ = true; - return true; -} -void RC522Trigger::process(const uint8_t *uid, uint8_t uid_length) { - char buf[32]; - format_uid(buf, uid, uid_length); - this->trigger(std::string(buf)); -} - } // namespace rc522_spi } // namespace esphome diff --git a/esphome/components/rc522_spi/rc522_spi.h b/esphome/components/rc522_spi/rc522_spi.h index 51d0a9cb69..58edbbed4f 100644 --- a/esphome/components/rc522_spi/rc522_spi.h +++ b/esphome/components/rc522_spi/rc522_spi.h @@ -10,292 +10,46 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/automation.h" -#include "esphome/components/binary_sensor/binary_sensor.h" +#include "esphome/components/rc522/rc522.h" #include "esphome/components/spi/spi.h" namespace esphome { namespace rc522_spi { -class RC522BinarySensor; -class RC522Trigger; - -class RC522 : public PollingComponent, - public spi::SPIDevice { +class RC522Spi : public rc522::RC522, + public spi::SPIDevice { public: void setup() override; void dump_config() override; - void update() override; - float get_setup_priority() const override { return setup_priority::DATA; }; - - void loop() override; - - void register_tag(RC522BinarySensor *tag) { this->binary_sensors_.push_back(tag); } - void register_trigger(RC522Trigger *trig) { this->triggers_.push_back(trig); } - - void set_reset_pin(GPIOPin *reset) { this->reset_pin_ = reset; } - protected: - enum PcdRegister : uint8_t { - // Page 0: Command and status - // 0x00 // reserved for future use - COMMAND_REG = 0x01 << 1, // starts and stops command execution - COM_I_EN_REG = 0x02 << 1, // enable and disable interrupt request control bits - DIV_I_EN_REG = 0x03 << 1, // enable and disable interrupt request control bits - COM_IRQ_REG = 0x04 << 1, // interrupt request bits - DIV_IRQ_REG = 0x05 << 1, // interrupt request bits - ERROR_REG = 0x06 << 1, // error bits showing the error status of the last command executed - STATUS1_REG = 0x07 << 1, // communication status bits - STATUS2_REG = 0x08 << 1, // receiver and transmitter status bits - FIFO_DATA_REG = 0x09 << 1, // input and output of 64 uint8_t FIFO buffer - FIFO_LEVEL_REG = 0x0A << 1, // number of uint8_ts stored in the FIFO buffer - WATER_LEVEL_REG = 0x0B << 1, // level for FIFO underflow and overflow warning - CONTROL_REG = 0x0C << 1, // miscellaneous control registers - BIT_FRAMING_REG = 0x0D << 1, // adjustments for bit-oriented frames - COLL_REG = 0x0E << 1, // bit position of the first bit-collision detected on the RF interface - // 0x0F // reserved for future use - - // Page 1: Command - // 0x10 // reserved for future use - MODE_REG = 0x11 << 1, // defines general modes for transmitting and receiving - TX_MODE_REG = 0x12 << 1, // defines transmission data rate and framing - RX_MODE_REG = 0x13 << 1, // defines reception data rate and framing - TX_CONTROL_REG = 0x14 << 1, // controls the logical behavior of the antenna driver pins TX1 and TX2 - TX_ASK_REG = 0x15 << 1, // controls the setting of the transmission modulation - TX_SEL_REG = 0x16 << 1, // selects the internal sources for the antenna driver - RX_SEL_REG = 0x17 << 1, // selects internal receiver settings - RX_THRESHOLD_REG = 0x18 << 1, // selects thresholds for the bit decoder - DEMOD_REG = 0x19 << 1, // defines demodulator settings - // 0x1A // reserved for future use - // 0x1B // reserved for future use - MF_TX_REG = 0x1C << 1, // controls some MIFARE communication transmit parameters - MF_RX_REG = 0x1D << 1, // controls some MIFARE communication receive parameters - // 0x1E // reserved for future use - SERIAL_SPEED_REG = 0x1F << 1, // selects the speed of the serial UART interface - - // Page 2: Configuration - // 0x20 // reserved for future use - CRC_RESULT_REG_H = 0x21 << 1, // shows the MSB and LSB values of the CRC calculation - CRC_RESULT_REG_L = 0x22 << 1, - // 0x23 // reserved for future use - MOD_WIDTH_REG = 0x24 << 1, // controls the ModWidth setting? - // 0x25 // reserved for future use - RF_CFG_REG = 0x26 << 1, // configures the receiver gain - GS_N_REG = 0x27 << 1, // selects the conductance of the antenna driver pins TX1 and TX2 for modulation - CW_GS_P_REG = 0x28 << 1, // defines the conductance of the p-driver output during periods of no modulation - MOD_GS_P_REG = 0x29 << 1, // defines the conductance of the p-driver output during periods of modulation - T_MODE_REG = 0x2A << 1, // defines settings for the internal timer - T_PRESCALER_REG = 0x2B << 1, // the lower 8 bits of the TPrescaler value. The 4 high bits are in TModeReg. - T_RELOAD_REG_H = 0x2C << 1, // defines the 16-bit timer reload value - T_RELOAD_REG_L = 0x2D << 1, - T_COUNTER_VALUE_REG_H = 0x2E << 1, // shows the 16-bit timer value - T_COUNTER_VALUE_REG_L = 0x2F << 1, - - // Page 3: Test Registers - // 0x30 // reserved for future use - TEST_SEL1_REG = 0x31 << 1, // general test signal configuration - TEST_SEL2_REG = 0x32 << 1, // general test signal configuration - TEST_PIN_EN_REG = 0x33 << 1, // enables pin output driver on pins D1 to D7 - TEST_PIN_VALUE_REG = 0x34 << 1, // defines the values for D1 to D7 when it is used as an I/O bus - TEST_BUS_REG = 0x35 << 1, // shows the status of the internal test bus - AUTO_TEST_REG = 0x36 << 1, // controls the digital self-test - VERSION_REG = 0x37 << 1, // shows the software version - ANALOG_TEST_REG = 0x38 << 1, // controls the pins AUX1 and AUX2 - TEST_DA_C1_REG = 0x39 << 1, // defines the test value for TestDAC1 - TEST_DA_C2_REG = 0x3A << 1, // defines the test value for TestDAC2 - TEST_ADC_REG = 0x3B << 1 // shows the value of ADC I and Q channels - // 0x3C // reserved for production tests - // 0x3D // reserved for production tests - // 0x3E // reserved for production tests - // 0x3F // reserved for production tests - }; - - // MFRC522 commands. Described in chapter 10 of the datasheet. - enum PcdCommand : uint8_t { - PCD_IDLE = 0x00, // no action, cancels current command execution - PCD_MEM = 0x01, // stores 25 uint8_ts into the internal buffer - PCD_GENERATE_RANDOM_ID = 0x02, // generates a 10-uint8_t random ID number - PCD_CALC_CRC = 0x03, // activates the CRC coprocessor or performs a self-test - PCD_TRANSMIT = 0x04, // transmits data from the FIFO buffer - PCD_NO_CMD_CHANGE = 0x07, // no command change, can be used to modify the CommandReg register bits without - // affecting the command, for example, the PowerDown bit - PCD_RECEIVE = 0x08, // activates the receiver circuits - PCD_TRANSCEIVE = - 0x0C, // transmits data from FIFO buffer to antenna and automatically activates the receiver after transmission - PCD_MF_AUTHENT = 0x0E, // performs the MIFARE standard authentication as a reader - PCD_SOFT_RESET = 0x0F // resets the MFRC522 - }; - - // Commands sent to the PICC. - enum PiccCommand : uint8_t { - // The commands used by the PCD to manage communication with several PICCs (ISO 14443-3, Type A, section 6.4) - PICC_CMD_REQA = 0x26, // REQuest command, Type A. Invites PICCs in state IDLE to go to READY and prepare for - // anticollision or selection. 7 bit frame. - PICC_CMD_WUPA = 0x52, // Wake-UP command, Type A. Invites PICCs in state IDLE and HALT to go to READY(*) and - // prepare for anticollision or selection. 7 bit frame. - PICC_CMD_CT = 0x88, // Cascade Tag. Not really a command, but used during anti collision. - PICC_CMD_SEL_CL1 = 0x93, // Anti collision/Select, Cascade Level 1 - PICC_CMD_SEL_CL2 = 0x95, // Anti collision/Select, Cascade Level 2 - PICC_CMD_SEL_CL3 = 0x97, // Anti collision/Select, Cascade Level 3 - PICC_CMD_HLTA = 0x50, // HaLT command, Type A. Instructs an ACTIVE PICC to go to state HALT. - PICC_CMD_RATS = 0xE0, // Request command for Answer To Reset. - // The commands used for MIFARE Classic (from http://www.mouser.com/ds/2/302/MF1S503x-89574.pdf, Section 9) - // Use PCD_MFAuthent to authenticate access to a sector, then use these commands to read/write/modify the blocks on - // the sector. - // The read/write commands can also be used for MIFARE Ultralight. - PICC_CMD_MF_AUTH_KEY_A = 0x60, // Perform authentication with Key A - PICC_CMD_MF_AUTH_KEY_B = 0x61, // Perform authentication with Key B - PICC_CMD_MF_READ = - 0x30, // Reads one 16 uint8_t block from the authenticated sector of the PICC. Also used for MIFARE Ultralight. - PICC_CMD_MF_WRITE = 0xA0, // Writes one 16 uint8_t block to the authenticated sector of the PICC. Called - // "COMPATIBILITY WRITE" for MIFARE Ultralight. - PICC_CMD_MF_DECREMENT = - 0xC0, // Decrements the contents of a block and stores the result in the internal data register. - PICC_CMD_MF_INCREMENT = - 0xC1, // Increments the contents of a block and stores the result in the internal data register. - PICC_CMD_MF_RESTORE = 0xC2, // Reads the contents of a block into the internal data register. - PICC_CMD_MF_TRANSFER = 0xB0, // Writes the contents of the internal data register to a block. - // The commands used for MIFARE Ultralight (from http://www.nxp.com/documents/data_sheet/MF0ICU1.pdf, Section 8.6) - // The PICC_CMD_MF_READ and PICC_CMD_MF_WRITE can also be used for MIFARE Ultralight. - PICC_CMD_UL_WRITE = 0xA2 // Writes one 4 uint8_t page to the PICC. - }; - - // Return codes from the functions in this class. Remember to update GetStatusCodeName() if you add more. - // last value set to 0xff, then compiler uses less ram, it seems some optimisations are triggered - enum StatusCode : uint8_t { - STATUS_OK, // Success - STATUS_ERROR, // Error in communication - STATUS_COLLISION, // Collission detected - STATUS_TIMEOUT, // Timeout in communication. - STATUS_NO_ROOM, // A buffer is not big enough. - STATUS_INTERNAL_ERROR, // Internal error in the code. Should not happen ;-) - STATUS_INVALID, // Invalid argument. - STATUS_CRC_WRONG, // The CRC_A does not match - STATUS_MIFARE_NACK = 0xff // A MIFARE PICC responded with NAK. - }; - - // A struct used for passing the UID of a PICC. - using Uid = struct { - uint8_t size; // Number of uint8_ts in the UID. 4, 7 or 10. - uint8_t uiduint8_t[10]; - uint8_t sak; // The SAK (Select acknowledge) uint8_t returned from the PICC after successful selection. - }; - - Uid uid_; - uint32_t update_wait_{0}; - - void pcd_reset_(); - void initialize_(); - void pcd_antenna_on_(); - uint8_t pcd_read_register_(PcdRegister reg ///< The register to read from. One of the PCD_Register enums. - ); + uint8_t pcd_read_register(PcdRegister reg ///< The register to read from. One of the PCD_Register enums. + ) override; /** * Reads a number of uint8_ts from the specified register in the MFRC522 chip. * The interface is described in the datasheet section 8.1.2. */ - void pcd_read_register_(PcdRegister reg, ///< The register to read from. One of the PCD_Register enums. - uint8_t count, ///< The number of uint8_ts to read - uint8_t *values, ///< uint8_t array to store the values in. - uint8_t rx_align ///< Only bit positions rxAlign..7 in values[0] are updated. - ); - void pcd_write_register_(PcdRegister reg, ///< The register to write to. One of the PCD_Register enums. - uint8_t value ///< The value to write. - ); + void pcd_read_register(PcdRegister reg, ///< The register to read from. One of the PCD_Register enums. + uint8_t count, ///< The number of uint8_ts to read + uint8_t *values, ///< uint8_t array to store the values in. + uint8_t rx_align ///< Only bit positions rxAlign..7 in values[0] are updated. + ) override; + void pcd_write_register(PcdRegister reg, ///< The register to write to. One of the PCD_Register enums. + uint8_t value ///< The value to write. + ) override; /** * Writes a number of uint8_ts to the specified register in the MFRC522 chip. * The interface is described in the datasheet section 8.1.2. */ - void pcd_write_register_(PcdRegister reg, ///< The register to write to. One of the PCD_Register enums. - uint8_t count, ///< The number of uint8_ts to write to the register - uint8_t *values ///< The values to write. uint8_t array. - ); - - StatusCode picc_request_a_( - uint8_t *buffer_atqa, ///< The buffer to store the ATQA (Answer to request) in - uint8_t *buffer_size ///< Buffer size, at least two uint8_ts. Also number of uint8_ts returned if STATUS_OK. - ); - StatusCode picc_reqa_or_wupa_( - uint8_t command, ///< The command to send - PICC_CMD_REQA or PICC_CMD_WUPA - uint8_t *buffer_atqa, ///< The buffer to store the ATQA (Answer to request) in - uint8_t *buffer_size ///< Buffer size, at least two uint8_ts. Also number of uint8_ts returned if STATUS_OK. - ); - void pcd_set_register_bit_mask_(PcdRegister reg, ///< The register to update. One of the PCD_Register enums. - uint8_t mask ///< The bits to set. - ); - void pcd_clear_register_bit_mask_(PcdRegister reg, ///< The register to update. One of the PCD_Register enums. - uint8_t mask ///< The bits to clear. - ); - - StatusCode pcd_transceive_data_(uint8_t *send_data, uint8_t send_len, uint8_t *back_data, uint8_t *back_len, - uint8_t *valid_bits = nullptr, uint8_t rx_align = 0, bool check_crc = false); - StatusCode pcd_communicate_with_picc_(uint8_t command, uint8_t wait_i_rq, uint8_t *send_data, uint8_t send_len, - uint8_t *back_data = nullptr, uint8_t *back_len = nullptr, - uint8_t *valid_bits = nullptr, uint8_t rx_align = 0, bool check_crc = false); - StatusCode pcd_calculate_crc_( - uint8_t *data, ///< In: Pointer to the data to transfer to the FIFO for CRC calculation. - uint8_t length, ///< In: The number of uint8_ts to transfer. - uint8_t *result ///< Out: Pointer to result buffer. Result is written to result[0..1], low uint8_t first. - ); - RC522::StatusCode picc_is_new_card_present_(); - bool picc_read_card_serial_(); - StatusCode picc_select_( - Uid *uid, ///< Pointer to Uid struct. Normally output, but can also be used to supply a known UID. - uint8_t valid_bits = 0 ///< The number of known UID bits supplied in *uid. Normally 0. If set you must also - ///< supply uid->size. - ); - - /** Read a data frame from the RC522 and return the result as a vector. - * - * Note that is_ready needs to be checked first before requesting this method. - * - * On failure, an empty vector is returned. - */ - std::vector r_c522_read_data_(); - - GPIOPin *reset_pin_{nullptr}; - uint8_t reset_count_{0}; - uint32_t reset_timeout_{0}; - bool initialize_pending_{false}; - std::vector binary_sensors_; - std::vector triggers_; - - enum RC522Error { - NONE = 0, - RESET_FAILED, - } error_code_{NONE}; + void pcd_write_register(PcdRegister reg, ///< The register to write to. One of the PCD_Register enums. + uint8_t count, ///< The number of uint8_ts to write to the register + uint8_t *values ///< The values to write. uint8_t array. + ) override; }; -class RC522BinarySensor : public binary_sensor::BinarySensor { - public: - void set_uid(const std::vector &uid) { uid_ = uid; } - - bool process(const uint8_t *data, uint8_t len); - - void on_scan_end() { - if (!this->found_) { - this->publish_state(false); - } - this->found_ = false; - } - - protected: - std::vector uid_; - bool found_{false}; -}; - -class RC522Trigger : public Trigger { - public: - void process(const uint8_t *uid, uint8_t uid_length); -}; - -#ifndef MFRC522_SPICLOCK -#define MFRC522_SPICLOCK SPI_CLOCK_DIV4 // MFRC522 accept upto 10MHz -#endif - } // namespace rc522_spi } // namespace esphome diff --git a/tests/test1.yaml b/tests/test1.yaml index 58fc7d4bb2..ef98c10ad5 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -201,7 +201,7 @@ mcp23s08: - id: 'mcp23s08_hub' cs_pin: GPIO12 deviceaddress: 0 - + mcp23s17: - id: 'mcp23s17_hub' cs_pin: GPIO12 @@ -813,7 +813,7 @@ esp32_touch: binary_sensor: - platform: gpio - name: "MCP23S08 Pin #1" + name: 'MCP23S08 Pin #1' pin: mcp23s08: mcp23s08_hub # Use pin number 1 @@ -822,7 +822,7 @@ binary_sensor: mode: INPUT_PULLUP inverted: False - platform: gpio - name: "MCP23S17 Pin #1" + name: 'MCP23S17 Pin #1' pin: mcp23s17: mcp23s17_hub # Use pin number 1 @@ -1391,7 +1391,7 @@ climate: switch: - platform: gpio - name: "MCP23S08 Pin #0" + name: 'MCP23S08 Pin #0' pin: mcp23s08: mcp23s08_hub # Use pin number 0 @@ -1399,7 +1399,7 @@ switch: mode: OUTPUT inverted: False - platform: gpio - name: "MCP23S17 Pin #0" + name: 'MCP23S17 Pin #0' pin: mcp23s17: mcp23s17_hub # Use pin number 0 @@ -1823,6 +1823,12 @@ rc522_spi: - lambda: |- ESP_LOGD("main", "Found tag %s", x.c_str()); +rc522_i2c: + update_interval: 1s + on_tag: + - lambda: |- + ESP_LOGD("main", "Found tag %s", x.c_str()); + gps: time: @@ -1933,7 +1939,7 @@ text_sensor: value: '0' - canbus.send: can_id: 23 - data: [ 0x10, 0x20, 0x30 ] + data: [0x10, 0x20, 0x30] - platform: template name: Template Text Sensor id: template_text @@ -1967,15 +1973,15 @@ canbus: can_id: 4 bit_rate: 50kbps on_frame: - - can_id: 500 - then: - - lambda: |- - std::string b(x.begin(), x.end()); - ESP_LOGD("canid 500", "%s", &b[0] ); - - can_id: 23 - then: - - if: - condition: - lambda: 'return x[0] == 0x11;' - then: - light.toggle: living_room_lights + - can_id: 500 + then: + - lambda: |- + std::string b(x.begin(), x.end()); + ESP_LOGD("canid 500", "%s", &b[0] ); + - can_id: 23 + then: + - if: + condition: + lambda: 'return x[0] == 0x11;' + then: + light.toggle: living_room_lights From bf453ad8cd64848e9d23db70ec46845812a19918 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=B6sch?= Date: Tue, 12 Jan 2021 19:37:22 +0100 Subject: [PATCH 011/111] make time components polling components (#1443) * make real time clock components polling components * add test --- esphome/components/ds1307/ds1307.cpp | 3 ++- esphome/components/ds1307/ds1307.h | 1 + esphome/components/gps/time/__init__.py | 4 ++-- esphome/components/gps/time/gps_time.h | 4 +--- .../homeassistant/time/homeassistant_time.cpp | 14 +++++--------- .../homeassistant/time/homeassistant_time.h | 1 + esphome/components/sntp/sntp_component.cpp | 1 + esphome/components/sntp/sntp_component.h | 1 + esphome/components/time/__init__.py | 4 ++-- esphome/components/time/real_time_clock.cpp | 2 +- esphome/components/time/real_time_clock.h | 2 +- tests/test1.yaml | 4 ++-- 12 files changed, 20 insertions(+), 21 deletions(-) diff --git a/esphome/components/ds1307/ds1307.cpp b/esphome/components/ds1307/ds1307.cpp index 990767ddd4..599ec16e4f 100644 --- a/esphome/components/ds1307/ds1307.cpp +++ b/esphome/components/ds1307/ds1307.cpp @@ -14,9 +14,10 @@ void DS1307Component::setup() { if (!this->read_rtc_()) { this->mark_failed(); } - this->set_interval(15 * 60 * 1000, [&]() { this->read(); }); } +void DS1307Component::update() { this->read(); } + void DS1307Component::dump_config() { ESP_LOGCONFIG(TAG, "DS1307:"); LOG_I2C_DEVICE(this); diff --git a/esphome/components/ds1307/ds1307.h b/esphome/components/ds1307/ds1307.h index 1f732a7c7d..8e318bf395 100644 --- a/esphome/components/ds1307/ds1307.h +++ b/esphome/components/ds1307/ds1307.h @@ -10,6 +10,7 @@ namespace ds1307 { class DS1307Component : public time::RealTimeClock, public i2c::I2CDevice { public: void setup() override; + void update() override; void dump_config() override; float get_setup_priority() const override; void read(); diff --git a/esphome/components/gps/time/__init__.py b/esphome/components/gps/time/__init__.py index bf746d19b2..421d2e6717 100644 --- a/esphome/components/gps/time/__init__.py +++ b/esphome/components/gps/time/__init__.py @@ -6,12 +6,12 @@ from .. import gps_ns, GPSListener, CONF_GPS_ID, GPS DEPENDENCIES = ['gps'] -GPSTime = gps_ns.class_('GPSTime', time_.RealTimeClock, GPSListener) +GPSTime = gps_ns.class_('GPSTime', cg.PollingComponent, time_.RealTimeClock, GPSListener) CONFIG_SCHEMA = time_.TIME_SCHEMA.extend({ cv.GenerateID(): cv.declare_id(GPSTime), cv.GenerateID(CONF_GPS_ID): cv.use_id(GPS), -}).extend(cv.COMPONENT_SCHEMA) +}).extend(cv.polling_component_schema('5min')) def to_code(config): diff --git a/esphome/components/gps/time/gps_time.h b/esphome/components/gps/time/gps_time.h index f6462be3e0..a1f69a7130 100644 --- a/esphome/components/gps/time/gps_time.h +++ b/esphome/components/gps/time/gps_time.h @@ -9,13 +9,11 @@ namespace gps { class GPSTime : public time::RealTimeClock, public GPSListener { public: + void update() override { this->from_tiny_gps_(this->get_tiny_gps()); }; void on_update(TinyGPSPlus &tiny_gps) override { if (!this->has_time_) this->from_tiny_gps_(tiny_gps); } - void setup() override { - this->set_interval(5 * 60 * 1000, [this]() { this->from_tiny_gps_(this->get_tiny_gps()); }); - } protected: void from_tiny_gps_(TinyGPSPlus &tiny_gps); diff --git a/esphome/components/homeassistant/time/homeassistant_time.cpp b/esphome/components/homeassistant/time/homeassistant_time.cpp index e9d97690fb..9ace8cf67f 100644 --- a/esphome/components/homeassistant/time/homeassistant_time.cpp +++ b/esphome/components/homeassistant/time/homeassistant_time.cpp @@ -10,17 +10,13 @@ void HomeassistantTime::dump_config() { ESP_LOGCONFIG(TAG, "Home Assistant Time:"); ESP_LOGCONFIG(TAG, " Timezone: '%s'", this->timezone_.c_str()); } -float HomeassistantTime::get_setup_priority() const { return setup_priority::DATA; } -void HomeassistantTime::setup() { - global_homeassistant_time = this; - this->set_interval(15 * 60 * 1000, []() { - // re-request time every 15 minutes - api::global_api_server->request_time(); - }); -} +float HomeassistantTime::get_setup_priority() const { return setup_priority::DATA; } + +void HomeassistantTime::setup() { global_homeassistant_time = this; } + +void HomeassistantTime::update() { api::global_api_server->request_time(); } HomeassistantTime *global_homeassistant_time = nullptr; - } // namespace homeassistant } // namespace esphome diff --git a/esphome/components/homeassistant/time/homeassistant_time.h b/esphome/components/homeassistant/time/homeassistant_time.h index 8ab09d1185..94f4704c2f 100644 --- a/esphome/components/homeassistant/time/homeassistant_time.h +++ b/esphome/components/homeassistant/time/homeassistant_time.h @@ -10,6 +10,7 @@ namespace homeassistant { class HomeassistantTime : public time::RealTimeClock { public: void setup() override; + void update() override; void dump_config() override; void set_epoch_time(uint32_t epoch) { this->synchronize_epoch_(epoch); } float get_setup_priority() const override; diff --git a/esphome/components/sntp/sntp_component.cpp b/esphome/components/sntp/sntp_component.cpp index 253f3ba2c1..641d66091c 100644 --- a/esphome/components/sntp/sntp_component.cpp +++ b/esphome/components/sntp/sntp_component.cpp @@ -42,6 +42,7 @@ void SNTPComponent::dump_config() { ESP_LOGCONFIG(TAG, " Server 3: '%s'", this->server_3_.c_str()); ESP_LOGCONFIG(TAG, " Timezone: '%s'", this->timezone_.c_str()); } +void SNTPComponent::update() {} void SNTPComponent::loop() { if (this->has_time_) return; diff --git a/esphome/components/sntp/sntp_component.h b/esphome/components/sntp/sntp_component.h index 785f458d6c..4c70a6b09f 100644 --- a/esphome/components/sntp/sntp_component.h +++ b/esphome/components/sntp/sntp_component.h @@ -24,6 +24,7 @@ class SNTPComponent : public time::RealTimeClock { } float get_setup_priority() const override { return setup_priority::AFTER_WIFI; } + void update() override; void loop() override; protected: diff --git a/esphome/components/time/__init__.py b/esphome/components/time/__init__.py index 5f071aef11..e5ed5034ab 100644 --- a/esphome/components/time/__init__.py +++ b/esphome/components/time/__init__.py @@ -22,7 +22,7 @@ CODEOWNERS = ['@OttoWinter'] IS_PLATFORM_COMPONENT = True time_ns = cg.esphome_ns.namespace('time') -RealTimeClock = time_ns.class_('RealTimeClock', cg.Component) +RealTimeClock = time_ns.class_('RealTimeClock', cg.PollingComponent) CronTrigger = time_ns.class_('CronTrigger', automation.Trigger.template(), cg.Component) ESPTime = time_ns.struct('ESPTime') TimeHasTimeCondition = time_ns.class_('TimeHasTimeCondition', Condition) @@ -294,7 +294,7 @@ TIME_SCHEMA = cv.Schema({ cv.Optional(CONF_CRON): validate_cron_raw, cv.Optional(CONF_AT): validate_time_at, }, validate_cron_keys), -}) +}).extend(cv.polling_component_schema('15min')) @coroutine diff --git a/esphome/components/time/real_time_clock.cpp b/esphome/components/time/real_time_clock.cpp index cdcfcb14ad..7d7c1013dd 100644 --- a/esphome/components/time/real_time_clock.cpp +++ b/esphome/components/time/real_time_clock.cpp @@ -15,7 +15,7 @@ RealTimeClock::RealTimeClock() = default; void RealTimeClock::call_setup() { setenv("TZ", this->timezone_.c_str(), 1); tzset(); - this->setup(); + PollingComponent::call_setup(); } void RealTimeClock::synchronize_epoch_(uint32_t epoch) { struct timeval timev { diff --git a/esphome/components/time/real_time_clock.h b/esphome/components/time/real_time_clock.h index 8de4509089..880a4e9d5c 100644 --- a/esphome/components/time/real_time_clock.h +++ b/esphome/components/time/real_time_clock.h @@ -106,7 +106,7 @@ struct ESPTime { /// The C library (newlib) available on ESPs only supports TZ strings that specify an offset and DST info; /// you cannot specify zone names or paths to zoneinfo files. /// \see https://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html -class RealTimeClock : public Component { +class RealTimeClock : public PollingComponent { public: explicit RealTimeClock(); diff --git a/tests/test1.yaml b/tests/test1.yaml index ef98c10ad5..4598049732 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -1843,6 +1843,7 @@ time: then: - lambda: 'ESP_LOGD("main", "time");' - platform: gps + update_interval: 1h on_time: seconds: 0 minutes: /15 @@ -1851,13 +1852,12 @@ time: id: ds1307_time - platform: ds1307 id: ds1307_time + update_interval: never on_time: seconds: 0 then: ds1307.read - - cover: - platform: template name: 'Template Cover' From e7b1d2efaa8d944850395a22ff33ec21858e1f7a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Jan 2021 23:53:35 -0300 Subject: [PATCH 012/111] Bump voluptuous from 0.12.0 to 0.12.1 (#1411) Bumps [voluptuous](https://github.com/alecthomas/voluptuous) from 0.12.0 to 0.12.1. - [Release notes](https://github.com/alecthomas/voluptuous/releases) - [Changelog](https://github.com/alecthomas/voluptuous/blob/master/CHANGELOG.md) - [Commits](https://github.com/alecthomas/voluptuous/compare/0.12.0...0.12.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 78aa0442b9..ee73c4c747 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -voluptuous==0.12.0 +voluptuous==0.12.1 PyYAML==5.3.1 paho-mqtt==1.5.1 colorama==0.4.4 From 36089a1400747a5396ea0bc9a0ce5d75b0ca84fa Mon Sep 17 00:00:00 2001 From: SenexCrenshaw <35600301+SenexCrenshaw@users.noreply.github.com> Date: Wed, 13 Jan 2021 13:33:19 -0500 Subject: [PATCH 013/111] Updated Mcp3008 to support reference_voltage and voltage_sampler::VoltageSampler (#1387) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/mcp3008/mcp3008.cpp | 30 +++++++++++++++++--------- esphome/components/mcp3008/mcp3008.h | 19 +++++++++------- esphome/components/mcp3008/sensor.py | 14 ++++++++---- esphome/components/spi/spi.h | 1 + tests/test1.yaml | 11 ++++++++++ 5 files changed, 53 insertions(+), 22 deletions(-) diff --git a/esphome/components/mcp3008/mcp3008.cpp b/esphome/components/mcp3008/mcp3008.cpp index a4d019ab8f..d09c2e4e92 100644 --- a/esphome/components/mcp3008/mcp3008.cpp +++ b/esphome/components/mcp3008/mcp3008.cpp @@ -15,19 +15,18 @@ void MCP3008::setup() { void MCP3008::dump_config() { ESP_LOGCONFIG(TAG, "MCP3008:"); - LOG_PIN(" CS Pin: ", this->cs_); + LOG_PIN(" CS Pin:", this->cs_); } -float MCP3008::read_data_(uint8_t pin) { - uint8_t data_msb = 0; - uint8_t data_lsb = 0; +float MCP3008::read_data(uint8_t pin) { + uint8_t data_msb, data_lsb = 0; uint8_t command = ((0x01 << 7) | // start bit ((pin & 0x07) << 4)); // channel number this->enable(); - this->transfer_byte(0x01); + data_msb = this->transfer_byte(command) & 0x03; data_lsb = this->transfer_byte(0x00); @@ -35,18 +34,29 @@ float MCP3008::read_data_(uint8_t pin) { int data = data_msb << 8 | data_lsb; - return data / 1024.0f; + return data / 1023.0f; } -MCP3008Sensor::MCP3008Sensor(MCP3008 *parent, std::string name, uint8_t pin) +MCP3008Sensor::MCP3008Sensor(MCP3008 *parent, std::string name, uint8_t pin, float reference_voltage) : PollingComponent(1000), parent_(parent), pin_(pin) { this->set_name(name); + this->reference_voltage_ = reference_voltage; } + +float MCP3008Sensor::get_setup_priority() const { return setup_priority::DATA; } + void MCP3008Sensor::setup() { LOG_SENSOR("", "Setting up MCP3008 Sensor '%s'...", this); } -void MCP3008Sensor::update() { - float value_v = this->parent_->read_data_(pin_); - this->publish_state(value_v); +void MCP3008Sensor::dump_config() { + ESP_LOGCONFIG(TAG, "MCP3008Sensor:"); + ESP_LOGCONFIG(TAG, " Pin: %u", this->pin_); + ESP_LOGCONFIG(TAG, " Reference Voltage: %.2fV", this->reference_voltage_); } +float MCP3008Sensor::sample() { + float value_v = this->parent_->read_data(pin_); + value_v = (value_v * this->reference_voltage_); + return value_v; +} +void MCP3008Sensor::update() { this->publish_state(this->sample()); } } // namespace mcp3008 } // namespace esphome diff --git a/esphome/components/mcp3008/mcp3008.h b/esphome/components/mcp3008/mcp3008.h index 594bdc4204..129f299a14 100644 --- a/esphome/components/mcp3008/mcp3008.h +++ b/esphome/components/mcp3008/mcp3008.h @@ -4,38 +4,41 @@ #include "esphome/core/esphal.h" #include "esphome/components/sensor/sensor.h" #include "esphome/components/spi/spi.h" +#include "esphome/components/voltage_sampler/voltage_sampler.h" namespace esphome { namespace mcp3008 { -class MCP3008Sensor; - class MCP3008 : public Component, public spi::SPIDevice { // At 3.3V 2MHz is too fast 1.35MHz is about right + spi::DATA_RATE_75KHZ> { // Running at the slowest max speed supported by the + // mcp3008. 2.7v = 75ksps public: MCP3008() = default; void setup() override; void dump_config() override; float get_setup_priority() const override; + float read_data(uint8_t pin); protected: - float read_data_(uint8_t pin); - - friend class MCP3008Sensor; }; -class MCP3008Sensor : public PollingComponent, public sensor::Sensor { +class MCP3008Sensor : public PollingComponent, public sensor::Sensor, public voltage_sampler::VoltageSampler { public: - MCP3008Sensor(MCP3008 *parent, std::string name, uint8_t pin); + MCP3008Sensor(MCP3008 *parent, std::string name, uint8_t pin, float reference_voltage); + void set_reference_voltage(float reference_voltage) { reference_voltage_ = reference_voltage; } void setup() override; void update() override; + void dump_config() override; + float get_setup_priority() const override; + float sample() override; protected: MCP3008 *parent_; uint8_t pin_; + float reference_voltage_; }; } // namespace mcp3008 diff --git a/esphome/components/mcp3008/sensor.py b/esphome/components/mcp3008/sensor.py index ec7df0429a..9248d51a42 100644 --- a/esphome/components/mcp3008/sensor.py +++ b/esphome/components/mcp3008/sensor.py @@ -1,23 +1,29 @@ import esphome.codegen as cg import esphome.config_validation as cv -from esphome.components import sensor +from esphome.components import sensor, voltage_sampler from esphome.const import CONF_ID, CONF_NUMBER, CONF_NAME from . import mcp3008_ns, MCP3008 +AUTO_LOAD = ['voltage_sampler'] + DEPENDENCIES = ['mcp3008'] -MCP3008Sensor = mcp3008_ns.class_('MCP3008Sensor', sensor.Sensor, cg.PollingComponent) - +MCP3008Sensor = mcp3008_ns.class_('MCP3008Sensor', sensor.Sensor, cg.PollingComponent, + voltage_sampler.VoltageSampler) +CONF_REFERENCE_VOLTAGE = 'reference_voltage' CONF_MCP3008_ID = 'mcp3008_id' CONFIG_SCHEMA = sensor.SENSOR_SCHEMA.extend({ cv.GenerateID(): cv.declare_id(MCP3008Sensor), cv.GenerateID(CONF_MCP3008_ID): cv.use_id(MCP3008), cv.Required(CONF_NUMBER): cv.int_, + cv.Optional(CONF_REFERENCE_VOLTAGE, default='3.3V'): cv.voltage, }).extend(cv.polling_component_schema('1s')) def to_code(config): parent = yield cg.get_variable(config[CONF_MCP3008_ID]) - var = cg.new_Pvariable(config[CONF_ID], parent, config[CONF_NAME], config[CONF_NUMBER]) + var = cg.new_Pvariable(config[CONF_ID], parent, config[CONF_NAME], + config[CONF_NUMBER], config[CONF_REFERENCE_VOLTAGE]) yield cg.register_component(var, config) + yield sensor.register_sensor(var, config) diff --git a/esphome/components/spi/spi.h b/esphome/components/spi/spi.h index 38ed9fb1d4..64364d70c9 100644 --- a/esphome/components/spi/spi.h +++ b/esphome/components/spi/spi.h @@ -50,6 +50,7 @@ enum SPIClockPhase { */ enum SPIDataRate : uint32_t { DATA_RATE_1KHZ = 1000, + DATA_RATE_75KHZ = 75000, DATA_RATE_200KHZ = 200000, DATA_RATE_1MHZ = 1000000, DATA_RATE_2MHZ = 2000000, diff --git a/tests/test1.yaml b/tests/test1.yaml index 4598049732..f2ebca05ab 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -197,6 +197,10 @@ wled: adalight: +mcp3008: + - id: 'mcp3008_hub' + cs_pin: GPIO12 + mcp23s08: - id: 'mcp23s08_hub' cs_pin: GPIO12 @@ -207,6 +211,7 @@ mcp23s17: cs_pin: GPIO12 deviceaddress: 1 + sensor: - platform: adc pin: A0 @@ -801,6 +806,12 @@ sensor: id: ph_ezo address: 99 unit_of_measurement: 'pH' + - platform: mcp3008 + update_interval: 5s + mcp3008_id: 'mcp3008_hub' + id: freezer_temp_source + reference_voltage: 3.19 + number: 0 esp32_touch: setup_mode: False From 5fcd1e391da8b7f9b02799ddb381007d375e6164 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 15 Jan 2021 09:29:55 +1300 Subject: [PATCH 014/111] Add NDEF reading and writing to PN532 (#1351) --- CODEOWNERS | 1 + esphome/components/nfc/__init__.py | 7 + esphome/components/nfc/ndef_message.cpp | 106 ++++++++ esphome/components/nfc/ndef_message.h | 31 +++ esphome/components/nfc/ndef_record.cpp | 85 ++++++ esphome/components/nfc/ndef_record.h | 101 +++++++ esphome/components/nfc/nfc.cpp | 107 ++++++++ esphome/components/nfc/nfc.h | 57 ++++ esphome/components/nfc/nfc_tag.cpp | 9 + esphome/components/nfc/nfc_tag.h | 47 ++++ esphome/components/pn532/__init__.py | 34 ++- esphome/components/pn532/pn532.cpp | 179 +++++++++---- esphome/components/pn532/pn532.h | 66 ++++- .../components/pn532/pn532_mifare_classic.cpp | 249 ++++++++++++++++++ .../pn532/pn532_mifare_ultralight.cpp | 180 +++++++++++++ esphome/components/pn532_i2c/pn532_i2c.cpp | 2 +- 16 files changed, 1206 insertions(+), 55 deletions(-) create mode 100644 esphome/components/nfc/__init__.py create mode 100644 esphome/components/nfc/ndef_message.cpp create mode 100644 esphome/components/nfc/ndef_message.h create mode 100644 esphome/components/nfc/ndef_record.cpp create mode 100644 esphome/components/nfc/ndef_record.h create mode 100644 esphome/components/nfc/nfc.cpp create mode 100644 esphome/components/nfc/nfc.h create mode 100644 esphome/components/nfc/nfc_tag.cpp create mode 100644 esphome/components/nfc/nfc_tag.h create mode 100644 esphome/components/pn532/pn532_mifare_classic.cpp create mode 100644 esphome/components/pn532/pn532_mifare_ultralight.cpp diff --git a/CODEOWNERS b/CODEOWNERS index fb287b1cc9..5cf0f591d2 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -48,6 +48,7 @@ esphome/components/mcp23s17/* @SenexCrenshaw esphome/components/mcp2515/* @danielschramm @mvturnho esphome/components/mcp9808/* @k7hpn esphome/components/network/* @esphome/core +esphome/components/nfc/* @jesserockz esphome/components/ota/* @esphome/core esphome/components/output/* @esphome/core esphome/components/pid/* @OttoWinter diff --git a/esphome/components/nfc/__init__.py b/esphome/components/nfc/__init__.py new file mode 100644 index 0000000000..ae3c9a4c0a --- /dev/null +++ b/esphome/components/nfc/__init__.py @@ -0,0 +1,7 @@ +import esphome.codegen as cg + +CODEOWNERS = ['@jesserockz'] + +nfc_ns = cg.esphome_ns.namespace('nfc') + +NfcTag = nfc_ns.class_('NfcTag') diff --git a/esphome/components/nfc/ndef_message.cpp b/esphome/components/nfc/ndef_message.cpp new file mode 100644 index 0000000000..caba833932 --- /dev/null +++ b/esphome/components/nfc/ndef_message.cpp @@ -0,0 +1,106 @@ +#include "ndef_message.h" + +namespace esphome { +namespace nfc { + +static const char *TAG = "nfc.ndef_message"; + +NdefMessage::NdefMessage(std::vector &data) { + ESP_LOGV(TAG, "Building NdefMessage with %zu bytes", data.size()); + uint8_t index = 0; + while (index <= data.size()) { + uint8_t tnf_byte = data[index++]; + bool me = tnf_byte & 0x40; + bool sr = tnf_byte & 0x10; + bool il = tnf_byte & 0x08; + uint8_t tnf = tnf_byte & 0x07; + + ESP_LOGVV(TAG, "me=%s, sr=%s, il=%s, tnf=%d", YESNO(me), YESNO(sr), YESNO(il), tnf); + + auto record = new NdefRecord(); + record->set_tnf(tnf); + + uint8_t type_length = data[index++]; + uint32_t payload_length = 0; + if (sr) { + payload_length = data[index++]; + } else { + payload_length = (static_cast(data[index]) << 24) | (static_cast(data[index + 1]) << 16) | + (static_cast(data[index + 2]) << 8) | static_cast(data[index + 3]); + index += 4; + } + + uint8_t id_length = 0; + if (il) { + id_length = data[index++]; + } + + ESP_LOGVV(TAG, "Lengths: type=%d, payload=%d, id=%d", type_length, payload_length, id_length); + + std::string type_str(data.begin() + index, data.begin() + index + type_length); + record->set_type(type_str); + index += type_length; + + if (il) { + std::string id_str(data.begin() + index, data.begin() + index + id_length); + record->set_id(id_str); + index += id_length; + } + + uint8_t payload_identifier = 0x00; + if (type_str == "U") { + payload_identifier = data[index++]; + payload_length -= 1; + } + + std::string payload_str(data.begin() + index, data.begin() + index + payload_length); + + if (payload_identifier > 0x00 && payload_identifier <= PAYLOAD_IDENTIFIERS_COUNT) { + payload_str.insert(0, PAYLOAD_IDENTIFIERS[payload_identifier]); + } + + record->set_payload(payload_str); + index += payload_length; + + this->add_record(record); + ESP_LOGV(TAG, "Adding record type %s = %s", record->get_type().c_str(), record->get_payload().c_str()); + + if (me) + break; + } +} + +bool NdefMessage::add_record(NdefRecord *record) { + if (this->records_.size() >= MAX_NDEF_RECORDS) { + ESP_LOGE(TAG, "Too many records. Max: %d", MAX_NDEF_RECORDS); + return false; + } + this->records_.push_back(record); + return true; +} + +bool NdefMessage::add_text_record(const std::string &text) { return this->add_text_record(text, "en"); }; + +bool NdefMessage::add_text_record(const std::string &text, const std::string &encoding) { + std::string payload = to_string(text.length()) + encoding + text; + auto r = new NdefRecord(TNF_WELL_KNOWN, "T", payload); + return this->add_record(r); +} + +bool NdefMessage::add_uri_record(const std::string &uri) { + auto r = new NdefRecord(TNF_WELL_KNOWN, "U", uri); + return this->add_record(r); +} + +std::vector NdefMessage::encode() { + std::vector data; + + for (uint8_t i = 0; i < this->records_.size(); i++) { + auto encoded_record = this->records_[i]->encode(i == 0, (i + 1) == this->records_.size()); + data.insert(data.end(), encoded_record.begin(), encoded_record.end()); + } + return data; +} + +} // namespace nfc +} // namespace esphome diff --git a/esphome/components/nfc/ndef_message.h b/esphome/components/nfc/ndef_message.h new file mode 100644 index 0000000000..e47a7b992a --- /dev/null +++ b/esphome/components/nfc/ndef_message.h @@ -0,0 +1,31 @@ +#pragma once + +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" +#include "ndef_record.h" + +namespace esphome { +namespace nfc { + +static const uint8_t MAX_NDEF_RECORDS = 4; + +class NdefMessage { + public: + NdefMessage(){}; + NdefMessage(std::vector &data); + + std::vector get_records() { return this->records_; }; + + bool add_record(NdefRecord *record); + bool add_text_record(const std::string &text); + bool add_text_record(const std::string &text, const std::string &encoding); + bool add_uri_record(const std::string &uri); + + std::vector encode(); + + protected: + std::vector records_; +}; + +} // namespace nfc +} // namespace esphome diff --git a/esphome/components/nfc/ndef_record.cpp b/esphome/components/nfc/ndef_record.cpp new file mode 100644 index 0000000000..53fa221e41 --- /dev/null +++ b/esphome/components/nfc/ndef_record.cpp @@ -0,0 +1,85 @@ +#include "ndef_record.h" + +namespace esphome { +namespace nfc { + +static const char* TAG = "nfc.ndef_record"; + +uint32_t NdefRecord::get_encoded_size() { + uint32_t size = 2; + if (this->payload_.length() > 255) { + size += 4; + } else { + size += 1; + } + if (this->id_.length()) { + size += 1; + } + size += (this->type_.length() + this->payload_.length() + this->id_.length()); + return size; +} + +std::vector NdefRecord::encode(bool first, bool last) { + std::vector data; + + data.push_back(this->get_tnf_byte(first, last)); + + data.push_back(this->type_.length()); + + uint8_t payload_prefix = 0x00; + uint8_t payload_prefix_length = 0x00; + for (uint8_t i = 1; i < PAYLOAD_IDENTIFIERS_COUNT; i++) { + std::string prefix = PAYLOAD_IDENTIFIERS[i]; + if (this->payload_.substr(0, prefix.length()).find(prefix) != std::string::npos) { + payload_prefix = i; + payload_prefix_length = prefix.length(); + break; + } + } + + uint32_t payload_length = this->payload_.length() - payload_prefix_length + 1; + + if (payload_length <= 255) { + data.push_back(payload_length); + } else { + data.push_back(0); + data.push_back(0); + data.push_back((payload_length >> 8) & 0xFF); + data.push_back(payload_length & 0xFF); + } + + if (this->id_.length()) { + data.push_back(this->id_.length()); + } + + data.insert(data.end(), this->type_.begin(), this->type_.end()); + + if (this->id_.length()) { + data.insert(data.end(), this->id_.begin(), this->id_.end()); + } + + data.push_back(payload_prefix); + + data.insert(data.end(), this->payload_.begin() + payload_prefix_length, this->payload_.end()); + return data; +} + +uint8_t NdefRecord::get_tnf_byte(bool first, bool last) { + uint8_t value = this->tnf_; + if (first) { + value = value | 0x80; + } + if (last) { + value = value | 0x40; + } + if (this->payload_.length() <= 255) { + value = value | 0x10; + } + if (this->id_.length()) { + value = value | 0x08; + } + return value; +}; + +} // namespace nfc +} // namespace esphome diff --git a/esphome/components/nfc/ndef_record.h b/esphome/components/nfc/ndef_record.h new file mode 100644 index 0000000000..bc03ccf093 --- /dev/null +++ b/esphome/components/nfc/ndef_record.h @@ -0,0 +1,101 @@ +#pragma once + +#include "esphome/core/log.h" +#include "esphome/core/helpers.h" + +namespace esphome { +namespace nfc { + +static const uint8_t TNF_EMPTY = 0x00; +static const uint8_t TNF_WELL_KNOWN = 0x01; +static const uint8_t TNF_MIME_MEDIA = 0x02; +static const uint8_t TNF_ABSOLUTE_URI = 0x03; +static const uint8_t TNF_EXTERNAL_TYPE = 0x04; +static const uint8_t TNF_UNKNOWN = 0x05; +static const uint8_t TNF_UNCHANGED = 0x06; +static const uint8_t TNF_RESERVED = 0x07; + +static const uint8_t PAYLOAD_IDENTIFIERS_COUNT = 0x23; +static const char *PAYLOAD_IDENTIFIERS[] = {"", + "http://www.", + "https://www.", + "http://", + "https://", + "tel:", + "mailto:", + "ftp://anonymous:anonymous@", + "ftp://ftp.", + "ftps://", + "sftp://", + "smb://", + "nfs://", + "ftp://", + "dav://", + "news:", + "telnet://", + "imap:", + "rtsp://", + "urn:", + "pop:", + "sip:", + "sips:", + "tftp:", + "btspp://", + "btl2cap://", + "btgoep://", + "tcpobex://", + "irdaobex://", + "file://", + "urn:epc:id:", + "urn:epc:tag:", + "urn:epc:pat:", + "urn:epc:raw:", + "urn:epc:", + "urn:nfc:"}; + +class NdefRecord { + public: + NdefRecord(){}; + NdefRecord(uint8_t tnf, const std::string &type, const std::string &payload) { + this->tnf_ = tnf; + this->type_ = type; + this->set_payload(payload); + }; + NdefRecord(uint8_t tnf, const std::string &type, const std::string &payload, const std::string &id) { + this->tnf_ = tnf; + this->type_ = type; + this->set_payload(payload); + this->id_ = id; + }; + NdefRecord(const NdefRecord &rhs) { + this->tnf_ = rhs.tnf_; + this->type_ = rhs.type_; + this->payload_ = rhs.payload_; + this->payload_identifier_ = rhs.payload_identifier_; + this->id_ = rhs.id_; + }; + void set_tnf(uint8_t tnf) { this->tnf_ = tnf; }; + void set_type(const std::string &type) { this->type_ = type; }; + void set_payload_identifier(uint8_t payload_identifier) { this->payload_identifier_ = payload_identifier; }; + void set_payload(const std::string &payload) { this->payload_ = payload; }; + void set_id(const std::string &id) { this->id_ = id; }; + + uint32_t get_encoded_size(); + + std::vector encode(bool first, bool last); + uint8_t get_tnf_byte(bool first, bool last); + + const std::string &get_type() { return this->type_; }; + const std::string &get_id() { return this->id_; }; + const std::string &get_payload() { return this->payload_; }; + + protected: + uint8_t tnf_; + std::string type_; + uint8_t payload_identifier_; + std::string payload_; + std::string id_; +}; + +} // namespace nfc +} // namespace esphome diff --git a/esphome/components/nfc/nfc.cpp b/esphome/components/nfc/nfc.cpp new file mode 100644 index 0000000000..d1f7cbe15f --- /dev/null +++ b/esphome/components/nfc/nfc.cpp @@ -0,0 +1,107 @@ +#include "nfc.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace nfc { + +static const char *TAG = "nfc"; + +std::string format_uid(std::vector &uid) { + char buf[(uid.size() * 2) + uid.size() - 1]; + int offset = 0; + for (uint8_t i = 0; i < uid.size(); i++) { + const char *format = "%02X"; + if (i + 1 < uid.size()) + format = "%02X-"; + offset += sprintf(buf + offset, format, uid[i]); + } + return std::string(buf); +} + +std::string format_bytes(std::vector &bytes) { + char buf[(bytes.size() * 2) + bytes.size() - 1]; + int offset = 0; + for (uint8_t i = 0; i < bytes.size(); i++) { + const char *format = "%02X"; + if (i + 1 < bytes.size()) + format = "%02X "; + offset += sprintf(buf + offset, format, bytes[i]); + } + return std::string(buf); +} + +uint8_t guess_tag_type(uint8_t uid_length) { + if (uid_length == 4) { + return TAG_TYPE_MIFARE_CLASSIC; + } else { + return TAG_TYPE_2; + } +} + +uint8_t get_mifare_classic_ndef_start_index(std::vector &data) { + for (uint8_t i = 0; i < MIFARE_CLASSIC_BLOCK_SIZE; i++) { + if (data[i] == 0x00) { + // Do nothing, skip + } else if (data[i] == 0x03) { + return i; + } else { + return -2; + } + } + return -1; +} + +bool decode_mifare_classic_tlv(std::vector &data, uint32_t &message_length, uint8_t &message_start_index) { + uint8_t i = get_mifare_classic_ndef_start_index(data); + if (i < 0 || data[i] != 0x03) { + ESP_LOGE(TAG, "Error, Can't decode message length."); + return false; + } + if (data[i + 1] == 0xFF) { + message_length = ((0xFF & data[i + 2]) << 8) | (0xFF & data[i + 3]); + message_start_index = i + MIFARE_CLASSIC_LONG_TLV_SIZE; + } else { + message_length = data[i + 1]; + message_start_index = i + MIFARE_CLASSIC_SHORT_TLV_SIZE; + } + return true; +} + +uint32_t get_mifare_ultralight_buffer_size(uint32_t message_length) { + uint32_t buffer_size = message_length + 2 + 1; + if (buffer_size % MIFARE_ULTRALIGHT_READ_SIZE != 0) + buffer_size = ((buffer_size / MIFARE_ULTRALIGHT_READ_SIZE) + 1) * MIFARE_ULTRALIGHT_READ_SIZE; + return buffer_size; +} + +uint32_t get_mifare_classic_buffer_size(uint32_t message_length) { + uint32_t buffer_size = message_length; + if (message_length < 255) { + buffer_size += MIFARE_CLASSIC_SHORT_TLV_SIZE + 1; + } else { + buffer_size += MIFARE_CLASSIC_LONG_TLV_SIZE + 1; + } + if (buffer_size % MIFARE_CLASSIC_BLOCK_SIZE != 0) { + buffer_size = ((buffer_size / MIFARE_CLASSIC_BLOCK_SIZE) + 1) * MIFARE_CLASSIC_BLOCK_SIZE; + } + return buffer_size; +} + +bool mifare_classic_is_first_block(uint8_t block_num) { + if (block_num < 128) { + return (block_num % 4 == 0); + } else { + return (block_num % 16 == 0); + } +} + +bool mifare_classic_is_trailer_block(uint8_t block_num) { + if (block_num < 128) { + return ((block_num + 1) % 4 == 0); + } else { + return ((block_num + 1) % 16 == 0); + } +} + +} // namespace nfc +} // namespace esphome diff --git a/esphome/components/nfc/nfc.h b/esphome/components/nfc/nfc.h new file mode 100644 index 0000000000..cf56f9f4de --- /dev/null +++ b/esphome/components/nfc/nfc.h @@ -0,0 +1,57 @@ +#pragma once + +#include "esphome/core/log.h" +#include "esphome/core/helpers.h" +#include "ndef_record.h" +#include "ndef_message.h" +#include "nfc_tag.h" + +namespace esphome { +namespace nfc { + +static const uint8_t MIFARE_CLASSIC_BLOCK_SIZE = 16; +static const uint8_t MIFARE_CLASSIC_LONG_TLV_SIZE = 4; +static const uint8_t MIFARE_CLASSIC_SHORT_TLV_SIZE = 2; + +static const uint8_t MIFARE_ULTRALIGHT_PAGE_SIZE = 4; +static const uint8_t MIFARE_ULTRALIGHT_READ_SIZE = 4; +static const uint8_t MIFARE_ULTRALIGHT_DATA_START_PAGE = 4; +static const uint8_t MIFARE_ULTRALIGHT_MAX_PAGE = 63; + +static const uint8_t TAG_TYPE_MIFARE_CLASSIC = 0; +static const uint8_t TAG_TYPE_1 = 1; +static const uint8_t TAG_TYPE_2 = 2; +static const uint8_t TAG_TYPE_3 = 3; +static const uint8_t TAG_TYPE_4 = 4; +static const uint8_t TAG_TYPE_UNKNOWN = 99; + +// Mifare Commands +static const uint8_t MIFARE_CMD_AUTH_A = 0x60; +static const uint8_t MIFARE_CMD_AUTH_B = 0x61; +static const uint8_t MIFARE_CMD_READ = 0x30; +static const uint8_t MIFARE_CMD_WRITE = 0xA0; +static const uint8_t MIFARE_CMD_WRITE_ULTRALIGHT = 0xA2; + +static const char *MIFARE_CLASSIC = "Mifare Classic"; +static const char *NFC_FORUM_TYPE_2 = "NFC Forum Type 2"; +static const char *ERROR = "Error"; + +static const uint8_t DEFAULT_KEY[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; +static const uint8_t NDEF_KEY[6] = {0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7}; +static const uint8_t MAD_KEY[6] = {0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5}; + +std::string format_uid(std::vector &uid); +std::string format_bytes(std::vector &bytes); + +uint8_t guess_tag_type(uint8_t uid_length); +uint8_t get_mifare_classic_ndef_start_index(std::vector &data); +bool decode_mifare_classic_tlv(std::vector &data, uint32_t &message_length, uint8_t &message_start_index); +uint32_t get_mifare_classic_buffer_size(uint32_t message_length); + +bool mifare_classic_is_first_block(uint8_t block_num); +bool mifare_classic_is_trailer_block(uint8_t block_num); + +uint32_t get_mifare_ultralight_buffer_size(uint32_t message_length); + +} // namespace nfc +} // namespace esphome diff --git a/esphome/components/nfc/nfc_tag.cpp b/esphome/components/nfc/nfc_tag.cpp new file mode 100644 index 0000000000..39e84e2032 --- /dev/null +++ b/esphome/components/nfc/nfc_tag.cpp @@ -0,0 +1,9 @@ +#include "nfc_tag.h" + +namespace esphome { +namespace nfc { + +static const char *TAG = "nfc.tag"; + +} // namespace nfc +} // namespace esphome diff --git a/esphome/components/nfc/nfc_tag.h b/esphome/components/nfc/nfc_tag.h new file mode 100644 index 0000000000..ff0d1c39b4 --- /dev/null +++ b/esphome/components/nfc/nfc_tag.h @@ -0,0 +1,47 @@ +#pragma once + +#include "esphome/core/log.h" +#include "ndef_message.h" + +namespace esphome { +namespace nfc { + +class NfcTag { + public: + NfcTag() { + this->uid_ = {}; + this->tag_type_ = "Unknown"; + }; + NfcTag(std::vector &uid) { + this->uid_ = uid; + this->tag_type_ = "Unknown"; + }; + NfcTag(std::vector &uid, const std::string &tag_type) { + this->uid_ = uid; + this->tag_type_ = tag_type; + }; + NfcTag(std::vector &uid, const std::string &tag_type, nfc::NdefMessage *ndef_message) { + this->uid_ = uid; + this->tag_type_ = tag_type; + this->ndef_message_ = ndef_message; + }; + NfcTag(std::vector &uid, const std::string &tag_type, std::vector &ndef_data) { + this->uid_ = uid; + this->tag_type_ = tag_type; + this->ndef_message_ = new NdefMessage(ndef_data); + }; + + std::vector &get_uid() { return this->uid_; }; + const std::string &get_tag_type() { return this->tag_type_; }; + bool has_ndef_message() { return this->ndef_message_ != nullptr; }; + NdefMessage *get_ndef_message() { return this->ndef_message_; }; + void set_ndef_message(NdefMessage *ndef_message) { this->ndef_message_ = ndef_message; }; + + protected: + std::vector uid_; + std::string tag_type_; + NdefMessage *ndef_message_{nullptr}; +}; + +} // namespace nfc +} // namespace esphome diff --git a/esphome/components/pn532/__init__.py b/esphome/components/pn532/__init__.py index 02215e2f5a..c96ebe2b4d 100644 --- a/esphome/components/pn532/__init__.py +++ b/esphome/components/pn532/__init__.py @@ -1,24 +1,34 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import automation -from esphome.const import CONF_ON_TAG, CONF_TRIGGER_ID +from esphome.components import nfc +from esphome.const import CONF_ID, CONF_ON_TAG, CONF_TRIGGER_ID from esphome.core import coroutine CODEOWNERS = ['@OttoWinter', '@jesserockz'] -AUTO_LOAD = ['binary_sensor'] +AUTO_LOAD = ['binary_sensor', 'nfc'] MULTI_CONF = True CONF_PN532_ID = 'pn532_id' +CONF_ON_FINISHED_WRITE = 'on_finished_write' pn532_ns = cg.esphome_ns.namespace('pn532') PN532 = pn532_ns.class_('PN532', cg.PollingComponent) -PN532Trigger = pn532_ns.class_('PN532Trigger', automation.Trigger.template(cg.std_string)) +PN532OnTagTrigger = pn532_ns.class_('PN532OnTagTrigger', + automation.Trigger.template(cg.std_string, nfc.NfcTag)) +PN532OnFinishedWriteTrigger = pn532_ns.class_('PN532OnFinishedWriteTrigger', + automation.Trigger.template()) + +PN532IsWritingCondition = pn532_ns.class_('PN532IsWritingCondition', automation.Condition) PN532_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(PN532), cv.Optional(CONF_ON_TAG): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PN532Trigger), + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PN532OnTagTrigger), + }), + cv.Optional(CONF_ON_FINISHED_WRITE): automation.validate_automation({ + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PN532OnFinishedWriteTrigger), }), }).extend(cv.polling_component_schema('1s')) @@ -36,4 +46,18 @@ def setup_pn532(var, config): for conf in config.get(CONF_ON_TAG, []): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID]) cg.add(var.register_trigger(trigger)) - yield automation.build_automation(trigger, [(cg.std_string, 'x')], conf) + yield automation.build_automation(trigger, [(cg.std_string, 'x'), (nfc.NfcTag, 'tag')], + conf) + + for conf in config.get(CONF_ON_FINISHED_WRITE, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + yield automation.build_automation(trigger, [], conf) + + +@automation.register_condition('pn532.is_writing', PN532IsWritingCondition, cv.Schema({ + cv.GenerateID(): cv.use_id(PN532), +})) +def pn532_is_writing_to_code(config, condition_id, template_arg, args): + var = cg.new_Pvariable(condition_id, template_arg) + yield cg.register_parented(var, config[CONF_ID]) + yield var diff --git a/esphome/components/pn532/pn532.cpp b/esphome/components/pn532/pn532.cpp index a9601185bb..e2511648b6 100644 --- a/esphome/components/pn532/pn532.cpp +++ b/esphome/components/pn532/pn532.cpp @@ -11,18 +11,6 @@ namespace pn532 { static const char *TAG = "pn532"; -std::string format_uid(std::vector &uid) { - char buf[32]; - int offset = 0; - for (uint8_t i = 0; i < uid.size(); i++) { - const char *format = "%02X"; - if (i + 1 < uid.size()) - format = "%02X-"; - offset += sprintf(buf + offset, format, uid[i]); - } - return std::string(buf); -} - void PN532::setup() { ESP_LOGCONFIG(TAG, "Setting up PN532..."); @@ -152,23 +140,56 @@ void PN532::loop() { this->current_uid_ = nfcid; - for (auto *trigger : this->triggers_) - trigger->process(nfcid); + if (next_task_ == READ) { + auto tag = this->read_tag_(nfcid); + for (auto *trigger : this->triggers_) + trigger->process(tag); - if (report) { - ESP_LOGD(TAG, "Found new tag '%s'", format_uid(nfcid).c_str()); + if (report) { + ESP_LOGD(TAG, "Found new tag '%s'", nfc::format_uid(nfcid).c_str()); + if (tag->has_ndef_message()) { + auto message = tag->get_ndef_message(); + auto records = message->get_records(); + ESP_LOGD(TAG, " NDEF formatted records:"); + for (auto &record : records) { + ESP_LOGD(TAG, " %s - %s", record->get_type().c_str(), record->get_payload().c_str()); + } + } + } + } else if (next_task_ == CLEAN) { + ESP_LOGD(TAG, " Tag cleaning..."); + if (!this->clean_tag_(nfcid)) { + ESP_LOGE(TAG, " Tag was not fully cleaned successfully"); + } + ESP_LOGD(TAG, " Tag cleaned!"); + } else if (next_task_ == FORMAT) { + ESP_LOGD(TAG, " Tag formatting..."); + if (!this->format_tag_(nfcid)) { + ESP_LOGE(TAG, "Error formatting tag as NDEF"); + } + ESP_LOGD(TAG, " Tag formatted!"); + } else if (next_task_ == WRITE) { + if (this->next_task_message_to_write_ != nullptr) { + ESP_LOGD(TAG, " Tag writing..."); + ESP_LOGD(TAG, " Tag formatting..."); + if (!this->format_tag_(nfcid)) { + ESP_LOGE(TAG, " Tag could not be formatted for writing"); + } else { + ESP_LOGD(TAG, " Writing NDEF data"); + if (!this->write_tag_(nfcid, this->next_task_message_to_write_)) { + ESP_LOGE(TAG, " Failed to write message to tag"); + } + ESP_LOGD(TAG, " Finished writing NDEF data"); + delete this->next_task_message_to_write_; + this->next_task_message_to_write_ = nullptr; + this->on_finished_write_callback_.call(); + } + } } - this->turn_off_rf_(); -} + this->read_mode(); -void PN532::turn_off_rf_() { - ESP_LOGVV(TAG, "Turning RF field OFF"); - this->write_command_({ - PN532_COMMAND_RFCONFIGURATION, - 0x1, // RF Field - 0x0 // Off - }); + this->turn_off_rf_(); } bool PN532::write_command_(const std::vector &data) { @@ -208,6 +229,22 @@ bool PN532::write_command_(const std::vector &data) { return this->read_ack_(); } +bool PN532::read_ack_() { + ESP_LOGVV(TAG, "Reading ACK..."); + + std::vector data; + if (!this->read_data(data, 6)) { + return false; + } + + bool matches = (data[1] == 0x00 && // preamble + data[2] == 0x00 && // start of packet + data[3] == 0xFF && data[4] == 0x00 && // ACK packet code + data[5] == 0xFF && data[6] == 0x00); // postamble + ESP_LOGVV(TAG, "ACK valid: %s", YESNO(matches)); + return matches; +} + bool PN532::read_response_(uint8_t command, std::vector &data) { ESP_LOGV(TAG, "Reading response"); uint8_t len = this->read_response_length_(); @@ -258,13 +295,6 @@ bool PN532::read_response_(uint8_t command, std::vector &data) { data.erase(data.begin(), data.begin() + 2); // Remove TFI and command code data.erase(data.end() - 2, data.end()); // Remove checksum and postamble -#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE - ESP_LOGD(TAG, "PN532 Data Frame: (%u)", data.size()); // NOLINT - for (uint8_t dat : data) { - ESP_LOGD(TAG, " 0x%02X", dat); - } -#endif - return true; } @@ -299,20 +329,81 @@ uint8_t PN532::read_response_length_() { return len; } -bool PN532::read_ack_() { - ESP_LOGVV(TAG, "Reading ACK..."); +void PN532::turn_off_rf_() { + ESP_LOGVV(TAG, "Turning RF field OFF"); + this->write_command_({ + PN532_COMMAND_RFCONFIGURATION, + 0x01, // RF Field + 0x00, // Off + }); +} - std::vector data; - if (!this->read_data(data, 6)) { - return false; +nfc::NfcTag *PN532::read_tag_(std::vector &uid) { + uint8_t type = nfc::guess_tag_type(uid.size()); + + if (type == nfc::TAG_TYPE_MIFARE_CLASSIC) { + ESP_LOGD(TAG, "Mifare classic"); + return this->read_mifare_classic_tag_(uid); + } else if (type == nfc::TAG_TYPE_2) { + ESP_LOGD(TAG, "Mifare ultralight"); + return this->read_mifare_ultralight_tag_(uid); + } else if (type == nfc::TAG_TYPE_UNKNOWN) { + ESP_LOGV(TAG, "Cannot determine tag type"); + return new nfc::NfcTag(uid); + } else { + return new nfc::NfcTag(uid); } +} - bool matches = (data[1] == 0x00 && // preamble - data[2] == 0x00 && // start of packet - data[3] == 0xFF && data[4] == 0x00 && // ACK packet code - data[5] == 0xFF && data[6] == 0x00); // postamble - ESP_LOGVV(TAG, "ACK valid: %s", YESNO(matches)); - return matches; +void PN532::read_mode() { + this->next_task_ = READ; + ESP_LOGD(TAG, "Waiting to read next tag"); +} +void PN532::clean_mode() { + this->next_task_ = CLEAN; + ESP_LOGD(TAG, "Waiting to clean next tag"); +} +void PN532::format_mode() { + this->next_task_ = FORMAT; + ESP_LOGD(TAG, "Waiting to format next tag"); +} +void PN532::write_mode(nfc::NdefMessage *message) { + this->next_task_ = WRITE; + this->next_task_message_to_write_ = message; + ESP_LOGD(TAG, "Waiting to write next tag"); +} + +bool PN532::clean_tag_(std::vector &uid) { + uint8_t type = nfc::guess_tag_type(uid.size()); + if (type == nfc::TAG_TYPE_MIFARE_CLASSIC) { + return this->format_mifare_classic_mifare_(uid); + } else if (type == nfc::TAG_TYPE_2) { + return this->clean_mifare_ultralight_(); + } + ESP_LOGE(TAG, "Unsupported Tag for formatting"); + return false; +} + +bool PN532::format_tag_(std::vector &uid) { + uint8_t type = nfc::guess_tag_type(uid.size()); + if (type == nfc::TAG_TYPE_MIFARE_CLASSIC) { + return this->format_mifare_classic_ndef_(uid); + } else if (type == nfc::TAG_TYPE_2) { + return this->clean_mifare_ultralight_(); + } + ESP_LOGE(TAG, "Unsupported Tag for formatting"); + return false; +} + +bool PN532::write_tag_(std::vector &uid, nfc::NdefMessage *message) { + uint8_t type = nfc::guess_tag_type(uid.size()); + if (type == nfc::TAG_TYPE_MIFARE_CLASSIC) { + return this->write_mifare_classic_tag_(uid, message); + } else if (type == nfc::TAG_TYPE_2) { + return this->write_mifare_ultralight_tag_(uid, message); + } + ESP_LOGE(TAG, "Unsupported Tag for formatting"); + return false; } float PN532::get_setup_priority() const { return setup_priority::DATA; } @@ -350,7 +441,7 @@ bool PN532BinarySensor::process(std::vector &data) { this->found_ = true; return true; } -void PN532Trigger::process(std::vector &data) { this->trigger(format_uid(data)); } +void PN532OnTagTrigger::process(nfc::NfcTag *tag) { this->trigger(nfc::format_uid(tag->get_uid()), *tag); } } // namespace pn532 } // namespace esphome diff --git a/esphome/components/pn532/pn532.h b/esphome/components/pn532/pn532.h index af49a02400..6b3bef2918 100644 --- a/esphome/components/pn532/pn532.h +++ b/esphome/components/pn532/pn532.h @@ -3,6 +3,8 @@ #include "esphome/core/component.h" #include "esphome/core/automation.h" #include "esphome/components/binary_sensor/binary_sensor.h" +#include "esphome/components/nfc/nfc_tag.h" +#include "esphome/components/nfc/nfc.h" namespace esphome { namespace pn532 { @@ -14,7 +16,7 @@ static const uint8_t PN532_COMMAND_INDATAEXCHANGE = 0x40; static const uint8_t PN532_COMMAND_INLISTPASSIVETARGET = 0x4A; class PN532BinarySensor; -class PN532Trigger; +class PN532OnTagTrigger; class PN532 : public PollingComponent { public: @@ -28,7 +30,18 @@ class PN532 : public PollingComponent { void loop() override; void register_tag(PN532BinarySensor *tag) { this->binary_sensors_.push_back(tag); } - void register_trigger(PN532Trigger *trig) { this->triggers_.push_back(trig); } + void register_trigger(PN532OnTagTrigger *trig) { this->triggers_.push_back(trig); } + + void add_on_finished_write_callback(std::function callback) { + this->on_finished_write_callback_.add(std::move(callback)); + } + + bool is_writing() { return this->next_task_ != READ; }; + + void read_mode(); + void clean_mode(); + void format_mode(); + void write_mode(nfc::NdefMessage *message); protected: void turn_off_rf_(); @@ -40,15 +53,46 @@ class PN532 : public PollingComponent { virtual bool write_data(const std::vector &data) = 0; virtual bool read_data(std::vector &data, uint8_t len) = 0; + nfc::NfcTag *read_tag_(std::vector &uid); + + bool format_tag_(std::vector &uid); + bool clean_tag_(std::vector &uid); + bool write_tag_(std::vector &uid, nfc::NdefMessage *message); + + nfc::NfcTag *read_mifare_classic_tag_(std::vector &uid); + bool read_mifare_classic_block_(uint8_t block_num, std::vector &data); + bool write_mifare_classic_block_(uint8_t block_num, std::vector &data); + bool auth_mifare_classic_block_(std::vector &uid, uint8_t block_num, uint8_t key_num, const uint8_t *key); + bool format_mifare_classic_mifare_(std::vector &uid); + bool format_mifare_classic_ndef_(std::vector &uid); + bool write_mifare_classic_tag_(std::vector &uid, nfc::NdefMessage *message); + + nfc::NfcTag *read_mifare_ultralight_tag_(std::vector &uid); + bool read_mifare_ultralight_page_(uint8_t page_num, std::vector &data); + bool is_mifare_ultralight_formatted_(); + uint16_t read_mifare_ultralight_capacity_(); + bool find_mifare_ultralight_ndef_(uint8_t &message_length, uint8_t &message_start_index); + bool write_mifare_ultralight_page_(uint8_t page_num, std::vector &write_data); + bool write_mifare_ultralight_tag_(std::vector &uid, nfc::NdefMessage *message); + bool clean_mifare_ultralight_(); + bool requested_read_{false}; std::vector binary_sensors_; - std::vector triggers_; + std::vector triggers_; std::vector current_uid_; + nfc::NdefMessage *next_task_message_to_write_; + enum NfcTask { + READ = 0, + CLEAN, + FORMAT, + WRITE, + } next_task_{READ}; enum PN532Error { NONE = 0, WAKEUP_FAILED, SAM_COMMAND_FAILED, } error_code_{NONE}; + CallbackManager on_finished_write_callback_; }; class PN532BinarySensor : public binary_sensor::BinarySensor { @@ -69,9 +113,21 @@ class PN532BinarySensor : public binary_sensor::BinarySensor { bool found_{false}; }; -class PN532Trigger : public Trigger { +class PN532OnTagTrigger : public Trigger { public: - void process(std::vector &data); + void process(nfc::NfcTag *tag); +}; + +class PN532OnFinishedWriteTrigger : public Trigger<> { + public: + explicit PN532OnFinishedWriteTrigger(PN532 *parent) { + parent->add_on_finished_write_callback([this]() { this->trigger(); }); + } +}; + +template class PN532IsWritingCondition : public Condition, public Parented { + public: + bool check(Ts... x) override { return this->parent_->is_writing(); } }; } // namespace pn532 diff --git a/esphome/components/pn532/pn532_mifare_classic.cpp b/esphome/components/pn532/pn532_mifare_classic.cpp new file mode 100644 index 0000000000..4e8c255755 --- /dev/null +++ b/esphome/components/pn532/pn532_mifare_classic.cpp @@ -0,0 +1,249 @@ +#include "pn532.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace pn532 { + +static const char *TAG = "pn532.mifare_classic"; + +nfc::NfcTag *PN532::read_mifare_classic_tag_(std::vector &uid) { + uint8_t current_block = 4; + uint8_t message_start_index = 0; + uint32_t message_length = 0; + + if (this->auth_mifare_classic_block_(uid, current_block, nfc::MIFARE_CMD_AUTH_A, nfc::NDEF_KEY)) { + std::vector data; + if (this->read_mifare_classic_block_(current_block, data)) { + if (!nfc::decode_mifare_classic_tlv(data, message_length, message_start_index)) { + return new nfc::NfcTag(uid, nfc::ERROR); + } + } else { + ESP_LOGE(TAG, "Failed to read block %d", current_block); + return new nfc::NfcTag(uid, nfc::MIFARE_CLASSIC); + } + } else { + ESP_LOGV(TAG, "Tag is not NDEF formatted"); + return new nfc::NfcTag(uid, nfc::MIFARE_CLASSIC); + } + + uint32_t index = 0; + uint32_t buffer_size = nfc::get_mifare_classic_buffer_size(message_length); + std::vector buffer; + + while (index < buffer_size) { + if (nfc::mifare_classic_is_first_block(current_block)) { + if (!this->auth_mifare_classic_block_(uid, current_block, nfc::MIFARE_CMD_AUTH_A, nfc::NDEF_KEY)) { + ESP_LOGE(TAG, "Error, Block authentication failed for %d", current_block); + } + } + std::vector block_data; + if (this->read_mifare_classic_block_(current_block, block_data)) { + buffer.insert(buffer.end(), block_data.begin(), block_data.end()); + } else { + ESP_LOGE(TAG, "Error reading block %d", current_block); + } + + index += nfc::MIFARE_CLASSIC_BLOCK_SIZE; + current_block++; + + if (nfc::mifare_classic_is_trailer_block(current_block)) { + current_block++; + } + } + buffer.erase(buffer.begin(), buffer.begin() + message_start_index); + return new nfc::NfcTag(uid, nfc::MIFARE_CLASSIC, buffer); +} + +bool PN532::read_mifare_classic_block_(uint8_t block_num, std::vector &data) { + if (!this->write_command_({ + PN532_COMMAND_INDATAEXCHANGE, + 0x01, // One card + nfc::MIFARE_CMD_READ, + block_num, + })) { + return false; + } + + if (!this->read_response_(PN532_COMMAND_INDATAEXCHANGE, data) || data[0] != 0x00) { + return false; + } + data.erase(data.begin()); + + ESP_LOGVV(TAG, " Block %d: %s", block_num, nfc::format_bytes(data).c_str()); + return true; +} + +bool PN532::auth_mifare_classic_block_(std::vector &uid, uint8_t block_num, uint8_t key_num, + const uint8_t *key) { + std::vector data({ + PN532_COMMAND_INDATAEXCHANGE, + 0x01, // One card + key_num, // Mifare Key slot + block_num, // Block number + }); + data.insert(data.end(), key, key + 6); + data.insert(data.end(), uid.begin(), uid.end()); + if (!this->write_command_(data)) { + ESP_LOGE(TAG, "Authentication failed - Block %d", block_num); + return false; + } + + std::vector response; + if (!this->read_response_(PN532_COMMAND_INDATAEXCHANGE, response) || response[0] != 0x00) { + ESP_LOGE(TAG, "Authentication failed - Block 0x%02x", block_num); + return false; + } + + return true; +} + +bool PN532::format_mifare_classic_mifare_(std::vector &uid) { + std::vector blank_buffer( + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}); + std::vector trailer_buffer( + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x80, 0x69, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}); + + bool error = false; + + for (int block = 0; block < 64; block += 4) { + if (!this->auth_mifare_classic_block_(uid, block + 3, nfc::MIFARE_CMD_AUTH_B, nfc::DEFAULT_KEY)) { + continue; + } + if (block != 0) { + if (!this->write_mifare_classic_block_(block, blank_buffer)) { + ESP_LOGE(TAG, "Unable to write block %d", block); + error = true; + } + } + if (!this->write_mifare_classic_block_(block + 1, blank_buffer)) { + ESP_LOGE(TAG, "Unable to write block %d", block + 1); + error = true; + } + if (!this->write_mifare_classic_block_(block + 2, blank_buffer)) { + ESP_LOGE(TAG, "Unable to write block %d", block + 2); + error = true; + } + if (!this->write_mifare_classic_block_(block + 3, trailer_buffer)) { + ESP_LOGE(TAG, "Unable to write block %d", block + 3); + error = true; + } + } + + return !error; +} + +bool PN532::format_mifare_classic_ndef_(std::vector &uid) { + std::vector empty_ndef_message( + {0x03, 0x03, 0xD0, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}); + std::vector blank_block( + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}); + std::vector block_1_data( + {0x14, 0x01, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1}); + std::vector block_2_data( + {0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1}); + std::vector block_3_trailer( + {0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0x78, 0x77, 0x88, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}); + std::vector ndef_trailer( + {0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7, 0x7F, 0x07, 0x88, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}); + + if (!this->auth_mifare_classic_block_(uid, 0, nfc::MIFARE_CMD_AUTH_B, nfc::DEFAULT_KEY)) { + ESP_LOGE(TAG, "Unable to authenticate block 0 for formatting!"); + return false; + } + if (!this->write_mifare_classic_block_(1, block_1_data)) + return false; + if (!this->write_mifare_classic_block_(2, block_2_data)) + return false; + if (!this->write_mifare_classic_block_(3, block_3_trailer)) + return false; + + ESP_LOGD(TAG, "Sector 0 formatted to NDEF"); + + for (int block = 4; block < 64; block += 4) { + if (!this->auth_mifare_classic_block_(uid, block + 3, nfc::MIFARE_CMD_AUTH_B, nfc::DEFAULT_KEY)) { + return false; + } + if (block == 4) { + if (!this->write_mifare_classic_block_(block, empty_ndef_message)) + ESP_LOGE(TAG, "Unable to write block %d", block); + } else { + if (!this->write_mifare_classic_block_(block, blank_block)) + ESP_LOGE(TAG, "Unable to write block %d", block); + } + if (!this->write_mifare_classic_block_(block + 1, blank_block)) + ESP_LOGE(TAG, "Unable to write block %d", block + 1); + if (!this->write_mifare_classic_block_(block + 2, blank_block)) + ESP_LOGE(TAG, "Unable to write block %d", block + 2); + if (!this->write_mifare_classic_block_(block + 3, ndef_trailer)) + ESP_LOGE(TAG, "Unable to write trailer block %d", block + 3); + } + return true; +} + +bool PN532::write_mifare_classic_block_(uint8_t block_num, std::vector &write_data) { + std::vector data({ + PN532_COMMAND_INDATAEXCHANGE, + 0x01, // One card + nfc::MIFARE_CMD_WRITE, + block_num, + }); + data.insert(data.end(), write_data.begin(), write_data.end()); + if (!this->write_command_(data)) { + ESP_LOGE(TAG, "Error writing block %d", block_num); + return false; + } + + std::vector response; + if (!this->read_response_(PN532_COMMAND_INDATAEXCHANGE, response)) { + ESP_LOGE(TAG, "Error writing block %d", block_num); + return false; + } + + return true; +} + +bool PN532::write_mifare_classic_tag_(std::vector &uid, nfc::NdefMessage *message) { + auto encoded = message->encode(); + + uint32_t message_length = encoded.size(); + uint32_t buffer_length = nfc::get_mifare_classic_buffer_size(message_length); + + encoded.insert(encoded.begin(), 0x03); + if (message_length < 255) { + encoded.insert(encoded.begin() + 1, message_length); + } else { + encoded.insert(encoded.begin() + 1, 0xFF); + encoded.insert(encoded.begin() + 2, (message_length >> 8) & 0xFF); + encoded.insert(encoded.begin() + 3, message_length & 0xFF); + } + encoded.push_back(0xFE); + + encoded.resize(buffer_length, 0); + + uint32_t index = 0; + uint8_t current_block = 4; + + while (index < buffer_length) { + if (nfc::mifare_classic_is_first_block(current_block)) { + if (!this->auth_mifare_classic_block_(uid, current_block, nfc::MIFARE_CMD_AUTH_A, nfc::NDEF_KEY)) { + return false; + } + } + + std::vector data(encoded.begin() + index, encoded.begin() + index + nfc::MIFARE_CLASSIC_BLOCK_SIZE); + if (!this->write_mifare_classic_block_(current_block, data)) { + return false; + } + index += nfc::MIFARE_CLASSIC_BLOCK_SIZE; + current_block++; + + if (nfc::mifare_classic_is_trailer_block(current_block)) { + // Skipping as cannot write to trailer + current_block++; + } + } + return true; +} + +} // namespace pn532 +} // namespace esphome diff --git a/esphome/components/pn532/pn532_mifare_ultralight.cpp b/esphome/components/pn532/pn532_mifare_ultralight.cpp new file mode 100644 index 0000000000..00cb18aacd --- /dev/null +++ b/esphome/components/pn532/pn532_mifare_ultralight.cpp @@ -0,0 +1,180 @@ +#include "pn532.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace pn532 { + +static const char *TAG = "pn532.mifare_ultralight"; + +nfc::NfcTag *PN532::read_mifare_ultralight_tag_(std::vector &uid) { + if (!this->is_mifare_ultralight_formatted_()) { + ESP_LOGD(TAG, "Not NDEF formatted"); + return new nfc::NfcTag(uid, nfc::NFC_FORUM_TYPE_2); + } + + uint8_t message_length; + uint8_t message_start_index; + if (!this->find_mifare_ultralight_ndef_(message_length, message_start_index)) { + return new nfc::NfcTag(uid, nfc::NFC_FORUM_TYPE_2); + } + + if (message_length == 0) { + return new nfc::NfcTag(uid, nfc::NFC_FORUM_TYPE_2); + } + std::vector data; + uint8_t index = 0; + for (uint8_t page = nfc::MIFARE_ULTRALIGHT_DATA_START_PAGE; page < nfc::MIFARE_ULTRALIGHT_MAX_PAGE; page++) { + std::vector page_data; + if (!this->read_mifare_ultralight_page_(page, page_data)) { + ESP_LOGE(TAG, "Error reading page %d", page); + return new nfc::NfcTag(uid, nfc::NFC_FORUM_TYPE_2); + } + data.insert(data.end(), page_data.begin(), page_data.end()); + + if (index >= (message_length + message_start_index)) + break; + + index += page_data.size(); + } + + data.erase(data.begin(), data.begin() + message_start_index); + + return new nfc::NfcTag(uid, nfc::NFC_FORUM_TYPE_2, data); +} + +bool PN532::read_mifare_ultralight_page_(uint8_t page_num, std::vector &data) { + if (!this->write_command_({ + PN532_COMMAND_INDATAEXCHANGE, + 0x01, // One card + nfc::MIFARE_CMD_READ, + page_num, + })) { + return false; + } + + if (!this->read_response_(PN532_COMMAND_INDATAEXCHANGE, data) || data[0] != 0x00) { + return false; + } + data.erase(data.begin()); + // We only want 1 page of data but the PN532 returns 4 at once. + data.erase(data.begin() + 4, data.end()); + + ESP_LOGVV(TAG, "Pages %d-%d: %s", page_num, page_num + 4, nfc::format_bytes(data).c_str()); + + return true; +} + +bool PN532::is_mifare_ultralight_formatted_() { + std::vector data; + if (this->read_mifare_ultralight_page_(4, data)) { + return !(data[0] == 0xFF && data[1] == 0xFF && data[2] == 0xFF && data[3] == 0xFF); + } + return true; +} + +uint16_t PN532::read_mifare_ultralight_capacity_() { + std::vector data; + if (this->read_mifare_ultralight_page_(3, data)) { + return data[2] * 8U; + } + return 0; +} + +bool PN532::find_mifare_ultralight_ndef_(uint8_t &message_length, uint8_t &message_start_index) { + std::vector data; + for (int page = 4; page < 6; page++) { + std::vector page_data; + if (!this->read_mifare_ultralight_page_(page, page_data)) { + return false; + } + data.insert(data.end(), page_data.begin(), page_data.end()); + } + if (data[0] == 0x03) { + message_length = data[1]; + message_start_index = 2; + return true; + } else if (data[5] == 0x03) { + message_length = data[6]; + message_start_index = 7; + return true; + } + return false; +} + +bool PN532::write_mifare_ultralight_tag_(std::vector &uid, nfc::NdefMessage *message) { + uint32_t capacity = this->read_mifare_ultralight_capacity_(); + + auto encoded = message->encode(); + + uint32_t message_length = encoded.size(); + uint32_t buffer_length = nfc::get_mifare_ultralight_buffer_size(message_length); + + if (buffer_length > capacity) { + ESP_LOGE(TAG, "Message length exceeds tag capacity %d > %d", buffer_length, capacity); + return false; + } + + encoded.insert(encoded.begin(), 0x03); + if (message_length < 255) { + encoded.insert(encoded.begin() + 1, message_length); + } else { + encoded.insert(encoded.begin() + 1, 0xFF); + encoded.insert(encoded.begin() + 2, (message_length >> 8) & 0xFF); + encoded.insert(encoded.begin() + 2, message_length & 0xFF); + } + encoded.push_back(0xFE); + + encoded.resize(buffer_length, 0); + + uint32_t index = 0; + uint8_t current_page = nfc::MIFARE_ULTRALIGHT_DATA_START_PAGE; + + while (index < buffer_length) { + std::vector data(encoded.begin() + index, encoded.begin() + index + nfc::MIFARE_ULTRALIGHT_PAGE_SIZE); + if (!this->write_mifare_ultralight_page_(current_page, data)) { + return false; + } + index += nfc::MIFARE_ULTRALIGHT_PAGE_SIZE; + current_page++; + } + return true; +} + +bool PN532::clean_mifare_ultralight_() { + uint32_t capacity = this->read_mifare_ultralight_capacity_(); + uint8_t pages = (capacity / nfc::MIFARE_ULTRALIGHT_PAGE_SIZE) + nfc::MIFARE_ULTRALIGHT_DATA_START_PAGE; + + std::vector blank_data = {0x00, 0x00, 0x00, 0x00}; + + for (int i = nfc::MIFARE_ULTRALIGHT_DATA_START_PAGE; i < pages; i++) { + if (!this->write_mifare_ultralight_page_(i, blank_data)) { + return false; + } + } + return true; +} + +bool PN532::write_mifare_ultralight_page_(uint8_t page_num, std::vector &write_data) { + std::vector data({ + PN532_COMMAND_INDATAEXCHANGE, + 0x01, // One card + nfc::MIFARE_CMD_WRITE_ULTRALIGHT, + page_num, + }); + data.insert(data.end(), write_data.begin(), write_data.end()); + if (!this->write_command_(data)) { + ESP_LOGE(TAG, "Error writing page %d", page_num); + return false; + } + + std::vector response; + if (!this->read_response_(PN532_COMMAND_INDATAEXCHANGE, response)) { + ESP_LOGE(TAG, "Error writing page %d", page_num); + return false; + } + + return true; +} + +} // namespace pn532 +} // namespace esphome diff --git a/esphome/components/pn532_i2c/pn532_i2c.cpp b/esphome/components/pn532_i2c/pn532_i2c.cpp index b959e764e7..162487de58 100644 --- a/esphome/components/pn532_i2c/pn532_i2c.cpp +++ b/esphome/components/pn532_i2c/pn532_i2c.cpp @@ -14,7 +14,7 @@ static const char *TAG = "pn532_i2c"; bool PN532I2C::write_data(const std::vector &data) { return this->write_bytes_raw(data.data(), data.size()); } bool PN532I2C::read_data(std::vector &data, uint8_t len) { - delay(5); + delay(1); std::vector ready; ready.resize(1); From d9a2651a5a949a8436fd24092e0a596585ee50b9 Mon Sep 17 00:00:00 2001 From: David Zovko Date: Sat, 16 Jan 2021 01:19:35 +0100 Subject: [PATCH 015/111] Inkplate 6 support for ESPHome (#1283) * Add Inkplate 6 support Inkplate 6 is e-paper display based on ESP32. This commit adds support for integrating Inkplate 6 into the ESPHome. Find more info here: inkplate.io * Greyscale working * Update inkplate.h * Fix formatting * Formatting * Update esphome/components/inkplate6/display.py Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> * Update esphome/components/inkplate6/display.py Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> * Fix some lint errors Ignore some lint errors Only allow on ESP32 * Update the codeowners file Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- CODEOWNERS | 1 + esphome/components/inkplate6/__init__.py | 1 + esphome/components/inkplate6/display.py | 141 +++++ esphome/components/inkplate6/inkplate.cpp | 630 ++++++++++++++++++++++ esphome/components/inkplate6/inkplate.h | 157 ++++++ 5 files changed, 930 insertions(+) create mode 100644 esphome/components/inkplate6/__init__.py create mode 100644 esphome/components/inkplate6/display.py create mode 100644 esphome/components/inkplate6/inkplate.cpp create mode 100644 esphome/components/inkplate6/inkplate.h diff --git a/CODEOWNERS b/CODEOWNERS index 5cf0f591d2..4c24586bb6 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -37,6 +37,7 @@ esphome/components/globals/* @esphome/core esphome/components/gpio/* @esphome/core esphome/components/homeassistant/* @OttoWinter esphome/components/i2c/* @esphome/core +esphome/components/inkplate6/* @jesserockz esphome/components/integration/* @OttoWinter esphome/components/interval/* @esphome/core esphome/components/json/* @OttoWinter diff --git a/esphome/components/inkplate6/__init__.py b/esphome/components/inkplate6/__init__.py new file mode 100644 index 0000000000..ba7653988b --- /dev/null +++ b/esphome/components/inkplate6/__init__.py @@ -0,0 +1 @@ +CODEOWNERS = ['@jesserockz'] diff --git a/esphome/components/inkplate6/display.py b/esphome/components/inkplate6/display.py new file mode 100644 index 0000000000..4f35901bd4 --- /dev/null +++ b/esphome/components/inkplate6/display.py @@ -0,0 +1,141 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome import pins +from esphome.components import display, i2c +from esphome.const import CONF_FULL_UPDATE_EVERY, CONF_ID, CONF_LAMBDA, CONF_PAGES, \ + CONF_WAKEUP_PIN, ESP_PLATFORM_ESP32 + +DEPENDENCIES = ['i2c'] +ESP_PLATFORMS = [ESP_PLATFORM_ESP32] + +CONF_DISPLAY_DATA_0_PIN = 'display_data_0_pin' +CONF_DISPLAY_DATA_1_PIN = 'display_data_1_pin' +CONF_DISPLAY_DATA_2_PIN = 'display_data_2_pin' +CONF_DISPLAY_DATA_3_PIN = 'display_data_3_pin' +CONF_DISPLAY_DATA_4_PIN = 'display_data_4_pin' +CONF_DISPLAY_DATA_5_PIN = 'display_data_5_pin' +CONF_DISPLAY_DATA_6_PIN = 'display_data_6_pin' +CONF_DISPLAY_DATA_7_PIN = 'display_data_7_pin' + +CONF_CL_PIN = 'cl_pin' +CONF_CKV_PIN = 'ckv_pin' +CONF_GREYSCALE = 'greyscale' +CONF_GMOD_PIN = 'gmod_pin' +CONF_GPIO0_ENABLE_PIN = 'gpio0_enable_pin' +CONF_LE_PIN = 'le_pin' +CONF_OE_PIN = 'oe_pin' +CONF_PARTIAL_UPDATING = 'partial_updating' +CONF_POWERUP_PIN = 'powerup_pin' +CONF_SPH_PIN = 'sph_pin' +CONF_SPV_PIN = 'spv_pin' +CONF_VCOM_PIN = 'vcom_pin' + + +inkplate6_ns = cg.esphome_ns.namespace('inkplate6') +Inkplate6 = inkplate6_ns.class_('Inkplate6', cg.PollingComponent, i2c.I2CDevice, + display.DisplayBuffer) + +CONFIG_SCHEMA = cv.All(display.FULL_DISPLAY_SCHEMA.extend({ + cv.GenerateID(): cv.declare_id(Inkplate6), + cv.Optional(CONF_GREYSCALE, default=False): cv.boolean, + cv.Optional(CONF_PARTIAL_UPDATING, default=True): cv.boolean, + cv.Optional(CONF_FULL_UPDATE_EVERY, default=10): cv.uint32_t, + # Control pins + cv.Required(CONF_CKV_PIN): pins.gpio_output_pin_schema, + cv.Required(CONF_GMOD_PIN): pins.gpio_output_pin_schema, + cv.Required(CONF_GPIO0_ENABLE_PIN): pins.gpio_output_pin_schema, + cv.Required(CONF_OE_PIN): pins.gpio_output_pin_schema, + cv.Required(CONF_POWERUP_PIN): pins.gpio_output_pin_schema, + cv.Required(CONF_SPH_PIN): pins.gpio_output_pin_schema, + cv.Required(CONF_SPV_PIN): pins.gpio_output_pin_schema, + cv.Required(CONF_VCOM_PIN): pins.gpio_output_pin_schema, + cv.Required(CONF_WAKEUP_PIN): pins.gpio_output_pin_schema, + cv.Optional(CONF_CL_PIN, default=0): pins.internal_gpio_output_pin_schema, + cv.Optional(CONF_LE_PIN, default=2): pins.internal_gpio_output_pin_schema, + # Data pins + cv.Optional(CONF_DISPLAY_DATA_0_PIN, default=4): pins.internal_gpio_output_pin_schema, + cv.Optional(CONF_DISPLAY_DATA_1_PIN, default=5): pins.internal_gpio_output_pin_schema, + cv.Optional(CONF_DISPLAY_DATA_2_PIN, default=18): pins.internal_gpio_output_pin_schema, + cv.Optional(CONF_DISPLAY_DATA_3_PIN, default=19): pins.internal_gpio_output_pin_schema, + cv.Optional(CONF_DISPLAY_DATA_4_PIN, default=23): pins.internal_gpio_output_pin_schema, + cv.Optional(CONF_DISPLAY_DATA_5_PIN, default=25): pins.internal_gpio_output_pin_schema, + cv.Optional(CONF_DISPLAY_DATA_6_PIN, default=26): pins.internal_gpio_output_pin_schema, + cv.Optional(CONF_DISPLAY_DATA_7_PIN, default=27): pins.internal_gpio_output_pin_schema, +}).extend(cv.polling_component_schema('5s').extend(i2c.i2c_device_schema(0x48))), + cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA)) + + +def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + + yield cg.register_component(var, config) + yield display.register_display(var, config) + yield i2c.register_i2c_device(var, config) + + if CONF_LAMBDA in config: + lambda_ = yield cg.process_lambda(config[CONF_LAMBDA], [(display.DisplayBufferRef, 'it')], + return_type=cg.void) + cg.add(var.set_writer(lambda_)) + + cg.add(var.set_greyscale(config[CONF_GREYSCALE])) + cg.add(var.set_partial_updating(config[CONF_PARTIAL_UPDATING])) + cg.add(var.set_full_update_every(config[CONF_FULL_UPDATE_EVERY])) + + ckv = yield cg.gpio_pin_expression(config[CONF_CKV_PIN]) + cg.add(var.set_ckv_pin(ckv)) + + gmod = yield cg.gpio_pin_expression(config[CONF_GMOD_PIN]) + cg.add(var.set_gmod_pin(gmod)) + + gpio0_enable = yield cg.gpio_pin_expression(config[CONF_GPIO0_ENABLE_PIN]) + cg.add(var.set_gpio0_enable_pin(gpio0_enable)) + + oe = yield cg.gpio_pin_expression(config[CONF_OE_PIN]) + cg.add(var.set_oe_pin(oe)) + + powerup = yield cg.gpio_pin_expression(config[CONF_POWERUP_PIN]) + cg.add(var.set_powerup_pin(powerup)) + + sph = yield cg.gpio_pin_expression(config[CONF_SPH_PIN]) + cg.add(var.set_sph_pin(sph)) + + spv = yield cg.gpio_pin_expression(config[CONF_SPV_PIN]) + cg.add(var.set_spv_pin(spv)) + + vcom = yield cg.gpio_pin_expression(config[CONF_VCOM_PIN]) + cg.add(var.set_vcom_pin(vcom)) + + wakeup = yield cg.gpio_pin_expression(config[CONF_WAKEUP_PIN]) + cg.add(var.set_wakeup_pin(wakeup)) + + cl = yield cg.gpio_pin_expression(config[CONF_CL_PIN]) + cg.add(var.set_cl_pin(cl)) + + le = yield cg.gpio_pin_expression(config[CONF_LE_PIN]) + cg.add(var.set_le_pin(le)) + + display_data_0 = yield cg.gpio_pin_expression(config[CONF_DISPLAY_DATA_0_PIN]) + cg.add(var.set_display_data_0_pin(display_data_0)) + + display_data_1 = yield cg.gpio_pin_expression(config[CONF_DISPLAY_DATA_1_PIN]) + cg.add(var.set_display_data_1_pin(display_data_1)) + + display_data_2 = yield cg.gpio_pin_expression(config[CONF_DISPLAY_DATA_2_PIN]) + cg.add(var.set_display_data_2_pin(display_data_2)) + + display_data_3 = yield cg.gpio_pin_expression(config[CONF_DISPLAY_DATA_3_PIN]) + cg.add(var.set_display_data_3_pin(display_data_3)) + + display_data_4 = yield cg.gpio_pin_expression(config[CONF_DISPLAY_DATA_4_PIN]) + cg.add(var.set_display_data_4_pin(display_data_4)) + + display_data_5 = yield cg.gpio_pin_expression(config[CONF_DISPLAY_DATA_5_PIN]) + cg.add(var.set_display_data_5_pin(display_data_5)) + + display_data_6 = yield cg.gpio_pin_expression(config[CONF_DISPLAY_DATA_6_PIN]) + cg.add(var.set_display_data_6_pin(display_data_6)) + + display_data_7 = yield cg.gpio_pin_expression(config[CONF_DISPLAY_DATA_7_PIN]) + cg.add(var.set_display_data_7_pin(display_data_7)) + + cg.add_build_flag('-DBOARD_HAS_PSRAM') diff --git a/esphome/components/inkplate6/inkplate.cpp b/esphome/components/inkplate6/inkplate.cpp new file mode 100644 index 0000000000..3b17ba8f52 --- /dev/null +++ b/esphome/components/inkplate6/inkplate.cpp @@ -0,0 +1,630 @@ +#include "inkplate.h" +#include "esphome/core/log.h" +#include "esphome/core/application.h" +#include "esphome/core/helpers.h" + +#ifdef ARDUINO_ARCH_ESP32 + +namespace esphome { +namespace inkplate6 { + +static const char *TAG = "inkplate"; + +void Inkplate6::setup() { + this->initialize_(); + + this->vcom_pin_->setup(); + this->powerup_pin_->setup(); + this->wakeup_pin_->setup(); + this->gpio0_enable_pin_->setup(); + this->gpio0_enable_pin_->digital_write(true); + + this->cl_pin_->setup(); + this->le_pin_->setup(); + this->ckv_pin_->setup(); + this->gmod_pin_->setup(); + this->oe_pin_->setup(); + this->sph_pin_->setup(); + this->spv_pin_->setup(); + + this->display_data_0_pin_->setup(); + this->display_data_1_pin_->setup(); + this->display_data_2_pin_->setup(); + this->display_data_3_pin_->setup(); + this->display_data_4_pin_->setup(); + this->display_data_5_pin_->setup(); + this->display_data_6_pin_->setup(); + this->display_data_7_pin_->setup(); + + this->clean(); + this->display(); +} +void Inkplate6::initialize_() { + uint32_t buffer_size = this->get_buffer_length_(); + + if (this->partial_buffer_ != nullptr) { + free(this->partial_buffer_); // NOLINT + } + if (this->partial_buffer_2_ != nullptr) { + free(this->partial_buffer_2_); // NOLINT + } + if (this->buffer_ != nullptr) { + free(this->buffer_); // NOLINT + } + + this->buffer_ = (uint8_t *) ps_malloc(buffer_size); + if (this->buffer_ == nullptr) { + ESP_LOGE(TAG, "Could not allocate buffer for display!"); + this->mark_failed(); + return; + } + if (!this->greyscale_) { + this->partial_buffer_ = (uint8_t *) ps_malloc(buffer_size); + if (this->partial_buffer_ == nullptr) { + ESP_LOGE(TAG, "Could not allocate partial buffer for display!"); + this->mark_failed(); + return; + } + this->partial_buffer_2_ = (uint8_t *) ps_malloc(buffer_size * 2); + if (this->partial_buffer_2_ == nullptr) { + ESP_LOGE(TAG, "Could not allocate partial buffer 2 for display!"); + this->mark_failed(); + return; + } + memset(this->partial_buffer_, 0, buffer_size); + memset(this->partial_buffer_2_, 0, buffer_size * 2); + } + + memset(this->buffer_, 0, buffer_size); +} +float Inkplate6::get_setup_priority() const { return setup_priority::PROCESSOR; } +size_t Inkplate6::get_buffer_length_() { + if (this->greyscale_) { + return size_t(this->get_width_internal()) * size_t(this->get_height_internal()) / 2u; + } else { + return size_t(this->get_width_internal()) * size_t(this->get_height_internal()) / 8u; + } +} +void Inkplate6::update() { + this->do_update_(); + + if (this->full_update_every_ > 0 && this->partial_updates_ >= this->full_update_every_) { + this->block_partial_ = true; + } + + this->display(); +} +void HOT Inkplate6::draw_absolute_pixel_internal(int x, int y, Color color) { + if (x >= this->get_width_internal() || y >= this->get_height_internal() || x < 0 || y < 0) + return; + + if (this->greyscale_) { + int x1 = x / 2; + int x_sub = x % 2; + uint32_t pos = (x1 + y * (this->get_width_internal() / 2)); + uint8_t current = this->buffer_[pos]; + + // float px = (0.2126 * (color.red / 255.0)) + (0.7152 * (color.green / 255.0)) + (0.0722 * (color.blue / 255.0)); + // px = pow(px, 1.5); + // uint8_t gs = (uint8_t)(px*7); + + uint8_t gs = ((color.red * 2126 / 10000) + (color.green * 7152 / 10000) + (color.blue * 722 / 10000)) >> 5; + this->buffer_[pos] = (pixelMaskGLUT[x_sub] & current) | (x_sub ? gs : gs << 4); + + } else { + int x1 = x / 8; + int x_sub = x % 8; + uint32_t pos = (x1 + y * (this->get_width_internal() / 8)); + uint8_t current = this->partial_buffer_[pos]; + this->partial_buffer_[pos] = (~pixelMaskLUT[x_sub] & current) | (color.is_on() ? 0 : pixelMaskLUT[x_sub]); + } +} +void Inkplate6::dump_config() { + LOG_DISPLAY("", "Inkplate", this); + ESP_LOGCONFIG(TAG, " Greyscale: %s", YESNO(this->greyscale_)); + ESP_LOGCONFIG(TAG, " Partial Updating: %s", YESNO(this->partial_updating_)); + ESP_LOGCONFIG(TAG, " Full Update Every: %d", this->full_update_every_); + // Log pins + LOG_PIN(" CKV Pin: ", this->ckv_pin_); + LOG_PIN(" CL Pin: ", this->cl_pin_); + LOG_PIN(" GPIO0 Enable Pin: ", this->gpio0_enable_pin_); + LOG_PIN(" GMOD Pin: ", this->gmod_pin_); + LOG_PIN(" LE Pin: ", this->le_pin_); + LOG_PIN(" OE Pin: ", this->oe_pin_); + LOG_PIN(" POWERUP Pin: ", this->powerup_pin_); + LOG_PIN(" SPH Pin: ", this->sph_pin_); + LOG_PIN(" SPV Pin: ", this->spv_pin_); + LOG_PIN(" VCOM Pin: ", this->vcom_pin_); + LOG_PIN(" WAKEUP Pin: ", this->wakeup_pin_); + + LOG_PIN(" Data 0 Pin: ", this->display_data_0_pin_); + LOG_PIN(" Data 1 Pin: ", this->display_data_1_pin_); + LOG_PIN(" Data 2 Pin: ", this->display_data_2_pin_); + LOG_PIN(" Data 3 Pin: ", this->display_data_3_pin_); + LOG_PIN(" Data 4 Pin: ", this->display_data_4_pin_); + LOG_PIN(" Data 5 Pin: ", this->display_data_5_pin_); + LOG_PIN(" Data 6 Pin: ", this->display_data_6_pin_); + LOG_PIN(" Data 7 Pin: ", this->display_data_7_pin_); + + LOG_UPDATE_INTERVAL(this); +} +void Inkplate6::eink_off_() { + ESP_LOGV(TAG, "Eink off called"); + unsigned long start_time = millis(); + if (panel_on_ == 0) + return; + panel_on_ = 0; + this->gmod_pin_->digital_write(false); + this->oe_pin_->digital_write(false); + + GPIO.out &= ~(get_data_pin_mask_() | (1 << this->cl_pin_->get_pin()) | (1 << this->le_pin_->get_pin())); + + this->sph_pin_->digital_write(false); + this->spv_pin_->digital_write(false); + + this->powerup_pin_->digital_write(false); + this->wakeup_pin_->digital_write(false); + this->vcom_pin_->digital_write(false); + + pins_z_state_(); +} +void Inkplate6::eink_on_() { + ESP_LOGV(TAG, "Eink on called"); + unsigned long start_time = millis(); + if (panel_on_ == 1) + return; + panel_on_ = 1; + pins_as_outputs_(); + this->wakeup_pin_->digital_write(true); + this->powerup_pin_->digital_write(true); + this->vcom_pin_->digital_write(true); + + this->write_byte(0x01, 0x3F); + + delay(40); + + this->write_byte(0x0D, 0x80); + + delay(2); + + this->read_byte(0x00, &temperature_, 0); + + this->le_pin_->digital_write(false); + this->oe_pin_->digital_write(false); + this->cl_pin_->digital_write(false); + this->sph_pin_->digital_write(true); + this->gmod_pin_->digital_write(true); + this->spv_pin_->digital_write(true); + this->ckv_pin_->digital_write(false); + this->oe_pin_->digital_write(true); +} +void Inkplate6::fill(Color color) { + ESP_LOGV(TAG, "Fill called"); + unsigned long start_time = millis(); + + if (this->greyscale_) { + uint8_t fill = ((color.red * 2126 / 10000) + (color.green * 7152 / 10000) + (color.blue * 722 / 10000)) >> 5; + for (uint32_t i = 0; i < this->get_buffer_length_(); i++) + this->buffer_[i] = (fill << 4) | fill; + } else { + uint8_t fill = color.is_on() ? 0x00 : 0xFF; + for (uint32_t i = 0; i < this->get_buffer_length_(); i++) + this->partial_buffer_[i] = fill; + } + + ESP_LOGV(TAG, "Fill finished (%lums)", millis() - start_time); +} +void Inkplate6::display() { + ESP_LOGV(TAG, "Display called"); + unsigned long start_time = millis(); + + if (this->greyscale_) { + this->display3b_(); + } else { + if (this->partial_updating_ && this->partial_update_()) { + ESP_LOGV(TAG, "Display finished (partial) (%lums)", millis() - start_time); + return; + } + this->display1b_(); + } + ESP_LOGV(TAG, "Display finished (full) (%lums)", millis() - start_time); +} +void Inkplate6::display1b_() { + ESP_LOGV(TAG, "Display1b called"); + unsigned long start_time = millis(); + + for (int i = 0; i < this->get_buffer_length_(); i++) { + this->buffer_[i] &= this->partial_buffer_[i]; + this->buffer_[i] |= this->partial_buffer_[i]; + } + + uint16_t pos; + uint32_t send; + uint8_t data; + uint8_t buffer_value; + eink_on_(); + clean_fast_(0, 1); + clean_fast_(1, 5); + clean_fast_(2, 1); + clean_fast_(0, 5); + clean_fast_(2, 1); + clean_fast_(1, 12); + clean_fast_(2, 1); + clean_fast_(0, 11); + + ESP_LOGV(TAG, "Display1b start loops (%lums)", millis() - start_time); + for (int k = 0; k < 3; k++) { + pos = this->get_buffer_length_() - 1; + vscan_start_(); + for (int i = 0; i < this->get_height_internal(); i++) { + buffer_value = this->buffer_[pos]; + data = LUTB[(buffer_value >> 4) & 0x0F]; + send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | + (((data & B11100000) >> 5) << 25); + hscan_start_(send); + data = LUTB[buffer_value & 0x0F]; + send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | + (((data & B11100000) >> 5) << 25); + GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin()); + GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin()); + pos--; + + for (int j = 0; j < (this->get_width_internal() / 8) - 1; j++) { + buffer_value = this->buffer_[pos]; + data = LUTB[(buffer_value >> 4) & 0x0F]; + send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | + (((data & B11100000) >> 5) << 25); + GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin()); + GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin()); + data = LUTB[buffer_value & 0x0F]; + send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | + (((data & B11100000) >> 5) << 25); + GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin()); + GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin()); + pos--; + } + GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin()); + GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin()); + vscan_end_(); + } + delayMicroseconds(230); + } + ESP_LOGV(TAG, "Display1b first loop x %d (%lums)", 3, millis() - start_time); + + pos = this->get_buffer_length_() - 1; + vscan_start_(); + for (int i = 0; i < this->get_height_internal(); i++) { + buffer_value = this->buffer_[pos]; + data = LUT2[(buffer_value >> 4) & 0x0F]; + send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | + (((data & B11100000) >> 5) << 25); + hscan_start_(send); + data = LUT2[buffer_value & 0x0F]; + send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | + (((data & B11100000) >> 5) << 25); + GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin()); + GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin()); + pos--; + for (int j = 0; j < (this->get_width_internal() / 8) - 1; j++) { + buffer_value = this->buffer_[pos]; + data = LUT2[(buffer_value >> 4) & 0x0F]; + send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | + (((data & B11100000) >> 5) << 25); + GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin()); + GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin()); + data = LUT2[buffer_value & 0x0F]; + send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | + (((data & B11100000) >> 5) << 25); + GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin()); + GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin()); + pos--; + } + GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin()); + GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin()); + vscan_end_(); + } + delayMicroseconds(230); + ESP_LOGV(TAG, "Display1b second loop (%lums)", millis() - start_time); + + vscan_start_(); + for (int i = 0; i < this->get_height_internal(); i++) { + buffer_value = this->buffer_[pos]; + data = 0b00000000; + send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | + (((data & B11100000) >> 5) << 25); + hscan_start_(send); + GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin()); + GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin()); + for (int j = 0; j < (this->get_width_internal() / 8) - 1; j++) { + GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin()); + GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin()); + GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin()); + GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin()); + } + GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin()); + GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin()); + vscan_end_(); + } + delayMicroseconds(230); + ESP_LOGV(TAG, "Display1b third loop (%lums)", millis() - start_time); + + vscan_start_(); + eink_off_(); + this->block_partial_ = false; + this->partial_updates_ = 0; + ESP_LOGV(TAG, "Display1b finished (%lums)", millis() - start_time); +} +void Inkplate6::display3b_() { + ESP_LOGV(TAG, "Display3b called"); + unsigned long start_time = millis(); + + eink_on_(); + clean_fast_(0, 1); + clean_fast_(1, 12); + clean_fast_(2, 1); + clean_fast_(0, 11); + clean_fast_(2, 1); + clean_fast_(1, 12); + clean_fast_(2, 1); + clean_fast_(0, 11); + + for (int k = 0; k < 8; k++) { + uint32_t pos = this->get_buffer_length_() - 1; + uint32_t send; + uint8_t pix1; + uint8_t pix2; + uint8_t pix3; + uint8_t pix4; + uint8_t pixel; + uint8_t pixel2; + + vscan_start_(); + for (int i = 0; i < this->get_height_internal(); i++) { + pix1 = this->buffer_[pos--]; + pix2 = this->buffer_[pos--]; + pix3 = this->buffer_[pos--]; + pix4 = this->buffer_[pos--]; + pixel = (waveform3Bit[pix1 & 0x07][k] << 6) | (waveform3Bit[(pix1 >> 4) & 0x07][k] << 4) | + (waveform3Bit[pix2 & 0x07][k] << 2) | (waveform3Bit[(pix2 >> 4) & 0x07][k] << 0); + pixel2 = (waveform3Bit[pix3 & 0x07][k] << 6) | (waveform3Bit[(pix3 >> 4) & 0x07][k] << 4) | + (waveform3Bit[pix4 & 0x07][k] << 2) | (waveform3Bit[(pix4 >> 4) & 0x07][k] << 0); + + send = ((pixel & B00000011) << 4) | (((pixel & B00001100) >> 2) << 18) | (((pixel & B00010000) >> 4) << 23) | + (((pixel & B11100000) >> 5) << 25); + hscan_start_(send); + send = ((pixel2 & B00000011) << 4) | (((pixel2 & B00001100) >> 2) << 18) | (((pixel2 & B00010000) >> 4) << 23) | + (((pixel2 & B11100000) >> 5) << 25); + GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin()); + GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin()); + + for (int j = 0; j < (this->get_width_internal() / 8) - 1; j++) { + pix1 = this->buffer_[pos--]; + pix2 = this->buffer_[pos--]; + pix3 = this->buffer_[pos--]; + pix4 = this->buffer_[pos--]; + pixel = (waveform3Bit[pix1 & 0x07][k] << 6) | (waveform3Bit[(pix1 >> 4) & 0x07][k] << 4) | + (waveform3Bit[pix2 & 0x07][k] << 2) | (waveform3Bit[(pix2 >> 4) & 0x07][k] << 0); + pixel2 = (waveform3Bit[pix3 & 0x07][k] << 6) | (waveform3Bit[(pix3 >> 4) & 0x07][k] << 4) | + (waveform3Bit[pix4 & 0x07][k] << 2) | (waveform3Bit[(pix4 >> 4) & 0x07][k] << 0); + + send = ((pixel & B00000011) << 4) | (((pixel & B00001100) >> 2) << 18) | (((pixel & B00010000) >> 4) << 23) | + (((pixel & B11100000) >> 5) << 25); + GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin()); + GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin()); + + send = ((pixel2 & B00000011) << 4) | (((pixel2 & B00001100) >> 2) << 18) | (((pixel2 & B00010000) >> 4) << 23) | + (((pixel2 & B11100000) >> 5) << 25); + GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin()); + GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin()); + } + GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin()); + GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin()); + vscan_end_(); + } + delayMicroseconds(230); + } + clean_fast_(2, 1); + clean_fast_(3, 1); + vscan_start_(); + eink_off_(); + ESP_LOGV(TAG, "Display3b finished (%lums)", millis() - start_time); +} +bool Inkplate6::partial_update_() { + ESP_LOGV(TAG, "Partial update called"); + unsigned long start_time = millis(); + if (this->greyscale_) + return false; + if (this->block_partial_) + return false; + + this->partial_updates_++; + + uint16_t pos = this->get_buffer_length_() - 1; + uint32_t send; + uint8_t data; + uint8_t diffw, diffb; + uint32_t n = (this->get_buffer_length_() * 2) - 1; + + for (int i = 0; i < this->get_height_internal(); i++) { + for (int j = 0; j < (this->get_width_internal() / 8); j++) { + diffw = (this->buffer_[pos] ^ this->partial_buffer_[pos]) & ~(this->partial_buffer_[pos]); + diffb = (this->buffer_[pos] ^ this->partial_buffer_[pos]) & this->partial_buffer_[pos]; + pos--; + this->partial_buffer_2_[n--] = LUTW[diffw >> 4] & LUTB[diffb >> 4]; + this->partial_buffer_2_[n--] = LUTW[diffw & 0x0F] & LUTB[diffb & 0x0F]; + } + } + ESP_LOGV(TAG, "Partial update buffer built after (%lums)", millis() - start_time); + + eink_on_(); + for (int k = 0; k < 5; k++) { + vscan_start_(); + n = (this->get_buffer_length_() * 2) - 1; + for (int i = 0; i < this->get_height_internal(); i++) { + data = this->partial_buffer_2_[n--]; + send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | + (((data & B11100000) >> 5) << 25); + hscan_start_(send); + for (int j = 0; j < (this->get_width_internal() / 4) - 1; j++) { + data = this->partial_buffer_2_[n--]; + send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | + (((data & B11100000) >> 5) << 25); + GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin()); + GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin()); + } + GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin()); + GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin()); + vscan_end_(); + } + delayMicroseconds(230); + ESP_LOGV(TAG, "Partial update loop k=%d (%lums)", k, millis() - start_time); + } + clean_fast_(2, 2); + clean_fast_(3, 1); + vscan_start_(); + eink_off_(); + + for (int i = 0; i < this->get_buffer_length_(); i++) { + this->buffer_[i] = this->partial_buffer_[i]; + } + ESP_LOGV(TAG, "Partial update finished (%lums)", millis() - start_time); + return true; +} +void Inkplate6::vscan_start_() { + this->ckv_pin_->digital_write(true); + delayMicroseconds(7); + this->spv_pin_->digital_write(false); + delayMicroseconds(10); + this->ckv_pin_->digital_write(false); + delayMicroseconds(0); + this->ckv_pin_->digital_write(true); + delayMicroseconds(8); + this->spv_pin_->digital_write(true); + delayMicroseconds(10); + this->ckv_pin_->digital_write(false); + delayMicroseconds(0); + this->ckv_pin_->digital_write(true); + delayMicroseconds(18); + this->ckv_pin_->digital_write(false); + delayMicroseconds(0); + this->ckv_pin_->digital_write(true); + delayMicroseconds(18); + this->ckv_pin_->digital_write(false); + delayMicroseconds(0); + this->ckv_pin_->digital_write(true); +} +void Inkplate6::vscan_write_() { + this->ckv_pin_->digital_write(false); + this->le_pin_->digital_write(true); + this->le_pin_->digital_write(false); + delayMicroseconds(0); + this->sph_pin_->digital_write(false); + this->cl_pin_->digital_write(true); + this->cl_pin_->digital_write(false); + this->sph_pin_->digital_write(true); + this->ckv_pin_->digital_write(true); +} +void Inkplate6::hscan_start_(uint32_t d) { + this->sph_pin_->digital_write(false); + GPIO.out_w1ts = (d) | (1 << this->cl_pin_->get_pin()); + GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin()); + this->sph_pin_->digital_write(true); +} +void Inkplate6::vscan_end_() { + this->ckv_pin_->digital_write(false); + this->le_pin_->digital_write(true); + this->le_pin_->digital_write(false); + delayMicroseconds(1); + this->ckv_pin_->digital_write(true); +} +void Inkplate6::clean() { + ESP_LOGV(TAG, "Clean called"); + unsigned long start_time = millis(); + + eink_on_(); + clean_fast_(0, 1); // White + clean_fast_(0, 8); // White to White + clean_fast_(0, 1); // White to Black + clean_fast_(0, 8); // Black to Black + clean_fast_(2, 1); // Black to White + clean_fast_(1, 10); // White to White + ESP_LOGV(TAG, "Clean finished (%lums)", millis() - start_time); +} +void Inkplate6::clean_fast_(uint8_t c, uint8_t rep) { + ESP_LOGV(TAG, "Clean fast called with: (%d, %d)", c, rep); + unsigned long start_time = millis(); + + eink_on_(); + uint8_t data = 0; + if (c == 0) // White + data = B10101010; + else if (c == 1) // Black + data = B01010101; + else if (c == 2) // Discharge + data = B00000000; + else if (c == 3) // Skip + data = B11111111; + + uint32_t send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | + (((data & B11100000) >> 5) << 25); + + for (int k = 0; k < rep; k++) { + vscan_start_(); + for (int i = 0; i < this->get_height_internal(); i++) { + hscan_start_(send); + GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin()); + GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin()); + for (int j = 0; j < this->get_width_internal() / 8; j++) { + GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin()); + GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin()); + GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin()); + GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin()); + } + GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin()); + GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin()); + vscan_end_(); + } + delayMicroseconds(230); + ESP_LOGV(TAG, "Clean fast rep loop %d finished (%lums)", k, millis() - start_time); + } + ESP_LOGV(TAG, "Clean fast finished (%lums)", millis() - start_time); +} +void Inkplate6::pins_z_state_() { + this->ckv_pin_->pin_mode(INPUT); + this->sph_pin_->pin_mode(INPUT); + + this->oe_pin_->pin_mode(INPUT); + this->gmod_pin_->pin_mode(INPUT); + this->spv_pin_->pin_mode(INPUT); + + this->display_data_0_pin_->pin_mode(INPUT); + this->display_data_1_pin_->pin_mode(INPUT); + this->display_data_2_pin_->pin_mode(INPUT); + this->display_data_3_pin_->pin_mode(INPUT); + this->display_data_4_pin_->pin_mode(INPUT); + this->display_data_5_pin_->pin_mode(INPUT); + this->display_data_6_pin_->pin_mode(INPUT); + this->display_data_7_pin_->pin_mode(INPUT); +} +void Inkplate6::pins_as_outputs_() { + this->ckv_pin_->pin_mode(OUTPUT); + this->sph_pin_->pin_mode(OUTPUT); + + this->oe_pin_->pin_mode(OUTPUT); + this->gmod_pin_->pin_mode(OUTPUT); + this->spv_pin_->pin_mode(OUTPUT); + + this->display_data_0_pin_->pin_mode(OUTPUT); + this->display_data_1_pin_->pin_mode(OUTPUT); + this->display_data_2_pin_->pin_mode(OUTPUT); + this->display_data_3_pin_->pin_mode(OUTPUT); + this->display_data_4_pin_->pin_mode(OUTPUT); + this->display_data_5_pin_->pin_mode(OUTPUT); + this->display_data_6_pin_->pin_mode(OUTPUT); + this->display_data_7_pin_->pin_mode(OUTPUT); +} + +} // namespace inkplate6 +} // namespace esphome + +#endif diff --git a/esphome/components/inkplate6/inkplate.h b/esphome/components/inkplate6/inkplate.h new file mode 100644 index 0000000000..94d14b4f6e --- /dev/null +++ b/esphome/components/inkplate6/inkplate.h @@ -0,0 +1,157 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/i2c/i2c.h" +#include "esphome/components/display/display_buffer.h" + +#ifdef ARDUINO_ARCH_ESP32 + +namespace esphome { +namespace inkplate6 { + +class Inkplate6 : public PollingComponent, public display::DisplayBuffer, public i2c::I2CDevice { + public: + const uint8_t LUT2[16] = {B10101010, B10101001, B10100110, B10100101, B10011010, B10011001, B10010110, B10010101, + B01101010, B01101001, B01100110, B01100101, B01011010, B01011001, B01010110, B01010101}; + const uint8_t LUTW[16] = {B11111111, B11111110, B11111011, B11111010, B11101111, B11101110, B11101011, B11101010, + B10111111, B10111110, B10111011, B10111010, B10101111, B10101110, B10101011, B10101010}; + const uint8_t LUTB[16] = {B11111111, B11111101, B11110111, B11110101, B11011111, B11011101, B11010111, B11010101, + B01111111, B01111101, B01110111, B01110101, B01011111, B01011101, B01010111, B01010101}; + const uint8_t pixelMaskLUT[8] = {B00000001, B00000010, B00000100, B00001000, + B00010000, B00100000, B01000000, B10000000}; + const uint8_t pixelMaskGLUT[2] = {B00001111, B11110000}; + const uint8_t waveform3Bit[8][8] = {{0, 0, 0, 0, 1, 1, 1, 0}, {1, 2, 2, 2, 1, 1, 1, 0}, {0, 1, 2, 1, 1, 2, 1, 0}, + {0, 2, 1, 2, 1, 2, 1, 0}, {0, 0, 0, 1, 1, 1, 2, 0}, {2, 1, 1, 1, 2, 1, 2, 0}, + {1, 1, 1, 2, 1, 2, 2, 0}, {0, 0, 0, 0, 0, 0, 2, 0}}; + const uint32_t waveform[50] = { + 0x00000008, 0x00000008, 0x00200408, 0x80281888, 0x60a81898, 0x60a8a8a8, 0x60a8a8a8, 0x6068a868, 0x6868a868, + 0x6868a868, 0x68686868, 0x6a686868, 0x5a686868, 0x5a686868, 0x5a586a68, 0x5a5a6a68, 0x5a5a6a68, 0x55566a68, + 0x55565a64, 0x55555654, 0x55555556, 0x55555556, 0x55555556, 0x55555516, 0x55555596, 0x15555595, 0x95955595, + 0x95959595, 0x95949495, 0x94949495, 0x94949495, 0xa4949494, 0x9494a4a4, 0x84a49494, 0x84948484, 0x84848484, + 0x84848484, 0x84848484, 0xa5a48484, 0xa9a4a4a8, 0xa9a8a8a8, 0xa5a9a9a4, 0xa5a5a5a4, 0xa1a5a5a1, 0xa9a9a9a9, + 0xa9a9a9a9, 0xa9a9a9a9, 0xa9a9a9a9, 0x15151515, 0x11111111}; + + void set_greyscale(bool greyscale) { + this->greyscale_ = greyscale; + this->initialize_(); + this->block_partial_ = true; + } + void set_partial_updating(bool partial_updating) { this->partial_updating_ = partial_updating; } + void set_full_update_every(uint32_t full_update_every) { this->full_update_every_ = full_update_every; } + + void set_display_data_0_pin(GPIOPin *data) { this->display_data_0_pin_ = data; } + void set_display_data_1_pin(GPIOPin *data) { this->display_data_1_pin_ = data; } + void set_display_data_2_pin(GPIOPin *data) { this->display_data_2_pin_ = data; } + void set_display_data_3_pin(GPIOPin *data) { this->display_data_3_pin_ = data; } + void set_display_data_4_pin(GPIOPin *data) { this->display_data_4_pin_ = data; } + void set_display_data_5_pin(GPIOPin *data) { this->display_data_5_pin_ = data; } + void set_display_data_6_pin(GPIOPin *data) { this->display_data_6_pin_ = data; } + void set_display_data_7_pin(GPIOPin *data) { this->display_data_7_pin_ = data; } + + void set_ckv_pin(GPIOPin *ckv) { this->ckv_pin_ = ckv; } + void set_cl_pin(GPIOPin *cl) { this->cl_pin_ = cl; } + void set_gpio0_enable_pin(GPIOPin *gpio0_enable) { this->gpio0_enable_pin_ = gpio0_enable; } + void set_gmod_pin(GPIOPin *gmod) { this->gmod_pin_ = gmod; } + void set_le_pin(GPIOPin *le) { this->le_pin_ = le; } + void set_oe_pin(GPIOPin *oe) { this->oe_pin_ = oe; } + void set_powerup_pin(GPIOPin *powerup) { this->powerup_pin_ = powerup; } + void set_sph_pin(GPIOPin *sph) { this->sph_pin_ = sph; } + void set_spv_pin(GPIOPin *spv) { this->spv_pin_ = spv; } + void set_vcom_pin(GPIOPin *vcom) { this->vcom_pin_ = vcom; } + void set_wakeup_pin(GPIOPin *wakeup) { this->wakeup_pin_ = wakeup; } + + float get_setup_priority() const override; + + void dump_config() override; + + void display(); + void clean(); + void fill(Color color) override; + + void update() override; + + void setup() override; + + uint8_t get_panel_state() { return this->panel_on_; } + bool get_greyscale() { return this->greyscale_; } + bool get_partial_updating() { return this->partial_updating_; } + uint8_t get_temperature() { return this->temperature_; } + + protected: + void draw_absolute_pixel_internal(int x, int y, Color color) override; + void display1b_(); + void display3b_(); + void initialize_(); + bool partial_update_(); + void clean_fast_(uint8_t c, uint8_t rep); + + void hscan_start_(uint32_t d); + void vscan_end_(); + void vscan_start_(); + void vscan_write_(); + + void eink_off_(); + void eink_on_(); + + void setup_pins_(); + void pins_z_state_(); + void pins_as_outputs_(); + + int get_width_internal() override { return 800; } + + int get_height_internal() override { return 600; } + + size_t get_buffer_length_(); + + int get_data_pin_mask_() { + int data = 0; + data |= (1 << this->display_data_0_pin_->get_pin()); + data |= (1 << this->display_data_1_pin_->get_pin()); + data |= (1 << this->display_data_2_pin_->get_pin()); + data |= (1 << this->display_data_3_pin_->get_pin()); + data |= (1 << this->display_data_4_pin_->get_pin()); + data |= (1 << this->display_data_5_pin_->get_pin()); + data |= (1 << this->display_data_6_pin_->get_pin()); + data |= (1 << this->display_data_7_pin_->get_pin()); + return data; + } + + uint8_t panel_on_ = 0; + uint8_t temperature_; + + uint8_t *partial_buffer_{nullptr}; + uint8_t *partial_buffer_2_{nullptr}; + + uint32_t full_update_every_; + uint32_t partial_updates_{0}; + + bool block_partial_; + bool greyscale_; + bool partial_updating_; + + GPIOPin *display_data_0_pin_; + GPIOPin *display_data_1_pin_; + GPIOPin *display_data_2_pin_; + GPIOPin *display_data_3_pin_; + GPIOPin *display_data_4_pin_; + GPIOPin *display_data_5_pin_; + GPIOPin *display_data_6_pin_; + GPIOPin *display_data_7_pin_; + + GPIOPin *ckv_pin_; + GPIOPin *cl_pin_; + GPIOPin *gpio0_enable_pin_; + GPIOPin *gmod_pin_; + GPIOPin *le_pin_; + GPIOPin *oe_pin_; + GPIOPin *powerup_pin_; + GPIOPin *sph_pin_; + GPIOPin *spv_pin_; + GPIOPin *vcom_pin_; + GPIOPin *wakeup_pin_; +}; + +} // namespace inkplate6 +} // namespace esphome + +#endif From 4c105398f75af0d0dcfbecb5f320c2e1df4832d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=B6sch?= Date: Mon, 18 Jan 2021 13:34:50 +0100 Subject: [PATCH 016/111] time sync notification (#1442) * add on_time_sync trigger * cleanup lint * fix review remark (sntp didn't trigger callbacks) --- esphome/components/sntp/sntp_component.cpp | 1 + esphome/components/time/__init__.py | 14 ++++++++++++-- esphome/components/time/automation.cpp | 4 ++++ esphome/components/time/automation.h | 7 +++++++ esphome/components/time/real_time_clock.cpp | 2 ++ esphome/components/time/real_time_clock.h | 6 ++++++ esphome/const.py | 1 + tests/test1.yaml | 5 +---- 8 files changed, 34 insertions(+), 6 deletions(-) diff --git a/esphome/components/sntp/sntp_component.cpp b/esphome/components/sntp/sntp_component.cpp index 641d66091c..4050609dd8 100644 --- a/esphome/components/sntp/sntp_component.cpp +++ b/esphome/components/sntp/sntp_component.cpp @@ -54,6 +54,7 @@ void SNTPComponent::loop() { char buf[128]; time.strftime(buf, sizeof(buf), "%c"); ESP_LOGD(TAG, "Synchronized time: %s", buf); + this->time_sync_callback_.call(); this->has_time_ = true; } diff --git a/esphome/components/time/__init__.py b/esphome/components/time/__init__.py index e5ed5034ab..5f30a8f2ee 100644 --- a/esphome/components/time/__init__.py +++ b/esphome/components/time/__init__.py @@ -11,8 +11,8 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import automation from esphome.const import CONF_ID, CONF_CRON, CONF_DAYS_OF_MONTH, CONF_DAYS_OF_WEEK, CONF_HOURS, \ - CONF_MINUTES, CONF_MONTHS, CONF_ON_TIME, CONF_SECONDS, CONF_TIMEZONE, CONF_TRIGGER_ID, \ - CONF_AT, CONF_SECOND, CONF_HOUR, CONF_MINUTE + CONF_MINUTES, CONF_MONTHS, CONF_ON_TIME, CONF_ON_TIME_SYNC, CONF_SECONDS, CONF_TIMEZONE, \ + CONF_TRIGGER_ID, CONF_AT, CONF_SECOND, CONF_HOUR, CONF_MINUTE from esphome.core import coroutine, coroutine_with_priority from esphome.automation import Condition @@ -24,6 +24,7 @@ IS_PLATFORM_COMPONENT = True time_ns = cg.esphome_ns.namespace('time') RealTimeClock = time_ns.class_('RealTimeClock', cg.PollingComponent) CronTrigger = time_ns.class_('CronTrigger', automation.Trigger.template(), cg.Component) +SyncTrigger = time_ns.class_('SyncTrigger', automation.Trigger.template(), cg.Component) ESPTime = time_ns.struct('ESPTime') TimeHasTimeCondition = time_ns.class_('TimeHasTimeCondition', Condition) @@ -294,6 +295,9 @@ TIME_SCHEMA = cv.Schema({ cv.Optional(CONF_CRON): validate_cron_raw, cv.Optional(CONF_AT): validate_time_at, }, validate_cron_keys), + cv.Optional(CONF_ON_TIME_SYNC): automation.validate_automation({ + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SyncTrigger), + }), }).extend(cv.polling_component_schema('15min')) @@ -320,6 +324,12 @@ def setup_time_core_(time_var, config): yield cg.register_component(trigger, conf) yield automation.build_automation(trigger, [], conf) + for conf in config.get(CONF_ON_TIME_SYNC, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], time_var) + + yield cg.register_component(trigger, conf) + yield automation.build_automation(trigger, [], conf) + @coroutine def register_time(time_var, config): diff --git a/esphome/components/time/automation.cpp b/esphome/components/time/automation.cpp index 1232e6f834..8e41fba0da 100644 --- a/esphome/components/time/automation.cpp +++ b/esphome/components/time/automation.cpp @@ -75,5 +75,9 @@ void CronTrigger::add_days_of_week(const std::vector &days_of_week) { } float CronTrigger::get_setup_priority() const { return setup_priority::HARDWARE; } +SyncTrigger::SyncTrigger(RealTimeClock *rtc) : rtc_(rtc) { + rtc->add_on_time_sync_callback([this]() { this->trigger(); }); +} + } // namespace time } // namespace esphome diff --git a/esphome/components/time/automation.h b/esphome/components/time/automation.h index 978d25fbd4..6167aac4f7 100644 --- a/esphome/components/time/automation.h +++ b/esphome/components/time/automation.h @@ -37,5 +37,12 @@ class CronTrigger : public Trigger<>, public Component { optional last_check_; }; +class SyncTrigger : public Trigger<>, public Component { + public: + explicit SyncTrigger(RealTimeClock *rtc); + + protected: + RealTimeClock *rtc_; +}; } // namespace time } // namespace esphome diff --git a/esphome/components/time/real_time_clock.cpp b/esphome/components/time/real_time_clock.cpp index 7d7c1013dd..44ff505ecc 100644 --- a/esphome/components/time/real_time_clock.cpp +++ b/esphome/components/time/real_time_clock.cpp @@ -38,6 +38,8 @@ void RealTimeClock::synchronize_epoch_(uint32_t epoch) { char buf[128]; time.strftime(buf, sizeof(buf), "%c"); ESP_LOGD(TAG, "Synchronized time: %s", buf); + + this->time_sync_callback_.call(); } size_t ESPTime::strftime(char *buffer, size_t buffer_len, const char *format) { diff --git a/esphome/components/time/real_time_clock.h b/esphome/components/time/real_time_clock.h index 880a4e9d5c..a809401c33 100644 --- a/esphome/components/time/real_time_clock.h +++ b/esphome/components/time/real_time_clock.h @@ -127,11 +127,17 @@ class RealTimeClock : public PollingComponent { void call_setup() override; + void add_on_time_sync_callback(std::function callback) { + this->time_sync_callback_.add(std::move(callback)); + }; + protected: /// Report a unix epoch as current time. void synchronize_epoch_(uint32_t epoch); std::string timezone_{}; + + CallbackManager time_sync_callback_; }; template class TimeHasTimeCondition : public Condition { diff --git a/esphome/const.py b/esphome/const.py index 8ab739768f..184954b4c6 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -350,6 +350,7 @@ CONF_ON_SHUTDOWN = 'on_shutdown' CONF_ON_STATE = 'on_state' CONF_ON_TAG = 'on_tag' CONF_ON_TIME = 'on_time' +CONF_ON_TIME_SYNC = 'on_time_sync' CONF_ON_TURN_OFF = 'on_turn_off' CONF_ON_TURN_ON = 'on_turn_on' CONF_ON_VALUE = 'on_value' diff --git a/tests/test1.yaml b/tests/test1.yaml index f2ebca05ab..ae10e0e0c7 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -1854,10 +1854,7 @@ time: then: - lambda: 'ESP_LOGD("main", "time");' - platform: gps - update_interval: 1h - on_time: - seconds: 0 - minutes: /15 + on_time_sync: then: ds1307.write: id: ds1307_time From b28735d63bcce20138a17a12048a26d974baf92b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=B6sch?= Date: Mon, 18 Jan 2021 13:35:35 +0100 Subject: [PATCH 017/111] rename read/write to read/time/write_time (#1468) --- esphome/components/ds1307/ds1307.cpp | 6 +++--- esphome/components/ds1307/ds1307.h | 8 ++++---- esphome/components/ds1307/time.py | 8 ++++---- tests/test1.yaml | 4 ++-- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/esphome/components/ds1307/ds1307.cpp b/esphome/components/ds1307/ds1307.cpp index 599ec16e4f..2f33768132 100644 --- a/esphome/components/ds1307/ds1307.cpp +++ b/esphome/components/ds1307/ds1307.cpp @@ -16,7 +16,7 @@ void DS1307Component::setup() { } } -void DS1307Component::update() { this->read(); } +void DS1307Component::update() { this->read_time(); } void DS1307Component::dump_config() { ESP_LOGCONFIG(TAG, "DS1307:"); @@ -29,7 +29,7 @@ void DS1307Component::dump_config() { float DS1307Component::get_setup_priority() const { return setup_priority::DATA; } -void DS1307Component::read() { +void DS1307Component::read_time() { if (!this->read_rtc_()) { return; } @@ -53,7 +53,7 @@ void DS1307Component::read() { time::RealTimeClock::synchronize_epoch_(rtc_time.timestamp); } -void DS1307Component::write() { +void DS1307Component::write_time() { auto now = time::RealTimeClock::utcnow(); if (!now.is_valid()) { ESP_LOGE(TAG, "Invalid system time, not syncing to RTC."); diff --git a/esphome/components/ds1307/ds1307.h b/esphome/components/ds1307/ds1307.h index 8e318bf395..2e9ac2275c 100644 --- a/esphome/components/ds1307/ds1307.h +++ b/esphome/components/ds1307/ds1307.h @@ -13,8 +13,8 @@ class DS1307Component : public time::RealTimeClock, public i2c::I2CDevice { void update() override; void dump_config() override; float get_setup_priority() const override; - void read(); - void write(); + void read_time(); + void write_time(); protected: bool read_rtc_(); @@ -59,12 +59,12 @@ class DS1307Component : public time::RealTimeClock, public i2c::I2CDevice { template class WriteAction : public Action, public Parented { public: - void play(Ts... x) override { this->parent_->write(); } + void play(Ts... x) override { this->parent_->write_time(); } }; template class ReadAction : public Action, public Parented { public: - void play(Ts... x) override { this->parent_->read(); } + void play(Ts... x) override { this->parent_->read_time(); } }; } // namespace ds1307 } // namespace esphome diff --git a/esphome/components/ds1307/time.py b/esphome/components/ds1307/time.py index c381fb7b2a..371dc85be8 100644 --- a/esphome/components/ds1307/time.py +++ b/esphome/components/ds1307/time.py @@ -18,19 +18,19 @@ CONFIG_SCHEMA = time.TIME_SCHEMA.extend({ }).extend(i2c.i2c_device_schema(0x68)) -@automation.register_action('ds1307.write', WriteAction, cv.Schema({ +@automation.register_action('ds1307.write_time', WriteAction, cv.Schema({ cv.GenerateID(): cv.use_id(DS1307Component), })) -def ds1307_write_to_code(config, action_id, template_arg, args): +def ds1307_write_time_to_code(config, action_id, template_arg, args): var = cg.new_Pvariable(action_id, template_arg) yield cg.register_parented(var, config[CONF_ID]) yield var -@automation.register_action('ds1307.read', ReadAction, automation.maybe_simple_id({ +@automation.register_action('ds1307.read_time', ReadAction, automation.maybe_simple_id({ cv.GenerateID(): cv.use_id(DS1307Component), })) -def ds1307_read_to_code(config, action_id, template_arg, args): +def ds1307_read_time_to_code(config, action_id, template_arg, args): var = cg.new_Pvariable(action_id, template_arg) yield cg.register_parented(var, config[CONF_ID]) yield var diff --git a/tests/test1.yaml b/tests/test1.yaml index ae10e0e0c7..5aed1dac44 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -1856,7 +1856,7 @@ time: - platform: gps on_time_sync: then: - ds1307.write: + ds1307.write_time: id: ds1307_time - platform: ds1307 id: ds1307_time @@ -1864,7 +1864,7 @@ time: on_time: seconds: 0 then: - ds1307.read + ds1307.read_time cover: - platform: template From 8352f52fef81a1170249874bd3d5a5665a93f16f Mon Sep 17 00:00:00 2001 From: Zixuan Wang Date: Thu, 21 Jan 2021 23:51:40 -0800 Subject: [PATCH 018/111] Improve ccs811 precision (#1428) --- esphome/components/ccs811/ccs811.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/esphome/components/ccs811/ccs811.cpp b/esphome/components/ccs811/ccs811.cpp index 538d7fe1f5..e7928d4d8b 100644 --- a/esphome/components/ccs811/ccs811.cpp +++ b/esphome/components/ccs811/ccs811.cpp @@ -102,10 +102,14 @@ void CCS811Component::send_env_data_() { // temperature has a 25° offset to allow negative temperatures temperature += 25; - // only 0.5 fractions are supported (application note) - auto hum_value = static_cast(roundf(humidity * 2)); - auto temp_value = static_cast(roundf(temperature * 2)); - this->write_bytes(0x05, {hum_value, 0x00, temp_value, 0x00}); + // At page 18 of: + // https://cdn.sparkfun.com/datasheets/BreakoutBoards/CCS811_Programming_Guide.pdf + // Reference code: + // https://github.com/adafruit/Adafruit_CCS811/blob/0990f5c620354d8bc087c4706bec091d8e6e5dfd/Adafruit_CCS811.cpp#L135-L142 + uint16_t hum_conv = static_cast(lroundf(humidity * 512.0f + 0.5f)); + uint16_t temp_conv = static_cast(lroundf(temperature * 512.0f + 0.5f)); + this->write_bytes(0x05, {(uint8_t)((hum_conv >> 8) & 0xff), (uint8_t)((hum_conv & 0xff)), + (uint8_t)((temp_conv >> 8) & 0xff), (uint8_t)((temp_conv & 0xff))}); } void CCS811Component::dump_config() { ESP_LOGCONFIG(TAG, "CCS811"); From f084ab339bb1381ebffb86d2b409dc5aa57f7ea6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20T=C3=B6lke?= Date: Fri, 22 Jan 2021 08:55:49 +0100 Subject: [PATCH 019/111] Make fade_to*, lighten, and darken const (#1450) --- esphome/components/light/addressable_light.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/esphome/components/light/addressable_light.h b/esphome/components/light/addressable_light.h index a95d70f274..4e7ec4b931 100644 --- a/esphome/components/light/addressable_light.h +++ b/esphome/components/light/addressable_light.h @@ -149,10 +149,10 @@ struct ESPColor { return ESPColor(uint8_t((uint16_t(r) * 255U / max_rgb)), uint8_t((uint16_t(g) * 255U / max_rgb)), uint8_t((uint16_t(b) * 255U / max_rgb)), w); } - ESPColor fade_to_white(uint8_t amnt) { return ESPColor(255, 255, 255, 255) - (*this * amnt); } - ESPColor fade_to_black(uint8_t amnt) { return *this * amnt; } - ESPColor lighten(uint8_t delta) { return *this + delta; } - ESPColor darken(uint8_t delta) { return *this - delta; } + ESPColor fade_to_white(uint8_t amnt) const { return ESPColor(255, 255, 255, 255) - (*this * amnt); } + ESPColor fade_to_black(uint8_t amnt) const { return *this * amnt; } + ESPColor lighten(uint8_t delta) const { return *this + delta; } + ESPColor darken(uint8_t delta) const { return *this - delta; } static const ESPColor BLACK; static const ESPColor WHITE; From 52c67d715bb7f763702343f4af4431124df31ffe Mon Sep 17 00:00:00 2001 From: Guillermo Ruffino Date: Sat, 23 Jan 2021 19:44:20 -0300 Subject: [PATCH 020/111] add http request tests (#1448) * add http request tests * add to test3 for esp8266 * move test action to another trigger --- tests/test1.yaml | 29 +++++++++++++++++++++++++---- tests/test3.yaml | 23 +++++++++++++++++++++++ 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/tests/test1.yaml b/tests/test1.yaml index 5aed1dac44..e1088e0d5a 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -26,6 +26,25 @@ esphome: green: !lambda 'return 255;' blue: 0% white: 100% + - http_request.get: + url: https://esphome.io + headers: + Content-Type: application/json + verify_ssl: false + - http_request.post: + url: https://esphome.io + verify_ssl: false + json: + key: !lambda |- + return id(template_text).state; + greeting: 'Hello World' + - http_request.send: + method: PUT + url: https://esphome.io + headers: + Content-Type: application/json + body: 'Some data' + verify_ssl: false build_path: build/test1 packages: @@ -50,6 +69,10 @@ wifi: reboot_timeout: 120s power_save_mode: none +http_request: + useragent: esphome/device + timeout: 10s + mqtt: broker: '192.168.178.84' port: 1883 @@ -211,7 +234,6 @@ mcp23s17: cs_pin: GPIO12 deviceaddress: 1 - sensor: - platform: adc pin: A0 @@ -1727,7 +1749,7 @@ display: lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); - platform: ssd1322_spi - model: "SSD1322 256x64" + model: 'SSD1322 256x64' cs_pin: GPIO23 dc_pin: GPIO23 reset_pin: GPIO23 @@ -1863,8 +1885,7 @@ time: update_interval: never on_time: seconds: 0 - then: - ds1307.read_time + then: ds1307.read_time cover: - platform: template diff --git a/tests/test3.yaml b/tests/test3.yaml index 64d5dbfc9b..b38a7414b8 100644 --- a/tests/test3.yaml +++ b/tests/test3.yaml @@ -886,6 +886,25 @@ rf_bridge: code: 'ABC123' - rf_bridge.send_raw: raw: 'AAA5070008001000ABC12355' + - http_request.get: + url: https://esphome.io + headers: + Content-Type: application/json + verify_ssl: false + - http_request.post: + url: https://esphome.io + verify_ssl: false + json: + key: !lambda |- + return id(version_sensor).state; + greeting: 'Hello World' + - http_request.send: + method: PUT + url: https://esphome.io + headers: + Content-Type: application/json + body: 'Some data' + verify_ssl: false display: - platform: max7219digit @@ -897,3 +916,7 @@ display: id: my_matrix lambda: |- it.printdigit("hello"); + +http_request: + useragent: esphome/device + timeout: 10s From c7c71089ced0f10dd7c520dd6063302bfcf37ba0 Mon Sep 17 00:00:00 2001 From: Andrew Zaborowski Date: Sun, 24 Jan 2021 00:17:15 +0100 Subject: [PATCH 021/111] codegen: Lambda improvements (#1476) * Use #line directives in generated C++ code for lambdas The #line directive in gcc is meant specifically for pieces of imported code included in generated code, exactly what happens with lambdas in the yaml files: https://gcc.gnu.org/onlinedocs/cpp/Line-Control.html With this change, if I add the following at line 165 of kithen.yaml: - lambda: undefined_var == 5; then "$ esphome kitchen.yaml compile" shows the following: INFO Reading configuration kitchen.yaml... INFO Generating C++ source... INFO Compiling app... INFO Running: platformio run -d kitchen <...> Compiling .pioenvs/kitchen/src/main.cpp.o kitchen.yaml: In lambda function: kitchen.yaml:165:7: error: 'undefined_var' was not declared in this scope *** [.pioenvs/kitchen/src/main.cpp.o] Error 1 == [FAILED] Took 2.37 seconds == * Silence gcc warning on multiline macros in lambdas When the \ is used at the end of the C++ source in a lambda (line continuation, often used in preprocessor macros), esphome will copy that into main.cpp once as code and once as a // commment. gcc will complain about the multiline commment: Compiling .pioenvs/kitchen/src/main.cpp.o kitchen.yaml:640:3: warning: multi-line comment [-Wcomment] Try to replace the \ with a "" for lack of a better idea. --- esphome/config_validation.py | 9 +++++++-- esphome/core.py | 19 ++++++++++++++++++- esphome/cpp_generator.py | 15 ++++++++++----- esphome/yaml_util.py | 15 +++++++++++++-- 4 files changed, 48 insertions(+), 10 deletions(-) diff --git a/esphome/config_validation.py b/esphome/config_validation.py index a5b1559f2f..e0d4d088a9 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -17,9 +17,10 @@ from esphome.const import ALLOWED_NAME_CHARS, CONF_AVAILABILITY, CONF_COMMAND_TO CONF_HOUR, CONF_MINUTE, CONF_SECOND, CONF_VALUE, CONF_UPDATE_INTERVAL, CONF_TYPE_ID, \ CONF_TYPE, CONF_PACKAGES from esphome.core import CORE, HexInt, IPAddress, Lambda, TimePeriod, TimePeriodMicroseconds, \ - TimePeriodMilliseconds, TimePeriodSeconds, TimePeriodMinutes + TimePeriodMilliseconds, TimePeriodSeconds, TimePeriodMinutes, DocumentLocation from esphome.helpers import list_starts_with, add_class_to_obj from esphome.voluptuous_schema import _Schema +from esphome.yaml_util import ESPHomeDataBase _LOGGER = logging.getLogger(__name__) @@ -982,7 +983,11 @@ LAMBDA_ENTITY_ID_PROG = re.compile(r'id\(\s*([a-zA-Z0-9_]+\.[.a-zA-Z0-9_]+)\s*\) def lambda_(value): """Coerce this configuration option to a lambda.""" if not isinstance(value, Lambda): - value = Lambda(string_strict(value)) + start_mark = None + if isinstance(value, ESPHomeDataBase) and value.esp_range is not None: + start_mark = DocumentLocation.copy(value.esp_range.start_mark) + start_mark.line += value.content_offset + value = Lambda(string_strict(value), start_mark) entity_id_parts = re.split(LAMBDA_ENTITY_ID_PROG, value.value) if len(entity_id_parts) != 1: entity_ids = ' '.join("'{}'".format(entity_id_parts[i]) diff --git a/esphome/core.py b/esphome/core.py index 0065b750c4..c9f7222a0c 100644 --- a/esphome/core.py +++ b/esphome/core.py @@ -227,7 +227,7 @@ LAMBDA_PROG = re.compile(r'id\(\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\)(\.?)') class Lambda: - def __init__(self, value): + def __init__(self, value, start_mark=None): # pylint: disable=protected-access if isinstance(value, Lambda): self._value = value._value @@ -235,6 +235,7 @@ class Lambda: self._value = value self._parts = None self._requires_ids = None + self._source_location = start_mark # https://stackoverflow.com/a/241506/229052 def comment_remover(self, text): @@ -277,6 +278,10 @@ class Lambda: def __repr__(self): return f'Lambda<{self.value}>' + @property + def source_location(self): + return self._source_location + class ID: def __init__(self, id, is_declaration=False, type=None, is_manual=None): @@ -334,9 +339,21 @@ class DocumentLocation: mark.column ) + @classmethod + def copy(cls, location): + return cls( + location.document, + location.line, + location.column + ) + def __str__(self): return f'{self.document} {self.line}:{self.column}' + @property + def as_line_directive(self): + return f'#line {self.line + 1} "{self.document}"' + class DocumentRange: def __init__(self, start_mark: DocumentLocation, end_mark: DocumentLocation): diff --git a/esphome/cpp_generator.py b/esphome/cpp_generator.py index 3f87257e26..a82edfd3f7 100644 --- a/esphome/cpp_generator.py +++ b/esphome/cpp_generator.py @@ -1,6 +1,7 @@ import abc import inspect import math +import re # pylint: disable=unused-import, wrong-import-order from typing import Any, Generator, List, Optional, Tuple, Type, Union, Sequence @@ -188,13 +189,14 @@ class ParameterListExpression(Expression): class LambdaExpression(Expression): - __slots__ = ("parts", "parameters", "capture", "return_type") + __slots__ = ("parts", "parameters", "capture", "return_type", "source") - def __init__(self, parts, parameters, capture: str = '=', return_type=None): + def __init__(self, parts, parameters, capture: str = '=', return_type=None, source=None): self.parts = parts if not isinstance(parameters, ParameterListExpression): parameters = ParameterListExpression(*parameters) self.parameters = parameters + self.source = source self.capture = capture self.return_type = safe_exp(return_type) if return_type is not None else None @@ -202,7 +204,10 @@ class LambdaExpression(Expression): cpp = f'[{self.capture}]({self.parameters})' if self.return_type is not None: cpp += f' -> {self.return_type}' - cpp += f' {{\n{self.content}\n}}' + cpp += ' {\n' + if self.source is not None: + cpp += f'{self.source.as_line_directive}\n' + cpp += f'{self.content}\n}}' return indent_all_but_first_and_last(cpp) @property @@ -360,7 +365,7 @@ class LineComment(Statement): self.value = value def __str__(self): - parts = self.value.split('\n') + parts = re.sub(r'\\\s*\n', r'\n', self.value, re.MULTILINE).split('\n') parts = [f'// {x}' for x in parts] return '\n'.join(parts) @@ -555,7 +560,7 @@ def process_lambda( else: parts[i * 3 + 1] = var parts[i * 3 + 2] = '' - yield LambdaExpression(parts, parameters, capture, return_type) + yield LambdaExpression(parts, parameters, capture, return_type, value.source_location) def is_template(value): diff --git a/esphome/yaml_util.py b/esphome/yaml_util.py index 857a986538..1efe179011 100644 --- a/esphome/yaml_util.py +++ b/esphome/yaml_util.py @@ -11,7 +11,8 @@ import yaml.constructor from esphome import core from esphome.config_helpers import read_config_file -from esphome.core import EsphomeError, IPAddress, Lambda, MACAddress, TimePeriod, DocumentRange +from esphome.core import EsphomeError, IPAddress, Lambda, MACAddress, TimePeriod, \ + DocumentRange, DocumentLocation from esphome.helpers import add_class_to_obj from esphome.util import OrderedDict, filter_yaml_files @@ -30,9 +31,16 @@ class ESPHomeDataBase: def esp_range(self): return getattr(self, '_esp_range', None) + @property + def content_offset(self): + return getattr(self, '_content_offset', 0) + def from_node(self, node): # pylint: disable=attribute-defined-outside-init self._esp_range = DocumentRange.from_marks(node.start_mark, node.end_mark) + if isinstance(node, yaml.ScalarNode): + if node.style is not None and node.style in '|>': + self._content_offset = 1 class ESPForceValue: @@ -257,7 +265,10 @@ class ESPHomeLoader(yaml.SafeLoader): # pylint: disable=too-many-ancestors @_add_data_ref def construct_lambda(self, node): - return Lambda(str(node.value)) + start_mark = DocumentLocation.from_mark(node.start_mark) + if node.style is not None and node.style in '|>': + start_mark.line += 1 + return Lambda(str(node.value), start_mark) @_add_data_ref def construct_force(self, node): From 36a2ce2c23ac02dd899c974aec60743e513438bc Mon Sep 17 00:00:00 2001 From: SenexCrenshaw <35600301+SenexCrenshaw@users.noreply.github.com> Date: Mon, 25 Jan 2021 19:14:23 -0500 Subject: [PATCH 022/111] SPI wasnt being disabled after display update (#1493) --- esphome/components/st7735/st7735.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/components/st7735/st7735.cpp b/esphome/components/st7735/st7735.cpp index e433a08334..2a3d8fc903 100644 --- a/esphome/components/st7735/st7735.cpp +++ b/esphome/components/st7735/st7735.cpp @@ -454,6 +454,7 @@ void HOT ST7735::write_display_data_() { } else { this->write_array(this->buffer_, this->get_buffer_length()); } + this->disable(); } void ST7735::spi_master_write_addr_(uint16_t addr1, uint16_t addr2) { From 9d38543cb02b034e38dcffbe41c1833a813ad413 Mon Sep 17 00:00:00 2001 From: Paul Nicholls Date: Tue, 26 Jan 2021 17:44:10 +1300 Subject: [PATCH 023/111] Add support for string-type Tuya datapoints (#1488) --- esphome/components/tuya/tuya.cpp | 18 +++++++++++++++++- esphome/components/tuya/tuya.h | 1 + 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/esphome/components/tuya/tuya.cpp b/esphome/components/tuya/tuya.cpp index 2fd4b27937..75514dde19 100644 --- a/esphome/components/tuya/tuya.cpp +++ b/esphome/components/tuya/tuya.cpp @@ -42,6 +42,8 @@ void Tuya::dump_config() { ESP_LOGCONFIG(TAG, " Datapoint %d: switch (value: %s)", info.id, ONOFF(info.value_bool)); else if (info.type == TuyaDatapointType::INTEGER) ESP_LOGCONFIG(TAG, " Datapoint %d: int value (value: %d)", info.id, info.value_int); + else if (info.type == TuyaDatapointType::STRING) + ESP_LOGCONFIG(TAG, " Datapoint %d: string value (value: %s)", info.id, info.value_string.c_str()); else if (info.type == TuyaDatapointType::ENUM) ESP_LOGCONFIG(TAG, " Datapoint %d: enum (value: %d)", info.id, info.value_enum); else if (info.type == TuyaDatapointType::BITMASK) @@ -283,6 +285,9 @@ void Tuya::handle_datapoint_(const uint8_t *buffer, size_t len) { return; datapoint.value_uint = encode_uint32(data[0], data[1], data[2], data[3]); break; + case TuyaDatapointType::STRING: + datapoint.value_string = std::string(reinterpret_cast(data), data_len); + break; case TuyaDatapointType::ENUM: if (data_len != 1) return; @@ -339,7 +344,13 @@ void Tuya::set_datapoint_value(TuyaDatapoint datapoint) { ESP_LOGV(TAG, "Datapoint %u set to %u", datapoint.id, datapoint.value_uint); for (auto &other : this->datapoints_) { if (other.id == datapoint.id) { - if (other.value_uint == datapoint.value_uint) { + // String value is stored outside the union; must be checked separately. + if (datapoint.type == TuyaDatapointType::STRING) { + if (other.value_string == datapoint.value_string) { + ESP_LOGV(TAG, "Not sending unchanged value"); + return; + } + } else if (other.value_uint == datapoint.value_uint) { ESP_LOGV(TAG, "Not sending unchanged value"); return; } @@ -359,6 +370,11 @@ void Tuya::set_datapoint_value(TuyaDatapoint datapoint) { data.push_back(datapoint.value_uint >> 8); data.push_back(datapoint.value_uint >> 0); break; + case TuyaDatapointType::STRING: + for (char const &c : datapoint.value_string) { + data.push_back(c); + } + break; case TuyaDatapointType::ENUM: data.push_back(datapoint.value_enum); break; diff --git a/esphome/components/tuya/tuya.h b/esphome/components/tuya/tuya.h index ba20cfd314..ddbbb48edf 100644 --- a/esphome/components/tuya/tuya.h +++ b/esphome/components/tuya/tuya.h @@ -30,6 +30,7 @@ struct TuyaDatapoint { uint8_t value_enum; uint16_t value_bitmask; }; + std::string value_string; }; struct TuyaDatapointListener { From 7b9c2d2978b0bee484935f8cabf760e58914ea31 Mon Sep 17 00:00:00 2001 From: mhentschke Date: Tue, 26 Jan 2021 15:49:14 -0300 Subject: [PATCH 024/111] Added options to control pulse duration on Climate_IR_LG Component (#1470) * Added options to control pulse duration on Climate_IR_LG Component. This is usefull as some equipment from LG (Tested in Brazil AC unit) use different pulse durations in their protocol. * Fixed C++ linting issues * Fixed Python linting issues * fixed spaces on parameters linting issue * fixed spacing clint * Removed unused constants * Removed wrong spacing * Changed int to time period in all new fields Changed cv._int to cv.positive_time_period_microseconds in the time definitions for the new options * Fixed the time defaults Time defaults fixed for Climate_IR_LG. --- esphome/components/climate_ir_lg/climate.py | 17 ++++++++++++ .../climate_ir_lg/climate_ir_lg.cpp | 26 +++++++------------ .../components/climate_ir_lg/climate_ir_lg.h | 11 ++++++++ 3 files changed, 38 insertions(+), 16 deletions(-) diff --git a/esphome/components/climate_ir_lg/climate.py b/esphome/components/climate_ir_lg/climate.py index 37bf9e2628..a3f5730680 100644 --- a/esphome/components/climate_ir_lg/climate.py +++ b/esphome/components/climate_ir_lg/climate.py @@ -8,11 +8,28 @@ AUTO_LOAD = ['climate_ir'] climate_ir_lg_ns = cg.esphome_ns.namespace('climate_ir_lg') LgIrClimate = climate_ir_lg_ns.class_('LgIrClimate', climate_ir.ClimateIR) +CONF_HEADER_HIGH = 'header_high' +CONF_HEADER_LOW = 'header_low' +CONF_BIT_HIGH = 'bit_high' +CONF_BIT_ONE_LOW = 'bit_one_low' +CONF_BIT_ZERO_LOW = 'bit_zero_low' + CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend({ cv.GenerateID(): cv.declare_id(LgIrClimate), + cv.Optional(CONF_HEADER_HIGH, default='8000us'): cv.positive_time_period_microseconds, + cv.Optional(CONF_HEADER_LOW, default='4000us'): cv.positive_time_period_microseconds, + cv.Optional(CONF_BIT_HIGH, default='600us'): cv.positive_time_period_microseconds, + cv.Optional(CONF_BIT_ONE_LOW, default='1600us'): cv.positive_time_period_microseconds, + cv.Optional(CONF_BIT_ZERO_LOW, default='550us'): cv.positive_time_period_microseconds, }) def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) yield climate_ir.register_climate_ir(var, config) + + cg.add(var.set_header_high(config[CONF_HEADER_HIGH])) + cg.add(var.set_header_low(config[CONF_HEADER_LOW])) + cg.add(var.set_bit_high(config[CONF_BIT_HIGH])) + cg.add(var.set_bit_one_low(config[CONF_BIT_ONE_LOW])) + cg.add(var.set_bit_zero_low(config[CONF_BIT_ZERO_LOW])) diff --git a/esphome/components/climate_ir_lg/climate_ir_lg.cpp b/esphome/components/climate_ir_lg/climate_ir_lg.cpp index 80677e6de3..da4aaba9da 100644 --- a/esphome/components/climate_ir_lg/climate_ir_lg.cpp +++ b/esphome/components/climate_ir_lg/climate_ir_lg.cpp @@ -28,13 +28,6 @@ const uint8_t TEMP_RANGE = TEMP_MAX - TEMP_MIN + 1; const uint32_t TEMP_MASK = 0XF00; const uint32_t TEMP_SHIFT = 8; -// Constants -static const uint32_t HEADER_HIGH_US = 8000; -static const uint32_t HEADER_LOW_US = 4000; -static const uint32_t BIT_HIGH_US = 600; -static const uint32_t BIT_ONE_LOW_US = 1600; -static const uint32_t BIT_ZERO_LOW_US = 550; - const uint16_t BITS = 28; void LgIrClimate::transmit_state() { @@ -108,13 +101,13 @@ bool LgIrClimate::on_receive(remote_base::RemoteReceiveData data) { uint8_t nbits = 0; uint32_t remote_state = 0; - if (!data.expect_item(HEADER_HIGH_US, HEADER_LOW_US)) + if (!data.expect_item(this->header_high_, this->header_low_)) return false; for (nbits = 0; nbits < 32; nbits++) { - if (data.expect_item(BIT_HIGH_US, BIT_ONE_LOW_US)) { + if (data.expect_item(this->bit_high_, this->bit_one_low_)) { remote_state = (remote_state << 1) | 1; - } else if (data.expect_item(BIT_HIGH_US, BIT_ZERO_LOW_US)) { + } else if (data.expect_item(this->bit_high_, this->bit_zero_low_)) { remote_state = (remote_state << 1) | 0; } else if (nbits == BITS) { break; @@ -179,15 +172,16 @@ void LgIrClimate::transmit_(uint32_t value) { data->set_carrier_frequency(38000); data->reserve(2 + BITS * 2u); - data->item(HEADER_HIGH_US, HEADER_LOW_US); + data->item(this->header_high_, this->header_low_); for (uint32_t mask = 1UL << (BITS - 1); mask != 0; mask >>= 1) { - if (value & mask) - data->item(BIT_HIGH_US, BIT_ONE_LOW_US); - else - data->item(BIT_HIGH_US, BIT_ZERO_LOW_US); + if (value & mask) { + data->item(this->bit_high_, this->bit_one_low_); + } else { + data->item(this->bit_high_, this->bit_zero_low_); + } } - data->mark(BIT_HIGH_US); + data->mark(this->bit_high_); transmit.perform(); } void LgIrClimate::calc_checksum_(uint32_t &value) { diff --git a/esphome/components/climate_ir_lg/climate_ir_lg.h b/esphome/components/climate_ir_lg/climate_ir_lg.h index 204482e7a8..6b38b3247b 100644 --- a/esphome/components/climate_ir_lg/climate_ir_lg.h +++ b/esphome/components/climate_ir_lg/climate_ir_lg.h @@ -25,6 +25,11 @@ class LgIrClimate : public climate_ir::ClimateIR { this->swing_mode = climate::CLIMATE_SWING_OFF; climate_ir::ClimateIR::control(call); } + void set_header_high(uint32_t header_high) { this->header_high_ = header_high; } + void set_header_low(uint32_t header_low) { this->header_low_ = header_low; } + void set_bit_high(uint32_t bit_high) { this->bit_high_ = bit_high; } + void set_bit_one_low(uint32_t bit_one_low) { this->bit_one_low_ = bit_one_low; } + void set_bit_zero_low(uint32_t bit_zero_low) { this->bit_zero_low_ = bit_zero_low; } protected: /// Transmit via IR the state of this climate controller. @@ -37,6 +42,12 @@ class LgIrClimate : public climate_ir::ClimateIR { void calc_checksum_(uint32_t &value); void transmit_(uint32_t value); + uint32_t header_high_; + uint32_t header_low_; + uint32_t bit_high_; + uint32_t bit_one_low_; + uint32_t bit_zero_low_; + climate::ClimateMode mode_before_{climate::CLIMATE_MODE_OFF}; }; From d3b758d10ab8b4b211dedd21534e1a12c13a62a7 Mon Sep 17 00:00:00 2001 From: Klarstein <62219057+Klarstein@users.noreply.github.com> Date: Wed, 27 Jan 2021 07:16:59 +0100 Subject: [PATCH 025/111] Add docker healthcheck (#1492) --- docker/Dockerfile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docker/Dockerfile b/docker/Dockerfile index 899a1654c6..12b06ec284 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -15,6 +15,10 @@ ENV USERNAME="" PASSWORD="" # Expose the dashboard to Docker EXPOSE 6052 +# Run healthcheck (heartbeat) +HEALTHCHECK --interval=5m --timeout=3s \ + CMD curl --fail http://localhost:6052 || exit 1 + # The directory the user should mount their configuration files to WORKDIR /config # Set entrypoint to esphome so that the user doesn't have to type 'esphome' From 1d378e416d73d62efbacb27eeae0d2103d815c74 Mon Sep 17 00:00:00 2001 From: nikito7 <45373783+nikito7@users.noreply.github.com> Date: Wed, 27 Jan 2021 07:14:43 +0000 Subject: [PATCH 026/111] Add support for MHO-C401 (#1486) Committer: nikito7 Co-authored-by: vevsvevs Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Co-authored-by: nikito7 --- CODEOWNERS | 1 + esphome/components/xiaomi_ble/xiaomi_ble.cpp | 3 + esphome/components/xiaomi_ble/xiaomi_ble.h | 3 +- esphome/components/xiaomi_mhoc401/__init__.py | 0 esphome/components/xiaomi_mhoc401/sensor.py | 43 ++++++++++ .../xiaomi_mhoc401/xiaomi_mhoc401.cpp | 81 +++++++++++++++++++ .../xiaomi_mhoc401/xiaomi_mhoc401.h | 36 +++++++++ 7 files changed, 166 insertions(+), 1 deletion(-) create mode 100644 esphome/components/xiaomi_mhoc401/__init__.py create mode 100644 esphome/components/xiaomi_mhoc401/sensor.py create mode 100644 esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.cpp create mode 100644 esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.h diff --git a/CODEOWNERS b/CODEOWNERS index 4c24586bb6..0106b87fa8 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -100,3 +100,4 @@ esphome/components/version/* @esphome/core esphome/components/web_server_base/* @OttoWinter esphome/components/whirlpool/* @glmnet esphome/components/xiaomi_lywsd03mmc/* @ahpohl +esphome/components/xiaomi_mhoc401/* @vevsvevs diff --git a/esphome/components/xiaomi_ble/xiaomi_ble.cpp b/esphome/components/xiaomi_ble/xiaomi_ble.cpp index 75af1e4b7c..033269f66c 100644 --- a/esphome/components/xiaomi_ble/xiaomi_ble.cpp +++ b/esphome/components/xiaomi_ble/xiaomi_ble.cpp @@ -194,6 +194,9 @@ optional parse_xiaomi_header(const esp32_ble_tracker::Service result.name = "MJYD02YLA"; if (raw.size() == 19) result.raw_offset -= 6; + } else if ((raw[2] == 0x87) && (raw[3] == 0x03)) { // square body, e-ink display + result.type = XiaomiParseResult::TYPE_MHOC401; + result.name = "MHOC401"; } else { ESP_LOGVV(TAG, "parse_xiaomi_header(): unknown device, no magic bytes."); return {}; diff --git a/esphome/components/xiaomi_ble/xiaomi_ble.h b/esphome/components/xiaomi_ble/xiaomi_ble.h index ad73226159..3973dac80f 100644 --- a/esphome/components/xiaomi_ble/xiaomi_ble.h +++ b/esphome/components/xiaomi_ble/xiaomi_ble.h @@ -21,7 +21,8 @@ struct XiaomiParseResult { TYPE_JQJCY01YM, TYPE_MUE4094RT, TYPE_WX08ZM, - TYPE_MJYD02YLA + TYPE_MJYD02YLA, + TYPE_MHOC401 } type; std::string name; optional temperature; diff --git a/esphome/components/xiaomi_mhoc401/__init__.py b/esphome/components/xiaomi_mhoc401/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/xiaomi_mhoc401/sensor.py b/esphome/components/xiaomi_mhoc401/sensor.py new file mode 100644 index 0000000000..579ca0619a --- /dev/null +++ b/esphome/components/xiaomi_mhoc401/sensor.py @@ -0,0 +1,43 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import sensor, esp32_ble_tracker +from esphome.const import CONF_BATTERY_LEVEL, CONF_HUMIDITY, CONF_MAC_ADDRESS, CONF_TEMPERATURE, \ + UNIT_CELSIUS, ICON_THERMOMETER, UNIT_PERCENT, ICON_WATER_PERCENT, ICON_BATTERY, CONF_ID, \ + CONF_BINDKEY + +CODEOWNERS = ['@vevsvevs'] +DEPENDENCIES = ['esp32_ble_tracker'] +AUTO_LOAD = ['xiaomi_ble'] + +xiaomi_mhoc401_ns = cg.esphome_ns.namespace('xiaomi_mhoc401') +XiaomiMHOC401 = xiaomi_mhoc401_ns.class_('XiaomiMHOC401', + esp32_ble_tracker.ESPBTDeviceListener, + cg.Component) + +CONFIG_SCHEMA = cv.Schema({ + cv.GenerateID(): cv.declare_id(XiaomiMHOC401), + cv.Required(CONF_BINDKEY): cv.bind_key, + cv.Required(CONF_MAC_ADDRESS): cv.mac_address, + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 0), + cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(UNIT_PERCENT, ICON_BATTERY, 0), +}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) + + +def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + yield cg.register_component(var, config) + yield esp32_ble_tracker.register_ble_device(var, config) + + cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex)) + cg.add(var.set_bindkey(config[CONF_BINDKEY])) + + if CONF_TEMPERATURE in config: + sens = yield sensor.new_sensor(config[CONF_TEMPERATURE]) + cg.add(var.set_temperature(sens)) + if CONF_HUMIDITY in config: + sens = yield sensor.new_sensor(config[CONF_HUMIDITY]) + cg.add(var.set_humidity(sens)) + if CONF_BATTERY_LEVEL in config: + sens = yield sensor.new_sensor(config[CONF_BATTERY_LEVEL]) + cg.add(var.set_battery_level(sens)) diff --git a/esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.cpp b/esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.cpp new file mode 100644 index 0000000000..a41df3e6a1 --- /dev/null +++ b/esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.cpp @@ -0,0 +1,81 @@ +#include "xiaomi_mhoc401.h" +#include "esphome/core/log.h" + +#ifdef ARDUINO_ARCH_ESP32 + +namespace esphome { +namespace xiaomi_mhoc401 { + +static const char *TAG = "xiaomi_mhoc401"; + +void XiaomiMHOC401::dump_config() { + ESP_LOGCONFIG(TAG, "Xiaomi MHOC401"); + ESP_LOGCONFIG(TAG, " Bindkey: %s", hexencode(this->bindkey_, 16).c_str()); + LOG_SENSOR(" ", "Temperature", this->temperature_); + LOG_SENSOR(" ", "Humidity", this->humidity_); + LOG_SENSOR(" ", "Battery Level", this->battery_level_); +} + +bool XiaomiMHOC401::parse_device(const esp32_ble_tracker::ESPBTDevice &device) { + if (device.address_uint64() != this->address_) { + ESP_LOGVV(TAG, "parse_device(): unknown MAC address."); + return false; + } + ESP_LOGVV(TAG, "parse_device(): MAC address %s found.", device.address_str().c_str()); + + bool success = false; + for (auto &service_data : device.get_service_datas()) { + auto res = xiaomi_ble::parse_xiaomi_header(service_data); + if (!res.has_value()) { + continue; + } + if (res->is_duplicate) { + continue; + } + if (res->has_encryption && + (!(xiaomi_ble::decrypt_xiaomi_payload(const_cast &>(service_data.data), this->bindkey_, + this->address_)))) { + continue; + } + if (!(xiaomi_ble::parse_xiaomi_message(service_data.data, *res))) { + continue; + } + if (res->humidity.has_value() && this->humidity_ != nullptr) { + // see https://github.com/custom-components/sensor.mitemp_bt/issues/7#issuecomment-595948254 + *res->humidity = trunc(*res->humidity); + } + if (!(xiaomi_ble::report_xiaomi_results(res, device.address_str()))) { + continue; + } + if (res->temperature.has_value() && this->temperature_ != nullptr) + this->temperature_->publish_state(*res->temperature); + if (res->humidity.has_value() && this->humidity_ != nullptr) + this->humidity_->publish_state(*res->humidity); + if (res->battery_level.has_value() && this->battery_level_ != nullptr) + this->battery_level_->publish_state(*res->battery_level); + success = true; + } + + if (!success) { + return false; + } + + return true; +} + +void XiaomiMHOC401::set_bindkey(const std::string &bindkey) { + memset(bindkey_, 0, 16); + if (bindkey.size() != 32) { + return; + } + char temp[3] = {0}; + for (int i = 0; i < 16; i++) { + strncpy(temp, &(bindkey.c_str()[i * 2]), 2); + bindkey_[i] = std::strtoul(temp, NULL, 16); + } +} + +} // namespace xiaomi_mhoc401 +} // namespace esphome + +#endif diff --git a/esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.h b/esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.h new file mode 100644 index 0000000000..e80916f855 --- /dev/null +++ b/esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.h @@ -0,0 +1,36 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" +#include "esphome/components/xiaomi_ble/xiaomi_ble.h" + +#ifdef ARDUINO_ARCH_ESP32 + +namespace esphome { +namespace xiaomi_mhoc401 { + +class XiaomiMHOC401 : public Component, public esp32_ble_tracker::ESPBTDeviceListener { + public: + void set_address(uint64_t address) { address_ = address; }; + void set_bindkey(const std::string &bindkey); + + bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override; + void dump_config() override; + float get_setup_priority() const override { return setup_priority::DATA; } + void set_temperature(sensor::Sensor *temperature) { temperature_ = temperature; } + void set_humidity(sensor::Sensor *humidity) { humidity_ = humidity; } + void set_battery_level(sensor::Sensor *battery_level) { battery_level_ = battery_level; } + + protected: + uint64_t address_; + uint8_t bindkey_[16]; + sensor::Sensor *temperature_{nullptr}; + sensor::Sensor *humidity_{nullptr}; + sensor::Sensor *battery_level_{nullptr}; +}; + +} // namespace xiaomi_mhoc401 +} // namespace esphome + +#endif From 4eeb111fa3bfdc5754aa00b8599a8fb3df2119a7 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Sat, 30 Jan 2021 16:50:09 +1300 Subject: [PATCH 027/111] Allow SCD30 sensors to be optional (#1502) --- esphome/components/scd30/sensor.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/esphome/components/scd30/sensor.py b/esphome/components/scd30/sensor.py index e1e66b636e..eef6f44390 100644 --- a/esphome/components/scd30/sensor.py +++ b/esphome/components/scd30/sensor.py @@ -23,10 +23,10 @@ def remove_altitude_suffix(value): CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(SCD30Component), - cv.Required(CONF_CO2): sensor.sensor_schema(UNIT_PARTS_PER_MILLION, + cv.Optional(CONF_CO2): sensor.sensor_schema(UNIT_PARTS_PER_MILLION, ICON_MOLECULE_CO2, 0), - cv.Required(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1), - cv.Required(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 1), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 1), cv.Optional(CONF_AUTOMATIC_SELF_CALIBRATION, default=True): cv.boolean, cv.Optional(CONF_ALTITUDE_COMPENSATION): cv.All(remove_altitude_suffix, cv.int_range(min=0, max=0xFFFF, From f402c89539c01510ca63f1c0ae0c6ef3da92835f Mon Sep 17 00:00:00 2001 From: hcoohb Date: Tue, 2 Feb 2021 00:59:27 +1000 Subject: [PATCH 028/111] fix esp8266 remote_transmitter using incorrect timings (#1465) * replace delay by delayMicroseconds in delay_microseconds_accurate * Use delay(0) to let wifi and os function run * Linting * Remove unneeded delayMicroseconds, keep it for low usec * Avoid micros() overflow issue --- esphome/core/helpers.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index f0e0c65f66..3e7472d2fe 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -171,15 +171,17 @@ uint8_t crc8(uint8_t *data, uint8_t len) { } return crc; } + void delay_microseconds_accurate(uint32_t usec) { if (usec == 0) return; - - if (usec <= 16383UL) { + if (usec < 5000UL) { delayMicroseconds(usec); - } else { - delay(usec / 1000UL); - delayMicroseconds(usec % 1000UL); + return; + } + uint32_t start = micros(); + while (micros() - start < usec) { + delay(0); } } From bccaa78a90a3cbc2fee644a5b30133ee957f511e Mon Sep 17 00:00:00 2001 From: Guillermo Ruffino Date: Tue, 2 Feb 2021 20:30:20 -0300 Subject: [PATCH 029/111] RC522 increased retry loop count (#1506) --- esphome/components/rc522/rc522.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/rc522/rc522.cpp b/esphome/components/rc522/rc522.cpp index ff8d18e3ea..8182c1e8c9 100644 --- a/esphome/components/rc522/rc522.cpp +++ b/esphome/components/rc522/rc522.cpp @@ -349,7 +349,7 @@ RC522::StatusCode RC522::pcd_communicate_with_picc_( // transmitting. Each iteration of the do-while-loop takes 17.86μs. // TODO check/modify for other architectures than Arduino Uno 16bit uint16_t i; - for (i = 4; i > 0; i--) { + for (i = 2000; i > 0; i--) { uint8_t n = pcd_read_register( COM_IRQ_REG); // ComIrqReg[7..0] bits are: Set1 TxIRq RxIRq IdleIRq HiAlertIRq LoAlertIRq ErrIRq TimerIRq if (n & wait_i_rq) { // One of the interrupts that signal success has been set. From b351cd94d7de9743f3f0f76ceb30440d40091a77 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Sat, 6 Feb 2021 11:02:20 +1300 Subject: [PATCH 030/111] Fix PN532 SPI communication (#1511) --- esphome/components/pn532/pn532.cpp | 100 ++--------------- esphome/components/pn532/pn532.h | 4 +- .../components/pn532/pn532_mifare_classic.cpp | 6 +- .../pn532/pn532_mifare_ultralight.cpp | 4 +- esphome/components/pn532_i2c/pn532_i2c.cpp | 84 ++++++++++++++ esphome/components/pn532_i2c/pn532_i2c.h | 2 + esphome/components/pn532_spi/pn532_spi.cpp | 105 ++++++++++++++++-- esphome/components/pn532_spi/pn532_spi.h | 1 + 8 files changed, 203 insertions(+), 103 deletions(-) diff --git a/esphome/components/pn532/pn532.cpp b/esphome/components/pn532/pn532.cpp index e2511648b6..1cc2b19c2e 100644 --- a/esphome/components/pn532/pn532.cpp +++ b/esphome/components/pn532/pn532.cpp @@ -22,7 +22,7 @@ void PN532::setup() { } std::vector version_data; - if (!this->read_response_(PN532_COMMAND_VERSION_DATA, version_data)) { + if (!this->read_response(PN532_COMMAND_VERSION_DATA, version_data)) { ESP_LOGE(TAG, "Error getting version"); this->mark_failed(); return; @@ -42,7 +42,7 @@ void PN532::setup() { } std::vector wakeup_result; - if (!this->read_response_(PN532_COMMAND_SAMCONFIGURATION, wakeup_result)) { + if (!this->read_response(PN532_COMMAND_SAMCONFIGURATION, wakeup_result)) { this->error_code_ = WAKEUP_FAILED; this->mark_failed(); return; @@ -62,7 +62,7 @@ void PN532::setup() { } std::vector sam_result; - if (!this->read_response_(PN532_COMMAND_SAMCONFIGURATION, sam_result)) { + if (!this->read_response(PN532_COMMAND_SAMCONFIGURATION, sam_result)) { ESP_LOGV(TAG, "Invalid SAM result: (%u)", sam_result.size()); // NOLINT for (uint8_t dat : sam_result) { ESP_LOGV(TAG, " 0x%02X", dat); @@ -97,7 +97,7 @@ void PN532::loop() { return; std::vector read; - bool success = this->read_response_(PN532_COMMAND_INLISTPASSIVETARGET, read); + bool success = this->read_response(PN532_COMMAND_INLISTPASSIVETARGET, read); this->requested_read_ = false; @@ -230,7 +230,7 @@ bool PN532::write_command_(const std::vector &data) { } bool PN532::read_ack_() { - ESP_LOGVV(TAG, "Reading ACK..."); + ESP_LOGV(TAG, "Reading ACK..."); std::vector data; if (!this->read_data(data, 6)) { @@ -241,96 +241,18 @@ bool PN532::read_ack_() { data[2] == 0x00 && // start of packet data[3] == 0xFF && data[4] == 0x00 && // ACK packet code data[5] == 0xFF && data[6] == 0x00); // postamble - ESP_LOGVV(TAG, "ACK valid: %s", YESNO(matches)); + ESP_LOGV(TAG, "ACK valid: %s", YESNO(matches)); return matches; } -bool PN532::read_response_(uint8_t command, std::vector &data) { - ESP_LOGV(TAG, "Reading response"); - uint8_t len = this->read_response_length_(); - if (len == 0) { - return false; - } - - ESP_LOGV(TAG, "Reading response of length %d", len); - if (!this->read_data(data, 6 + len + 2)) { - ESP_LOGD(TAG, "No response data"); - return false; - } - - if (data[1] != 0x00 && data[2] != 0x00 && data[3] != 0xFF) { - // invalid packet - ESP_LOGV(TAG, "read data invalid preamble!"); - return false; - } - - bool valid_header = (static_cast(data[4] + data[5]) == 0 && // LCS, len + lcs = 0 - data[6] == 0xD5 && // TFI - frame from PN532 to system controller - data[7] == command + 1); // Correct command response - - if (!valid_header) { - ESP_LOGV(TAG, "read data invalid header!"); - return false; - } - - data.erase(data.begin(), data.begin() + 6); // Remove headers - - uint8_t checksum = 0; - for (int i = 0; i < len + 1; i++) { - uint8_t dat = data[i]; - checksum += dat; - } - checksum = ~checksum + 1; - - if (data[len + 1] != checksum) { - ESP_LOGV(TAG, "read data invalid checksum! %02X != %02X", data[len], checksum); - return false; - } - - if (data[len + 2] != 0x00) { - ESP_LOGV(TAG, "read data invalid postamble!"); - return false; - } - - data.erase(data.begin(), data.begin() + 2); // Remove TFI and command code - data.erase(data.end() - 2, data.end()); // Remove checksum and postamble - - return true; -} - -uint8_t PN532::read_response_length_() { - std::vector data; - if (!this->read_data(data, 6)) { - return 0; - } - - if (data[1] != 0x00 && data[2] != 0x00 && data[3] != 0xFF) { - // invalid packet - ESP_LOGV(TAG, "read data invalid preamble!"); - return 0; - } - - bool valid_header = (static_cast(data[4] + data[5]) == 0 && // LCS, len + lcs = 0 - data[6] == 0xD5); // TFI - frame from PN532 to system controller - - if (!valid_header) { - ESP_LOGV(TAG, "read data invalid header!"); - return 0; - } - - this->write_data({0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00}); // NACK - Retransmit last message - - // full length of message, including TFI - uint8_t full_len = data[4]; - // length of data, excluding TFI - uint8_t len = full_len - 1; - if (full_len == 0) - len = 0; - return len; +void PN532::send_nack_() { + ESP_LOGV(TAG, "Sending NACK for retransmit"); + this->write_data({0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00}); + delay(10); } void PN532::turn_off_rf_() { - ESP_LOGVV(TAG, "Turning RF field OFF"); + ESP_LOGV(TAG, "Turning RF field OFF"); this->write_command_({ PN532_COMMAND_RFCONFIGURATION, 0x01, // RF Field diff --git a/esphome/components/pn532/pn532.h b/esphome/components/pn532/pn532.h index 6b3bef2918..95a2e0dd2a 100644 --- a/esphome/components/pn532/pn532.h +++ b/esphome/components/pn532/pn532.h @@ -46,12 +46,12 @@ class PN532 : public PollingComponent { protected: void turn_off_rf_(); bool write_command_(const std::vector &data); - bool read_response_(uint8_t command, std::vector &data); bool read_ack_(); - uint8_t read_response_length_(); + void send_nack_(); virtual bool write_data(const std::vector &data) = 0; virtual bool read_data(std::vector &data, uint8_t len) = 0; + virtual bool read_response(uint8_t command, std::vector &data) = 0; nfc::NfcTag *read_tag_(std::vector &uid); diff --git a/esphome/components/pn532/pn532_mifare_classic.cpp b/esphome/components/pn532/pn532_mifare_classic.cpp index 4e8c255755..93d394330d 100644 --- a/esphome/components/pn532/pn532_mifare_classic.cpp +++ b/esphome/components/pn532/pn532_mifare_classic.cpp @@ -64,7 +64,7 @@ bool PN532::read_mifare_classic_block_(uint8_t block_num, std::vector & return false; } - if (!this->read_response_(PN532_COMMAND_INDATAEXCHANGE, data) || data[0] != 0x00) { + if (!this->read_response(PN532_COMMAND_INDATAEXCHANGE, data) || data[0] != 0x00) { return false; } data.erase(data.begin()); @@ -89,7 +89,7 @@ bool PN532::auth_mifare_classic_block_(std::vector &uid, uint8_t block_ } std::vector response; - if (!this->read_response_(PN532_COMMAND_INDATAEXCHANGE, response) || response[0] != 0x00) { + if (!this->read_response(PN532_COMMAND_INDATAEXCHANGE, response) || response[0] != 0x00) { ESP_LOGE(TAG, "Authentication failed - Block 0x%02x", block_num); return false; } @@ -194,7 +194,7 @@ bool PN532::write_mifare_classic_block_(uint8_t block_num, std::vector } std::vector response; - if (!this->read_response_(PN532_COMMAND_INDATAEXCHANGE, response)) { + if (!this->read_response(PN532_COMMAND_INDATAEXCHANGE, response)) { ESP_LOGE(TAG, "Error writing block %d", block_num); return false; } diff --git a/esphome/components/pn532/pn532_mifare_ultralight.cpp b/esphome/components/pn532/pn532_mifare_ultralight.cpp index 00cb18aacd..bd62806c1e 100644 --- a/esphome/components/pn532/pn532_mifare_ultralight.cpp +++ b/esphome/components/pn532/pn532_mifare_ultralight.cpp @@ -52,7 +52,7 @@ bool PN532::read_mifare_ultralight_page_(uint8_t page_num, std::vector return false; } - if (!this->read_response_(PN532_COMMAND_INDATAEXCHANGE, data) || data[0] != 0x00) { + if (!this->read_response(PN532_COMMAND_INDATAEXCHANGE, data) || data[0] != 0x00) { return false; } data.erase(data.begin()); @@ -168,7 +168,7 @@ bool PN532::write_mifare_ultralight_page_(uint8_t page_num, std::vector } std::vector response; - if (!this->read_response_(PN532_COMMAND_INDATAEXCHANGE, response)) { + if (!this->read_response(PN532_COMMAND_INDATAEXCHANGE, response)) { ESP_LOGE(TAG, "Error writing page %d", page_num); return false; } diff --git a/esphome/components/pn532_i2c/pn532_i2c.cpp b/esphome/components/pn532_i2c/pn532_i2c.cpp index 162487de58..37147c5eb9 100644 --- a/esphome/components/pn532_i2c/pn532_i2c.cpp +++ b/esphome/components/pn532_i2c/pn532_i2c.cpp @@ -36,6 +36,90 @@ bool PN532I2C::read_data(std::vector &data, uint8_t len) { return true; } +bool PN532I2C::read_response(uint8_t command, std::vector &data) { + ESP_LOGV(TAG, "Reading response"); + uint8_t len = this->read_response_length_(); + if (len == 0) { + return false; + } + + ESP_LOGV(TAG, "Reading response of length %d", len); + if (!this->read_data(data, 6 + len + 2)) { + ESP_LOGD(TAG, "No response data"); + return false; + } + + if (data[1] != 0x00 && data[2] != 0x00 && data[3] != 0xFF) { + // invalid packet + ESP_LOGV(TAG, "read data invalid preamble!"); + return false; + } + + bool valid_header = (static_cast(data[4] + data[5]) == 0 && // LCS, len + lcs = 0 + data[6] == 0xD5 && // TFI - frame from PN532 to system controller + data[7] == command + 1); // Correct command response + + if (!valid_header) { + ESP_LOGV(TAG, "read data invalid header!"); + return false; + } + + data.erase(data.begin(), data.begin() + 6); // Remove headers + + uint8_t checksum = 0; + for (int i = 0; i < len + 1; i++) { + uint8_t dat = data[i]; + checksum += dat; + } + checksum = ~checksum + 1; + + if (data[len + 1] != checksum) { + ESP_LOGV(TAG, "read data invalid checksum! %02X != %02X", data[len], checksum); + return false; + } + + if (data[len + 2] != 0x00) { + ESP_LOGV(TAG, "read data invalid postamble!"); + return false; + } + + data.erase(data.begin(), data.begin() + 2); // Remove TFI and command code + data.erase(data.end() - 2, data.end()); // Remove checksum and postamble + + return true; +} + +uint8_t PN532I2C::read_response_length_() { + std::vector data; + if (!this->read_data(data, 6)) { + return 0; + } + + if (data[1] != 0x00 && data[2] != 0x00 && data[3] != 0xFF) { + // invalid packet + ESP_LOGV(TAG, "read data invalid preamble!"); + return 0; + } + + bool valid_header = (static_cast(data[4] + data[5]) == 0 && // LCS, len + lcs = 0 + data[6] == 0xD5); // TFI - frame from PN532 to system controller + + if (!valid_header) { + ESP_LOGV(TAG, "read data invalid header!"); + return 0; + } + + this->send_nack_(); + + // full length of message, including TFI + uint8_t full_len = data[4]; + // length of data, excluding TFI + uint8_t len = full_len - 1; + if (full_len == 0) + len = 0; + return len; +} + void PN532I2C::dump_config() { PN532::dump_config(); LOG_I2C_DEVICE(this); diff --git a/esphome/components/pn532_i2c/pn532_i2c.h b/esphome/components/pn532_i2c/pn532_i2c.h index 23cb00bb10..296d73e042 100644 --- a/esphome/components/pn532_i2c/pn532_i2c.h +++ b/esphome/components/pn532_i2c/pn532_i2c.h @@ -14,6 +14,8 @@ class PN532I2C : public pn532::PN532, public i2c::I2CDevice { protected: bool write_data(const std::vector &data) override; bool read_data(std::vector &data, uint8_t len) override; + bool read_response(uint8_t command, std::vector &data) override; + uint8_t read_response_length_(); }; } // namespace pn532_i2c diff --git a/esphome/components/pn532_spi/pn532_spi.cpp b/esphome/components/pn532_spi/pn532_spi.cpp index 3da799fb24..6f87ad8ca7 100644 --- a/esphome/components/pn532_spi/pn532_spi.cpp +++ b/esphome/components/pn532_spi/pn532_spi.cpp @@ -26,7 +26,7 @@ bool PN532Spi::write_data(const std::vector &data) { delay(2); // First byte, communication mode: Write data this->write_byte(0x01); - + ESP_LOGV(TAG, "Writing data: %s", hexencode(data).c_str()); this->write_array(data.data(), data.size()); this->disable(); @@ -34,31 +34,122 @@ bool PN532Spi::write_data(const std::vector &data) { } bool PN532Spi::read_data(std::vector &data, uint8_t len) { - this->enable(); - // First byte, communication mode: Read state - this->write_byte(0x02); + ESP_LOGV(TAG, "Waiting for ready byte..."); uint32_t start_time = millis(); while (true) { - if (this->read_byte() & 0x01) + this->enable(); + // First byte, communication mode: Read state + this->write_byte(0x02); + bool ready = this->read_byte() == 0x01; + this->disable(); + if (ready) break; + ESP_LOGV(TAG, "Not ready yet..."); if (millis() - start_time > 100) { - this->disable(); ESP_LOGV(TAG, "Timed out waiting for readiness from PN532!"); return false; } + yield(); } // Read data (transmission from the PN532 to the host) + this->enable(); + delay(2); this->write_byte(0x03); + ESP_LOGV(TAG, "Reading data..."); + data.resize(len); this->read_array(data.data(), len); this->disable(); data.insert(data.begin(), 0x01); + ESP_LOGV(TAG, "Read data: %s", hexencode(data).c_str()); return true; -}; +} + +bool PN532Spi::read_response(uint8_t command, std::vector &data) { + ESP_LOGV(TAG, "Reading response"); + + uint32_t start_time = millis(); + while (true) { + this->enable(); + // First byte, communication mode: Read state + this->write_byte(0x02); + bool ready = this->read_byte() == 0x01; + this->disable(); + if (ready) + break; + ESP_LOGV(TAG, "Not ready yet..."); + + if (millis() - start_time > 100) { + ESP_LOGV(TAG, "Timed out waiting for readiness from PN532!"); + return false; + } + yield(); + } + + this->enable(); + delay(2); + this->write_byte(0x03); + + std::vector header(7); + this->read_array(header.data(), 7); + + ESP_LOGV(TAG, "Header data: %s", hexencode(header).c_str()); + + if (header[0] != 0x00 && header[1] != 0x00 && header[2] != 0xFF) { + // invalid packet + ESP_LOGV(TAG, "read data invalid preamble!"); + return false; + } + + bool valid_header = (static_cast(header[3] + header[4]) == 0 && // LCS, len + lcs = 0 + header[5] == 0xD5 && // TFI - frame from PN532 to system controller + header[6] == command + 1); // Correct command response + + if (!valid_header) { + ESP_LOGV(TAG, "read data invalid header!"); + return false; + } + + // full length of message, including command response + uint8_t full_len = header[3]; + // length of data, excluding command response + uint8_t len = full_len - 1; + if (full_len == 0) + len = 0; + + ESP_LOGV(TAG, "Reading response of length %d", len); + + data.resize(len + 1); + this->read_array(data.data(), len + 1); + this->disable(); + + ESP_LOGV(TAG, "Response data: %s", hexencode(data).c_str()); + + uint8_t checksum = header[5] + header[6]; // TFI + Command response code + for (int i = 0; i < len - 1; i++) { + uint8_t dat = data[i]; + checksum += dat; + } + checksum = ~checksum + 1; + + if (data[len - 1] != checksum) { + ESP_LOGV(TAG, "read data invalid checksum! %02X != %02X", data[len - 1], checksum); + return false; + } + + if (data[len] != 0x00) { + ESP_LOGV(TAG, "read data invalid postamble!"); + return false; + } + + data.erase(data.end() - 2, data.end()); // Remove checksum and postamble + + return true; +} void PN532Spi::dump_config() { PN532::dump_config(); diff --git a/esphome/components/pn532_spi/pn532_spi.h b/esphome/components/pn532_spi/pn532_spi.h index 967b8a66cf..d98bd447c8 100644 --- a/esphome/components/pn532_spi/pn532_spi.h +++ b/esphome/components/pn532_spi/pn532_spi.h @@ -18,6 +18,7 @@ class PN532Spi : public pn532::PN532, protected: bool write_data(const std::vector &data) override; bool read_data(std::vector &data, uint8_t len) override; + bool read_response(uint8_t command, std::vector &data) override; }; } // namespace pn532_spi From de3377132d9be2b498e58b5949171d09ddc510d8 Mon Sep 17 00:00:00 2001 From: fkirill Date: Sat, 6 Feb 2021 15:04:47 +1100 Subject: [PATCH 031/111] Adding support for the Inkbird IBS-TH1 Mini sensor (#1099) --- CODEOWNERS | 1 + .../inkbird_ibsth1_mini/__init__.py | 0 .../inkbird_ibsth1_mini.cpp | 86 +++++++++++++++++++ .../inkbird_ibsth1_mini/inkbird_ibsth1_mini.h | 34 ++++++++ .../components/inkbird_ibsth1_mini/sensor.py | 38 ++++++++ tests/test2.yaml | 8 ++ 6 files changed, 167 insertions(+) create mode 100644 esphome/components/inkbird_ibsth1_mini/__init__.py create mode 100644 esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.cpp create mode 100644 esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.h create mode 100644 esphome/components/inkbird_ibsth1_mini/sensor.py diff --git a/CODEOWNERS b/CODEOWNERS index 0106b87fa8..d5df9e8109 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -37,6 +37,7 @@ esphome/components/globals/* @esphome/core esphome/components/gpio/* @esphome/core esphome/components/homeassistant/* @OttoWinter esphome/components/i2c/* @esphome/core +esphome/components/inkbird_ibsth1_mini/* @fkirill esphome/components/inkplate6/* @jesserockz esphome/components/integration/* @OttoWinter esphome/components/interval/* @esphome/core diff --git a/esphome/components/inkbird_ibsth1_mini/__init__.py b/esphome/components/inkbird_ibsth1_mini/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.cpp b/esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.cpp new file mode 100644 index 0000000000..a1c1c5d201 --- /dev/null +++ b/esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.cpp @@ -0,0 +1,86 @@ +#include "inkbird_ibsth1_mini.h" +#include "esphome/core/log.h" + +#ifdef ARDUINO_ARCH_ESP32 + +namespace esphome { +namespace inkbird_ibsth1_mini { + +static const char *TAG = "inkbird_ibsth1_mini"; + +void InkbirdIBSTH1_MINI::dump_config() { + ESP_LOGCONFIG(TAG, "Inkbird IBS TH1 MINI"); + LOG_SENSOR(" ", "Temperature", this->temperature_); + LOG_SENSOR(" ", "Humidity", this->humidity_); + LOG_SENSOR(" ", "Battery Level", this->battery_level_); +} + +bool InkbirdIBSTH1_MINI::parse_device(const esp32_ble_tracker::ESPBTDevice &device) { + // The below is based on my research and reverse engineering of a single device + // It is entirely possible that some of that may be inaccurate or incomplete + + // for Inkbird IBS-TH1 Mini device we expect + // 1) expected mac address + // 2) device address type == PUBLIC + // 3) no service datas + // 4) one manufacturer datas + // 5) the manufacturer datas should contain a 16-bit uuid amd a 7-byte data vector + // 6) the 7-byte data component should have data[2] == 0 and data[6] == 8 + + // the address should match the address we declared + if (device.address_uint64() != this->address_) { + ESP_LOGVV(TAG, "parse_device(): unknown MAC address."); + return false; + } + if (device.get_address_type() != BLE_ADDR_TYPE_PUBLIC) { + ESP_LOGVV(TAG, "parse_device(): address is not public"); + return false; + } + if (device.get_service_datas().size() != 0) { + ESP_LOGVV(TAG, "parse_device(): service_data is expected to be empty"); + return false; + } + auto mnfDatas = device.get_manufacturer_datas(); + if (mnfDatas.size() != 1) { + ESP_LOGVV(TAG, "parse_device(): manufacturer_datas is expected to have a single element"); + return false; + } + auto mnfData = mnfDatas[0]; + if (mnfData.uuid.get_uuid().len != ESP_UUID_LEN_16) { + ESP_LOGVV(TAG, "parse_device(): manufacturer data element is expected to have uuid of length 16"); + return false; + } + if (mnfData.data.size() != 7) { + ESP_LOGVV(TAG, "parse_device(): manufacturer data element length is expected to be of length 7"); + return false; + } + if ((mnfData.data[2] != 0) || (mnfData.data[6] != 8)) { + ESP_LOGVV(TAG, "parse_device(): unexpected data"); + return false; + } + + // sensor output encoding + // data[5] is a battery level + // data[0] and data[1] is humidity * 100 (in pct) + // uuid is a temperature * 100 (in Celcius) + auto battery_level = mnfData.data[5]; + auto temperature = mnfData.uuid.get_uuid().uuid.uuid16 / 100.0f; + auto humidity = ((mnfData.data[1] << 8) + mnfData.data[0]) / 100.0f; + + if (this->temperature_ != nullptr) { + this->temperature_->publish_state(temperature); + } + if (this->humidity_ != nullptr) { + this->humidity_->publish_state(humidity); + } + if (this->battery_level_ != nullptr) { + this->battery_level_->publish_state(battery_level); + } + + return true; +} + +} // namespace inkbird_ibsth1_mini +} // namespace esphome + +#endif diff --git a/esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.h b/esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.h new file mode 100644 index 0000000000..38e72dad17 --- /dev/null +++ b/esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.h @@ -0,0 +1,34 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" + +#ifdef ARDUINO_ARCH_ESP32 + +namespace esphome { +namespace inkbird_ibsth1_mini { + +class InkbirdIBSTH1_MINI : public Component, public esp32_ble_tracker::ESPBTDeviceListener { + public: + void set_address(uint64_t address) { address_ = address; } + + bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override; + + void dump_config() override; + float get_setup_priority() const override { return setup_priority::DATA; } + void set_temperature(sensor::Sensor *temperature) { temperature_ = temperature; } + void set_humidity(sensor::Sensor *humidity) { humidity_ = humidity; } + void set_battery_level(sensor::Sensor *battery_level) { battery_level_ = battery_level; } + + protected: + uint64_t address_; + sensor::Sensor *temperature_{nullptr}; + sensor::Sensor *humidity_{nullptr}; + sensor::Sensor *battery_level_{nullptr}; +}; + +} // namespace inkbird_ibsth1_mini +} // namespace esphome + +#endif diff --git a/esphome/components/inkbird_ibsth1_mini/sensor.py b/esphome/components/inkbird_ibsth1_mini/sensor.py new file mode 100644 index 0000000000..93d90d2e6f --- /dev/null +++ b/esphome/components/inkbird_ibsth1_mini/sensor.py @@ -0,0 +1,38 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import sensor, esp32_ble_tracker +from esphome.const import CONF_BATTERY_LEVEL, CONF_HUMIDITY, CONF_MAC_ADDRESS, CONF_TEMPERATURE, \ + UNIT_CELSIUS, ICON_THERMOMETER, UNIT_PERCENT, ICON_WATER_PERCENT, ICON_BATTERY, CONF_ID + +CODEOWNERS = ['@fkirill'] +DEPENDENCIES = ['esp32_ble_tracker'] + +inkbird_ibsth1_mini_ns = cg.esphome_ns.namespace('inkbird_ibsth1_mini') +InkbirdUBSTH1_MINI = inkbird_ibsth1_mini_ns.class_( + 'InkbirdIBSTH1_MINI', esp32_ble_tracker.ESPBTDeviceListener, cg.Component) + +CONFIG_SCHEMA = cv.Schema({ + cv.GenerateID(): cv.declare_id(InkbirdUBSTH1_MINI), + cv.Required(CONF_MAC_ADDRESS): cv.mac_address, + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 1), + cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(UNIT_PERCENT, ICON_BATTERY, 0), +}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) + + +def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + yield cg.register_component(var, config) + yield esp32_ble_tracker.register_ble_device(var, config) + + cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex)) + + if CONF_TEMPERATURE in config: + sens = yield sensor.new_sensor(config[CONF_TEMPERATURE]) + cg.add(var.set_temperature(sens)) + if CONF_HUMIDITY in config: + sens = yield sensor.new_sensor(config[CONF_HUMIDITY]) + cg.add(var.set_humidity(sens)) + if CONF_BATTERY_LEVEL in config: + sens = yield sensor.new_sensor(config[CONF_BATTERY_LEVEL]) + cg.add(var.set_battery_level(sens)) diff --git a/tests/test2.yaml b/tests/test2.yaml index b975090531..b109aad758 100644 --- a/tests/test2.yaml +++ b/tests/test2.yaml @@ -181,6 +181,14 @@ sensor: name: 'ATC Battery-Level' battery_voltage: name: 'ATC Battery-Voltage' + - platform: inkbird_ibsth1_mini + mac_address: 38:81:D7:0A:9C:11 + temperature: + name: 'Inkbird IBS-TH1 Temperature' + humidity: + name: 'Inkbird IBS-TH1 Humidity' + battery_level: + name: 'Inkbird IBS-TH1 Battery Level' time: - platform: homeassistant From 28e39f7f765e91355d7d837630f32e965026c4d8 Mon Sep 17 00:00:00 2001 From: Guillermo Ruffino Date: Sat, 6 Feb 2021 12:09:15 -0300 Subject: [PATCH 032/111] Add config validator location (#1490) * show validation source location for id * show validation source location for lambda * refactor lambda #line position * account content offset on made lambdas * lint * remove redundant check --- esphome/config.py | 4 ++++ esphome/config_validation.py | 14 +++++--------- esphome/core.py | 15 +-------------- esphome/cpp_generator.py | 9 ++++++++- esphome/yaml_util.py | 19 ++++++++++++------- 5 files changed, 30 insertions(+), 31 deletions(-) diff --git a/esphome/config.py b/esphome/config.py index 0484414929..5dc539f828 100644 --- a/esphome/config.py +++ b/esphome/config.py @@ -268,6 +268,8 @@ class Config(OrderedDict): data = data[item_index] except (KeyError, IndexError, TypeError): return doc_range + if isinstance(data, core.ID): + data = data.id if isinstance(data, ESPHomeDataBase) and data.esp_range is not None: doc_range = data.esp_range @@ -700,6 +702,8 @@ def line_info(obj, highlight=True): """Display line config source.""" if not highlight: return None + if isinstance(obj, core.ID): + obj = obj.id if isinstance(obj, ESPHomeDataBase) and obj.esp_range is not None: mark = obj.esp_range.start_mark source = "[source {}:{}]".format(mark.document, mark.line + 1) diff --git a/esphome/config_validation.py b/esphome/config_validation.py index e0d4d088a9..046e9af185 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -17,10 +17,10 @@ from esphome.const import ALLOWED_NAME_CHARS, CONF_AVAILABILITY, CONF_COMMAND_TO CONF_HOUR, CONF_MINUTE, CONF_SECOND, CONF_VALUE, CONF_UPDATE_INTERVAL, CONF_TYPE_ID, \ CONF_TYPE, CONF_PACKAGES from esphome.core import CORE, HexInt, IPAddress, Lambda, TimePeriod, TimePeriodMicroseconds, \ - TimePeriodMilliseconds, TimePeriodSeconds, TimePeriodMinutes, DocumentLocation + TimePeriodMilliseconds, TimePeriodSeconds, TimePeriodMinutes from esphome.helpers import list_starts_with, add_class_to_obj from esphome.voluptuous_schema import _Schema -from esphome.yaml_util import ESPHomeDataBase +from esphome.yaml_util import make_data_base _LOGGER = logging.getLogger(__name__) @@ -983,11 +983,7 @@ LAMBDA_ENTITY_ID_PROG = re.compile(r'id\(\s*([a-zA-Z0-9_]+\.[.a-zA-Z0-9_]+)\s*\) def lambda_(value): """Coerce this configuration option to a lambda.""" if not isinstance(value, Lambda): - start_mark = None - if isinstance(value, ESPHomeDataBase) and value.esp_range is not None: - start_mark = DocumentLocation.copy(value.esp_range.start_mark) - start_mark.line += value.content_offset - value = Lambda(string_strict(value), start_mark) + value = make_data_base(Lambda(string_strict(value)), value) entity_id_parts = re.split(LAMBDA_ENTITY_ID_PROG, value.value) if len(entity_id_parts) != 1: entity_ids = ' '.join("'{}'".format(entity_id_parts[i]) @@ -1182,8 +1178,8 @@ class OnlyWith(Optional): # pylint: disable=unsupported-membership-test if (self._component in CORE.raw_config or (CONF_PACKAGES in CORE.raw_config and - self._component in - {list(x.keys())[0] for x in CORE.raw_config[CONF_PACKAGES].values()})): + self._component in + {list(x.keys())[0] for x in CORE.raw_config[CONF_PACKAGES].values()})): return self._default return vol.UNDEFINED diff --git a/esphome/core.py b/esphome/core.py index c9f7222a0c..57b065c488 100644 --- a/esphome/core.py +++ b/esphome/core.py @@ -227,7 +227,7 @@ LAMBDA_PROG = re.compile(r'id\(\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\)(\.?)') class Lambda: - def __init__(self, value, start_mark=None): + def __init__(self, value): # pylint: disable=protected-access if isinstance(value, Lambda): self._value = value._value @@ -235,7 +235,6 @@ class Lambda: self._value = value self._parts = None self._requires_ids = None - self._source_location = start_mark # https://stackoverflow.com/a/241506/229052 def comment_remover(self, text): @@ -278,10 +277,6 @@ class Lambda: def __repr__(self): return f'Lambda<{self.value}>' - @property - def source_location(self): - return self._source_location - class ID: def __init__(self, id, is_declaration=False, type=None, is_manual=None): @@ -339,14 +334,6 @@ class DocumentLocation: mark.column ) - @classmethod - def copy(cls, location): - return cls( - location.document, - location.line, - location.column - ) - def __str__(self): return f'{self.document} {self.line}:{self.column}' diff --git a/esphome/cpp_generator.py b/esphome/cpp_generator.py index a82edfd3f7..cfa178c4f9 100644 --- a/esphome/cpp_generator.py +++ b/esphome/cpp_generator.py @@ -2,6 +2,7 @@ import abc import inspect import math import re +from esphome.yaml_util import ESPHomeDataBase # pylint: disable=unused-import, wrong-import-order from typing import Any, Generator, List, Optional, Tuple, Type, Union, Sequence @@ -560,7 +561,13 @@ def process_lambda( else: parts[i * 3 + 1] = var parts[i * 3 + 2] = '' - yield LambdaExpression(parts, parameters, capture, return_type, value.source_location) + + if isinstance(value, ESPHomeDataBase) and value.esp_range is not None: + location = value.esp_range.start_mark + location.line += value.content_offset + else: + location = None + yield LambdaExpression(parts, parameters, capture, return_type, location) def is_template(value): diff --git a/esphome/yaml_util.py b/esphome/yaml_util.py index 1efe179011..5909e99cb2 100644 --- a/esphome/yaml_util.py +++ b/esphome/yaml_util.py @@ -12,7 +12,7 @@ import yaml.constructor from esphome import core from esphome.config_helpers import read_config_file from esphome.core import EsphomeError, IPAddress, Lambda, MACAddress, TimePeriod, \ - DocumentRange, DocumentLocation + DocumentRange from esphome.helpers import add_class_to_obj from esphome.util import OrderedDict, filter_yaml_files @@ -42,14 +42,22 @@ class ESPHomeDataBase: if node.style is not None and node.style in '|>': self._content_offset = 1 + def from_database(self, database): + # pylint: disable=attribute-defined-outside-init + self._esp_range = database.esp_range + self._content_offset = database.content_offset + class ESPForceValue: pass -def make_data_base(value): +def make_data_base(value, from_database: ESPHomeDataBase = None): try: - return add_class_to_obj(value, ESPHomeDataBase) + value = add_class_to_obj(value, ESPHomeDataBase) + if from_database is not None: + value.from_database(from_database) + return value except TypeError: # Adding class failed, ignore error return value @@ -265,10 +273,7 @@ class ESPHomeLoader(yaml.SafeLoader): # pylint: disable=too-many-ancestors @_add_data_ref def construct_lambda(self, node): - start_mark = DocumentLocation.from_mark(node.start_mark) - if node.style is not None and node.style in '|>': - start_mark.line += 1 - return Lambda(str(node.value), start_mark) + return Lambda(str(node.value)) @_add_data_ref def construct_force(self, node): From 04d8593f38d6a412fd39ce3f8b39b8f6fc7bf65a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20JOURDIN?= <2821990+JJK801@users.noreply.github.com> Date: Sat, 6 Feb 2021 16:18:48 +0100 Subject: [PATCH 033/111] Add MCP4725 DAC Component (#1418) * Add MCP4725 DAC * Fix lint * Fix lint * Fix lint * Lint & cleanup * Lint again * One more lint * add test Co-authored-by: Guillermo Ruffino --- esphome/components/mcp4725/__init__.py | 0 esphome/components/mcp4725/mcp4725.cpp | 38 ++++++++++++++++++++++++++ esphome/components/mcp4725/mcp4725.h | 23 ++++++++++++++++ esphome/components/mcp4725/output.py | 20 ++++++++++++++ tests/test1.yaml | 5 ++++ 5 files changed, 86 insertions(+) create mode 100644 esphome/components/mcp4725/__init__.py create mode 100644 esphome/components/mcp4725/mcp4725.cpp create mode 100644 esphome/components/mcp4725/mcp4725.h create mode 100644 esphome/components/mcp4725/output.py diff --git a/esphome/components/mcp4725/__init__.py b/esphome/components/mcp4725/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/mcp4725/mcp4725.cpp b/esphome/components/mcp4725/mcp4725.cpp new file mode 100644 index 0000000000..85028d2f39 --- /dev/null +++ b/esphome/components/mcp4725/mcp4725.cpp @@ -0,0 +1,38 @@ +#include "mcp4725.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace mcp4725 { + +static const char *TAG = "mcp4725"; + +void MCP4725::setup() { + ESP_LOGCONFIG(TAG, "Setting up MCP4725 (0x%02X)...", this->address_); + + this->parent_->raw_begin_transmission(this->address_); + + if (!this->parent_->raw_end_transmission(this->address_)) { + this->error_code_ = COMMUNICATION_FAILED; + this->mark_failed(); + + return; + } +} + +void MCP4725::dump_config() { + LOG_I2C_DEVICE(this); + + if (this->error_code_ == COMMUNICATION_FAILED) { + ESP_LOGE(TAG, "Communication with MCP4725 failed!"); + } +} + +// https://learn.sparkfun.com/tutorials/mcp4725-digital-to-analog-converter-hookup-guide?_ga=2.176055202.1402343014.1607953301-893095255.1606753886 +void MCP4725::write_state(float state) { + const uint16_t value = (uint16_t) round(state * (pow(2, MCP4725_RES) - 1)); + + this->write_byte_16(64, value << 4); +} + +} // namespace mcp4725 +} // namespace esphome diff --git a/esphome/components/mcp4725/mcp4725.h b/esphome/components/mcp4725/mcp4725.h new file mode 100644 index 0000000000..d6fa52e323 --- /dev/null +++ b/esphome/components/mcp4725/mcp4725.h @@ -0,0 +1,23 @@ +#pragma once + +#include "esphome/components/output/float_output.h" +#include "esphome/core/component.h" +#include "esphome/components/i2c/i2c.h" + +static const uint8_t MCP4725_ADDR = 0x60; +static const uint8_t MCP4725_RES = 12; + +namespace esphome { +namespace mcp4725 { +class MCP4725 : public Component, public output::FloatOutput, public i2c::I2CDevice { + public: + void setup() override; + void dump_config() override; + void write_state(float state) override; + + protected: + enum ErrorCode { NONE = 0, COMMUNICATION_FAILED } error_code_{NONE}; +}; + +} // namespace mcp4725 +} // namespace esphome diff --git a/esphome/components/mcp4725/output.py b/esphome/components/mcp4725/output.py new file mode 100644 index 0000000000..f9593db1f5 --- /dev/null +++ b/esphome/components/mcp4725/output.py @@ -0,0 +1,20 @@ +import esphome.config_validation as cv +import esphome.codegen as cg +from esphome.components import output, i2c +from esphome.const import CONF_ID + +DEPENDENCIES = ['i2c'] + +mcp4725 = cg.esphome_ns.namespace('mcp4725') +MCP4725 = mcp4725.class_('MCP4725', output.FloatOutput, cg.Component, i2c.I2CDevice) + +CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend({ + cv.Required(CONF_ID): cv.declare_id(MCP4725), +}).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(0x60)) + + +def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + yield cg.register_component(var, config) + yield i2c.register_i2c_device(var, config) + yield output.register_output(var, config) diff --git a/tests/test1.yaml b/tests/test1.yaml index e1088e0d5a..1893a32b30 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -1192,6 +1192,8 @@ output: - platform: esp32_dac pin: GPIO25 id: dac_output + - platform: mcp4725 + id: mcp4725_dac_output e131: @@ -1561,6 +1563,9 @@ switch: - output.set_level: id: dac_output level: !lambda 'return 0.5;' + - output.set_level: + id: mcp4725_dac_output + level: !lambda 'return 0.5;' turn_off_action: - switch.turn_on: living_room_lights_off restore_state: False From 23cf120977468cebefd863aa9c6ef6b97b7994f9 Mon Sep 17 00:00:00 2001 From: rspaargaren Date: Wed, 10 Feb 2021 13:20:31 +0100 Subject: [PATCH 034/111] Added codeowners (#1487) --- CODEOWNERS | 1 + esphome/components/max7219digit/display.py | 81 +++++++++++++--------- 2 files changed, 50 insertions(+), 32 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index d5df9e8109..0f22c8ace9 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -45,6 +45,7 @@ esphome/components/json/* @OttoWinter esphome/components/ledc/* @OttoWinter esphome/components/light/* @esphome/core esphome/components/logger/* @esphome/core +esphome/components/max7219digit/* @rspaargaren esphome/components/mcp23s08/* @SenexCrenshaw esphome/components/mcp23s17/* @SenexCrenshaw esphome/components/mcp2515/* @danielschramm @mvturnho diff --git a/esphome/components/max7219digit/display.py b/esphome/components/max7219digit/display.py index e9aba13287..4863312b5a 100644 --- a/esphome/components/max7219digit/display.py +++ b/esphome/components/max7219digit/display.py @@ -3,45 +3,61 @@ import esphome.config_validation as cv from esphome.components import display, spi from esphome.const import CONF_ID, CONF_INTENSITY, CONF_LAMBDA, CONF_NUM_CHIPS -DEPENDENCIES = ['spi'] +CODEOWNERS = ["@rspaargaren"] +DEPENDENCIES = ["spi"] -CONF_ROTATE_CHIP = 'rotate_chip' -CONF_SCROLL_SPEED = 'scroll_speed' -CONF_SCROLL_DWELL = 'scroll_dwell' -CONF_SCROLL_DELAY = 'scroll_delay' -CONF_SCROLL_ENABLE = 'scroll_enable' -CONF_SCROLL_MODE = 'scroll_mode' -CONF_REVERSE_ENABLE = 'reverse_enable' +CONF_ROTATE_CHIP = "rotate_chip" +CONF_SCROLL_SPEED = "scroll_speed" +CONF_SCROLL_DWELL = "scroll_dwell" +CONF_SCROLL_DELAY = "scroll_delay" +CONF_SCROLL_ENABLE = "scroll_enable" +CONF_SCROLL_MODE = "scroll_mode" +CONF_REVERSE_ENABLE = "reverse_enable" SCROLL_MODES = { - 'CONTINUOUS': 0, - 'STOP': 1, + "CONTINUOUS": 0, + "STOP": 1, } CHIP_MODES = { - '0': 0, - '90': 1, - '180': 2, - '270': 3, + "0": 0, + "90": 1, + "180": 2, + "270": 3, } -max7219_ns = cg.esphome_ns.namespace('max7219digit') -MAX7219Component = max7219_ns.class_('MAX7219Component', cg.PollingComponent, spi.SPIDevice, - display.DisplayBuffer) -MAX7219ComponentRef = MAX7219Component.operator('ref') +max7219_ns = cg.esphome_ns.namespace("max7219digit") +MAX7219Component = max7219_ns.class_( + "MAX7219Component", cg.PollingComponent, spi.SPIDevice, display.DisplayBuffer +) +MAX7219ComponentRef = MAX7219Component.operator("ref") -CONFIG_SCHEMA = display.BASIC_DISPLAY_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(MAX7219Component), - cv.Optional(CONF_NUM_CHIPS, default=4): cv.int_range(min=1, max=255), - cv.Optional(CONF_INTENSITY, default=15): cv.int_range(min=0, max=15), - cv.Optional(CONF_ROTATE_CHIP, default='0'): cv.enum(CHIP_MODES, upper=True), - cv.Optional(CONF_SCROLL_MODE, default='CONTINUOUS'): cv.enum(SCROLL_MODES, upper=True), - cv.Optional(CONF_SCROLL_ENABLE, default=True): cv.boolean, - cv.Optional(CONF_SCROLL_SPEED, default='250ms'): cv.positive_time_period_milliseconds, - cv.Optional(CONF_SCROLL_DELAY, default='1000ms'): cv.positive_time_period_milliseconds, - cv.Optional(CONF_SCROLL_DWELL, default='1000ms'): cv.positive_time_period_milliseconds, - cv.Optional(CONF_REVERSE_ENABLE, default=False): cv.boolean, -}).extend(cv.polling_component_schema('500ms')).extend(spi.spi_device_schema(cs_pin_required=True)) +CONFIG_SCHEMA = ( + display.BASIC_DISPLAY_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(MAX7219Component), + cv.Optional(CONF_NUM_CHIPS, default=4): cv.int_range(min=1, max=255), + cv.Optional(CONF_INTENSITY, default=15): cv.int_range(min=0, max=15), + cv.Optional(CONF_ROTATE_CHIP, default="0"): cv.enum(CHIP_MODES, upper=True), + cv.Optional(CONF_SCROLL_MODE, default="CONTINUOUS"): cv.enum( + SCROLL_MODES, upper=True + ), + cv.Optional(CONF_SCROLL_ENABLE, default=True): cv.boolean, + cv.Optional( + CONF_SCROLL_SPEED, default="250ms" + ): cv.positive_time_period_milliseconds, + cv.Optional( + CONF_SCROLL_DELAY, default="1000ms" + ): cv.positive_time_period_milliseconds, + cv.Optional( + CONF_SCROLL_DWELL, default="1000ms" + ): cv.positive_time_period_milliseconds, + cv.Optional(CONF_REVERSE_ENABLE, default=False): cv.boolean, + } + ) + .extend(cv.polling_component_schema("500ms")) + .extend(spi.spi_device_schema(cs_pin_required=True)) +) def to_code(config): @@ -61,6 +77,7 @@ def to_code(config): cg.add(var.set_reverse(config[CONF_REVERSE_ENABLE])) if CONF_LAMBDA in config: - lambda_ = yield cg.process_lambda(config[CONF_LAMBDA], [(MAX7219ComponentRef, 'it')], - return_type=cg.void) + lambda_ = yield cg.process_lambda( + config[CONF_LAMBDA], [(MAX7219ComponentRef, "it")], return_type=cg.void + ) cg.add(var.set_writer(lambda_)) From 50006e4c425961d6a1764e7334f622e9809a75c5 Mon Sep 17 00:00:00 2001 From: Klarstein <62219057+Klarstein@users.noreply.github.com> Date: Fri, 12 Feb 2021 03:26:55 +0100 Subject: [PATCH 035/111] Update Dockerfile health check timings (#1517) --- docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 12b06ec284..bbee4b2434 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -16,7 +16,7 @@ ENV USERNAME="" PASSWORD="" EXPOSE 6052 # Run healthcheck (heartbeat) -HEALTHCHECK --interval=5m --timeout=3s \ +HEALTHCHECK --interval=30s --timeout=30s \ CMD curl --fail http://localhost:6052 || exit 1 # The directory the user should mount their configuration files to From e288aa07fbda1aa5247f50e4fed8ea8f0568340d Mon Sep 17 00:00:00 2001 From: SenexCrenshaw <35600301+SenexCrenshaw@users.noreply.github.com> Date: Sat, 13 Feb 2021 03:34:59 -0500 Subject: [PATCH 036/111] Fixed ST7735 transfer_byte to write_byte without `miso` (#1529) --- esphome/components/st7735/st7735.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/st7735/st7735.cpp b/esphome/components/st7735/st7735.cpp index 2a3d8fc903..5cd79da2cb 100644 --- a/esphome/components/st7735/st7735.cpp +++ b/esphome/components/st7735/st7735.cpp @@ -407,7 +407,7 @@ void HOT ST7735::senddata_(const uint8_t *data_bytes, uint8_t num_data_bytes) { this->cs_->digital_write(false); this->enable(); for (uint8_t i = 0; i < num_data_bytes; i++) { - this->transfer_byte(pgm_read_byte(data_bytes++)); // write byte - SPI library + this->write_byte(pgm_read_byte(data_bytes++)); // write byte - SPI library } this->cs_->digital_write(true); this->disable(); From 57d61853745ebc99813734fb363a618b1eef2ac0 Mon Sep 17 00:00:00 2001 From: Justin Gerhardt Date: Sat, 13 Feb 2021 03:36:39 -0500 Subject: [PATCH 037/111] Correct Native API Wire Format Documentation (#1528) --- esphome/components/api/api.proto | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index 271b1d6c5f..85706fcfcc 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -46,6 +46,7 @@ service APIConnection { // The Home Assistant protocol is structured as a simple // TCP socket with short binary messages encoded in the protocol buffers format // First, a message in this protocol has a specific format: +// * A zero byte. // * VarInt denoting the size of the message object. (type is not part of this) // * VarInt denoting the type of message. // * The message object encoded as a ProtoBuf message From 81b512a7b3ddbb63e86aab673eb4aa3aaccf74a0 Mon Sep 17 00:00:00 2001 From: Frank Bakker Date: Sat, 13 Feb 2021 09:57:06 +0100 Subject: [PATCH 038/111] Added energy sensor to hlw8012 (#1198) --- esphome/components/hlw8012/hlw8012.cpp | 6 ++++++ esphome/components/hlw8012/hlw8012.h | 3 +++ esphome/components/hlw8012/sensor.py | 8 ++++++-- tests/test1.yaml | 3 +++ 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/esphome/components/hlw8012/hlw8012.cpp b/esphome/components/hlw8012/hlw8012.cpp index 2b3b4ec2d9..379dbe578e 100644 --- a/esphome/components/hlw8012/hlw8012.cpp +++ b/esphome/components/hlw8012/hlw8012.cpp @@ -79,6 +79,12 @@ void HLW8012Component::update() { this->power_sensor_->publish_state(power); } + if (this->energy_sensor_ != nullptr) { + cf_total_pulses_ += raw_cf; + float energy = cf_total_pulses_ * power_multiplier_micros / 3600 / 1000000.0f; + this->energy_sensor_->publish_state(energy); + } + if (this->change_mode_at_++ == this->change_mode_every_) { this->current_mode_ = !this->current_mode_; ESP_LOGV(TAG, "Changing mode to %s mode", this->current_mode_ ? "CURRENT" : "VOLTAGE"); diff --git a/esphome/components/hlw8012/hlw8012.h b/esphome/components/hlw8012/hlw8012.h index 4e5dc0f67f..af1f2e9a8c 100644 --- a/esphome/components/hlw8012/hlw8012.h +++ b/esphome/components/hlw8012/hlw8012.h @@ -29,6 +29,7 @@ class HLW8012Component : public PollingComponent { void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; } void set_current_sensor(sensor::Sensor *current_sensor) { current_sensor_ = current_sensor; } void set_power_sensor(sensor::Sensor *power_sensor) { power_sensor_ = power_sensor; } + void set_energy_sensor(sensor::Sensor *energy_sensor) { energy_sensor_ = energy_sensor; } protected: uint32_t nth_value_{0}; @@ -37,6 +38,7 @@ class HLW8012Component : public PollingComponent { uint32_t change_mode_every_{8}; float current_resistor_{0.001}; float voltage_divider_{2351}; + uint64_t cf_total_pulses_{0}; GPIOPin *sel_pin_; GPIOPin *cf_pin_; pulse_counter::PulseCounterStorage cf_store_; @@ -45,6 +47,7 @@ class HLW8012Component : public PollingComponent { sensor::Sensor *voltage_sensor_{nullptr}; sensor::Sensor *current_sensor_{nullptr}; sensor::Sensor *power_sensor_{nullptr}; + sensor::Sensor *energy_sensor_{nullptr}; }; } // namespace hlw8012 diff --git a/esphome/components/hlw8012/sensor.py b/esphome/components/hlw8012/sensor.py index e1f02b8fd2..e1a0ec6f18 100644 --- a/esphome/components/hlw8012/sensor.py +++ b/esphome/components/hlw8012/sensor.py @@ -3,8 +3,8 @@ import esphome.config_validation as cv from esphome import pins from esphome.components import sensor from esphome.const import CONF_CHANGE_MODE_EVERY, CONF_INITIAL_MODE, CONF_CURRENT, \ - CONF_CURRENT_RESISTOR, CONF_ID, CONF_POWER, CONF_SEL_PIN, CONF_VOLTAGE, CONF_VOLTAGE_DIVIDER, \ - ICON_FLASH, UNIT_VOLT, UNIT_AMPERE, UNIT_WATT + CONF_CURRENT_RESISTOR, CONF_ID, CONF_POWER, CONF_ENERGY, CONF_SEL_PIN, CONF_VOLTAGE, \ + CONF_VOLTAGE_DIVIDER, ICON_FLASH, UNIT_VOLT, UNIT_AMPERE, UNIT_WATT, UNIT_WATT_HOURS AUTO_LOAD = ['pulse_counter'] @@ -29,6 +29,7 @@ CONFIG_SCHEMA = cv.Schema({ cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 1), cv.Optional(CONF_CURRENT): sensor.sensor_schema(UNIT_AMPERE, ICON_FLASH, 2), cv.Optional(CONF_POWER): sensor.sensor_schema(UNIT_WATT, ICON_FLASH, 1), + cv.Optional(CONF_ENERGY): sensor.sensor_schema(UNIT_WATT_HOURS, ICON_FLASH, 1), cv.Optional(CONF_CURRENT_RESISTOR, default=0.001): cv.resistance, cv.Optional(CONF_VOLTAGE_DIVIDER, default=2351): cv.positive_float, @@ -57,6 +58,9 @@ def to_code(config): if CONF_POWER in config: sens = yield sensor.new_sensor(config[CONF_POWER]) cg.add(var.set_power_sensor(sens)) + if CONF_ENERGY in config: + sens = yield sensor.new_sensor(config[CONF_ENERGY]) + cg.add(var.set_energy_sensor(sens)) cg.add(var.set_current_resistor(config[CONF_CURRENT_RESISTOR])) cg.add(var.set_voltage_divider(config[CONF_VOLTAGE_DIVIDER])) cg.add(var.set_change_mode_every(config[CONF_CHANGE_MODE_EVERY])) diff --git a/tests/test1.yaml b/tests/test1.yaml index 1893a32b30..cda2abfeb5 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -456,6 +456,9 @@ sensor: power: name: 'HLW8012 Power' id: hlw8012_power + energy: + name: "HLW8012 Energy" + id: hlw8012_energy update_interval: 15s current_resistor: 0.001 ohm voltage_divider: 2351 From b52f7cfe86a167bcd3f41baf2194243699a3e354 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Sat, 13 Feb 2021 03:07:11 -0600 Subject: [PATCH 039/111] MCP230xx open drain interrupt pins (#1243) --- esphome/components/mcp23008/__init__.py | 4 +++- esphome/components/mcp23008/mcp23008.cpp | 6 ++++-- esphome/components/mcp23008/mcp23008.h | 3 +++ esphome/components/mcp23017/__init__.py | 4 +++- esphome/components/mcp23017/mcp23017.cpp | 8 +++++--- esphome/components/mcp23017/mcp23017.h | 3 +++ esphome/const.py | 1 + tests/test1.yaml | 2 ++ 8 files changed, 24 insertions(+), 7 deletions(-) diff --git a/esphome/components/mcp23008/__init__.py b/esphome/components/mcp23008/__init__.py index 4241b6ba48..858b37b60a 100644 --- a/esphome/components/mcp23008/__init__.py +++ b/esphome/components/mcp23008/__init__.py @@ -2,7 +2,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins from esphome.components import i2c -from esphome.const import CONF_ID, CONF_NUMBER, CONF_MODE, CONF_INVERTED +from esphome.const import CONF_ID, CONF_NUMBER, CONF_MODE, CONF_INVERTED, CONF_OPEN_DRAIN_INTERRUPT DEPENDENCIES = ['i2c'] MULTI_CONF = True @@ -20,6 +20,7 @@ MCP23008GPIOPin = mcp23008_ns.class_('MCP23008GPIOPin', cg.GPIOPin) CONFIG_SCHEMA = cv.Schema({ cv.Required(CONF_ID): cv.declare_id(MCP23008), + cv.Optional(CONF_OPEN_DRAIN_INTERRUPT, default=False): cv.boolean, }).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(0x20)) @@ -27,6 +28,7 @@ def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) yield i2c.register_i2c_device(var, config) + cg.add(var.set_open_drain_ints(config[CONF_OPEN_DRAIN_INTERRUPT])) CONF_MCP23008 = 'mcp23008' diff --git a/esphome/components/mcp23008/mcp23008.cpp b/esphome/components/mcp23008/mcp23008.cpp index bf5bb55f2e..22c7322458 100644 --- a/esphome/components/mcp23008/mcp23008.cpp +++ b/esphome/components/mcp23008/mcp23008.cpp @@ -14,8 +14,10 @@ void MCP23008::setup() { return; } - // all pins input - this->write_reg_(MCP23008_IODIR, 0xFF); + if (this->open_drain_ints_) { + // enable open-drain interrupt pins, 3.3V-safe + this->write_reg_(MCP23008_IOCON, 0x04); + } } bool MCP23008::digital_read(uint8_t pin) { uint8_t bit = pin % 8; diff --git a/esphome/components/mcp23008/mcp23008.h b/esphome/components/mcp23008/mcp23008.h index b4e5d75fd4..e30a924dde 100644 --- a/esphome/components/mcp23008/mcp23008.h +++ b/esphome/components/mcp23008/mcp23008.h @@ -39,6 +39,8 @@ class MCP23008 : public Component, public i2c::I2CDevice { void digital_write(uint8_t pin, bool value); void pin_mode(uint8_t pin, uint8_t mode); + void set_open_drain_ints(const bool value) { open_drain_ints_ = value; } + float get_setup_priority() const override; protected: @@ -50,6 +52,7 @@ class MCP23008 : public Component, public i2c::I2CDevice { void update_reg_(uint8_t pin, bool pin_value, uint8_t reg_a); uint8_t olat_{0x00}; + bool open_drain_ints_; }; class MCP23008GPIOPin : public GPIOPin { diff --git a/esphome/components/mcp23017/__init__.py b/esphome/components/mcp23017/__init__.py index 4b798bf434..34f94b293a 100644 --- a/esphome/components/mcp23017/__init__.py +++ b/esphome/components/mcp23017/__init__.py @@ -2,7 +2,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins from esphome.components import i2c -from esphome.const import CONF_ID, CONF_NUMBER, CONF_MODE, CONF_INVERTED +from esphome.const import CONF_ID, CONF_NUMBER, CONF_MODE, CONF_INVERTED, CONF_OPEN_DRAIN_INTERRUPT DEPENDENCIES = ['i2c'] MULTI_CONF = True @@ -20,6 +20,7 @@ MCP23017GPIOPin = mcp23017_ns.class_('MCP23017GPIOPin', cg.GPIOPin) CONFIG_SCHEMA = cv.Schema({ cv.Required(CONF_ID): cv.declare_id(MCP23017), + cv.Optional(CONF_OPEN_DRAIN_INTERRUPT, default=False): cv.boolean, }).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(0x20)) @@ -27,6 +28,7 @@ def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) yield i2c.register_i2c_device(var, config) + cg.add(var.set_open_drain_ints(config[CONF_OPEN_DRAIN_INTERRUPT])) CONF_MCP23017 = 'mcp23017' diff --git a/esphome/components/mcp23017/mcp23017.cpp b/esphome/components/mcp23017/mcp23017.cpp index 9653aa680d..ec972668ef 100644 --- a/esphome/components/mcp23017/mcp23017.cpp +++ b/esphome/components/mcp23017/mcp23017.cpp @@ -14,9 +14,11 @@ void MCP23017::setup() { return; } - // all pins input - this->write_reg_(MCP23017_IODIRA, 0xFF); - this->write_reg_(MCP23017_IODIRB, 0xFF); + if (this->open_drain_ints_) { + // enable open-drain interrupt pins, 3.3V-safe + this->write_reg_(MCP23017_IOCONA, 0x04); + this->write_reg_(MCP23017_IOCONB, 0x04); + } } bool MCP23017::digital_read(uint8_t pin) { uint8_t bit = pin % 8; diff --git a/esphome/components/mcp23017/mcp23017.h b/esphome/components/mcp23017/mcp23017.h index 4389eeb6ff..5656dcc58d 100644 --- a/esphome/components/mcp23017/mcp23017.h +++ b/esphome/components/mcp23017/mcp23017.h @@ -51,6 +51,8 @@ class MCP23017 : public Component, public i2c::I2CDevice { void digital_write(uint8_t pin, bool value); void pin_mode(uint8_t pin, uint8_t mode); + void set_open_drain_ints(const bool value) { open_drain_ints_ = value; } + float get_setup_priority() const override; protected: @@ -63,6 +65,7 @@ class MCP23017 : public Component, public i2c::I2CDevice { uint8_t olat_a_{0x00}; uint8_t olat_b_{0x00}; + bool open_drain_ints_; }; class MCP23017GPIOPin : public GPIOPin { diff --git a/esphome/const.py b/esphome/const.py index 184954b4c6..0fec577d19 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -357,6 +357,7 @@ CONF_ON_VALUE = 'on_value' CONF_ON_VALUE_RANGE = 'on_value_range' CONF_ONE = 'one' CONF_OPEN_ACTION = 'open_action' +CONF_OPEN_DRAIN_INTERRUPT = 'open_drain_interrupt' CONF_OPEN_DURATION = 'open_duration' CONF_OPEN_ENDSTOP = 'open_endstop' CONF_OPTIMISTIC = 'optimistic' diff --git a/tests/test1.yaml b/tests/test1.yaml index cda2abfeb5..84dd8eeaf4 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -1921,10 +1921,12 @@ pcf8574: mcp23017: - id: 'mcp23017_hub' + open_drain_interrupt: 'true' mcp23008: - id: 'mcp23008_hub' address: 0x22 + open_drain_interrupt: 'true' mcp23016: - id: 'mcp23016_hub' From a3423021140a0c9fd17adec0e826304fd8c6d112 Mon Sep 17 00:00:00 2001 From: SenexCrenshaw <35600301+SenexCrenshaw@users.noreply.github.com> Date: Sun, 14 Feb 2021 00:21:43 -0500 Subject: [PATCH 040/111] st7735_conf_fixes (#1530) --- esphome/components/st7735/display.py | 97 +++++++++++++++++----------- tests/test1.yaml | 8 +-- 2 files changed, 65 insertions(+), 40 deletions(-) diff --git a/esphome/components/st7735/display.py b/esphome/components/st7735/display.py index 902f9c8beb..f1e7d37d51 100644 --- a/esphome/components/st7735/display.py +++ b/esphome/components/st7735/display.py @@ -4,50 +4,67 @@ from esphome import pins from esphome.components import spi from esphome.components import display from esphome.core import coroutine -from esphome.const import CONF_DC_PIN, CONF_ID, CONF_LAMBDA, CONF_MODEL, CONF_RESET_PIN, CONF_PAGES +from esphome.const import ( + CONF_DC_PIN, + CONF_ID, + CONF_LAMBDA, + CONF_MODEL, + CONF_RESET_PIN, + CONF_PAGES, +) from . import st7735_ns -CODEOWNERS = ['@SenexCrenshaw'] +CODEOWNERS = ["@SenexCrenshaw"] -DEPENDENCIES = ['spi'] +DEPENDENCIES = ["spi"] -CONF_DEVICEWIDTH = 'devicewidth' -CONF_DEVICEHEIGHT = 'deviceheight' -CONF_ROWSTART = 'rowstart' -CONF_COLSTART = 'colstart' -CONF_EIGHTBITCOLOR = 'eightbitcolor' -CONF_USEBGR = 'usebgr' +CONF_DEVICE_WIDTH = "device_width" +CONF_DEVICE_HEIGHT = "device_height" +CONF_ROW_START = "row_start" +CONF_COL_START = "col_start" +CONF_EIGHT_BIT_COLOR = "eight_bit_color" +CONF_USE_BGR = "use_bgr" -SPIST7735 = st7735_ns.class_('ST7735', cg.PollingComponent, display.DisplayBuffer, spi.SPIDevice) -ST7735Model = st7735_ns.enum('ST7735Model') +SPIST7735 = st7735_ns.class_( + "ST7735", cg.PollingComponent, display.DisplayBuffer, spi.SPIDevice +) +ST7735Model = st7735_ns.enum("ST7735Model") MODELS = { - 'INITR_GREENTAB': ST7735Model.ST7735_INITR_GREENTAB, - 'INITR_REDTAB': ST7735Model.ST7735_INITR_REDTAB, - 'INITR_BLACKTAB': ST7735Model.ST7735_INITR_BLACKTAB, - 'INITR_MINI160X80': ST7735Model.ST7735_INITR_MINI_160X80, - 'INITR_18BLACKTAB': ST7735Model.ST7735_INITR_18BLACKTAB, - 'INITR_18REDTAB': ST7735Model.ST7735_INITR_18REDTAB + "INITR_GREENTAB": ST7735Model.ST7735_INITR_GREENTAB, + "INITR_REDTAB": ST7735Model.ST7735_INITR_REDTAB, + "INITR_BLACKTAB": ST7735Model.ST7735_INITR_BLACKTAB, + "INITR_MINI160X80": ST7735Model.ST7735_INITR_MINI_160X80, + "INITR_18BLACKTAB": ST7735Model.ST7735_INITR_18BLACKTAB, + "INITR_18REDTAB": ST7735Model.ST7735_INITR_18REDTAB, } ST7735_MODEL = cv.enum(MODELS, upper=True, space="_") -ST7735_SCHEMA = display.FULL_DISPLAY_SCHEMA.extend({ - cv.Required(CONF_MODEL): ST7735_MODEL, - cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema -}).extend(cv.polling_component_schema('1s')) +ST7735_SCHEMA = display.FULL_DISPLAY_SCHEMA.extend( + { + cv.Required(CONF_MODEL): ST7735_MODEL, + cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, + } +).extend(cv.polling_component_schema("1s")) -CONFIG_SCHEMA = cv.All(ST7735_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(SPIST7735), - cv.Required(CONF_DC_PIN): pins.gpio_output_pin_schema, - cv.Required(CONF_DEVICEWIDTH): cv.int_, - cv.Required(CONF_DEVICEHEIGHT): cv.int_, - cv.Required(CONF_COLSTART): cv.int_, - cv.Required(CONF_ROWSTART): cv.int_, - cv.Optional(CONF_EIGHTBITCOLOR, default=False): cv.boolean, - cv.Optional(CONF_USEBGR, default=False): cv.boolean, -}).extend(cv.COMPONENT_SCHEMA).extend(spi.spi_device_schema()), - cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA)) +CONFIG_SCHEMA = cv.All( + ST7735_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(SPIST7735), + cv.Required(CONF_DC_PIN): pins.gpio_output_pin_schema, + cv.Required(CONF_DEVICE_WIDTH): cv.int_, + cv.Required(CONF_DEVICE_HEIGHT): cv.int_, + cv.Required(CONF_COL_START): cv.int_, + cv.Required(CONF_ROW_START): cv.int_, + cv.Optional(CONF_EIGHT_BIT_COLOR, default=False): cv.boolean, + cv.Optional(CONF_USE_BGR, default=False): cv.boolean, + } + ) + .extend(cv.COMPONENT_SCHEMA) + .extend(spi.spi_device_schema()), + cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA), +) @coroutine @@ -60,14 +77,22 @@ def setup_st7735(var, config): cg.add(var.set_reset_pin(reset)) if CONF_LAMBDA in config: lambda_ = yield cg.process_lambda( - config[CONF_LAMBDA], [(display.DisplayBufferRef, 'it')], return_type=cg.void) + config[CONF_LAMBDA], [(display.DisplayBufferRef, "it")], return_type=cg.void + ) cg.add(var.set_writer(lambda_)) def to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_MODEL], config[CONF_DEVICEWIDTH], - config[CONF_DEVICEHEIGHT], config[CONF_COLSTART], config[CONF_ROWSTART], - config[CONF_EIGHTBITCOLOR], config[CONF_USEBGR]) + var = cg.new_Pvariable( + config[CONF_ID], + config[CONF_MODEL], + config[CONF_DEVICE_WIDTH], + config[CONF_DEVICE_HEIGHT], + config[CONF_COL_START], + config[CONF_ROW_START], + config[CONF_EIGHT_BIT_COLOR], + config[CONF_USE_BGR], + ) yield setup_st7735(var, config) yield spi.register_spi_device(var, config) diff --git a/tests/test1.yaml b/tests/test1.yaml index 84dd8eeaf4..102c3ac703 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -1825,10 +1825,10 @@ display: dc_pin: GPIO16 reset_pin: GPIO23 rotation: 0 - devicewidth: 128 - deviceheight: 160 - colstart: 0 - rowstart: 0 + device_width: 128 + device_height: 160 + col_start: 0 + row_start: 0 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); tm1651: From f95be6a0dfe6685db97f0accb5ca4e8fb0885249 Mon Sep 17 00:00:00 2001 From: marecabo <23156476+marecabo@users.noreply.github.com> Date: Mon, 15 Feb 2021 16:49:02 +0100 Subject: [PATCH 041/111] Device class attribute for sensor component (#1525) * Add constants for sensor device_class * Add device_class attribute to sensor component * Add device_class attribute to sensor class * Add device_class to mhz19 temperature sensor * Add device_class to sensor in api component * Add test for device_class of sensor * Rename DEVICE_CLASS_NONE to DEVICE_CLASS_EMPTY for consistency * Make optional attributes of sensor component truly optional --- esphome/components/api/api.proto | 1 + esphome/components/api/api_connection.cpp | 1 + esphome/components/api/api_pb2.cpp | 9 ++++ esphome/components/api/api_pb2.h | 1 + esphome/components/mhz19/sensor.py | 5 +- esphome/components/sensor/__init__.py | 46 +++++++++++++++---- esphome/components/sensor/sensor.cpp | 7 +++ esphome/components/sensor/sensor.h | 19 ++++++++ esphome/const.py | 14 ++++++ tests/component_tests/sensor/test_sensor.py | 14 ++++++ tests/component_tests/sensor/test_sensor.yaml | 12 +++++ 11 files changed, 117 insertions(+), 12 deletions(-) create mode 100644 tests/component_tests/sensor/test_sensor.py create mode 100644 tests/component_tests/sensor/test_sensor.yaml diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index 85706fcfcc..aaa9477985 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -419,6 +419,7 @@ message ListEntitiesSensorResponse { string unit_of_measurement = 6; int32 accuracy_decimals = 7; bool force_update = 8; + string device_class = 9; } message SensorStateResponse { option (id) = 25; diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 431be5b4dc..ecbe5b79c6 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -382,6 +382,7 @@ bool APIConnection::send_sensor_info(sensor::Sensor *sensor) { msg.unit_of_measurement = sensor->get_unit_of_measurement(); msg.accuracy_decimals = sensor->get_accuracy_decimals(); msg.force_update = sensor->get_force_update(); + msg.device_class = sensor->get_device_class(); return this->send_list_entities_sensor_response(msg); } #endif diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index a7e521c699..7a6b55bf91 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -1494,6 +1494,10 @@ bool ListEntitiesSensorResponse::decode_length(uint32_t field_id, ProtoLengthDel this->unit_of_measurement = value.as_string(); return true; } + case 9: { + this->device_class = value.as_string(); + return true; + } default: return false; } @@ -1517,6 +1521,7 @@ void ListEntitiesSensorResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(6, this->unit_of_measurement); buffer.encode_int32(7, this->accuracy_decimals); buffer.encode_bool(8, this->force_update); + buffer.encode_string(9, this->device_class); } void ListEntitiesSensorResponse::dump_to(std::string &out) const { char buffer[64]; @@ -1554,6 +1559,10 @@ void ListEntitiesSensorResponse::dump_to(std::string &out) const { out.append(" force_update: "); out.append(YESNO(this->force_update)); out.append("\n"); + + out.append(" device_class: "); + out.append("'").append(this->device_class).append("'"); + out.append("\n"); out.append("}"); } bool SensorStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index b97320b48a..abee4a11d4 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -401,6 +401,7 @@ class ListEntitiesSensorResponse : public ProtoMessage { std::string unique_id{}; // NOLINT std::string icon{}; // NOLINT std::string unit_of_measurement{}; // NOLINT + std::string device_class{}; // NOLINT int32_t accuracy_decimals{0}; // NOLINT bool force_update{false}; // NOLINT void encode(ProtoWriteBuffer buffer) const override; diff --git a/esphome/components/mhz19/sensor.py b/esphome/components/mhz19/sensor.py index d0a7caee84..a3e6946efd 100644 --- a/esphome/components/mhz19/sensor.py +++ b/esphome/components/mhz19/sensor.py @@ -4,7 +4,7 @@ from esphome import automation from esphome.automation import maybe_simple_id from esphome.components import sensor, uart from esphome.const import CONF_CO2, CONF_ID, CONF_TEMPERATURE, ICON_MOLECULE_CO2, \ - UNIT_PARTS_PER_MILLION, UNIT_CELSIUS, ICON_THERMOMETER + UNIT_PARTS_PER_MILLION, UNIT_CELSIUS, ICON_EMPTY, DEVICE_CLASS_TEMPERATURE DEPENDENCIES = ['uart'] @@ -19,7 +19,8 @@ MHZ19ABCDisableAction = mhz19_ns.class_('MHZ19ABCDisableAction', automation.Acti CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(MHZ19Component), cv.Required(CONF_CO2): sensor.sensor_schema(UNIT_PARTS_PER_MILLION, ICON_MOLECULE_CO2, 0), - cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 0), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( + UNIT_CELSIUS, ICON_EMPTY, 0, DEVICE_CLASS_TEMPERATURE), cv.Optional(CONF_AUTOMATIC_BASELINE_CALIBRATION): cv.boolean, }).extend(cv.polling_component_schema('60s')).extend(uart.UART_DEVICE_SCHEMA) diff --git a/esphome/components/sensor/__init__.py b/esphome/components/sensor/__init__.py index 671bbe2b09..7def03c23e 100644 --- a/esphome/components/sensor/__init__.py +++ b/esphome/components/sensor/__init__.py @@ -4,15 +4,25 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import automation from esphome.components import mqtt -from esphome.const import CONF_ABOVE, CONF_ACCURACY_DECIMALS, CONF_ALPHA, CONF_BELOW, \ - CONF_EXPIRE_AFTER, CONF_FILTERS, CONF_FROM, CONF_ICON, CONF_ID, CONF_INTERNAL, \ +from esphome.const import CONF_DEVICE_CLASS, CONF_ABOVE, CONF_ACCURACY_DECIMALS, CONF_ALPHA, \ + CONF_BELOW, CONF_EXPIRE_AFTER, CONF_FILTERS, CONF_FROM, CONF_ICON, CONF_ID, CONF_INTERNAL, \ CONF_ON_RAW_VALUE, CONF_ON_VALUE, CONF_ON_VALUE_RANGE, CONF_SEND_EVERY, CONF_SEND_FIRST_AT, \ CONF_TO, CONF_TRIGGER_ID, CONF_UNIT_OF_MEASUREMENT, CONF_WINDOW_SIZE, CONF_NAME, CONF_MQTT_ID, \ - CONF_FORCE_UPDATE + CONF_FORCE_UPDATE, UNIT_EMPTY, ICON_EMPTY, DEVICE_CLASS_EMPTY, DEVICE_CLASS_BATTERY, \ + DEVICE_CLASS_CURRENT, DEVICE_CLASS_ENERGY, DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_ILLUMINANCE, \ + DEVICE_CLASS_SIGNAL_STRENGTH, DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_POWER, \ + DEVICE_CLASS_POWER_FACTOR, DEVICE_CLASS_PRESSURE, DEVICE_CLASS_TIMESTAMP, DEVICE_CLASS_VOLTAGE from esphome.core import CORE, coroutine, coroutine_with_priority from esphome.util import Registry CODEOWNERS = ['@esphome/core'] +DEVICE_CLASSES = [ + DEVICE_CLASS_EMPTY, DEVICE_CLASS_BATTERY, DEVICE_CLASS_CURRENT, DEVICE_CLASS_ENERGY, + DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_ILLUMINANCE, DEVICE_CLASS_SIGNAL_STRENGTH, + DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_POWER, DEVICE_CLASS_POWER_FACTOR, DEVICE_CLASS_PRESSURE, + DEVICE_CLASS_TIMESTAMP, DEVICE_CLASS_VOLTAGE +] + IS_PLATFORM_COMPONENT = True @@ -80,6 +90,7 @@ SensorInRangeCondition = sensor_ns.class_('SensorInRangeCondition', Filter) unit_of_measurement = cv.string_strict accuracy_decimals = cv.int_ icon = cv.icon +device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space='_') SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend({ cv.OnlyWith(CONF_MQTT_ID, 'mqtt'): cv.declare_id(mqtt.MQTTSensorComponent), @@ -87,6 +98,7 @@ SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend({ cv.Optional(CONF_UNIT_OF_MEASUREMENT): unit_of_measurement, cv.Optional(CONF_ICON): icon, cv.Optional(CONF_ACCURACY_DECIMALS): accuracy_decimals, + cv.Optional(CONF_DEVICE_CLASS): device_class, cv.Optional(CONF_FORCE_UPDATE, default=False): cv.boolean, cv.Optional(CONF_EXPIRE_AFTER): cv.All(cv.requires_component('mqtt'), cv.Any(None, cv.positive_time_period_milliseconds)), @@ -105,13 +117,25 @@ SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend({ }) -def sensor_schema(unit_of_measurement_, icon_, accuracy_decimals_): - # type: (str, str, int) -> cv.Schema - return SENSOR_SCHEMA.extend({ - cv.Optional(CONF_UNIT_OF_MEASUREMENT, default=unit_of_measurement_): unit_of_measurement, - cv.Optional(CONF_ICON, default=icon_): icon, - cv.Optional(CONF_ACCURACY_DECIMALS, default=accuracy_decimals_): accuracy_decimals, - }) +def sensor_schema(unit_of_measurement_=UNIT_EMPTY, icon_=ICON_EMPTY, accuracy_decimals_=0, + device_class_=DEVICE_CLASS_EMPTY): + # type: (str, str, int, str) -> cv.Schema + schema = SENSOR_SCHEMA + if unit_of_measurement_ != UNIT_EMPTY: + schema = schema.extend({ + cv.Optional(CONF_UNIT_OF_MEASUREMENT, default=unit_of_measurement_): unit_of_measurement + }) + if icon_ != ICON_EMPTY: + schema = schema.extend({cv.Optional(CONF_ICON, default=icon_): icon}) + if accuracy_decimals_ != 0: + schema = schema.extend({ + cv.Optional(CONF_ACCURACY_DECIMALS, default=accuracy_decimals_): accuracy_decimals, + }) + if device_class_ != DEVICE_CLASS_EMPTY: + schema = schema.extend({ + cv.Optional(CONF_DEVICE_CLASS, default=device_class_): device_class + }) + return schema @FILTER_REGISTRY.register('offset', OffsetFilter, cv.float_) @@ -253,6 +277,8 @@ def setup_sensor_core_(var, config): cg.add(var.set_name(config[CONF_NAME])) if CONF_INTERNAL in config: cg.add(var.set_internal(config[CONF_INTERNAL])) + if CONF_DEVICE_CLASS in config: + cg.add(var.set_device_class(config[CONF_DEVICE_CLASS])) if CONF_UNIT_OF_MEASUREMENT in config: cg.add(var.set_unit_of_measurement(config[CONF_UNIT_OF_MEASUREMENT])) if CONF_ICON in config: diff --git a/esphome/components/sensor/sensor.cpp b/esphome/components/sensor/sensor.cpp index e12e55e320..069a5c5923 100644 --- a/esphome/components/sensor/sensor.cpp +++ b/esphome/components/sensor/sensor.cpp @@ -40,6 +40,13 @@ std::string Sensor::get_icon() { return *this->icon_; return this->icon(); } +void Sensor::set_device_class(const std::string &device_class) { this->device_class_ = device_class; } +std::string Sensor::get_device_class() { + if (this->device_class_.has_value()) + return *this->device_class_; + return this->device_class(); +} +std::string Sensor::device_class() { return ""; } std::string Sensor::get_unit_of_measurement() { if (this->unit_of_measurement_.has_value()) return *this->unit_of_measurement_; diff --git a/esphome/components/sensor/sensor.h b/esphome/components/sensor/sensor.h index f23f022767..6bb6c876ab 100644 --- a/esphome/components/sensor/sensor.h +++ b/esphome/components/sensor/sensor.h @@ -10,6 +10,9 @@ namespace sensor { #define LOG_SENSOR(prefix, type, obj) \ if (obj != nullptr) { \ ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, type, obj->get_name().c_str()); \ + if (!obj->get_device_class().empty()) { \ + ESP_LOGCONFIG(TAG, "%s Device Class: '%s'", prefix, obj->get_device_class().c_str()); \ + } \ ESP_LOGCONFIG(TAG, "%s Unit of Measurement: '%s'", prefix, obj->get_unit_of_measurement().c_str()); \ ESP_LOGCONFIG(TAG, "%s Accuracy Decimals: %d", prefix, obj->get_accuracy_decimals()); \ if (!obj->get_icon().empty()) { \ @@ -122,6 +125,12 @@ class Sensor : public Nameable { */ float state; + /// Manually set the Home Assistant device class (see sensor::device_class) + void set_device_class(const std::string &device_class); + + /// Get the device class for this sensor, using the manual override if specified. + std::string get_device_class(); + /** This member variable stores the current raw state of the sensor. Unlike .state, * this will be updated immediately when publish_state is called. */ @@ -130,6 +139,14 @@ class Sensor : public Nameable { /// Return whether this sensor has gotten a full state (that passed through all filters) yet. bool has_state() const; + /** Override this to set the Home Assistant device class for this sensor. + * + * Return "" to disable this feature. + * + * @return The device class of this sensor, for example "temperature". + */ + virtual std::string device_class(); + /** A unique ID for this sensor, empty for no unique id. See unique ID requirements: * https://developers.home-assistant.io/docs/en/entity_registry_index.html#unique-id-requirements * @@ -174,6 +191,8 @@ class Sensor : public Nameable { /// Return the accuracy in decimals for this sensor. virtual int8_t accuracy_decimals(); // NOLINT + optional device_class_{}; ///< Stores the override of the device class + uint32_t hash_base() override; CallbackManager raw_callback_; ///< Storage for raw state callbacks. diff --git a/esphome/const.py b/esphome/const.py index 0fec577d19..0eeec36a54 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -663,3 +663,17 @@ UNIT_WATT_HOURS = 'Wh' DEVICE_CLASS_CONNECTIVITY = 'connectivity' DEVICE_CLASS_MOVING = 'moving' + +DEVICE_CLASS_EMPTY = '' +DEVICE_CLASS_BATTERY = 'battery' +DEVICE_CLASS_CURRENT = 'current' +DEVICE_CLASS_ENERGY = 'energy' +DEVICE_CLASS_HUMIDITY = 'humidity' +DEVICE_CLASS_ILLUMINANCE = 'illuminance' +DEVICE_CLASS_SIGNAL_STRENGTH = 'signal_strength' +DEVICE_CLASS_TEMPERATURE = 'temperature' +DEVICE_CLASS_POWER = 'power' +DEVICE_CLASS_POWER_FACTOR = 'power_factor' +DEVICE_CLASS_PRESSURE = 'pressure' +DEVICE_CLASS_TIMESTAMP = 'timestamp' +DEVICE_CLASS_VOLTAGE = 'voltage' diff --git a/tests/component_tests/sensor/test_sensor.py b/tests/component_tests/sensor/test_sensor.py new file mode 100644 index 0000000000..e82a024005 --- /dev/null +++ b/tests/component_tests/sensor/test_sensor.py @@ -0,0 +1,14 @@ +""" Tests for the sensor component """ + + +def test_sensor_device_class_set(generate_main): + """ + When the device_class of sensor is set in the yaml file, it should be registered in main + """ + # Given + + # When + main_cpp = generate_main("tests/component_tests/sensor/test_sensor.yaml") + + # Then + assert "s_1->set_device_class(\"voltage\");" in main_cpp diff --git a/tests/component_tests/sensor/test_sensor.yaml b/tests/component_tests/sensor/test_sensor.yaml new file mode 100644 index 0000000000..a38dd14041 --- /dev/null +++ b/tests/component_tests/sensor/test_sensor.yaml @@ -0,0 +1,12 @@ +esphome: + name: test + platform: ESP8266 + board: d1_mini_lite + +sensor: + - platform: adc + pin: A0 + id: s_1 + name: "test s1" + update_interval: 60s + device_class: "voltage" From dce20680d7fbf111921b2ce3bba9630422601c0d Mon Sep 17 00:00:00 2001 From: Chris Nussbaum Date: Mon, 15 Feb 2021 11:32:22 -0600 Subject: [PATCH 042/111] Add duration option to action start deep sleep (#1526) --- esphome/components/deep_sleep/__init__.py | 18 ++++++++++++++---- .../deep_sleep/deep_sleep_component.h | 8 +++++++- tests/test2.yaml | 8 ++++++++ 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/esphome/components/deep_sleep/__init__.py b/esphome/components/deep_sleep/__init__.py index 1b766c9928..9eafcc8609 100644 --- a/esphome/components/deep_sleep/__init__.py +++ b/esphome/components/deep_sleep/__init__.py @@ -84,18 +84,28 @@ def to_code(config): cg.add_define('USE_DEEP_SLEEP') -DEEP_SLEEP_ACTION_SCHEMA = automation.maybe_simple_id({ +DEEP_SLEEP_ENTER_SCHEMA = automation.maybe_simple_id({ + cv.GenerateID(): cv.use_id(DeepSleepComponent), + cv.Optional(CONF_SLEEP_DURATION): cv.positive_time_period_milliseconds, +}) + + +DEEP_SLEEP_PREVENT_SCHEMA = automation.maybe_simple_id({ cv.GenerateID(): cv.use_id(DeepSleepComponent), }) -@automation.register_action('deep_sleep.enter', EnterDeepSleepAction, DEEP_SLEEP_ACTION_SCHEMA) +@automation.register_action('deep_sleep.enter', EnterDeepSleepAction, DEEP_SLEEP_ENTER_SCHEMA) def deep_sleep_enter_to_code(config, action_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) - yield cg.new_Pvariable(action_id, template_arg, paren) + var = cg.new_Pvariable(action_id, template_arg, paren) + if CONF_SLEEP_DURATION in config: + template_ = yield cg.templatable(config[CONF_SLEEP_DURATION], args, cg.int32) + cg.add(var.set_sleep_duration(template_)) + yield var -@automation.register_action('deep_sleep.prevent', PreventDeepSleepAction, DEEP_SLEEP_ACTION_SCHEMA) +@automation.register_action('deep_sleep.prevent', PreventDeepSleepAction, DEEP_SLEEP_PREVENT_SCHEMA) def deep_sleep_prevent_to_code(config, action_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) yield cg.new_Pvariable(action_id, template_arg, paren) diff --git a/esphome/components/deep_sleep/deep_sleep_component.h b/esphome/components/deep_sleep/deep_sleep_component.h index 4372a3f66c..09212d7d17 100644 --- a/esphome/components/deep_sleep/deep_sleep_component.h +++ b/esphome/components/deep_sleep/deep_sleep_component.h @@ -84,8 +84,14 @@ extern bool global_has_deep_sleep; template class EnterDeepSleepAction : public Action { public: EnterDeepSleepAction(DeepSleepComponent *deep_sleep) : deep_sleep_(deep_sleep) {} + TEMPLATABLE_VALUE(uint32_t, sleep_duration); - void play(Ts... x) override { this->deep_sleep_->begin_sleep(true); } + void play(Ts... x) override { + if (this->sleep_duration_.has_value()) { + this->deep_sleep_->set_sleep_duration(this->sleep_duration_.value(x...)); + } + this->deep_sleep_->begin_sleep(true); + } protected: DeepSleepComponent *deep_sleep_; diff --git a/tests/test2.yaml b/tests/test2.yaml index b109aad758..333025358c 100644 --- a/tests/test2.yaml +++ b/tests/test2.yaml @@ -45,6 +45,12 @@ ota: logger: level: DEBUG +deep_sleep: + run_duration: 20s + sleep_duration: 50s + wakeup_pin: GPIO39 + wakeup_pin_mode: INVERT_WAKEUP + as3935_i2c: irq_pin: GPIO12 @@ -302,6 +308,8 @@ text_sensor: - homeassistant.tag_scanned: tag: 1234-abcd - homeassistant.tag_scanned: 1234-abcd + - deep_sleep.enter: + sleep_duration: 30min - platform: template name: 'Template Text Sensor' lambda: |- From 410fad3b41640b76c7f902fb4656d0b1c2598681 Mon Sep 17 00:00:00 2001 From: SenexCrenshaw <35600301+SenexCrenshaw@users.noreply.github.com> Date: Tue, 16 Feb 2021 02:42:14 -0500 Subject: [PATCH 043/111] fix DHT auto_detect check (#1536) --- esphome/components/dht/dht.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/esphome/components/dht/dht.cpp b/esphome/components/dht/dht.cpp index 988a5300dd..9626260cf2 100644 --- a/esphome/components/dht/dht.cpp +++ b/esphome/components/dht/dht.cpp @@ -32,19 +32,19 @@ void DHT::dump_config() { void DHT::update() { float temperature, humidity; - bool error; + bool success; if (this->model_ == DHT_MODEL_AUTO_DETECT) { this->model_ = DHT_MODEL_DHT22; - error = this->read_sensor_(&temperature, &humidity, false); - if (error) { + success = this->read_sensor_(&temperature, &humidity, false); + if (!success) { this->model_ = DHT_MODEL_DHT11; return; } } else { - error = this->read_sensor_(&temperature, &humidity, true); + success = this->read_sensor_(&temperature, &humidity, true); } - if (error) { + if (success) { ESP_LOGD(TAG, "Got Temperature=%.1f°C Humidity=%.1f%%", temperature, humidity); if (this->temperature_sensor_ != nullptr) From c92c439d17cb71789d7a342246459e7917f1d932 Mon Sep 17 00:00:00 2001 From: Kris Date: Wed, 17 Feb 2021 19:12:02 +0100 Subject: [PATCH 044/111] Added Waveshare 2.90inch V2 e-ink display (#1538) --- .../components/waveshare_epaper/display.py | 3 ++- .../waveshare_epaper/waveshare_epaper.cpp | 26 ++++++++++++++++--- .../waveshare_epaper/waveshare_epaper.h | 11 ++++++-- tests/test1.yaml | 9 +++++++ 4 files changed, 43 insertions(+), 6 deletions(-) diff --git a/esphome/components/waveshare_epaper/display.py b/esphome/components/waveshare_epaper/display.py index da2e30de00..fcbbc0a500 100644 --- a/esphome/components/waveshare_epaper/display.py +++ b/esphome/components/waveshare_epaper/display.py @@ -27,6 +27,7 @@ MODELS = { '2.13in-ttgo': ('a', WaveshareEPaperTypeAModel.TTGO_EPAPER_2_13_IN), '2.13in-ttgo-b73': ('a', WaveshareEPaperTypeAModel.TTGO_EPAPER_2_13_IN_B73), '2.90in': ('a', WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_2_9_IN), + '2.90inv2': ('a', WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_2_9_IN_V2), '2.70in': ('b', WaveshareEPaper2P7In), '2.90in-b': ('b', WaveshareEPaper2P9InB), '4.20in': ('b', WaveshareEPaper4P2In), @@ -41,7 +42,7 @@ def validate_full_update_every_only_type_a(value): return value if MODELS[value[CONF_MODEL]][0] != 'a': raise cv.Invalid("The 'full_update_every' option is only available for models " - "'1.54in', '2.13in' and '2.90in'.") + "'1.54in', '2.13in', '2.90in', and '2.90inV2'.") return value diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.cpp b/esphome/components/waveshare_epaper/waveshare_epaper.cpp index d57b814bb2..fa0cf6aa4f 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.cpp +++ b/esphome/components/waveshare_epaper/waveshare_epaper.cpp @@ -178,6 +178,13 @@ void WaveshareEPaperTypeA::initialize() { // COMMAND DATA ENTRY MODE SETTING this->command(0x11); this->data(0x03); // from top left to bottom right + + if (this->model_ == WAVESHARE_EPAPER_2_9_IN_V2) { + // RAM content option for Display Update + this->command(0x21); + this->data(0x00); + this->data(0x80); + } } void WaveshareEPaperTypeA::dump_config() { LOG_DISPLAY("", "Waveshare E-Paper", this); @@ -197,6 +204,9 @@ void WaveshareEPaperTypeA::dump_config() { case WAVESHARE_EPAPER_2_9_IN: ESP_LOGCONFIG(TAG, " Model: 2.9in"); break; + case WAVESHARE_EPAPER_2_9_IN_V2: + ESP_LOGCONFIG(TAG, " Model: 2.9inV2"); + break; } ESP_LOGCONFIG(TAG, " Full Update Every: %u", this->full_update_every_); LOG_PIN(" Reset Pin: ", this->reset_pin_); @@ -205,14 +215,15 @@ void WaveshareEPaperTypeA::dump_config() { LOG_UPDATE_INTERVAL(this); } void HOT WaveshareEPaperTypeA::display() { + bool full_update = this->at_update_ == 0; + bool prev_full_update = this->at_update_ == 1; + if (!this->wait_until_idle_()) { this->status_set_warning(); return; } if (this->full_update_every_ >= 2) { - bool prev_full_update = this->at_update_ == 1; - bool full_update = this->at_update_ == 0; if (full_update != prev_full_update) { if (this->model_ == TTGO_EPAPER_2_13_IN) { this->write_lut_(full_update ? FULL_UPDATE_LUT_TTGO : PARTIAL_UPDATE_LUT_TTGO, LUT_SIZE_TTGO); @@ -258,7 +269,12 @@ void HOT WaveshareEPaperTypeA::display() { // COMMAND DISPLAY UPDATE CONTROL 2 this->command(0x22); - this->data(0xC4); + if (this->model_ == WAVESHARE_EPAPER_2_9_IN_V2) { + this->data(full_update ? 0xF7 : 0xFF); + } else { + this->data(0xC4); + } + // COMMAND MASTER ACTIVATION this->command(0x20); // COMMAND TERMINATE FRAME READ WRITE @@ -278,6 +294,8 @@ int WaveshareEPaperTypeA::get_width_internal() { return 128; case WAVESHARE_EPAPER_2_9_IN: return 128; + case WAVESHARE_EPAPER_2_9_IN_V2: + return 128; } return 0; } @@ -293,6 +311,8 @@ int WaveshareEPaperTypeA::get_height_internal() { return 250; case WAVESHARE_EPAPER_2_9_IN: return 296; + case WAVESHARE_EPAPER_2_9_IN_V2: + return 296; } return 0; } diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.h b/esphome/components/waveshare_epaper/waveshare_epaper.h index 01b162fd35..8ea73d053a 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.h +++ b/esphome/components/waveshare_epaper/waveshare_epaper.h @@ -67,6 +67,7 @@ enum WaveshareEPaperTypeAModel { WAVESHARE_EPAPER_1_54_IN = 0, WAVESHARE_EPAPER_2_13_IN, WAVESHARE_EPAPER_2_9_IN, + WAVESHARE_EPAPER_2_9_IN_V2, TTGO_EPAPER_2_13_IN, TTGO_EPAPER_2_13_IN_B73, }; @@ -82,8 +83,14 @@ class WaveshareEPaperTypeA : public WaveshareEPaper { void display() override; void deep_sleep() override { - // COMMAND DEEP SLEEP MODE - this->command(0x10); + if (this->model_ == WAVESHARE_EPAPER_2_9_IN_V2) { + // COMMAND DEEP SLEEP MODE + this->command(0x10); + this->data(0x01); + } else { + // COMMAND DEEP SLEEP MODE + this->command(0x10); + } this->wait_until_idle_(); } diff --git a/tests/test1.yaml b/tests/test1.yaml index 102c3ac703..d6ed37c67c 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -1812,6 +1812,15 @@ display: full_update_every: 30 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); + - platform: waveshare_epaper + cs_pin: GPIO23 + dc_pin: GPIO23 + busy_pin: GPIO23 + reset_pin: GPIO23 + model: 2.90inv2 + full_update_every: 30 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); - platform: st7789v cs_pin: GPIO5 dc_pin: GPIO16 From acc1af0f51a518dfb4ceadb3c72cbbb7fbb86b88 Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Wed, 17 Feb 2021 11:26:22 -0800 Subject: [PATCH 045/111] Add reverse_enable for max7219 (#1489) --- esphome/components/max7219/display.py | 4 ++++ esphome/components/max7219/max7219.cpp | 8 +++++--- esphome/components/max7219/max7219.h | 2 ++ 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/esphome/components/max7219/display.py b/esphome/components/max7219/display.py index 0657f3f042..05b383004d 100644 --- a/esphome/components/max7219/display.py +++ b/esphome/components/max7219/display.py @@ -9,11 +9,14 @@ max7219_ns = cg.esphome_ns.namespace('max7219') MAX7219Component = max7219_ns.class_('MAX7219Component', cg.PollingComponent, spi.SPIDevice) MAX7219ComponentRef = MAX7219Component.operator('ref') +CONF_REVERSE_ENABLE = 'reverse_enable' + CONFIG_SCHEMA = display.BASIC_DISPLAY_SCHEMA.extend({ cv.GenerateID(): cv.declare_id(MAX7219Component), cv.Optional(CONF_NUM_CHIPS, default=1): cv.int_range(min=1, max=255), cv.Optional(CONF_INTENSITY, default=15): cv.int_range(min=0, max=15), + cv.Optional(CONF_REVERSE_ENABLE, default=False): cv.boolean, }).extend(cv.polling_component_schema('1s')).extend(spi.spi_device_schema()) @@ -25,6 +28,7 @@ def to_code(config): cg.add(var.set_num_chips(config[CONF_NUM_CHIPS])) cg.add(var.set_intensity(config[CONF_INTENSITY])) + cg.add(var.set_reverse(config[CONF_REVERSE_ENABLE])) if CONF_LAMBDA in config: lambda_ = yield cg.process_lambda(config[CONF_LAMBDA], [(MAX7219ComponentRef, 'it')], diff --git a/esphome/components/max7219/max7219.cpp b/esphome/components/max7219/max7219.cpp index 99eca6c14f..f58f203442 100644 --- a/esphome/components/max7219/max7219.cpp +++ b/esphome/components/max7219/max7219.cpp @@ -142,9 +142,11 @@ void MAX7219Component::dump_config() { void MAX7219Component::display() { for (uint8_t i = 0; i < 8; i++) { this->enable(); - for (uint8_t j = 0; j < this->num_chips_; j++) { - this->send_byte_(8 - i, this->buffer_[j * 8 + i]); - } + for (uint8_t j = 0; j < this->num_chips_; j++) + if (reverse_) + this->send_byte_(8 - i, buffer_[(num_chips_ - j - 1) * 8 + i]); + else + this->send_byte_(8 - i, buffer_[j * 8 + i]); this->disable(); } } diff --git a/esphome/components/max7219/max7219.h b/esphome/components/max7219/max7219.h index 1920268ba4..47b54a4c50 100644 --- a/esphome/components/max7219/max7219.h +++ b/esphome/components/max7219/max7219.h @@ -34,6 +34,7 @@ class MAX7219Component : public PollingComponent, void set_intensity(uint8_t intensity); void set_num_chips(uint8_t num_chips); + void set_reverse(bool reverse) { this->reverse_ = reverse; }; /// Evaluate the printf-format and print the result at the given position. uint8_t printf(uint8_t pos, const char *format, ...) __attribute__((format(printf, 3, 4))); @@ -60,6 +61,7 @@ class MAX7219Component : public PollingComponent, uint8_t intensity_{15}; /// Intensity of the display from 0 to 15 (most) uint8_t num_chips_{1}; uint8_t *buffer_; + bool reverse_{false}; optional writer_{}; }; From 6c8ace0ce89ad44a4553077f0b22d7d5fd8d578a Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 18 Feb 2021 12:26:59 +1300 Subject: [PATCH 046/111] Fix safe mode ota flashing under certain configurations (#1534) * Fix safe mode ota flashing under certain configurations by allowing the arduino loop to run instead of while(true) * rename to should_enter_safe_mode * Fix line length --- esphome/components/ota/__init__.py | 5 ++++- esphome/components/ota/ota_component.cpp | 7 +++---- esphome/components/ota/ota_component.h | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/esphome/components/ota/__init__.py b/esphome/components/ota/__init__.py index e4b6946116..8956227c17 100644 --- a/esphome/components/ota/__init__.py +++ b/esphome/components/ota/__init__.py @@ -1,3 +1,4 @@ +from esphome.cpp_generator import RawExpression import esphome.codegen as cg import esphome.config_validation as cv from esphome.const import ( @@ -31,7 +32,9 @@ def to_code(config): yield cg.register_component(var, config) if config[CONF_SAFE_MODE]: - cg.add(var.start_safe_mode(config[CONF_NUM_ATTEMPTS], config[CONF_REBOOT_TIMEOUT])) + condition = var.should_enter_safe_mode(config[CONF_NUM_ATTEMPTS], + config[CONF_REBOOT_TIMEOUT]) + cg.add(RawExpression(f"if ({condition}) return")) if CORE.is_esp8266: cg.add_library('Update', None) diff --git a/esphome/components/ota/ota_component.cpp b/esphome/components/ota/ota_component.cpp index b614139e07..c8221d1bdf 100644 --- a/esphome/components/ota/ota_component.cpp +++ b/esphome/components/ota/ota_component.cpp @@ -355,7 +355,7 @@ void OTAComponent::set_auth_password(const std::string &password) { this->passwo float OTAComponent::get_setup_priority() const { return setup_priority::AFTER_WIFI; } uint16_t OTAComponent::get_port() const { return this->port_; } void OTAComponent::set_port(uint16_t port) { this->port_ = port; } -void OTAComponent::start_safe_mode(uint8_t num_attempts, uint32_t enable_time) { +bool OTAComponent::should_enter_safe_mode(uint8_t num_attempts, uint32_t enable_time) { this->has_safe_mode_ = true; this->safe_mode_start_time_ = millis(); this->safe_mode_enable_time_ = enable_time; @@ -380,12 +380,11 @@ void OTAComponent::start_safe_mode(uint8_t num_attempts, uint32_t enable_time) { ESP_LOGI(TAG, "Waiting for OTA attempt."); - while (true) { - App.loop(); - } + return true; } else { // increment counter this->write_rtc_(this->safe_mode_rtc_value_ + 1); + return false; } } void OTAComponent::write_rtc_(uint32_t val) { this->rtc_.save(&val); } diff --git a/esphome/components/ota/ota_component.h b/esphome/components/ota/ota_component.h index 65d44482b8..f16725e324 100644 --- a/esphome/components/ota/ota_component.h +++ b/esphome/components/ota/ota_component.h @@ -47,7 +47,7 @@ class OTAComponent : public Component { /// Manually set the port OTA should listen on. void set_port(uint16_t port); - void start_safe_mode(uint8_t num_attempts, uint32_t enable_time); + bool should_enter_safe_mode(uint8_t num_attempts, uint32_t enable_time); // ========== INTERNAL METHODS ========== // (In most use cases you won't need these) From 25924ca4e83dc9645b1c072efd6c7d64923c50d6 Mon Sep 17 00:00:00 2001 From: Guillermo Ruffino Date: Fri, 19 Feb 2021 21:52:42 -0300 Subject: [PATCH 047/111] fix substitution losing track of document range (#1547) --- esphome/components/substitutions/__init__.py | 9 +++++ tests/test1.yaml | 39 +++++++++++--------- 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/esphome/components/substitutions/__init__.py b/esphome/components/substitutions/__init__.py index 9c5d444b2c..c4bc1b4371 100644 --- a/esphome/components/substitutions/__init__.py +++ b/esphome/components/substitutions/__init__.py @@ -4,6 +4,7 @@ import re import esphome.config_validation as cv from esphome import core from esphome.const import CONF_SUBSTITUTIONS +from esphome.yaml_util import ESPHomeDataBase, make_data_base CODEOWNERS = ['@esphome/core'] _LOGGER = logging.getLogger(__name__) @@ -68,6 +69,14 @@ def _expand_substitutions(substitutions, value, path): value = value[:i] + sub i = len(value) value += tail + + # orig_value can also already be a lambda with esp_range info, and only + # a plain string is sent in orig_value + if isinstance(orig_value, ESPHomeDataBase): + # even though string can get larger or smaller, the range should point + # to original document marks + return make_data_base(value, orig_value) + return value diff --git a/tests/test1.yaml b/tests/test1.yaml index d6ed37c67c..5da2ee03fe 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -1,5 +1,8 @@ substitutions: devicename: test1 + sensorname: my + textname: template + roomname: living_room esphome: name: test1 @@ -36,7 +39,7 @@ esphome: verify_ssl: false json: key: !lambda |- - return id(template_text).state; + return id(${textname}_text).state; greeting: 'Hello World' - http_request.send: method: PUT @@ -121,7 +124,7 @@ mqtt: int data = x["my_data"]; ESP_LOGD("main", "The data is: %d", data); - light.turn_on: - id: living_room_lights + id: ${roomname}_lights brightness: !lambda |- float brightness = 1.0; if (x.containsKey("brightness")) @@ -133,10 +136,10 @@ mqtt: effect = x["effect"]; return effect; - light.control: - id: living_room_lights - brightness: !lambda 'return id(living_room_lights).current_values.get_brightness() + 0.5;' + id: ${roomname}_lights + brightness: !lambda 'return id(${roomname}_lights).current_values.get_brightness() + 0.5;' - light.dim_relative: - id: living_room_lights + id: ${roomname}_lights relative_brightness: 5% - uart.write: id: uart0 @@ -278,9 +281,9 @@ sensor: then: - lambda: |- ESP_LOGD("main", "Got value %f", x); - id(my_sensor).publish_state(42.0); - ESP_LOGI("main", "Value of my sensor: %f", id(my_sensor).state); - ESP_LOGI("main", "Raw Value of my sensor: %f", id(my_sensor).state); + id(${sensorname}_sensor).publish_state(42.0); + ESP_LOGI("main", "Value of my sensor: %f", id(${sensorname}_sensor).state); + ESP_LOGI("main", "Raw Value of my sensor: %f", id(${sensorname}_sensor).state); on_value_range: above: 5 below: 10 @@ -307,7 +310,7 @@ sensor: - platform: ads1115 multiplexer: 'A0_A1' gain: 1.024 - id: my_sensor + id: ${sensorname}_sensor filters: state_topic: hi/me retain: false @@ -946,12 +949,12 @@ binary_sensor: name: 'Garage Door Open' id: garage_door lambda: |- - if (isnan(id(my_sensor).state)) { + if (isnan(id(${sensorname}_sensor).state)) { // isnan checks if the ultrasonic sensor echo // has timed out, resulting in a NaN (not a number) state // in that case, return {} to indicate that we don't know. return {}; - } else if (id(my_sensor).state > 30) { + } else if (id(${sensorname}_sensor).state > 30) { // Garage Door is open. return true; } else { @@ -1244,7 +1247,7 @@ light: state = 0; - platform: rgb name: 'Living Room Lights' - id: living_room_lights + id: ${roomname}_lights red: pca_0 green: pca_1 blue: pca_2 @@ -1400,14 +1403,14 @@ climate: name: TCL112 Climate With Sensor supports_heat: True supports_cool: True - sensor: my_sensor + sensor: ${sensorname}_sensor - platform: tcl112 name: TCL112 Climate - platform: coolix name: Coolix Climate With Sensor supports_heat: True supports_cool: True - sensor: my_sensor + sensor: ${sensorname}_sensor - platform: coolix name: Coolix Climate - platform: fujitsu_general @@ -1976,10 +1979,10 @@ text_sensor: qos: 2 on_value: - text_sensor.template.publish: - id: template_text + id: ${textname}_text state: Hello World - text_sensor.template.publish: - id: template_text + id: ${textname}_text state: |- return "Hello World2"; - globals.set: @@ -1990,7 +1993,7 @@ text_sensor: data: [0x10, 0x20, 0x30] - platform: template name: Template Text Sensor - id: template_text + id: ${textname}_text - platform: wifi_info ip_address: name: 'IP Address' @@ -2032,4 +2035,4 @@ canbus: condition: lambda: 'return x[0] == 0x11;' then: - light.toggle: living_room_lights + light.toggle: ${roomname}_lights From f81cddf22efa9afd3862ed192a3049d0d65dae09 Mon Sep 17 00:00:00 2001 From: dckiller51 <53062806+dckiller51@users.noreply.github.com> Date: Mon, 22 Feb 2021 10:23:12 +0100 Subject: [PATCH 048/111] Add Xiaomi Miscale v1 and v2 (#1368) --- esphome/components/xiaomi_miscale/__init__.py | 0 esphome/components/xiaomi_miscale/sensor.py | 30 +++++ .../xiaomi_miscale/xiaomi_miscale.cpp | 101 +++++++++++++++ .../xiaomi_miscale/xiaomi_miscale.h | 37 ++++++ .../components/xiaomi_miscale2/__init__.py | 0 esphome/components/xiaomi_miscale2/sensor.py | 34 +++++ .../xiaomi_miscale2/xiaomi_miscale2.cpp | 116 ++++++++++++++++++ .../xiaomi_miscale2/xiaomi_miscale2.h | 40 ++++++ esphome/const.py | 5 + 9 files changed, 363 insertions(+) create mode 100644 esphome/components/xiaomi_miscale/__init__.py create mode 100644 esphome/components/xiaomi_miscale/sensor.py create mode 100644 esphome/components/xiaomi_miscale/xiaomi_miscale.cpp create mode 100644 esphome/components/xiaomi_miscale/xiaomi_miscale.h create mode 100644 esphome/components/xiaomi_miscale2/__init__.py create mode 100644 esphome/components/xiaomi_miscale2/sensor.py create mode 100644 esphome/components/xiaomi_miscale2/xiaomi_miscale2.cpp create mode 100644 esphome/components/xiaomi_miscale2/xiaomi_miscale2.h diff --git a/esphome/components/xiaomi_miscale/__init__.py b/esphome/components/xiaomi_miscale/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/xiaomi_miscale/sensor.py b/esphome/components/xiaomi_miscale/sensor.py new file mode 100644 index 0000000000..a5e91a9178 --- /dev/null +++ b/esphome/components/xiaomi_miscale/sensor.py @@ -0,0 +1,30 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import sensor, esp32_ble_tracker +from esphome.const import CONF_MAC_ADDRESS, CONF_ID, CONF_WEIGHT, UNIT_KILOGRAM, \ + ICON_SCALE_BATHROOM + +DEPENDENCIES = ['esp32_ble_tracker'] + +xiaomi_miscale_ns = cg.esphome_ns.namespace('xiaomi_miscale') +XiaomiMiscale = xiaomi_miscale_ns.class_('XiaomiMiscale', + esp32_ble_tracker.ESPBTDeviceListener, + cg.Component) + +CONFIG_SCHEMA = cv.Schema({ + cv.GenerateID(): cv.declare_id(XiaomiMiscale), + cv.Required(CONF_MAC_ADDRESS): cv.mac_address, + cv.Optional(CONF_WEIGHT): sensor.sensor_schema(UNIT_KILOGRAM, ICON_SCALE_BATHROOM, 2), +}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) + + +def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + yield cg.register_component(var, config) + yield esp32_ble_tracker.register_ble_device(var, config) + + cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex)) + + if CONF_WEIGHT in config: + sens = yield sensor.new_sensor(config[CONF_WEIGHT]) + cg.add(var.set_weight(sens)) diff --git a/esphome/components/xiaomi_miscale/xiaomi_miscale.cpp b/esphome/components/xiaomi_miscale/xiaomi_miscale.cpp new file mode 100644 index 0000000000..441bca6270 --- /dev/null +++ b/esphome/components/xiaomi_miscale/xiaomi_miscale.cpp @@ -0,0 +1,101 @@ +#include "xiaomi_miscale.h" +#include "esphome/core/log.h" + +#ifdef ARDUINO_ARCH_ESP32 + +namespace esphome { +namespace xiaomi_miscale { + +static const char *TAG = "xiaomi_miscale"; + +void XiaomiMiscale::dump_config() { + ESP_LOGCONFIG(TAG, "Xiaomi Miscale"); + LOG_SENSOR(" ", "Weight", this->weight_); +} + +bool XiaomiMiscale::parse_device(const esp32_ble_tracker::ESPBTDevice &device) { + if (device.address_uint64() != this->address_) { + ESP_LOGVV(TAG, "parse_device(): unknown MAC address."); + return false; + } + ESP_LOGVV(TAG, "parse_device(): MAC address %s found.", device.address_str().c_str()); + + bool success = false; + for (auto &service_data : device.get_service_datas()) { + auto res = parse_header(service_data); + if (!res.has_value()) { + continue; + } + if (!(parse_message(service_data.data, *res))) { + continue; + } + if (!(report_results(res, device.address_str()))) { + continue; + } + if (res->weight.has_value() && this->weight_ != nullptr) + this->weight_->publish_state(*res->weight); + success = true; + } + + return success; +} + +optional XiaomiMiscale::parse_header(const esp32_ble_tracker::ServiceData &service_data) { + ParseResult result; + if (!service_data.uuid.contains(0x1D, 0x18)) { + ESP_LOGVV(TAG, "parse_header(): no service data UUID magic bytes."); + return {}; + } + + return result; +} + +bool XiaomiMiscale::parse_message(const std::vector &message, ParseResult &result) { + // exemple 1d18 a2 6036 e307 07 11 0f1f11 + // 1-2 Weight (MISCALE 181D) + // 3-4 Years (MISCALE 181D) + // 5 month (MISCALE 181D) + // 6 day (MISCALE 181D) + // 7 hour (MISCALE 181D) + // 8 minute (MISCALE 181D) + // 9 second (MISCALE 181D) + + const uint8_t *data = message.data(); + const int data_length = 10; + + if (message.size() != data_length) { + ESP_LOGVV(TAG, "parse_message(): payload has wrong size (%d)!", message.size()); + return false; + } + + // weight, 2 bytes, 16-bit unsigned integer, 1 kg + const int16_t weight = uint16_t(data[1]) | (uint16_t(data[2]) << 8); + if (data[0] == 0x22 || data[0] == 0xa2) + result.weight = weight * 0.01f / 2.0f; // unit 'kg' + else if (data[0] == 0x12 || data[0] == 0xb2) + result.weight = weight * 0.01f * 0.6; // unit 'jin' + else if (data[0] == 0x03 || data[0] == 0xb3) + result.weight = weight * 0.01f * 0.453592; // unit 'lbs' + + return true; +} + +bool XiaomiMiscale::report_results(const optional &result, const std::string &address) { + if (!result.has_value()) { + ESP_LOGVV(TAG, "report_results(): no results available."); + return false; + } + + ESP_LOGD(TAG, "Got Xiaomi Miscale (%s):", address.c_str()); + + if (result->weight.has_value()) { + ESP_LOGD(TAG, " Weight: %.2fkg", *result->weight); + } + + return true; +} + +} // namespace xiaomi_miscale +} // namespace esphome + +#endif diff --git a/esphome/components/xiaomi_miscale/xiaomi_miscale.h b/esphome/components/xiaomi_miscale/xiaomi_miscale.h new file mode 100644 index 0000000000..d9da4f9421 --- /dev/null +++ b/esphome/components/xiaomi_miscale/xiaomi_miscale.h @@ -0,0 +1,37 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" + +#ifdef ARDUINO_ARCH_ESP32 + +namespace esphome { +namespace xiaomi_miscale { + +struct ParseResult { + optional weight; +}; + +class XiaomiMiscale : public Component, public esp32_ble_tracker::ESPBTDeviceListener { + public: + void set_address(uint64_t address) { address_ = address; }; + + bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override; + void dump_config() override; + float get_setup_priority() const override { return setup_priority::DATA; } + void set_weight(sensor::Sensor *weight) { weight_ = weight; } + + protected: + uint64_t address_; + sensor::Sensor *weight_{nullptr}; + + optional parse_header(const esp32_ble_tracker::ServiceData &service_data); + bool parse_message(const std::vector &message, ParseResult &result); + bool report_results(const optional &result, const std::string &address); +}; + +} // namespace xiaomi_miscale +} // namespace esphome + +#endif diff --git a/esphome/components/xiaomi_miscale2/__init__.py b/esphome/components/xiaomi_miscale2/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/xiaomi_miscale2/sensor.py b/esphome/components/xiaomi_miscale2/sensor.py new file mode 100644 index 0000000000..9f32385d54 --- /dev/null +++ b/esphome/components/xiaomi_miscale2/sensor.py @@ -0,0 +1,34 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import sensor, esp32_ble_tracker +from esphome.const import CONF_MAC_ADDRESS, CONF_ID, CONF_WEIGHT, UNIT_KILOGRAM, \ + ICON_SCALE_BATHROOM, UNIT_OHM, CONF_IMPEDANCE, ICON_OMEGA + +DEPENDENCIES = ['esp32_ble_tracker'] + +xiaomi_miscale2_ns = cg.esphome_ns.namespace('xiaomi_miscale2') +XiaomiMiscale2 = xiaomi_miscale2_ns.class_('XiaomiMiscale2', + esp32_ble_tracker.ESPBTDeviceListener, + cg.Component) + +CONFIG_SCHEMA = cv.Schema({ + cv.GenerateID(): cv.declare_id(XiaomiMiscale2), + cv.Required(CONF_MAC_ADDRESS): cv.mac_address, + cv.Optional(CONF_WEIGHT): sensor.sensor_schema(UNIT_KILOGRAM, ICON_SCALE_BATHROOM, 2), + cv.Optional(CONF_IMPEDANCE): sensor.sensor_schema(UNIT_OHM, ICON_OMEGA, 0), +}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) + + +def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + yield cg.register_component(var, config) + yield esp32_ble_tracker.register_ble_device(var, config) + + cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex)) + + if CONF_WEIGHT in config: + sens = yield sensor.new_sensor(config[CONF_WEIGHT]) + cg.add(var.set_weight(sens)) + if CONF_IMPEDANCE in config: + sens = yield sensor.new_sensor(config[CONF_IMPEDANCE]) + cg.add(var.set_impedance(sens)) diff --git a/esphome/components/xiaomi_miscale2/xiaomi_miscale2.cpp b/esphome/components/xiaomi_miscale2/xiaomi_miscale2.cpp new file mode 100644 index 0000000000..2bc4656cb9 --- /dev/null +++ b/esphome/components/xiaomi_miscale2/xiaomi_miscale2.cpp @@ -0,0 +1,116 @@ +#include "xiaomi_miscale2.h" +#include "esphome/core/log.h" + +#ifdef ARDUINO_ARCH_ESP32 + +namespace esphome { +namespace xiaomi_miscale2 { + +static const char *TAG = "xiaomi_miscale2"; + +void XiaomiMiscale2::dump_config() { + ESP_LOGCONFIG(TAG, "Xiaomi Miscale2"); + LOG_SENSOR(" ", "Weight", this->weight_); + LOG_SENSOR(" ", "Impedance", this->impedance_); +} + +bool XiaomiMiscale2::parse_device(const esp32_ble_tracker::ESPBTDevice &device) { + if (device.address_uint64() != this->address_) { + ESP_LOGVV(TAG, "parse_device(): unknown MAC address."); + return false; + } + ESP_LOGVV(TAG, "parse_device(): MAC address %s found.", device.address_str().c_str()); + + bool success = false; + for (auto &service_data : device.get_service_datas()) { + auto res = parse_header(service_data); + if (!res.has_value()) { + continue; + } + if (!(parse_message(service_data.data, *res))) { + continue; + } + if (!(report_results(res, device.address_str()))) { + continue; + } + if (res->weight.has_value() && this->weight_ != nullptr) + this->weight_->publish_state(*res->weight); + if (res->impedance.has_value() && this->impedance_ != nullptr) + this->impedance_->publish_state(*res->impedance); + success = true; + } + + return success; +} + +optional XiaomiMiscale2::parse_header(const esp32_ble_tracker::ServiceData &service_data) { + ParseResult result; + if (!service_data.uuid.contains(0x1B, 0x18)) { + ESP_LOGVV(TAG, "parse_header(): no service data UUID magic bytes."); + return {}; + } + + return result; +} + +bool XiaomiMiscale2::parse_message(const std::vector &message, ParseResult &result) { + // 2-3 Years (MISCALE 2 181B) + // 4 month (MISCALE 2 181B) + // 5 day (MISCALE 2 181B) + // 6 hour (MISCALE 2 181B) + // 7 minute (MISCALE 2 181B) + // 8 second (MISCALE 2 181B) + // 9-10 impedance (MISCALE 2 181B) + // 11-12 weight (MISCALE 2 181B) + + const uint8_t *data = message.data(); + const int data_length = 13; + + if (message.size() != data_length) { + ESP_LOGVV(TAG, "parse_message(): payload has wrong size (%d)!", message.size()); + return false; + } + + bool is_Stabilized = ((data[1] & (1 << 5)) != 0) ? true : false; + bool loadRemoved = ((data[1] & (1 << 7)) != 0) ? true : false; + + // weight, 2 bytes, 16-bit unsigned integer, 1 kg + const int16_t weight = uint16_t(data[11]) | (uint16_t(data[12]) << 8); + if (data[0] == 0x02) + result.weight = weight * 0.01f / 2.0f; // unit 'kg' + else if (data[0] == 0x03) + result.weight = weight * 0.01f * 0.453592; // unit 'lbs' + + // impedance, 2 bytes, 16-bit + const int16_t impedance = uint16_t(data[9]) | (uint16_t(data[10]) << 8); + result.impedance = impedance; + + if (!is_Stabilized || loadRemoved || impedance == 0 || impedance >= 3000) { + return false; + } + + return true; +} + +bool XiaomiMiscale2::report_results(const optional &result, const std::string &address) { + if (!result.has_value()) { + ESP_LOGVV(TAG, "report_results(): no results available."); + return false; + } + + ESP_LOGD(TAG, "Got Xiaomi Miscale2 (%s):", address.c_str()); + + if (result->weight.has_value()) { + ESP_LOGD(TAG, " Weight: %.2fkg", *result->weight); + } + if (result->impedance.has_value()) { + ESP_LOGD(TAG, " Impedance: %.0fohm", *result->impedance); + } + + return true; +} + +} // namespace xiaomi_miscale2 +} // namespace esphome + +#endif diff --git a/esphome/components/xiaomi_miscale2/xiaomi_miscale2.h b/esphome/components/xiaomi_miscale2/xiaomi_miscale2.h new file mode 100644 index 0000000000..ead522e1f2 --- /dev/null +++ b/esphome/components/xiaomi_miscale2/xiaomi_miscale2.h @@ -0,0 +1,40 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" + +#ifdef ARDUINO_ARCH_ESP32 + +namespace esphome { +namespace xiaomi_miscale2 { + +struct ParseResult { + optional weight; + optional impedance; +}; + +class XiaomiMiscale2 : public Component, public esp32_ble_tracker::ESPBTDeviceListener { + public: + void set_address(uint64_t address) { address_ = address; }; + + bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override; + void dump_config() override; + float get_setup_priority() const override { return setup_priority::DATA; } + void set_weight(sensor::Sensor *weight) { weight_ = weight; } + void set_impedance(sensor::Sensor *impedance) { impedance_ = impedance; } + + protected: + uint64_t address_; + sensor::Sensor *weight_{nullptr}; + sensor::Sensor *impedance_{nullptr}; + + optional parse_header(const esp32_ble_tracker::ServiceData &service_data); + bool parse_message(const std::vector &message, ParseResult &result); + bool report_results(const optional &result, const std::string &address); +}; + +} // namespace xiaomi_miscale2 +} // namespace esphome + +#endif diff --git a/esphome/const.py b/esphome/const.py index 0eeec36a54..345cac89dd 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -244,6 +244,7 @@ CONF_IDLE_TIME = 'idle_time' CONF_IF = 'if' CONF_IIR_FILTER = 'iir_filter' CONF_ILLUMINANCE = 'illuminance' +CONF_IMPEDANCE = 'impedance' CONF_INCLUDES = 'includes' CONF_INDEX = 'index' CONF_INDOOR = 'indoor' @@ -565,6 +566,7 @@ CONF_WAKEUP_PIN = 'wakeup_pin' CONF_WARM_WHITE = 'warm_white' CONF_WARM_WHITE_COLOR_TEMPERATURE = 'warm_white_color_temperature' CONF_WATCHDOG_THRESHOLD = 'watchdog_threshold' +CONF_WEIGHT = 'weight' CONF_WHILE = 'while' CONF_WHITE = 'white' CONF_WIDTH = 'width' @@ -602,6 +604,7 @@ ICON_MAGNET = 'mdi:magnet' ICON_MOLECULE_CO2 = 'mdi:molecule-co2' ICON_MOTION_SENSOR = 'mdi:motion-sensor' ICON_NEW_BOX = 'mdi:new-box' +ICON_OMEGA = 'mdi:omega' ICON_PERCENT = 'mdi:percent' ICON_POWER = 'mdi:power' ICON_PULSE = 'mdi:pulse' @@ -610,6 +613,7 @@ ICON_RESTART = 'mdi:restart' ICON_ROTATE_RIGHT = 'mdi:rotate-right' ICON_RULER = 'mdi:ruler' ICON_SCALE = 'mdi:scale' +ICON_SCALE_BATHROOM = 'mdi:scale-bathroom' ICON_SCREEN_ROTATION = 'mdi:screen-rotation' ICON_SIGN_DIRECTION = 'mdi:sign-direction' ICON_SIGNAL = 'mdi:signal-distance-variant' @@ -636,6 +640,7 @@ UNIT_G = 'G' UNIT_HECTOPASCAL = 'hPa' UNIT_HERTZ = 'Hz' UNIT_KELVIN = 'K' +UNIT_KILOGRAM = 'kg' UNIT_KILOMETER = 'km' UNIT_KILOMETER_PER_HOUR = 'km/h' UNIT_LUX = 'lx' From 3d0310d0e08f34e04856a4d374be6b81b7a43bc9 Mon Sep 17 00:00:00 2001 From: spilin Date: Thu, 25 Feb 2021 23:11:15 +0200 Subject: [PATCH 049/111] Add dial support for sim800l component (#1558) --- esphome/components/sim800l/__init__.py | 16 ++++++++++++++++ esphome/components/sim800l/sim800l.cpp | 26 ++++++++++++++++++++++++++ esphome/components/sim800l/sim800l.h | 20 +++++++++++++++++++- tests/test3.yaml | 2 ++ 4 files changed, 63 insertions(+), 1 deletion(-) diff --git a/esphome/components/sim800l/__init__.py b/esphome/components/sim800l/__init__.py index 762e045598..0c4215d8e4 100644 --- a/esphome/components/sim800l/__init__.py +++ b/esphome/components/sim800l/__init__.py @@ -17,6 +17,7 @@ Sim800LReceivedMessageTrigger = sim800l_ns.class_('Sim800LReceivedMessageTrigger # Actions Sim800LSendSmsAction = sim800l_ns.class_('Sim800LSendSmsAction', automation.Action) +Sim800LDialAction = sim800l_ns.class_('Sim800LDialAction', automation.Action) CONF_ON_SMS_RECEIVED = 'on_sms_received' CONF_RECIPIENT = 'recipient' @@ -57,3 +58,18 @@ def sim800l_send_sms_to_code(config, action_id, template_arg, args): template_ = yield cg.templatable(config[CONF_MESSAGE], args, cg.std_string) cg.add(var.set_message(template_)) yield var + + +SIM800L_DIAL_SCHEMA = cv.Schema({ + cv.GenerateID(): cv.use_id(Sim800LComponent), + cv.Required(CONF_RECIPIENT): cv.templatable(cv.string_strict), +}) + + +@automation.register_action('sim800l.dial', Sim800LDialAction, SIM800L_DIAL_SCHEMA) +def sim800l_dial_to_code(config, action_id, template_arg, args): + paren = yield cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, paren) + template_ = yield cg.templatable(config[CONF_RECIPIENT], args, cg.std_string) + cg.add(var.set_recipient(template_)) + yield var diff --git a/esphome/components/sim800l/sim800l.cpp b/esphome/components/sim800l/sim800l.cpp index 9f8c733fa9..ac4c8fc2d2 100644 --- a/esphome/components/sim800l/sim800l.cpp +++ b/esphome/components/sim800l/sim800l.cpp @@ -20,6 +20,9 @@ void Sim800LComponent::update() { if (this->registered_ && this->send_pending_) { this->send_cmd_("AT+CSCS=\"GSM\""); this->state_ = STATE_SENDINGSMS1; + } else if (this->registered_ && this->dial_pending_) { + this->send_cmd_("AT+CSCS=\"GSM\""); + this->state_ = STATE_DIALING1; } else { this->send_cmd_("AT"); this->state_ = STATE_CHECK_AT; @@ -212,6 +215,23 @@ void Sim800LComponent::parse_cmd_(std::string message) { this->expect_ack_ = true; } break; + case STATE_DIALING1: + this->send_cmd_("ATD" + this->recipient_ + ';'); + this->state_ = STATE_DIALING2; + break; + case STATE_DIALING2: + if (message == "OK") { + // Dialing + ESP_LOGD(TAG, "Dialing: '%s'", this->recipient_.c_str()); + this->state_ = STATE_INIT; + this->dial_pending_ = false; + } else { + this->registered_ = false; + this->state_ = STATE_INIT; + this->send_cmd_("AT+CMEE=2"); + this->write(26); + } + break; default: ESP_LOGD(TAG, "Unhandled: %s - %d", message.c_str(), this->state_); break; @@ -259,6 +279,12 @@ void Sim800LComponent::dump_config() { ESP_LOGCONFIG(TAG, "SIM800L:"); ESP_LOGCONFIG(TAG, " RSSI: %d dB", this->rssi_); } +void Sim800LComponent::dial(std::string recipient) { + ESP_LOGD(TAG, "Dialing %s", recipient.c_str()); + this->recipient_ = recipient; + this->dial_pending_ = true; + this->update(); +} } // namespace sim800l } // namespace esphome diff --git a/esphome/components/sim800l/sim800l.h b/esphome/components/sim800l/sim800l.h index 696eb8890f..f8ccf88977 100644 --- a/esphome/components/sim800l/sim800l.h +++ b/esphome/components/sim800l/sim800l.h @@ -29,7 +29,9 @@ enum State { STATE_RECEIVEDSMS, STATE_DELETEDSMS, STATE_DISABLE_ECHO, - STATE_PARSE_SMS_OK + STATE_PARSE_SMS_OK, + STATE_DIALING1, + STATE_DIALING2 }; class Sim800LComponent : public uart::UARTDevice, public PollingComponent { @@ -42,6 +44,7 @@ class Sim800LComponent : public uart::UARTDevice, public PollingComponent { this->callback_.add(std::move(callback)); } void send_sms(std::string recipient, std::string message); + void dial(std::string recipient); protected: void send_cmd_(std::string); @@ -60,6 +63,7 @@ class Sim800LComponent : public uart::UARTDevice, public PollingComponent { std::string recipient_; std::string outgoing_message_; bool send_pending_; + bool dial_pending_; CallbackManager callback_; }; @@ -88,5 +92,19 @@ template class Sim800LSendSmsAction : public Action { Sim800LComponent *parent_; }; +template class Sim800LDialAction : public Action { + public: + Sim800LDialAction(Sim800LComponent *parent) : parent_(parent) {} + TEMPLATABLE_VALUE(std::string, recipient) + + void play(Ts... x) { + auto recipient = this->recipient_.value(x...); + this->parent_->dial(recipient); + } + + protected: + Sim800LComponent *parent_; +}; + } // namespace sim800l } // namespace esphome diff --git a/tests/test3.yaml b/tests/test3.yaml index b38a7414b8..46b2d6b3a0 100644 --- a/tests/test3.yaml +++ b/tests/test3.yaml @@ -842,6 +842,8 @@ sim800l: - sim800l.send_sms: message: 'hello you' recipient: '+1234' + - sim800l.dial: + recipient: '+1234' dfplayer: on_finished_playback: From 92b36720b69d76df290aa3d8f4a25829ae9e50d3 Mon Sep 17 00:00:00 2001 From: Otamay Date: Thu, 25 Feb 2021 15:26:19 -0600 Subject: [PATCH 050/111] Climate IR LG -keep previous temp and fan if swing (#1556) Swing IR command does not carry CLIMATE_FAN or TEMP within itself, so previous states sould be kept. Tested with actual LG IR remote controller. --- .../climate_ir_lg/climate_ir_lg.cpp | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/esphome/components/climate_ir_lg/climate_ir_lg.cpp b/esphome/components/climate_ir_lg/climate_ir_lg.cpp index da4aaba9da..c80f82ae39 100644 --- a/esphome/components/climate_ir_lg/climate_ir_lg.cpp +++ b/esphome/components/climate_ir_lg/climate_ir_lg.cpp @@ -139,24 +139,24 @@ bool LgIrClimate::on_receive(remote_base::RemoteReceiveData data) { } else { this->mode = climate::CLIMATE_MODE_COOL; } - } - // Temperature - if (this->mode == climate::CLIMATE_MODE_COOL) - this->target_temperature = ((remote_state & TEMP_MASK) >> TEMP_SHIFT) + 15; + // Temperature + if (this->mode == climate::CLIMATE_MODE_COOL) + this->target_temperature = ((remote_state & TEMP_MASK) >> TEMP_SHIFT) + 15; - // Fan Speed - if (this->mode == climate::CLIMATE_MODE_AUTO) { - this->fan_mode = climate::CLIMATE_FAN_AUTO; - } else if (this->mode == climate::CLIMATE_MODE_COOL || this->mode == climate::CLIMATE_MODE_DRY) { - if ((remote_state & FAN_MASK) == FAN_AUTO) + // Fan Speed + if (this->mode == climate::CLIMATE_MODE_AUTO) { this->fan_mode = climate::CLIMATE_FAN_AUTO; - else if ((remote_state & FAN_MASK) == FAN_MIN) - this->fan_mode = climate::CLIMATE_FAN_LOW; - else if ((remote_state & FAN_MASK) == FAN_MED) - this->fan_mode = climate::CLIMATE_FAN_MEDIUM; - else if ((remote_state & FAN_MASK) == FAN_MAX) - this->fan_mode = climate::CLIMATE_FAN_HIGH; + } else if (this->mode == climate::CLIMATE_MODE_COOL || this->mode == climate::CLIMATE_MODE_DRY) { + if ((remote_state & FAN_MASK) == FAN_AUTO) + this->fan_mode = climate::CLIMATE_FAN_AUTO; + else if ((remote_state & FAN_MASK) == FAN_MIN) + this->fan_mode = climate::CLIMATE_FAN_LOW; + else if ((remote_state & FAN_MASK) == FAN_MED) + this->fan_mode = climate::CLIMATE_FAN_MEDIUM; + else if ((remote_state & FAN_MASK) == FAN_MAX) + this->fan_mode = climate::CLIMATE_FAN_HIGH; + } } this->publish_state(); From b91723344ea88b69ff720d2a5a3b0a10e088f073 Mon Sep 17 00:00:00 2001 From: Kurt Kellner Date: Thu, 25 Feb 2021 15:12:06 -0700 Subject: [PATCH 051/111] Vl53l0x change address (#1126) * Added vl53l0x change address and timeout * Added vl53l0x change address and timeout * vl53l0x code cleanup and update test * remove executable bit * lint code cleanup * code review fixes including timeout default to 10ms * Code review cleanup and change a WARN log level message to DEBUG * Fix issue where warn should be temporary * Added name of sensor to warning message * Fix blacklist lint issue * Remove unused import --- esphome/components/vl53l0x/sensor.py | 41 +++++++++--- esphome/components/vl53l0x/vl53l0x_sensor.cpp | 63 +++++++++++++++++-- esphome/components/vl53l0x/vl53l0x_sensor.h | 13 ++++ tests/test3.yaml | 2 + 4 files changed, 107 insertions(+), 12 deletions(-) diff --git a/esphome/components/vl53l0x/sensor.py b/esphome/components/vl53l0x/sensor.py index 209016fe40..62208eef53 100644 --- a/esphome/components/vl53l0x/sensor.py +++ b/esphome/components/vl53l0x/sensor.py @@ -1,7 +1,9 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import CONF_ID, UNIT_METER, ICON_ARROW_EXPAND_VERTICAL +from esphome.const import (CONF_ID, UNIT_METER, ICON_ARROW_EXPAND_VERTICAL, CONF_ADDRESS, + CONF_TIMEOUT, CONF_ENABLE_PIN) +from esphome import pins DEPENDENCIES = ['i2c'] @@ -12,12 +14,31 @@ VL53L0XSensor = vl53l0x_ns.class_('VL53L0XSensor', sensor.Sensor, cg.PollingComp CONF_SIGNAL_RATE_LIMIT = 'signal_rate_limit' CONF_LONG_RANGE = 'long_range' -CONFIG_SCHEMA = sensor.sensor_schema(UNIT_METER, ICON_ARROW_EXPAND_VERTICAL, 2).extend({ - cv.GenerateID(): cv.declare_id(VL53L0XSensor), - cv.Optional(CONF_SIGNAL_RATE_LIMIT, default=0.25): cv.float_range( - min=0.0, max=512.0, min_included=False, max_included=False), - cv.Optional(CONF_LONG_RANGE, default=False): cv.boolean, -}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x29)) + +def check_keys(obj): + if obj[CONF_ADDRESS] != 0x29 and CONF_ENABLE_PIN not in obj: + msg = "Address other then 0x29 requires enable_pin definition to allow sensor\r" + msg += "re-addressing. Also if you have more then one VL53 device on the same\r" + msg += "i2c bus, then all VL53 devices must have enable_pin defined." + raise cv.Invalid(msg) + return obj + + +def check_timeout(value): + value = cv.positive_time_period_microseconds(value) + if value.total_seconds > 60: + raise cv.Invalid("Maximum timeout can not be greater then 60 seconds") + return value + + +CONFIG_SCHEMA = cv.All(sensor.sensor_schema(UNIT_METER, ICON_ARROW_EXPAND_VERTICAL, 2).extend({ + cv.GenerateID(): cv.declare_id(VL53L0XSensor), + cv.Optional(CONF_SIGNAL_RATE_LIMIT, default=0.25): cv.float_range( + min=0.0, max=512.0, min_included=False, max_included=False), + cv.Optional(CONF_LONG_RANGE, default=False): cv.boolean, + cv.Optional(CONF_TIMEOUT, default='10ms'): check_timeout, + cv.Optional(CONF_ENABLE_PIN): pins.gpio_output_pin_schema, + }).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x29)), check_keys) def to_code(config): @@ -25,5 +46,11 @@ def to_code(config): yield cg.register_component(var, config) cg.add(var.set_signal_rate_limit(config[CONF_SIGNAL_RATE_LIMIT])) cg.add(var.set_long_range(config[CONF_LONG_RANGE])) + cg.add(var.set_timeout_us(config[CONF_TIMEOUT])) + + if CONF_ENABLE_PIN in config: + enable = yield cg.gpio_pin_expression(config[CONF_ENABLE_PIN]) + cg.add(var.set_enable_pin(enable)) + yield sensor.register_sensor(var, config) yield i2c.register_i2c_device(var, config) diff --git a/esphome/components/vl53l0x/vl53l0x_sensor.cpp b/esphome/components/vl53l0x/vl53l0x_sensor.cpp index 8ce822352f..1926f7d113 100644 --- a/esphome/components/vl53l0x/vl53l0x_sensor.cpp +++ b/esphome/components/vl53l0x/vl53l0x_sensor.cpp @@ -14,20 +14,55 @@ namespace esphome { namespace vl53l0x { static const char *TAG = "vl53l0x"; +std::list VL53L0XSensor::vl53_sensors; +bool VL53L0XSensor::enable_pin_setup_complete = false; + +VL53L0XSensor::VL53L0XSensor() { VL53L0XSensor::vl53_sensors.push_back(this); } void VL53L0XSensor::dump_config() { LOG_SENSOR("", "VL53L0X", this); LOG_UPDATE_INTERVAL(this); LOG_I2C_DEVICE(this); + if (this->enable_pin_ != nullptr) { + LOG_PIN(" Enable Pin: ", this->enable_pin_); + } + ESP_LOGCONFIG(TAG, " Timeout: %u%s", this->timeout_us_, this->timeout_us_ > 0 ? "us" : " (no timeout)"); } + void VL53L0XSensor::setup() { + ESP_LOGD(TAG, "'%s' - setup BEGIN", this->name_.c_str()); + + if (!esphome::vl53l0x::VL53L0XSensor::enable_pin_setup_complete) { + for (auto &vl53_sensor : vl53_sensors) { + if (vl53_sensor->enable_pin_ != nullptr) { + // Disable the enable pin to force vl53 to HW Standby mode + ESP_LOGD(TAG, "i2c vl53l0x disable enable pins: GPIO%u", (vl53_sensor->enable_pin_)->get_pin()); + // Set enable pin as OUTPUT and disable the enable pin to force vl53 to HW Standby mode + vl53_sensor->enable_pin_->setup(); + vl53_sensor->enable_pin_->digital_write(false); + } + } + esphome::vl53l0x::VL53L0XSensor::enable_pin_setup_complete = true; + } + + if (this->enable_pin_ != nullptr) { + // Enable the enable pin to cause FW boot (to get back to 0x29 default address) + this->enable_pin_->digital_write(true); + delayMicroseconds(100); + } + + // Save the i2c address we want and force it to use the default 0x29 + // until we finish setup, then re-address to final desired address. + uint8_t final_address = address_; + this->set_i2c_address(0x29); + reg(0x89) |= 0x01; reg(0x88) = 0x00; reg(0x80) = 0x01; reg(0xFF) = 0x01; reg(0x00) = 0x00; - stop_variable_ = reg(0x91).get(); + this->stop_variable_ = reg(0x91).get(); reg(0x00) = 0x01; reg(0xFF) = 0x00; @@ -52,8 +87,15 @@ void VL53L0XSensor::setup() { reg(0x94) = 0x6B; reg(0x83) = 0x00; - while (reg(0x83).get() == 0x00) + this->timeout_start_us_ = micros(); + while (reg(0x83).get() == 0x00) { + if (this->timeout_us_ > 0 && ((uint16_t)(micros() - this->timeout_start_us_) > this->timeout_us_)) { + ESP_LOGE(TAG, "'%s' - setup timeout", this->name_.c_str()); + this->mark_failed(); + return; + } yield(); + } reg(0x83) = 0x01; uint8_t tmp = reg(0x92).get(); @@ -205,11 +247,22 @@ void VL53L0XSensor::setup() { return; } reg(0x01) = 0xE8; + + // Set the sensor to the desired final address + // The following is different for VL53L0X vs VL53L1X + // I2C_SXXXX_DEVICE_ADDRESS = 0x8A for VL53L0X + // I2C_SXXXX__DEVICE_ADDRESS = 0x0001 for VL53L1X + reg(0x8A) = final_address & 0x7F; + this->set_i2c_address(final_address); + + ESP_LOGD(TAG, "'%s' - setup END", this->name_.c_str()); } void VL53L0XSensor::update() { if (this->initiated_read_ || this->waiting_for_interrupt_) { this->publish_state(NAN); - this->status_set_warning(); + this->status_momentary_warning("update", 5000); + ESP_LOGW(TAG, "%s - update called before prior reading complete - initiated:%d waiting_for_interrupt:%d", + this->name_.c_str(), this->initiated_read_, this->waiting_for_interrupt_); } // initiate single shot measurement @@ -217,7 +270,7 @@ void VL53L0XSensor::update() { reg(0xFF) = 0x01; reg(0x00) = 0x00; - reg(0x91) = stop_variable_; + reg(0x91) = this->stop_variable_; reg(0x00) = 0x01; reg(0xFF) = 0x00; reg(0x80) = 0x00; @@ -246,7 +299,7 @@ void VL53L0XSensor::loop() { this->waiting_for_interrupt_ = false; if (range_mm >= 8190) { - ESP_LOGW(TAG, "'%s' - Distance is out of range, please move the target closer", this->name_.c_str()); + ESP_LOGD(TAG, "'%s' - Distance is out of range, please move the target closer", this->name_.c_str()); this->publish_state(NAN); return; } diff --git a/esphome/components/vl53l0x/vl53l0x_sensor.h b/esphome/components/vl53l0x/vl53l0x_sensor.h index 4939a9806c..2662b768ae 100644 --- a/esphome/components/vl53l0x/vl53l0x_sensor.h +++ b/esphome/components/vl53l0x/vl53l0x_sensor.h @@ -1,5 +1,7 @@ #pragma once +#include + #include "esphome/core/component.h" #include "esphome/components/sensor/sensor.h" #include "esphome/components/i2c/i2c.h" @@ -20,6 +22,8 @@ struct SequenceStepTimeouts { class VL53L0XSensor : public sensor::Sensor, public PollingComponent, public i2c::I2CDevice { public: + VL53L0XSensor(); + void setup() override; void dump_config() override; @@ -30,6 +34,8 @@ class VL53L0XSensor : public sensor::Sensor, public PollingComponent, public i2c void set_signal_rate_limit(float signal_rate_limit) { signal_rate_limit_ = signal_rate_limit; } void set_long_range(bool long_range) { long_range_ = long_range; } + void set_timeout_us(uint32_t timeout_us) { this->timeout_us_ = timeout_us; } + void set_enable_pin(GPIOPin *enable) { this->enable_pin_ = enable; } protected: uint32_t get_measurement_timing_budget_() { @@ -249,10 +255,17 @@ class VL53L0XSensor : public sensor::Sensor, public PollingComponent, public i2c float signal_rate_limit_; bool long_range_; + GPIOPin *enable_pin_{nullptr}; uint32_t measurement_timing_budget_us_; bool initiated_read_{false}; bool waiting_for_interrupt_{false}; uint8_t stop_variable_; + + uint16_t timeout_start_us_; + uint16_t timeout_us_{}; + + static std::list vl53_sensors; + static bool enable_pin_setup_complete; }; } // namespace vl53l0x diff --git a/tests/test3.yaml b/tests/test3.yaml index 46b2d6b3a0..30578b451d 100644 --- a/tests/test3.yaml +++ b/tests/test3.yaml @@ -229,6 +229,8 @@ sensor: name: 'VL53L0x Distance' address: 0x29 update_interval: 60s + enable_pin: GPIO13 + timeout: 200us - platform: apds9960 type: clear name: APDS9960 Clear From 87154e9b6f7fa35a715353e69465c045d89748e2 Mon Sep 17 00:00:00 2001 From: stubs12 <43162406+stubs12@users.noreply.github.com> Date: Thu, 25 Feb 2021 17:52:40 -0600 Subject: [PATCH 052/111] Tuya: Use queue for sending command messages (#1404) --- esphome/components/tuya/tuya.cpp | 116 ++++++++++++++++--------------- esphome/components/tuya/tuya.h | 13 +++- 2 files changed, 71 insertions(+), 58 deletions(-) diff --git a/esphome/components/tuya/tuya.cpp b/esphome/components/tuya/tuya.cpp index 75514dde19..f4a72e8109 100644 --- a/esphome/components/tuya/tuya.cpp +++ b/esphome/components/tuya/tuya.cpp @@ -9,7 +9,7 @@ static const char *TAG = "tuya"; static const int COMMAND_DELAY = 50; void Tuya::setup() { - this->set_interval("heartbeat", 1000, [this] { this->schedule_empty_command_(TuyaCommandType::HEARTBEAT); }); + this->set_interval("heartbeat", 1000, [this] { this->send_empty_command_(TuyaCommandType::HEARTBEAT); }); } void Tuya::loop() { @@ -18,15 +18,7 @@ void Tuya::loop() { this->read_byte(&c); this->handle_char_(c); } -} - -void Tuya::schedule_empty_command_(TuyaCommandType command) { - uint32_t delay = millis() - this->last_command_timestamp_; - if (delay > COMMAND_DELAY) { - send_empty_command_(command); - } else { - this->set_timeout(COMMAND_DELAY - delay, [this, command] { this->send_empty_command_(command); }); - } + process_command_queue_(); } void Tuya::dump_config() { @@ -122,7 +114,6 @@ void Tuya::handle_char_(uint8_t c) { } void Tuya::handle_command_(uint8_t command, uint8_t version, const uint8_t *buffer, size_t len) { - this->last_command_timestamp_ = millis(); switch ((TuyaCommandType) command) { case TuyaCommandType::HEARTBEAT: ESP_LOGV(TAG, "MCU Heartbeat (0x%02X)", buffer[0]); @@ -132,7 +123,7 @@ void Tuya::handle_command_(uint8_t command, uint8_t version, const uint8_t *buff } if (this->init_state_ == TuyaInitState::INIT_HEARTBEAT) { this->init_state_ = TuyaInitState::INIT_PRODUCT; - this->schedule_empty_command_(TuyaCommandType::PRODUCT_QUERY); + this->send_empty_command_(TuyaCommandType::PRODUCT_QUERY); } break; case TuyaCommandType::PRODUCT_QUERY: { @@ -151,7 +142,7 @@ void Tuya::handle_command_(uint8_t command, uint8_t version, const uint8_t *buff } if (this->init_state_ == TuyaInitState::INIT_PRODUCT) { this->init_state_ = TuyaInitState::INIT_CONF; - this->schedule_empty_command_(TuyaCommandType::CONF_QUERY); + this->send_empty_command_(TuyaCommandType::CONF_QUERY); } break; } @@ -164,16 +155,13 @@ void Tuya::handle_command_(uint8_t command, uint8_t version, const uint8_t *buff // If mcu returned status gpio, then we can ommit sending wifi state if (this->gpio_status_ != -1) { this->init_state_ = TuyaInitState::INIT_DATAPOINT; - this->schedule_empty_command_(TuyaCommandType::DATAPOINT_QUERY); + this->send_empty_command_(TuyaCommandType::DATAPOINT_QUERY); } else { this->init_state_ = TuyaInitState::INIT_WIFI; - this->set_timeout(COMMAND_DELAY, [this] { - // If we were following the spec to the letter we would send - // state updates until connected to both WiFi and API/MQTT. - // Instead we just claim to be connected immediately and move on. - uint8_t c[] = {0x04}; - this->send_command_(TuyaCommandType::WIFI_STATE, c, 1); - }); + // If we were following the spec to the letter we would send + // state updates until connected to both WiFi and API/MQTT. + // Instead we just claim to be connected immediately and move on. + this->send_command_(TuyaCommand{.cmd = TuyaCommandType::WIFI_STATE, .payload = std::vector{0x04}}); } } break; @@ -181,7 +169,7 @@ void Tuya::handle_command_(uint8_t command, uint8_t version, const uint8_t *buff case TuyaCommandType::WIFI_STATE: if (this->init_state_ == TuyaInitState::INIT_WIFI) { this->init_state_ = TuyaInitState::INIT_DATAPOINT; - this->schedule_empty_command_(TuyaCommandType::DATAPOINT_QUERY); + this->send_empty_command_(TuyaCommandType::DATAPOINT_QUERY); } break; case TuyaCommandType::WIFI_RESET: @@ -202,8 +190,7 @@ void Tuya::handle_command_(uint8_t command, uint8_t version, const uint8_t *buff case TuyaCommandType::DATAPOINT_QUERY: break; case TuyaCommandType::WIFI_TEST: { - uint8_t c[] = {0x00, 0x00}; - this->send_command_(TuyaCommandType::WIFI_TEST, c, 2); + this->send_command_(TuyaCommand{.cmd = TuyaCommandType::WIFI_TEST, .payload = std::vector{0x00, 0x00}}); break; } case TuyaCommandType::LOCAL_TIME_QUERY: { @@ -213,28 +200,26 @@ void Tuya::handle_command_(uint8_t command, uint8_t version, const uint8_t *buff auto now = time_id->now(); if (now.is_valid()) { - this->set_timeout(COMMAND_DELAY, [this, now] { - uint8_t year = now.year - 2000; - uint8_t month = now.month; - uint8_t day_of_month = now.day_of_month; - uint8_t hour = now.hour; - uint8_t minute = now.minute; - uint8_t second = now.second; - // Tuya days starts from Monday, esphome uses Sunday as day 1 - uint8_t day_of_week = now.day_of_week - 1; - if (day_of_week == 0) { - day_of_week = 7; - } - uint8_t c[] = {0x01, year, month, day_of_month, hour, minute, second, day_of_week}; - this->send_command_(TuyaCommandType::LOCAL_TIME_QUERY, c, 8); - }); + uint8_t year = now.year - 2000; + uint8_t month = now.month; + uint8_t day_of_month = now.day_of_month; + uint8_t hour = now.hour; + uint8_t minute = now.minute; + uint8_t second = now.second; + // Tuya days starts from Monday, esphome uses Sunday as day 1 + uint8_t day_of_week = now.day_of_week - 1; + if (day_of_week == 0) { + day_of_week = 7; + } + this->send_command_(TuyaCommand{ + .cmd = TuyaCommandType::LOCAL_TIME_QUERY, + .payload = std::vector{0x01, year, month, day_of_month, hour, minute, second, day_of_week}}); } else { ESP_LOGW(TAG, "TUYA_CMD_LOCAL_TIME_QUERY is not handled because time is not valid"); // By spec we need to notify MCU that the time was not obtained - this->set_timeout(COMMAND_DELAY, [this] { - uint8_t c[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - this->send_command_(TuyaCommandType::LOCAL_TIME_QUERY, c, 8); - }); + this->send_command_( + TuyaCommand{.cmd = TuyaCommandType::LOCAL_TIME_QUERY, + .payload = std::vector{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}); } } else { ESP_LOGW(TAG, "TUYA_CMD_LOCAL_TIME_QUERY is not handled because time is not configured"); @@ -321,24 +306,44 @@ void Tuya::handle_datapoint_(const uint8_t *buffer, size_t len) { listener.on_datapoint(datapoint); } -void Tuya::send_command_(TuyaCommandType command, const uint8_t *buffer, uint16_t len) { - uint8_t len_hi = len >> 8; - uint8_t len_lo = len >> 0; +void Tuya::send_raw_command_(TuyaCommand command) { + uint8_t len_hi = (uint8_t)(command.payload.size() >> 8); + uint8_t len_lo = (uint8_t)(command.payload.size() & 0xFF); uint8_t version = 0; - ESP_LOGV(TAG, "Sending Tuya: CMD=0x%02X VERSION=%u DATA=[%s] INIT_STATE=%u", command, version, // NOLINT - hexencode(buffer, len).c_str(), this->init_state_); + this->last_command_timestamp_ = millis(); - this->write_array({0x55, 0xAA, version, (uint8_t) command, len_hi, len_lo}); - if (len != 0) - this->write_array(buffer, len); + ESP_LOGV(TAG, "Sending Tuya: CMD=0x%02X VERSION=%u DATA=[%s] INIT_STATE=%u", command.cmd, version, // NOLINT + hexencode(command.payload).c_str(), this->init_state_); - uint8_t checksum = 0x55 + 0xAA + (uint8_t) command + len_hi + len_lo; - for (int i = 0; i < len; i++) - checksum += buffer[i]; + this->write_array({0x55, 0xAA, version, (uint8_t) command.cmd, len_hi, len_lo}); + if (!command.payload.empty()) + this->write_array(command.payload.data(), command.payload.size()); + + uint8_t checksum = 0x55 + 0xAA + (uint8_t) command.cmd + len_hi + len_lo; + for (auto &data : command.payload) + checksum += data; this->write_byte(checksum); } +void Tuya::process_command_queue_() { + uint32_t delay = millis() - this->last_command_timestamp_; + // Left check of delay since last command in case theres ever a command sent by calling send_raw_command_ directly + if (delay > COMMAND_DELAY && !command_queue_.empty()) { + this->send_raw_command_(command_queue_.front()); + this->command_queue_.erase(command_queue_.begin()); + } +} + +void Tuya::send_command_(TuyaCommand command) { + command_queue_.push_back(command); + process_command_queue_(); +} + +void Tuya::send_empty_command_(TuyaCommandType command) { + send_command_(TuyaCommand{.cmd = command, .payload = std::vector{0x04}}); +} + void Tuya::set_datapoint_value(TuyaDatapoint datapoint) { std::vector buffer; ESP_LOGV(TAG, "Datapoint %u set to %u", datapoint.id, datapoint.value_uint); @@ -389,7 +394,8 @@ void Tuya::set_datapoint_value(TuyaDatapoint datapoint) { buffer.push_back(data.size() >> 8); buffer.push_back(data.size() >> 0); buffer.insert(buffer.end(), data.begin(), data.end()); - this->send_command_(TuyaCommandType::DATAPOINT_DELIVER, buffer.data(), buffer.size()); + + this->send_command_(TuyaCommand{.cmd = TuyaCommandType::DATAPOINT_DELIVER, .payload = buffer}); } void Tuya::register_listener(uint8_t datapoint_id, const std::function &func) { diff --git a/esphome/components/tuya/tuya.h b/esphome/components/tuya/tuya.h index ddbbb48edf..a2b4040eb3 100644 --- a/esphome/components/tuya/tuya.h +++ b/esphome/components/tuya/tuya.h @@ -61,6 +61,11 @@ enum class TuyaInitState : uint8_t { INIT_DONE, }; +struct TuyaCommand { + TuyaCommandType cmd; + std::vector payload; +}; + class Tuya : public Component, public uart::UARTDevice { public: float get_setup_priority() const override { return setup_priority::LATE; } @@ -82,9 +87,10 @@ class Tuya : public Component, public uart::UARTDevice { bool validate_message_(); void handle_command_(uint8_t command, uint8_t version, const uint8_t *buffer, size_t len); - void send_command_(TuyaCommandType command, const uint8_t *buffer, uint16_t len); - void send_empty_command_(TuyaCommandType command) { this->send_command_(command, nullptr, 0); } - void schedule_empty_command_(TuyaCommandType command); + void send_raw_command_(TuyaCommand command); + void process_command_queue_(); + void send_command_(TuyaCommand command); + void send_empty_command_(TuyaCommandType command); #ifdef USE_TIME optional time_id_{}; @@ -98,6 +104,7 @@ class Tuya : public Component, public uart::UARTDevice { std::vector datapoints_; std::vector rx_message_; std::vector ignore_mcu_update_on_datapoints_{}; + std::vector command_queue_; }; } // namespace tuya From 4d6277330bd93f3f2e3086fa62d55b92da8e7eea Mon Sep 17 00:00:00 2001 From: Guillermo Ruffino Date: Fri, 26 Feb 2021 10:32:23 -0300 Subject: [PATCH 053/111] Update PULL_REQUEST_TEMPLATE.md --- .github/PULL_REQUEST_TEMPLATE.md | 40 +++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 94a5b7284e..fbdcf367ad 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,13 +1,47 @@ -## Description: +# What does this implement/fix? +Quick description -**Related issue (if applicable):** fixes +## Types of changes + +- [ ] Bugfix (non-breaking change which fixes an issue) +- [ ] New feature (non-breaking change which adds functionality) +- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) +- [ ] Configuration change (this will require users to update their yaml configuraiton files to keep working) + +**Related issue or feature (if applicable):** fixes **Pull request in [esphome-docs](https://github.com/esphome/esphome-docs) with documentation (if applicable):** esphome/esphome-docs# + +# Test Environment + +- [ ] ESP32 +- [ ] ESP8266 +- [ ] Windows +- [ ] Mac OS +- [ ] Linux + +## Example entry for `config.yaml`: + + +```yaml +# Example config.yaml + +``` + +# Explain your changes + +Describe your changes here to communicate to the maintainers **why we should accept this pull request**. +Very important to fill if no issue linked ## Checklist: - [ ] The code change is tested and works locally. - [ ] Tests have been added to verify that the new code works (under `tests/` folder). - + If user exposed functionality or configuration variables are added/changed: - [ ] Documentation added/updated in [esphome-docs](https://github.com/esphome/esphome-docs). From 6588e9380eb2c1b2bdebf4f9ecf31372ea3b7933 Mon Sep 17 00:00:00 2001 From: Robert Resch Date: Sat, 27 Feb 2021 22:47:12 +0100 Subject: [PATCH 054/111] Replace substitutions in substitutions first (#1567) --- esphome/components/substitutions/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/esphome/components/substitutions/__init__.py b/esphome/components/substitutions/__init__.py index c4bc1b4371..1cc5b50f47 100644 --- a/esphome/components/substitutions/__init__.py +++ b/esphome/components/substitutions/__init__.py @@ -136,4 +136,6 @@ def do_substitution_pass(config, command_line_substitutions): del substitutions[old] config[CONF_SUBSTITUTIONS] = substitutions + # Move substitutions to the first place to replace substitutions in them correctly + config.move_to_end(CONF_SUBSTITUTIONS, False) _substitute_item(substitutions, config, []) From 69d39ef0cd118add2006b7b7774b74f8f007a37e Mon Sep 17 00:00:00 2001 From: Otamay Date: Sat, 27 Feb 2021 15:55:27 -0600 Subject: [PATCH 055/111] Added heater to climate_ir_lg (#1555) * Added heater to climate_ir_lg * Code formatting * Code formatting --- .../components/climate_ir_lg/climate_ir_lg.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/esphome/components/climate_ir_lg/climate_ir_lg.cpp b/esphome/components/climate_ir_lg/climate_ir_lg.cpp index c80f82ae39..ee73d30796 100644 --- a/esphome/components/climate_ir_lg/climate_ir_lg.cpp +++ b/esphome/components/climate_ir_lg/climate_ir_lg.cpp @@ -9,6 +9,7 @@ static const char *TAG = "climate.climate_ir_lg"; const uint32_t COMMAND_ON = 0x00000; const uint32_t COMMAND_ON_AI = 0x03000; const uint32_t COMMAND_COOL = 0x08000; +const uint32_t COMMAND_HEAT = 0x0C000; const uint32_t COMMAND_OFF = 0xC0000; const uint32_t COMMAND_SWING = 0x10000; // On, 25C, Mode: Auto, Fan: Auto, Zone Follow: Off, Sensor Temp: Ignore. @@ -48,6 +49,9 @@ void LgIrClimate::transmit_state() { case climate::CLIMATE_MODE_COOL: remote_state |= COMMAND_COOL; break; + case climate::CLIMATE_MODE_HEAT: + remote_state |= COMMAND_HEAT; + break; case climate::CLIMATE_MODE_AUTO: remote_state |= COMMAND_AUTO; break; @@ -66,7 +70,8 @@ void LgIrClimate::transmit_state() { if (this->mode == climate::CLIMATE_MODE_OFF) { remote_state |= FAN_AUTO; - } else if (this->mode == climate::CLIMATE_MODE_COOL || this->mode == climate::CLIMATE_MODE_DRY) { + } else if (this->mode == climate::CLIMATE_MODE_COOL || this->mode == climate::CLIMATE_MODE_DRY || + this->mode == climate::CLIMATE_MODE_HEAT) { switch (this->fan_mode) { case climate::CLIMATE_FAN_HIGH: remote_state |= FAN_MAX; @@ -88,7 +93,7 @@ void LgIrClimate::transmit_state() { this->fan_mode = climate::CLIMATE_FAN_AUTO; // remote_state |= FAN_MODE_AUTO_DRY; } - if (this->mode == climate::CLIMATE_MODE_COOL) { + if (this->mode == climate::CLIMATE_MODE_COOL || this->mode == climate::CLIMATE_MODE_HEAT) { auto temp = (uint8_t) roundf(clamp(this->target_temperature, TEMP_MIN, TEMP_MAX)); remote_state |= ((temp - 15) << TEMP_SHIFT); } @@ -134,20 +139,23 @@ bool LgIrClimate::on_receive(remote_base::RemoteReceiveData data) { } else { if ((remote_state & COMMAND_MASK) == COMMAND_AUTO) this->mode = climate::CLIMATE_MODE_AUTO; - 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) { + this->mode = climate::CLIMATE_MODE_HEAT; } else { this->mode = climate::CLIMATE_MODE_COOL; } // Temperature - if (this->mode == climate::CLIMATE_MODE_COOL) + if (this->mode == climate::CLIMATE_MODE_COOL || this->mode == climate::CLIMATE_MODE_HEAT) this->target_temperature = ((remote_state & TEMP_MASK) >> TEMP_SHIFT) + 15; // Fan Speed if (this->mode == climate::CLIMATE_MODE_AUTO) { this->fan_mode = climate::CLIMATE_FAN_AUTO; - } else if (this->mode == climate::CLIMATE_MODE_COOL || this->mode == climate::CLIMATE_MODE_DRY) { + } 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) this->fan_mode = climate::CLIMATE_FAN_AUTO; else if ((remote_state & FAN_MASK) == FAN_MIN) From 342d5166a0906fb2742bf38f4d7bcac026edaf32 Mon Sep 17 00:00:00 2001 From: Guillermo Ruffino Date: Sat, 27 Feb 2021 19:21:07 -0300 Subject: [PATCH 056/111] More yaml validation (#1568) * validate keys * refactor line info --- esphome/config.py | 25 ++++++++++++++----------- esphome/yaml_util.py | 3 +++ 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/esphome/config.py b/esphome/config.py index 5dc539f828..957494ceac 100644 --- a/esphome/config.py +++ b/esphome/config.py @@ -265,6 +265,8 @@ class Config(OrderedDict): doc_range = None for item_index in path: try: + if item_index in data: + doc_range = [x for x in data.keys() if x == item_index][0].esp_range data = data[item_index] except (KeyError, IndexError, TypeError): return doc_range @@ -461,7 +463,6 @@ def validate_config(config, command_line_substitutions): while load_queue: domain, conf = load_queue.popleft() - domain = str(domain) if domain.startswith('.'): # Ignore top-level keys starting with a dot continue @@ -698,17 +699,16 @@ def load_config(command_line_substitutions): raise EsphomeError(f"Error while parsing config: {err}") from err -def line_info(obj, highlight=True): +def line_info(config, path, highlight=True): """Display line config source.""" if not highlight: return None - if isinstance(obj, core.ID): - obj = obj.id - if isinstance(obj, ESPHomeDataBase) and obj.esp_range is not None: - mark = obj.esp_range.start_mark + obj = config.get_deepest_document_range_for_path(path) + if obj: + mark = obj.start_mark source = "[source {}:{}]".format(mark.document, mark.line + 1) return color('cyan', source) - return None + return 'None' def _print_on_next_line(obj): @@ -749,7 +749,7 @@ def dump_dict(config, path, at_root=True): sep = color('red', sep) msg, _ = dump_dict(config, path_, at_root=False) msg = indent(msg) - inf = line_info(config.get_nested_item(path_), highlight=config.is_in_error_path(path_)) + inf = line_info(config, path_, highlight=config.is_in_error_path(path_)) if inf is not None: msg = inf + '\n' + msg elif msg: @@ -772,7 +772,7 @@ def dump_dict(config, path, at_root=True): st = color('red', st) msg, m = dump_dict(config, path_, at_root=False) - inf = line_info(config.get_nested_item(path_), highlight=config.is_in_error_path(path_)) + inf = line_info(config, path_, highlight=config.is_in_error_path(path_)) if m: msg = '\n' + indent(msg) @@ -849,8 +849,11 @@ def read_config(command_line_substitutions): if not res.is_in_error_path(path): continue - safe_print(color('bold_red', f'{domain}:') + ' ' + - (line_info(res.get_nested_item(path)) or '')) + errstr = color('bold_red', f'{domain}:') + errline = line_info(res, path) + if errline: + errstr += ' ' + errline + safe_print(errstr) safe_print(indent(dump_dict(res, path)[0])) return None return OrderedDict(res) diff --git a/esphome/yaml_util.py b/esphome/yaml_util.py index 5909e99cb2..eb88a8da62 100644 --- a/esphome/yaml_util.py +++ b/esphome/yaml_util.py @@ -146,6 +146,9 @@ class ESPHomeLoader(yaml.SafeLoader): # pylint: disable=too-many-ancestors raise yaml.constructor.ConstructorError( f'Invalid key "{key}" (not hashable)', key_node.start_mark) + key = make_data_base(str(key)) + key.from_node(key_node) + # Check if it is a duplicate key if key in seen_keys: raise yaml.constructor.ConstructorError( From 520c4331e3c74791f29712fb45b357277d3dc72f Mon Sep 17 00:00:00 2001 From: marecabo <23156476+marecabo@users.noreply.github.com> Date: Sat, 27 Feb 2021 23:28:06 +0100 Subject: [PATCH 057/111] Add default device classes to sensor components (#1533) * Add device_class arg to homeassistant sensor_schema call * Add device_class arg to mqtt_subscribe sensor_schema call * Add device_class arg to dht sensor_schema call * Add device_class arg to dht12 sensor_schema call * Add device_class arg to am2320 sensor_schema call * Add device_class arg to atc_mithermometer sensor_schema call * Add device_class arg to atm90e32 sensor_schema call * Add device_class arg to bh1750 sensor_schema call * Add device_class arg to ble_rssi sensor_schema call * Add device_class arg to bme280 sensor_schema call * Add device_class arg to bme680 sensor_schema call * Add device_class arg to bmp085 sensor_schema call * Add device_class arg to bmp280 sensor_schema call * Add device_class arg to binary_sensor_map sensor_schema call * Add device_class arg to apds9960 sensor_schema call * Add device_class arg to as3935 sensor_schema call * Add device_class arg to ccs811 sensor_schema call * Add device_class arg to cse7766 sensor_schema call * Add device_class arg to ct_clamp sensor_schema call * Add device_class arg to dallas sensor_schema call * Add device_class arg to duty_cycle sensor_schema call * Add device_class arg to esp32_hall sensor_schema call * Add device_class arg to hdc1080 sensor_schema call * Add device_class arg to hlw8012 sensor_schema call * Add device_class arg to hm3301 sensor_schema call * Add device_class arg to hmc5883l sensor_schema call * Add device_class arg to htu21d sensor_schema call * Add device_class arg to hx711 sensor_schema call * Add device_class arg to ina219 sensor_schema call * Add device_class arg to ina226 sensor_schema call * Add device_class arg to ina3221 sensor_schema call * Add device_class arg to ibsth1 sensor_schema call * Add device_class arg to max31855 sensor_schema call * Add device_class arg to max31856 sensor_schema call * Add device_class arg to max31865 sensor_schema call * Add device_class arg to mhz19 sensor_schema call * Add device_class arg to max6675 sensor_schema call * Add device_class arg to mpu6050 sensor_schema call * Add device_class arg to ms5611 sensor_schema call * Add device_class arg to mcp9808 sensor_schema call * Add device_class arg to ntc sensor_schema call * Add device_class arg to pid sensor_schema call * Add device_class arg to pmsx003 sensor_schema call * Add device_class arg to pulse_counter sensor_schema call * Add device_class arg to pulse_width sensor_schema call * Add device_class arg to pzem004t sensor_schema call * Add device_class arg to pzemac sensor_schema call * Add device_class arg to pzemdc sensor_schema call * Add device_class arg to qmc5883l sensor_schema call * Add device_class arg to resistance sensor_schema call * Add device_class arg to rotary_encoder sensor_schema call * Add device_class arg to ruuvitag sensor_schema call * Add device_class arg to scd30 sensor_schema call * Add device_class arg to sds011 sensor_schema call * Add device_class arg to senseair sensor_schema call * Add device_class arg to sgp30 sensor_schema call * Add device_class arg to sht3xd sensor_schema call * Add device_class arg to shtcx sensor_schema call * Add device_class arg to sps30 sensor_schema call * Add device_class arg to sts3x sensor_schema call * Add device_class arg to sun sensor_schema call * Add device_class arg to tcs34725 sensor_schema call * Add device_class arg to teleinfo sensor_schema call * Add device_class arg to template sensor_schema call * Add device_class arg to tmp102 sensor_schema call * Add device_class arg to tmp117 sensor_schema call * Add device_class arg to tsl2561 sensor_schema call * Add device_class arg to tx20 sensor_schema call * Add device_class arg to ultrasonic sensor_schema call * Add device_class arg to uptime sensor_schema call * Add device_class arg to vl53l0x sensor_schema call * Add device_class arg to wifi_signal sensor_schema call * Add device_class arg to xiaomi_cgd1 sensor_schema call * Add device_class arg to xiaomi_cgg1 sensor_schema call * Add device_class arg to xiaomi_gcls002 sensor_schema call * Add device_class arg to xiaomi_hhccjcy01 sensor_schema call * Add device_class arg to xiaomi_hhccpot002 sensor_schema call * Add device_class arg to xiaomi_jqjcy01ym sensor_schema call * Add device_class arg to xiaomi_lywsd02 sensor_schema call * Add device_class arg to xiaomi_lywsd03mmc sensor_schema call * Add device_class arg to xiaomi_lywsdcgq sensor_schema call * Add device_class arg to xiaomi_mhoc401 sensor_schema call * Add device_class arg to xiaomi_mjyd02yla sensor_schema call * Add device_class arg to xiaomi_wx08zm sensor_schema call * Add device_class arg to zyaura sensor_schema call * Add device_class arg to ads1115 sensor_schema call * Add device_class arg to adc sensor_schema call * Add device_class arg to ade7953 sensor_schema call * Add device_class arg to aht10 sensor_schema call * Make args of sensor_schema required * lint Co-authored-by: Guillermo Ruffino --- esphome/components/adc/sensor.py | 5 +- esphome/components/ade7953/sensor.py | 18 ++++--- esphome/components/ads1115/sensor.py | 5 +- esphome/components/aht10/sensor.py | 10 ++-- esphome/components/am2320/sensor.py | 10 ++-- esphome/components/apds9960/sensor.py | 4 +- esphome/components/as3935/sensor.py | 6 +-- .../components/atc_mithermometer/sensor.py | 17 ++++--- esphome/components/atm90e32/sensor.py | 21 +++++--- esphome/components/bh1750/sensor.py | 4 +- .../components/binary_sensor_map/sensor.py | 8 +-- esphome/components/ble_rssi/sensor.py | 17 ++++--- esphome/components/bme280/sensor.py | 10 ++-- esphome/components/bme680/sensor.py | 13 ++--- esphome/components/bmp085/sensor.py | 10 ++-- esphome/components/bmp280/sensor.py | 14 ++++-- esphome/components/ccs811/sensor.py | 7 +-- esphome/components/cse7766/sensor.py | 11 +++-- esphome/components/ct_clamp/sensor.py | 4 +- esphome/components/dallas/sensor.py | 8 +-- esphome/components/dht/sensor.py | 9 ++-- esphome/components/dht12/sensor.py | 8 +-- esphome/components/duty_cycle/sensor.py | 4 +- esphome/components/esp32_hall/sensor.py | 5 +- esphome/components/hdc1080/sensor.py | 10 ++-- esphome/components/hlw8012/sensor.py | 13 +++-- esphome/components/hm3301/sensor.py | 13 +++-- esphome/components/hmc5883l/sensor.py | 10 ++-- .../homeassistant/sensor/__init__.py | 4 +- esphome/components/htu21d/sensor.py | 10 ++-- esphome/components/hx711/sensor.py | 5 +- esphome/components/ina219/sensor.py | 17 ++++--- esphome/components/ina226/sensor.py | 17 ++++--- esphome/components/ina3221/sensor.py | 15 +++--- .../components/inkbird_ibsth1_mini/sensor.py | 12 +++-- esphome/components/max31855/sensor.py | 7 +-- esphome/components/max31856/sensor.py | 5 +- esphome/components/max31865/sensor.py | 4 +- esphome/components/max6675/sensor.py | 4 +- esphome/components/mcp9808/sensor.py | 4 +- esphome/components/mhz19/sensor.py | 7 +-- esphome/components/mpu6050/sensor.py | 14 +++--- .../mqtt_subscribe/sensor/__init__.py | 4 +- esphome/components/ms5611/sensor.py | 10 ++-- esphome/components/ntc/sensor.py | 6 +-- esphome/components/pid/sensor/__init__.py | 4 +- esphome/components/pmsx003/sensor.py | 22 +++++---- esphome/components/pulse_counter/sensor.py | 8 +-- esphome/components/pulse_width/sensor.py | 4 +- esphome/components/pzem004t/sensor.py | 16 +++--- esphome/components/pzemac/sensor.py | 25 ++++++---- esphome/components/pzemdc/sensor.py | 12 +++-- esphome/components/qmc5883l/sensor.py | 10 ++-- esphome/components/resistance/sensor.py | 4 +- esphome/components/rotary_encoder/sensor.py | 9 ++-- esphome/components/ruuvitag/sensor.py | 49 ++++++++++++------- esphome/components/scd30/sensor.py | 14 +++--- esphome/components/sds011/sensor.py | 11 +++-- esphome/components/senseair/sensor.py | 6 ++- esphome/components/sensor/__init__.py | 3 +- esphome/components/sgp30/sensor.py | 7 +-- esphome/components/sht3xd/sensor.py | 10 ++-- esphome/components/shtcx/sensor.py | 10 ++-- esphome/components/sps30/sensor.py | 24 ++++----- esphome/components/sts3x/sensor.py | 4 +- esphome/components/sun/sensor/__init__.py | 6 ++- esphome/components/tcs34725/sensor.py | 13 ++--- esphome/components/teleinfo/sensor.py | 5 +- .../components/template/sensor/__init__.py | 5 +- esphome/components/tmp102/sensor.py | 4 +- esphome/components/tmp117/sensor.py | 8 +-- esphome/components/tsl2561/sensor.py | 5 +- esphome/components/tx20/sensor.py | 6 +-- esphome/components/ultrasonic/sensor.py | 6 ++- esphome/components/uptime/sensor.py | 4 +- esphome/components/vl53l0x/sensor.py | 7 +-- esphome/components/wifi_signal/sensor.py | 6 ++- esphome/components/xiaomi_cgd1/sensor.py | 13 +++-- esphome/components/xiaomi_cgg1/sensor.py | 12 +++-- esphome/components/xiaomi_gcls002/sensor.py | 19 ++++--- esphome/components/xiaomi_hhccjcy01/sensor.py | 23 +++++---- .../components/xiaomi_hhccpot002/sensor.py | 9 ++-- esphome/components/xiaomi_jqjcy01ym/sensor.py | 19 ++++--- esphome/components/xiaomi_lywsd02/sensor.py | 12 +++-- .../components/xiaomi_lywsd03mmc/sensor.py | 13 +++-- esphome/components/xiaomi_lywsdcgq/sensor.py | 12 +++-- esphome/components/xiaomi_mhoc401/sensor.py | 13 +++-- esphome/components/xiaomi_miscale/sensor.py | 5 +- esphome/components/xiaomi_miscale2/sensor.py | 8 +-- .../xiaomi_mjyd02yla/binary_sensor.py | 16 +++--- .../components/xiaomi_wx08zm/binary_sensor.py | 9 ++-- esphome/components/zyaura/sensor.py | 16 +++--- 92 files changed, 557 insertions(+), 388 deletions(-) diff --git a/esphome/components/adc/sensor.py b/esphome/components/adc/sensor.py index 6a274f04af..3c06eb91bf 100644 --- a/esphome/components/adc/sensor.py +++ b/esphome/components/adc/sensor.py @@ -2,7 +2,8 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins from esphome.components import sensor, voltage_sampler -from esphome.const import CONF_ATTENUATION, CONF_ID, CONF_PIN, ICON_FLASH, UNIT_VOLT +from esphome.const import CONF_ATTENUATION, CONF_ID, CONF_PIN, DEVICE_CLASS_VOLTAGE, ICON_EMPTY, \ + UNIT_VOLT AUTO_LOAD = ['voltage_sampler'] @@ -26,7 +27,7 @@ adc_ns = cg.esphome_ns.namespace('adc') ADCSensor = adc_ns.class_('ADCSensor', sensor.Sensor, cg.PollingComponent, voltage_sampler.VoltageSampler) -CONFIG_SCHEMA = sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 2).extend({ +CONFIG_SCHEMA = sensor.sensor_schema(UNIT_VOLT, ICON_EMPTY, 2, DEVICE_CLASS_VOLTAGE).extend({ cv.GenerateID(): cv.declare_id(ADCSensor), cv.Required(CONF_PIN): validate_adc_pin, cv.SplitDefault(CONF_ATTENUATION, esp32='0db'): diff --git a/esphome/components/ade7953/sensor.py b/esphome/components/ade7953/sensor.py index d29e2dd13e..255695c4a5 100644 --- a/esphome/components/ade7953/sensor.py +++ b/esphome/components/ade7953/sensor.py @@ -2,8 +2,8 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, i2c from esphome import pins -from esphome.const import CONF_ID, CONF_VOLTAGE, \ - UNIT_VOLT, ICON_FLASH, UNIT_AMPERE, UNIT_WATT +from esphome.const import CONF_ID, CONF_VOLTAGE, DEVICE_CLASS_CURRENT, DEVICE_CLASS_POWER, \ + DEVICE_CLASS_VOLTAGE, ICON_EMPTY, UNIT_VOLT, UNIT_AMPERE, UNIT_WATT DEPENDENCIES = ['i2c'] @@ -19,11 +19,15 @@ CONF_ACTIVE_POWER_B = 'active_power_b' CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(ADE7953), cv.Optional(CONF_IRQ_PIN): pins.input_pin, - cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 1), - cv.Optional(CONF_CURRENT_A): sensor.sensor_schema(UNIT_AMPERE, ICON_FLASH, 2), - cv.Optional(CONF_CURRENT_B): sensor.sensor_schema(UNIT_AMPERE, ICON_FLASH, 2), - cv.Optional(CONF_ACTIVE_POWER_A): sensor.sensor_schema(UNIT_WATT, ICON_FLASH, 1), - cv.Optional(CONF_ACTIVE_POWER_B): sensor.sensor_schema(UNIT_WATT, ICON_FLASH, 1), + cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE), + cv.Optional(CONF_CURRENT_A): sensor.sensor_schema(UNIT_AMPERE, ICON_EMPTY, 2, + DEVICE_CLASS_CURRENT), + cv.Optional(CONF_CURRENT_B): sensor.sensor_schema(UNIT_AMPERE, ICON_EMPTY, 2, + DEVICE_CLASS_CURRENT), + cv.Optional(CONF_ACTIVE_POWER_A): sensor.sensor_schema(UNIT_WATT, ICON_EMPTY, 1, + DEVICE_CLASS_POWER), + cv.Optional(CONF_ACTIVE_POWER_B): sensor.sensor_schema(UNIT_WATT, ICON_EMPTY, 1, + DEVICE_CLASS_POWER), }).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x38)) diff --git a/esphome/components/ads1115/sensor.py b/esphome/components/ads1115/sensor.py index 55619b22e9..48f01337ef 100644 --- a/esphome/components/ads1115/sensor.py +++ b/esphome/components/ads1115/sensor.py @@ -1,7 +1,8 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, voltage_sampler -from esphome.const import CONF_GAIN, CONF_MULTIPLEXER, ICON_FLASH, UNIT_VOLT, CONF_ID +from esphome.const import CONF_GAIN, CONF_MULTIPLEXER, DEVICE_CLASS_VOLTAGE, ICON_EMPTY, \ + UNIT_VOLT, CONF_ID from . import ads1115_ns, ADS1115Component DEPENDENCIES = ['ads1115'] @@ -42,7 +43,7 @@ ADS1115Sensor = ads1115_ns.class_('ADS1115Sensor', sensor.Sensor, cg.PollingComp voltage_sampler.VoltageSampler) CONF_ADS1115_ID = 'ads1115_id' -CONFIG_SCHEMA = sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 3).extend({ +CONFIG_SCHEMA = sensor.sensor_schema(UNIT_VOLT, ICON_EMPTY, 3, DEVICE_CLASS_VOLTAGE).extend({ cv.GenerateID(): cv.declare_id(ADS1115Sensor), cv.GenerateID(CONF_ADS1115_ID): cv.use_id(ADS1115Component), cv.Required(CONF_MULTIPLEXER): cv.enum(MUX, upper=True, space='_'), diff --git a/esphome/components/aht10/sensor.py b/esphome/components/aht10/sensor.py index 71b0adce79..17cbb8892e 100644 --- a/esphome/components/aht10/sensor.py +++ b/esphome/components/aht10/sensor.py @@ -1,8 +1,8 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import CONF_HUMIDITY, CONF_ID, CONF_TEMPERATURE, \ - UNIT_CELSIUS, ICON_THERMOMETER, ICON_WATER_PERCENT, UNIT_PERCENT +from esphome.const import CONF_HUMIDITY, CONF_ID, CONF_TEMPERATURE, DEVICE_CLASS_HUMIDITY, \ + DEVICE_CLASS_TEMPERATURE, ICON_EMPTY, UNIT_CELSIUS, UNIT_PERCENT DEPENDENCIES = ['i2c'] @@ -11,8 +11,10 @@ AHT10Component = aht10_ns.class_('AHT10Component', cg.PollingComponent, i2c.I2CD CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(AHT10Component), - cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 2), - cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 2), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 2, + DEVICE_CLASS_TEMPERATURE), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 2, + DEVICE_CLASS_HUMIDITY), }).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x38)) diff --git a/esphome/components/am2320/sensor.py b/esphome/components/am2320/sensor.py index d62899663c..098315290d 100644 --- a/esphome/components/am2320/sensor.py +++ b/esphome/components/am2320/sensor.py @@ -1,8 +1,8 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import CONF_HUMIDITY, CONF_ID, CONF_TEMPERATURE, \ - UNIT_CELSIUS, ICON_THERMOMETER, ICON_WATER_PERCENT, UNIT_PERCENT +from esphome.const import CONF_HUMIDITY, CONF_ID, CONF_TEMPERATURE, DEVICE_CLASS_HUMIDITY, \ + DEVICE_CLASS_TEMPERATURE, UNIT_CELSIUS, ICON_EMPTY, UNIT_PERCENT DEPENDENCIES = ['i2c'] @@ -11,8 +11,10 @@ AM2320Component = am2320_ns.class_('AM2320Component', cg.PollingComponent, i2c.I CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(AM2320Component), - cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1), - cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 1), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, + DEVICE_CLASS_TEMPERATURE), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 1, + DEVICE_CLASS_HUMIDITY), }).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x5C)) diff --git a/esphome/components/apds9960/sensor.py b/esphome/components/apds9960/sensor.py index 58087cbe86..eb1008e713 100644 --- a/esphome/components/apds9960/sensor.py +++ b/esphome/components/apds9960/sensor.py @@ -1,7 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor -from esphome.const import CONF_TYPE, UNIT_PERCENT, ICON_LIGHTBULB +from esphome.const import CONF_TYPE, DEVICE_CLASS_EMPTY, UNIT_PERCENT, ICON_LIGHTBULB from . import APDS9960, CONF_APDS9960_ID DEPENDENCIES = ['apds9960'] @@ -14,7 +14,7 @@ TYPES = { 'PROXIMITY': 'set_proximity', } -CONFIG_SCHEMA = sensor.sensor_schema(UNIT_PERCENT, ICON_LIGHTBULB, 1).extend({ +CONFIG_SCHEMA = sensor.sensor_schema(UNIT_PERCENT, ICON_LIGHTBULB, 1, DEVICE_CLASS_EMPTY).extend({ cv.Required(CONF_TYPE): cv.one_of(*TYPES, upper=True), cv.GenerateID(CONF_APDS9960_ID): cv.use_id(APDS9960), }) diff --git a/esphome/components/as3935/sensor.py b/esphome/components/as3935/sensor.py index 016df8f2a1..ff6f1d6198 100644 --- a/esphome/components/as3935/sensor.py +++ b/esphome/components/as3935/sensor.py @@ -1,7 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor -from esphome.const import CONF_DISTANCE, CONF_LIGHTNING_ENERGY, \ +from esphome.const import CONF_DISTANCE, CONF_LIGHTNING_ENERGY, DEVICE_CLASS_EMPTY, \ UNIT_KILOMETER, UNIT_EMPTY, ICON_SIGNAL_DISTANCE_VARIANT, ICON_FLASH from . import AS3935, CONF_AS3935_ID @@ -10,9 +10,9 @@ DEPENDENCIES = ['as3935'] CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(CONF_AS3935_ID): cv.use_id(AS3935), cv.Optional(CONF_DISTANCE): - sensor.sensor_schema(UNIT_KILOMETER, ICON_SIGNAL_DISTANCE_VARIANT, 1), + sensor.sensor_schema(UNIT_KILOMETER, ICON_SIGNAL_DISTANCE_VARIANT, 1, DEVICE_CLASS_EMPTY), cv.Optional(CONF_LIGHTNING_ENERGY): - sensor.sensor_schema(UNIT_EMPTY, ICON_FLASH, 1), + sensor.sensor_schema(UNIT_EMPTY, ICON_FLASH, 1, DEVICE_CLASS_EMPTY), }).extend(cv.COMPONENT_SCHEMA) diff --git a/esphome/components/atc_mithermometer/sensor.py b/esphome/components/atc_mithermometer/sensor.py index 550cd8d0f8..73115ff4ba 100644 --- a/esphome/components/atc_mithermometer/sensor.py +++ b/esphome/components/atc_mithermometer/sensor.py @@ -2,8 +2,9 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, esp32_ble_tracker from esphome.const import CONF_BATTERY_LEVEL, CONF_BATTERY_VOLTAGE, CONF_MAC_ADDRESS, \ - CONF_HUMIDITY, CONF_TEMPERATURE, CONF_ID, UNIT_CELSIUS, UNIT_PERCENT, UNIT_VOLT, \ - ICON_BATTERY, ICON_THERMOMETER, ICON_WATER_PERCENT + CONF_HUMIDITY, CONF_TEMPERATURE, CONF_ID, DEVICE_CLASS_BATTERY, DEVICE_CLASS_HUMIDITY, \ + DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_VOLTAGE, ICON_EMPTY, UNIT_CELSIUS, UNIT_PERCENT, \ + UNIT_VOLT CODEOWNERS = ['@ahpohl'] @@ -17,10 +18,14 @@ ATCMiThermometer = atc_mithermometer_ns.class_('ATCMiThermometer', CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(ATCMiThermometer), cv.Required(CONF_MAC_ADDRESS): cv.mac_address, - cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1), - cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 0), - cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(UNIT_PERCENT, ICON_BATTERY, 0), - cv.Optional(CONF_BATTERY_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_BATTERY, 3), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, + DEVICE_CLASS_TEMPERATURE), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 0, + DEVICE_CLASS_HUMIDITY), + cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 0, + DEVICE_CLASS_BATTERY), + cv.Optional(CONF_BATTERY_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_EMPTY, 3, + DEVICE_CLASS_VOLTAGE), }).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) diff --git a/esphome/components/atm90e32/sensor.py b/esphome/components/atm90e32/sensor.py index f27fd3126a..f35232b7e9 100644 --- a/esphome/components/atm90e32/sensor.py +++ b/esphome/components/atm90e32/sensor.py @@ -3,7 +3,8 @@ import esphome.config_validation as cv from esphome.components import sensor, spi from esphome.const import \ CONF_ID, CONF_VOLTAGE, CONF_CURRENT, CONF_POWER, CONF_POWER_FACTOR, CONF_FREQUENCY, \ - ICON_FLASH, ICON_LIGHTBULB, ICON_CURRENT_AC, ICON_THERMOMETER, \ + DEVICE_CLASS_CURRENT, DEVICE_CLASS_EMPTY, DEVICE_CLASS_POWER, DEVICE_CLASS_POWER_FACTOR, \ + DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_VOLTAGE, ICON_EMPTY, ICON_LIGHTBULB, ICON_CURRENT_AC, \ UNIT_HERTZ, UNIT_VOLT, UNIT_AMPERE, UNIT_WATT, UNIT_EMPTY, UNIT_CELSIUS, UNIT_VOLT_AMPS_REACTIVE CONF_PHASE_A = 'phase_a' @@ -35,12 +36,14 @@ atm90e32_ns = cg.esphome_ns.namespace('atm90e32') ATM90E32Component = atm90e32_ns.class_('ATM90E32Component', cg.PollingComponent, spi.SPIDevice) ATM90E32_PHASE_SCHEMA = cv.Schema({ - cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 2), - cv.Optional(CONF_CURRENT): sensor.sensor_schema(UNIT_AMPERE, ICON_CURRENT_AC, 2), - cv.Optional(CONF_POWER): sensor.sensor_schema(UNIT_WATT, ICON_FLASH, 2), + cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_EMPTY, 2, DEVICE_CLASS_VOLTAGE), + cv.Optional(CONF_CURRENT): sensor.sensor_schema(UNIT_AMPERE, ICON_EMPTY, 2, + DEVICE_CLASS_CURRENT), + cv.Optional(CONF_POWER): sensor.sensor_schema(UNIT_WATT, ICON_EMPTY, 2, DEVICE_CLASS_POWER), cv.Optional(CONF_REACTIVE_POWER): sensor.sensor_schema(UNIT_VOLT_AMPS_REACTIVE, - ICON_LIGHTBULB, 2), - cv.Optional(CONF_POWER_FACTOR): sensor.sensor_schema(UNIT_EMPTY, ICON_FLASH, 2), + ICON_LIGHTBULB, 2, DEVICE_CLASS_EMPTY), + cv.Optional(CONF_POWER_FACTOR): sensor.sensor_schema(UNIT_EMPTY, ICON_EMPTY, 2, + DEVICE_CLASS_POWER_FACTOR), cv.Optional(CONF_GAIN_VOLTAGE, default=7305): cv.uint16_t, cv.Optional(CONF_GAIN_CT, default=27961): cv.uint16_t, }) @@ -50,8 +53,10 @@ CONFIG_SCHEMA = cv.Schema({ cv.Optional(CONF_PHASE_A): ATM90E32_PHASE_SCHEMA, cv.Optional(CONF_PHASE_B): ATM90E32_PHASE_SCHEMA, cv.Optional(CONF_PHASE_C): ATM90E32_PHASE_SCHEMA, - cv.Optional(CONF_FREQUENCY): sensor.sensor_schema(UNIT_HERTZ, ICON_CURRENT_AC, 1), - cv.Optional(CONF_CHIP_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1), + cv.Optional(CONF_FREQUENCY): sensor.sensor_schema(UNIT_HERTZ, ICON_CURRENT_AC, 1, + DEVICE_CLASS_EMPTY), + cv.Optional(CONF_CHIP_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, + DEVICE_CLASS_TEMPERATURE), cv.Required(CONF_LINE_FREQUENCY): cv.enum(LINE_FREQS, upper=True), cv.Optional(CONF_CURRENT_PHASES, default='3'): cv.enum(CURRENT_PHASES, upper=True), cv.Optional(CONF_GAIN_PGA, default='2X'): cv.enum(PGA_GAINS, upper=True), diff --git a/esphome/components/bh1750/sensor.py b/esphome/components/bh1750/sensor.py index 54735616d5..98777ab9c1 100644 --- a/esphome/components/bh1750/sensor.py +++ b/esphome/components/bh1750/sensor.py @@ -1,7 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import CONF_ID, CONF_RESOLUTION, UNIT_LUX, ICON_BRIGHTNESS_5 +from esphome.const import CONF_ID, CONF_RESOLUTION, DEVICE_CLASS_ILLUMINANCE, ICON_EMPTY, UNIT_LUX DEPENDENCIES = ['i2c'] @@ -16,7 +16,7 @@ BH1750_RESOLUTIONS = { BH1750Sensor = bh1750_ns.class_('BH1750Sensor', sensor.Sensor, cg.PollingComponent, i2c.I2CDevice) CONF_MEASUREMENT_TIME = 'measurement_time' -CONFIG_SCHEMA = sensor.sensor_schema(UNIT_LUX, ICON_BRIGHTNESS_5, 1).extend({ +CONFIG_SCHEMA = sensor.sensor_schema(UNIT_LUX, ICON_EMPTY, 1, DEVICE_CLASS_ILLUMINANCE).extend({ cv.GenerateID(): cv.declare_id(BH1750Sensor), cv.Optional(CONF_RESOLUTION, default=0.5): cv.enum(BH1750_RESOLUTIONS, float=True), cv.Optional(CONF_MEASUREMENT_TIME, default=69): cv.int_range(min=31, max=254), diff --git a/esphome/components/binary_sensor_map/sensor.py b/esphome/components/binary_sensor_map/sensor.py index 27f4654ded..ffd945bb5a 100644 --- a/esphome/components/binary_sensor_map/sensor.py +++ b/esphome/components/binary_sensor_map/sensor.py @@ -2,8 +2,8 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, binary_sensor -from esphome.const import CONF_ID, CONF_CHANNELS, CONF_VALUE, CONF_TYPE, UNIT_EMPTY, \ - ICON_CHECK_CIRCLE_OUTLINE, CONF_BINARY_SENSOR, CONF_GROUP +from esphome.const import CONF_ID, CONF_CHANNELS, CONF_VALUE, CONF_TYPE, DEVICE_CLASS_EMPTY, \ + UNIT_EMPTY, ICON_CHECK_CIRCLE_OUTLINE, CONF_BINARY_SENSOR, CONF_GROUP DEPENDENCIES = ['binary_sensor'] @@ -21,7 +21,9 @@ entry = { } CONFIG_SCHEMA = cv.typed_schema({ - CONF_GROUP: sensor.sensor_schema(UNIT_EMPTY, ICON_CHECK_CIRCLE_OUTLINE, 0).extend({ + CONF_GROUP: sensor.sensor_schema( + UNIT_EMPTY, ICON_CHECK_CIRCLE_OUTLINE, 0, DEVICE_CLASS_EMPTY + ).extend({ cv.GenerateID(): cv.declare_id(BinarySensorMap), cv.Required(CONF_CHANNELS): cv.All(cv.ensure_list(entry), cv.Length(min=1)), }), diff --git a/esphome/components/ble_rssi/sensor.py b/esphome/components/ble_rssi/sensor.py index 76a27e6f2b..16903dd9d0 100644 --- a/esphome/components/ble_rssi/sensor.py +++ b/esphome/components/ble_rssi/sensor.py @@ -1,7 +1,8 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, esp32_ble_tracker -from esphome.const import CONF_SERVICE_UUID, CONF_MAC_ADDRESS, CONF_ID, UNIT_DECIBEL, ICON_SIGNAL +from esphome.const import CONF_SERVICE_UUID, CONF_MAC_ADDRESS, CONF_ID, \ + DEVICE_CLASS_SIGNAL_STRENGTH, UNIT_DECIBEL, ICON_EMPTY DEPENDENCIES = ['esp32_ble_tracker'] @@ -9,12 +10,14 @@ ble_rssi_ns = cg.esphome_ns.namespace('ble_rssi') BLERSSISensor = ble_rssi_ns.class_('BLERSSISensor', sensor.Sensor, cg.Component, esp32_ble_tracker.ESPBTDeviceListener) -CONFIG_SCHEMA = cv.All(sensor.sensor_schema(UNIT_DECIBEL, ICON_SIGNAL, 0).extend({ - cv.GenerateID(): cv.declare_id(BLERSSISensor), - cv.Optional(CONF_MAC_ADDRESS): cv.mac_address, - cv.Optional(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid, -}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend( - cv.COMPONENT_SCHEMA), cv.has_exactly_one_key(CONF_MAC_ADDRESS, CONF_SERVICE_UUID)) +CONFIG_SCHEMA = cv.All( + sensor.sensor_schema(UNIT_DECIBEL, ICON_EMPTY, 0, DEVICE_CLASS_SIGNAL_STRENGTH).extend({ + cv.GenerateID(): cv.declare_id(BLERSSISensor), + cv.Optional(CONF_MAC_ADDRESS): cv.mac_address, + cv.Optional(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid, + }).extend( + esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA + ).extend(cv.COMPONENT_SCHEMA), cv.has_exactly_one_key(CONF_MAC_ADDRESS, CONF_SERVICE_UUID)) def to_code(config): diff --git a/esphome/components/bme280/sensor.py b/esphome/components/bme280/sensor.py index 651752102f..26e1df40ee 100644 --- a/esphome/components/bme280/sensor.py +++ b/esphome/components/bme280/sensor.py @@ -2,8 +2,8 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor from esphome.const import CONF_HUMIDITY, CONF_ID, CONF_IIR_FILTER, CONF_OVERSAMPLING, \ - CONF_PRESSURE, CONF_TEMPERATURE, ICON_THERMOMETER, \ - UNIT_CELSIUS, UNIT_HECTOPASCAL, ICON_GAUGE, ICON_WATER_PERCENT, UNIT_PERCENT + CONF_PRESSURE, CONF_TEMPERATURE, DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_PRESSURE, \ + DEVICE_CLASS_TEMPERATURE, ICON_EMPTY, UNIT_CELSIUS, UNIT_HECTOPASCAL, UNIT_PERCENT DEPENDENCIES = ['i2c'] @@ -32,17 +32,17 @@ BME280Component = bme280_ns.class_('BME280Component', cg.PollingComponent, i2c.I CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(BME280Component), cv.Optional(CONF_TEMPERATURE): - sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1).extend({ + sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE).extend({ cv.Optional(CONF_OVERSAMPLING, default='16X'): cv.enum(OVERSAMPLING_OPTIONS, upper=True), }), cv.Optional(CONF_PRESSURE): - sensor.sensor_schema(UNIT_HECTOPASCAL, ICON_GAUGE, 1).extend({ + sensor.sensor_schema(UNIT_HECTOPASCAL, ICON_EMPTY, 1, DEVICE_CLASS_PRESSURE).extend({ cv.Optional(CONF_OVERSAMPLING, default='16X'): cv.enum(OVERSAMPLING_OPTIONS, upper=True), }), cv.Optional(CONF_HUMIDITY): - sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 1).extend({ + sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 1, DEVICE_CLASS_HUMIDITY).extend({ cv.Optional(CONF_OVERSAMPLING, default='16X'): cv.enum(OVERSAMPLING_OPTIONS, upper=True), }), diff --git a/esphome/components/bme680/sensor.py b/esphome/components/bme680/sensor.py index 64973fb91c..705ddc41f7 100644 --- a/esphome/components/bme680/sensor.py +++ b/esphome/components/bme680/sensor.py @@ -4,8 +4,9 @@ from esphome import core from esphome.components import i2c, sensor from esphome.const import CONF_DURATION, CONF_GAS_RESISTANCE, CONF_HEATER, \ CONF_HUMIDITY, CONF_ID, CONF_IIR_FILTER, CONF_OVERSAMPLING, CONF_PRESSURE, \ - CONF_TEMPERATURE, UNIT_OHM, ICON_GAS_CYLINDER, UNIT_CELSIUS, \ - ICON_THERMOMETER, UNIT_HECTOPASCAL, ICON_GAUGE, ICON_WATER_PERCENT, UNIT_PERCENT + CONF_TEMPERATURE, DEVICE_CLASS_EMPTY, DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_PRESSURE, \ + DEVICE_CLASS_TEMPERATURE, UNIT_OHM, ICON_GAS_CYLINDER, UNIT_CELSIUS, ICON_EMPTY, \ + UNIT_HECTOPASCAL, UNIT_PERCENT DEPENDENCIES = ['i2c'] @@ -37,22 +38,22 @@ BME680Component = bme680_ns.class_('BME680Component', cg.PollingComponent, i2c.I CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(BME680Component), cv.Optional(CONF_TEMPERATURE): - sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1).extend({ + sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE).extend({ cv.Optional(CONF_OVERSAMPLING, default='16X'): cv.enum(OVERSAMPLING_OPTIONS, upper=True), }), cv.Optional(CONF_PRESSURE): - sensor.sensor_schema(UNIT_HECTOPASCAL, ICON_GAUGE, 1).extend({ + sensor.sensor_schema(UNIT_HECTOPASCAL, ICON_EMPTY, 1, DEVICE_CLASS_PRESSURE).extend({ cv.Optional(CONF_OVERSAMPLING, default='16X'): cv.enum(OVERSAMPLING_OPTIONS, upper=True), }), cv.Optional(CONF_HUMIDITY): - sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 1).extend({ + sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 1, DEVICE_CLASS_HUMIDITY).extend({ cv.Optional(CONF_OVERSAMPLING, default='16X'): cv.enum(OVERSAMPLING_OPTIONS, upper=True), }), cv.Optional(CONF_GAS_RESISTANCE): - sensor.sensor_schema(UNIT_OHM, ICON_GAS_CYLINDER, 1), + sensor.sensor_schema(UNIT_OHM, ICON_GAS_CYLINDER, 1, DEVICE_CLASS_EMPTY), cv.Optional(CONF_IIR_FILTER, default='OFF'): cv.enum(IIR_FILTER_OPTIONS, upper=True), cv.Optional(CONF_HEATER): cv.Any(None, cv.All(cv.Schema({ cv.Optional(CONF_TEMPERATURE, default=320): cv.int_range(min=200, max=400), diff --git a/esphome/components/bmp085/sensor.py b/esphome/components/bmp085/sensor.py index 558c6978b1..e15fe1335c 100644 --- a/esphome/components/bmp085/sensor.py +++ b/esphome/components/bmp085/sensor.py @@ -1,8 +1,8 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import CONF_ID, CONF_PRESSURE, CONF_TEMPERATURE, \ - UNIT_CELSIUS, ICON_THERMOMETER, ICON_GAUGE, UNIT_HECTOPASCAL +from esphome.const import CONF_ID, CONF_PRESSURE, CONF_TEMPERATURE, DEVICE_CLASS_PRESSURE, \ + DEVICE_CLASS_TEMPERATURE, UNIT_CELSIUS, ICON_EMPTY, UNIT_HECTOPASCAL DEPENDENCIES = ['i2c'] @@ -11,8 +11,10 @@ BMP085Component = bmp085_ns.class_('BMP085Component', cg.PollingComponent, i2c.I CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(BMP085Component), - cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1), - cv.Optional(CONF_PRESSURE): sensor.sensor_schema(UNIT_HECTOPASCAL, ICON_GAUGE, 1), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, + DEVICE_CLASS_TEMPERATURE), + cv.Optional(CONF_PRESSURE): sensor.sensor_schema(UNIT_HECTOPASCAL, ICON_EMPTY, 1, + DEVICE_CLASS_PRESSURE), }).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x77)) diff --git a/esphome/components/bmp280/sensor.py b/esphome/components/bmp280/sensor.py index 63c9655331..c485cdb710 100644 --- a/esphome/components/bmp280/sensor.py +++ b/esphome/components/bmp280/sensor.py @@ -1,9 +1,9 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import CONF_ID, CONF_PRESSURE, CONF_TEMPERATURE, \ - UNIT_CELSIUS, ICON_THERMOMETER, ICON_GAUGE, UNIT_HECTOPASCAL, \ - CONF_IIR_FILTER, CONF_OVERSAMPLING +from esphome.const import CONF_ID, CONF_PRESSURE, CONF_TEMPERATURE, DEVICE_CLASS_PRESSURE, \ + DEVICE_CLASS_TEMPERATURE, UNIT_CELSIUS, ICON_EMPTY, UNIT_HECTOPASCAL, CONF_IIR_FILTER, \ + CONF_OVERSAMPLING DEPENDENCIES = ['i2c'] @@ -31,10 +31,14 @@ BMP280Component = bmp280_ns.class_('BMP280Component', cg.PollingComponent, i2c.I CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(BMP280Component), - cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1).extend({ + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( + UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE + ).extend({ cv.Optional(CONF_OVERSAMPLING, default='16X'): cv.enum(OVERSAMPLING_OPTIONS, upper=True), }), - cv.Optional(CONF_PRESSURE): sensor.sensor_schema(UNIT_HECTOPASCAL, ICON_GAUGE, 1).extend({ + cv.Optional(CONF_PRESSURE): sensor.sensor_schema( + UNIT_HECTOPASCAL, ICON_EMPTY, 1, DEVICE_CLASS_PRESSURE + ).extend({ cv.Optional(CONF_OVERSAMPLING, default='16X'): cv.enum(OVERSAMPLING_OPTIONS, upper=True), }), cv.Optional(CONF_IIR_FILTER, default='OFF'): cv.enum(IIR_FILTER_OPTIONS, upper=True), diff --git a/esphome/components/ccs811/sensor.py b/esphome/components/ccs811/sensor.py index 0f755d5c11..f205121e11 100644 --- a/esphome/components/ccs811/sensor.py +++ b/esphome/components/ccs811/sensor.py @@ -1,7 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import CONF_ID, ICON_RADIATOR, UNIT_PARTS_PER_MILLION, \ +from esphome.const import CONF_ID, DEVICE_CLASS_EMPTY, ICON_RADIATOR, UNIT_PARTS_PER_MILLION, \ UNIT_PARTS_PER_BILLION, CONF_TEMPERATURE, CONF_HUMIDITY, ICON_MOLECULE_CO2 DEPENDENCIES = ['i2c'] @@ -16,8 +16,9 @@ CONF_BASELINE = 'baseline' CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(CCS811Component), cv.Required(CONF_ECO2): sensor.sensor_schema(UNIT_PARTS_PER_MILLION, ICON_MOLECULE_CO2, - 0), - cv.Required(CONF_TVOC): sensor.sensor_schema(UNIT_PARTS_PER_BILLION, ICON_RADIATOR, 0), + 0, DEVICE_CLASS_EMPTY), + cv.Required(CONF_TVOC): sensor.sensor_schema(UNIT_PARTS_PER_BILLION, ICON_RADIATOR, + 0, DEVICE_CLASS_EMPTY), cv.Optional(CONF_BASELINE): cv.hex_uint16_t, cv.Optional(CONF_TEMPERATURE): cv.use_id(sensor.Sensor), diff --git a/esphome/components/cse7766/sensor.py b/esphome/components/cse7766/sensor.py index a415d67688..1c5fcd8368 100644 --- a/esphome/components/cse7766/sensor.py +++ b/esphome/components/cse7766/sensor.py @@ -1,8 +1,8 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, uart -from esphome.const import CONF_CURRENT, CONF_ID, CONF_POWER, CONF_VOLTAGE, \ - UNIT_VOLT, ICON_FLASH, UNIT_AMPERE, UNIT_WATT +from esphome.const import CONF_CURRENT, CONF_ID, CONF_POWER, CONF_VOLTAGE, DEVICE_CLASS_CURRENT, \ + DEVICE_CLASS_POWER, DEVICE_CLASS_VOLTAGE, ICON_EMPTY, UNIT_VOLT, UNIT_AMPERE, UNIT_WATT DEPENDENCIES = ['uart'] @@ -12,9 +12,10 @@ CSE7766Component = cse7766_ns.class_('CSE7766Component', cg.PollingComponent, ua CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(CSE7766Component), - cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 1), - cv.Optional(CONF_CURRENT): sensor.sensor_schema(UNIT_AMPERE, ICON_FLASH, 2), - cv.Optional(CONF_POWER): sensor.sensor_schema(UNIT_WATT, ICON_FLASH, 1), + cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE), + cv.Optional(CONF_CURRENT): sensor.sensor_schema(UNIT_AMPERE, ICON_EMPTY, 2, + DEVICE_CLASS_CURRENT), + cv.Optional(CONF_POWER): sensor.sensor_schema(UNIT_WATT, ICON_EMPTY, 1, DEVICE_CLASS_POWER), }).extend(cv.polling_component_schema('60s')).extend(uart.UART_DEVICE_SCHEMA) diff --git a/esphome/components/ct_clamp/sensor.py b/esphome/components/ct_clamp/sensor.py index 42a3b66497..276fd8f1a3 100644 --- a/esphome/components/ct_clamp/sensor.py +++ b/esphome/components/ct_clamp/sensor.py @@ -1,7 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, voltage_sampler -from esphome.const import CONF_SENSOR, CONF_ID, ICON_FLASH, UNIT_AMPERE +from esphome.const import CONF_SENSOR, CONF_ID, DEVICE_CLASS_CURRENT, ICON_EMPTY, UNIT_AMPERE AUTO_LOAD = ['voltage_sampler'] CODEOWNERS = ['@jesserockz'] @@ -11,7 +11,7 @@ CONF_SAMPLE_DURATION = 'sample_duration' ct_clamp_ns = cg.esphome_ns.namespace('ct_clamp') CTClampSensor = ct_clamp_ns.class_('CTClampSensor', sensor.Sensor, cg.PollingComponent) -CONFIG_SCHEMA = sensor.sensor_schema(UNIT_AMPERE, ICON_FLASH, 2).extend({ +CONFIG_SCHEMA = sensor.sensor_schema(UNIT_AMPERE, ICON_EMPTY, 2, DEVICE_CLASS_CURRENT).extend({ cv.GenerateID(): cv.declare_id(CTClampSensor), cv.Required(CONF_SENSOR): cv.use_id(voltage_sampler.VoltageSampler), cv.Optional(CONF_SAMPLE_DURATION, default='200ms'): cv.positive_time_period_milliseconds, diff --git a/esphome/components/dallas/sensor.py b/esphome/components/dallas/sensor.py index df9d561995..457a21100f 100644 --- a/esphome/components/dallas/sensor.py +++ b/esphome/components/dallas/sensor.py @@ -1,13 +1,15 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor -from esphome.const import CONF_ADDRESS, CONF_DALLAS_ID, CONF_INDEX, CONF_RESOLUTION, UNIT_CELSIUS, \ - ICON_THERMOMETER, CONF_ID +from esphome.const import CONF_ADDRESS, CONF_DALLAS_ID, CONF_INDEX, CONF_RESOLUTION, \ + DEVICE_CLASS_TEMPERATURE, ICON_EMPTY, UNIT_CELSIUS, CONF_ID from . import DallasComponent, dallas_ns DallasTemperatureSensor = dallas_ns.class_('DallasTemperatureSensor', sensor.Sensor) -CONFIG_SCHEMA = cv.All(sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1).extend({ +CONFIG_SCHEMA = cv.All(sensor.sensor_schema( + UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE +).extend({ cv.GenerateID(): cv.declare_id(DallasTemperatureSensor), cv.GenerateID(CONF_DALLAS_ID): cv.use_id(DallasComponent), diff --git a/esphome/components/dht/sensor.py b/esphome/components/dht/sensor.py index 8749d85b52..a8a101ddf4 100644 --- a/esphome/components/dht/sensor.py +++ b/esphome/components/dht/sensor.py @@ -3,7 +3,8 @@ import esphome.config_validation as cv from esphome import pins from esphome.components import sensor from esphome.const import CONF_HUMIDITY, CONF_ID, CONF_MODEL, CONF_PIN, CONF_TEMPERATURE, \ - ICON_THERMOMETER, UNIT_CELSIUS, ICON_WATER_PERCENT, UNIT_PERCENT + ICON_EMPTY, UNIT_CELSIUS, UNIT_PERCENT, DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_HUMIDITY + from esphome.cpp_helpers import gpio_pin_expression dht_ns = cg.esphome_ns.namespace('dht') @@ -22,8 +23,10 @@ DHT = dht_ns.class_('DHT', cg.PollingComponent) CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(DHT), cv.Required(CONF_PIN): pins.gpio_input_pin_schema, - cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1), - cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 0), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, + DEVICE_CLASS_TEMPERATURE), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 0, + DEVICE_CLASS_HUMIDITY), cv.Optional(CONF_MODEL, default='auto detect'): cv.enum(DHT_MODELS, upper=True, space='_'), }).extend(cv.polling_component_schema('60s')) diff --git a/esphome/components/dht12/sensor.py b/esphome/components/dht12/sensor.py index 7d86e8c836..4e418dbc9e 100644 --- a/esphome/components/dht12/sensor.py +++ b/esphome/components/dht12/sensor.py @@ -2,7 +2,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor from esphome.const import CONF_HUMIDITY, CONF_ID, CONF_TEMPERATURE, \ - UNIT_CELSIUS, ICON_THERMOMETER, ICON_WATER_PERCENT, UNIT_PERCENT + UNIT_CELSIUS, ICON_EMPTY, UNIT_PERCENT, DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_HUMIDITY DEPENDENCIES = ['i2c'] @@ -11,8 +11,10 @@ DHT12Component = dht12_ns.class_('DHT12Component', cg.PollingComponent, i2c.I2CD CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(DHT12Component), - cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1), - cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 1), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, + DEVICE_CLASS_TEMPERATURE), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 1, + DEVICE_CLASS_HUMIDITY), }).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x5C)) diff --git a/esphome/components/duty_cycle/sensor.py b/esphome/components/duty_cycle/sensor.py index 51d99aae6a..930745cfc4 100644 --- a/esphome/components/duty_cycle/sensor.py +++ b/esphome/components/duty_cycle/sensor.py @@ -2,12 +2,12 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins from esphome.components import sensor -from esphome.const import CONF_ID, CONF_PIN, UNIT_PERCENT, ICON_PERCENT +from esphome.const import CONF_ID, CONF_PIN, DEVICE_CLASS_EMPTY, UNIT_PERCENT, ICON_PERCENT duty_cycle_ns = cg.esphome_ns.namespace('duty_cycle') DutyCycleSensor = duty_cycle_ns.class_('DutyCycleSensor', sensor.Sensor, cg.PollingComponent) -CONFIG_SCHEMA = sensor.sensor_schema(UNIT_PERCENT, ICON_PERCENT, 1).extend({ +CONFIG_SCHEMA = sensor.sensor_schema(UNIT_PERCENT, ICON_PERCENT, 1, DEVICE_CLASS_EMPTY).extend({ cv.GenerateID(): cv.declare_id(DutyCycleSensor), cv.Required(CONF_PIN): cv.All(pins.internal_gpio_input_pin_schema, pins.validate_has_interrupt), diff --git a/esphome/components/esp32_hall/sensor.py b/esphome/components/esp32_hall/sensor.py index ec24f1aab6..02a4b4900e 100644 --- a/esphome/components/esp32_hall/sensor.py +++ b/esphome/components/esp32_hall/sensor.py @@ -1,14 +1,15 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor -from esphome.const import CONF_ID, ESP_PLATFORM_ESP32, UNIT_MICROTESLA, ICON_MAGNET +from esphome.const import CONF_ID, DEVICE_CLASS_EMPTY, ESP_PLATFORM_ESP32, UNIT_MICROTESLA, \ + ICON_MAGNET ESP_PLATFORMS = [ESP_PLATFORM_ESP32] esp32_hall_ns = cg.esphome_ns.namespace('esp32_hall') ESP32HallSensor = esp32_hall_ns.class_('ESP32HallSensor', sensor.Sensor, cg.PollingComponent) -CONFIG_SCHEMA = sensor.sensor_schema(UNIT_MICROTESLA, ICON_MAGNET, 1).extend({ +CONFIG_SCHEMA = sensor.sensor_schema(UNIT_MICROTESLA, ICON_MAGNET, 1, DEVICE_CLASS_EMPTY).extend({ cv.GenerateID(): cv.declare_id(ESP32HallSensor), }).extend(cv.polling_component_schema('60s')) diff --git a/esphome/components/hdc1080/sensor.py b/esphome/components/hdc1080/sensor.py index 00b8296351..fe80374beb 100644 --- a/esphome/components/hdc1080/sensor.py +++ b/esphome/components/hdc1080/sensor.py @@ -1,8 +1,8 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import CONF_HUMIDITY, CONF_ID, CONF_TEMPERATURE, \ - ICON_THERMOMETER, ICON_WATER_PERCENT, UNIT_CELSIUS, UNIT_PERCENT +from esphome.const import CONF_HUMIDITY, CONF_ID, CONF_TEMPERATURE, DEVICE_CLASS_HUMIDITY, \ + DEVICE_CLASS_TEMPERATURE, ICON_EMPTY, UNIT_CELSIUS, UNIT_PERCENT DEPENDENCIES = ['i2c'] @@ -11,8 +11,10 @@ HDC1080Component = hdc1080_ns.class_('HDC1080Component', cg.PollingComponent, i2 CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(HDC1080Component), - cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1), - cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 0), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, + DEVICE_CLASS_TEMPERATURE), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 0, + DEVICE_CLASS_HUMIDITY), }).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x40)) diff --git a/esphome/components/hlw8012/sensor.py b/esphome/components/hlw8012/sensor.py index e1a0ec6f18..74230c53c1 100644 --- a/esphome/components/hlw8012/sensor.py +++ b/esphome/components/hlw8012/sensor.py @@ -4,7 +4,8 @@ from esphome import pins from esphome.components import sensor from esphome.const import CONF_CHANGE_MODE_EVERY, CONF_INITIAL_MODE, CONF_CURRENT, \ CONF_CURRENT_RESISTOR, CONF_ID, CONF_POWER, CONF_ENERGY, CONF_SEL_PIN, CONF_VOLTAGE, \ - CONF_VOLTAGE_DIVIDER, ICON_FLASH, UNIT_VOLT, UNIT_AMPERE, UNIT_WATT, UNIT_WATT_HOURS + CONF_VOLTAGE_DIVIDER, DEVICE_CLASS_CURRENT, DEVICE_CLASS_ENERGY, DEVICE_CLASS_POWER, \ + DEVICE_CLASS_VOLTAGE, ICON_EMPTY, UNIT_VOLT, UNIT_AMPERE, UNIT_WATT, UNIT_WATT_HOURS AUTO_LOAD = ['pulse_counter'] @@ -26,10 +27,12 @@ CONFIG_SCHEMA = cv.Schema({ cv.Required(CONF_CF1_PIN): cv.All(pins.internal_gpio_input_pullup_pin_schema, pins.validate_has_interrupt), - cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 1), - cv.Optional(CONF_CURRENT): sensor.sensor_schema(UNIT_AMPERE, ICON_FLASH, 2), - cv.Optional(CONF_POWER): sensor.sensor_schema(UNIT_WATT, ICON_FLASH, 1), - cv.Optional(CONF_ENERGY): sensor.sensor_schema(UNIT_WATT_HOURS, ICON_FLASH, 1), + cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE), + cv.Optional(CONF_CURRENT): sensor.sensor_schema(UNIT_AMPERE, ICON_EMPTY, 2, + DEVICE_CLASS_CURRENT), + cv.Optional(CONF_POWER): sensor.sensor_schema(UNIT_WATT, ICON_EMPTY, 1, DEVICE_CLASS_POWER), + cv.Optional(CONF_ENERGY): sensor.sensor_schema(UNIT_WATT_HOURS, ICON_EMPTY, 1, + DEVICE_CLASS_ENERGY), cv.Optional(CONF_CURRENT_RESISTOR, default=0.001): cv.resistance, cv.Optional(CONF_VOLTAGE_DIVIDER, default=2351): cv.positive_float, diff --git a/esphome/components/hm3301/sensor.py b/esphome/components/hm3301/sensor.py index ef7669bc03..67bfd3541a 100644 --- a/esphome/components/hm3301/sensor.py +++ b/esphome/components/hm3301/sensor.py @@ -1,7 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import CONF_ID, CONF_PM_2_5, CONF_PM_10_0, CONF_PM_1_0, \ +from esphome.const import CONF_ID, CONF_PM_2_5, CONF_PM_10_0, CONF_PM_1_0, DEVICE_CLASS_EMPTY, \ UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_CHEMICAL_WEAPON DEPENDENCIES = ['i2c'] @@ -32,13 +32,16 @@ CONFIG_SCHEMA = cv.All(cv.Schema({ cv.GenerateID(): cv.declare_id(HM3301Component), cv.Optional(CONF_PM_1_0): - sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_CHEMICAL_WEAPON, 0), + sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_CHEMICAL_WEAPON, 0, + DEVICE_CLASS_EMPTY), cv.Optional(CONF_PM_2_5): - sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_CHEMICAL_WEAPON, 0), + sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_CHEMICAL_WEAPON, 0, + DEVICE_CLASS_EMPTY), cv.Optional(CONF_PM_10_0): - sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_CHEMICAL_WEAPON, 0), + sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_CHEMICAL_WEAPON, 0, + DEVICE_CLASS_EMPTY), cv.Optional(CONF_AQI): - sensor.sensor_schema(UNIT_INDEX, ICON_CHEMICAL_WEAPON, 0).extend({ + sensor.sensor_schema(UNIT_INDEX, ICON_CHEMICAL_WEAPON, 0, DEVICE_CLASS_EMPTY).extend({ cv.Required(CONF_CALCULATION_TYPE): cv.enum(AQI_CALCULATION_TYPE, upper=True), }) }).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x40)), validate) diff --git a/esphome/components/hmc5883l/sensor.py b/esphome/components/hmc5883l/sensor.py index b063284698..d985694934 100644 --- a/esphome/components/hmc5883l/sensor.py +++ b/esphome/components/hmc5883l/sensor.py @@ -1,9 +1,9 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import (CONF_ADDRESS, CONF_ID, CONF_OVERSAMPLING, CONF_RANGE, ICON_MAGNET, - UNIT_MICROTESLA, UNIT_DEGREES, ICON_SCREEN_ROTATION, - CONF_UPDATE_INTERVAL) +from esphome.const import CONF_ADDRESS, CONF_ID, CONF_OVERSAMPLING, CONF_RANGE, \ + DEVICE_CLASS_EMPTY, ICON_MAGNET, UNIT_MICROTESLA, UNIT_DEGREES, ICON_SCREEN_ROTATION, \ + CONF_UPDATE_INTERVAL DEPENDENCIES = ['i2c'] @@ -65,8 +65,8 @@ def validate_enum(enum_values, units=None, int=True): return validate_enum_bound -field_strength_schema = sensor.sensor_schema(UNIT_MICROTESLA, ICON_MAGNET, 1) -heading_schema = sensor.sensor_schema(UNIT_DEGREES, ICON_SCREEN_ROTATION, 1) +field_strength_schema = sensor.sensor_schema(UNIT_MICROTESLA, ICON_MAGNET, 1, DEVICE_CLASS_EMPTY) +heading_schema = sensor.sensor_schema(UNIT_DEGREES, ICON_SCREEN_ROTATION, 1, DEVICE_CLASS_EMPTY) CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(HMC5883LComponent), diff --git a/esphome/components/homeassistant/sensor/__init__.py b/esphome/components/homeassistant/sensor/__init__.py index 577efca79b..a413518e07 100644 --- a/esphome/components/homeassistant/sensor/__init__.py +++ b/esphome/components/homeassistant/sensor/__init__.py @@ -1,7 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor -from esphome.const import CONF_ENTITY_ID, CONF_ID, ICON_EMPTY, UNIT_EMPTY +from esphome.const import CONF_ENTITY_ID, CONF_ID, ICON_EMPTY, UNIT_EMPTY, DEVICE_CLASS_EMPTY from .. import homeassistant_ns DEPENDENCIES = ['api'] @@ -9,7 +9,7 @@ DEPENDENCIES = ['api'] HomeassistantSensor = homeassistant_ns.class_('HomeassistantSensor', sensor.Sensor, cg.Component) -CONFIG_SCHEMA = sensor.sensor_schema(UNIT_EMPTY, ICON_EMPTY, 1).extend({ +CONFIG_SCHEMA = sensor.sensor_schema(UNIT_EMPTY, ICON_EMPTY, 1, DEVICE_CLASS_EMPTY).extend({ cv.GenerateID(): cv.declare_id(HomeassistantSensor), cv.Required(CONF_ENTITY_ID): cv.entity_id, }) diff --git a/esphome/components/htu21d/sensor.py b/esphome/components/htu21d/sensor.py index 20053d27dd..358eb33d6d 100644 --- a/esphome/components/htu21d/sensor.py +++ b/esphome/components/htu21d/sensor.py @@ -1,8 +1,8 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import CONF_HUMIDITY, CONF_ID, CONF_TEMPERATURE, \ - ICON_THERMOMETER, UNIT_CELSIUS, UNIT_PERCENT, ICON_WATER_PERCENT +from esphome.const import CONF_HUMIDITY, CONF_ID, CONF_TEMPERATURE, DEVICE_CLASS_HUMIDITY, \ + DEVICE_CLASS_TEMPERATURE, ICON_EMPTY, UNIT_CELSIUS, UNIT_PERCENT DEPENDENCIES = ['i2c'] @@ -11,8 +11,10 @@ HTU21DComponent = htu21d_ns.class_('HTU21DComponent', cg.PollingComponent, i2c.I CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(HTU21DComponent), - cv.Required(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1), - cv.Required(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 1), + cv.Required(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, + DEVICE_CLASS_TEMPERATURE), + cv.Required(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 1, + DEVICE_CLASS_HUMIDITY), }).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x40)) diff --git a/esphome/components/hx711/sensor.py b/esphome/components/hx711/sensor.py index 2fc333a243..ad060646dc 100644 --- a/esphome/components/hx711/sensor.py +++ b/esphome/components/hx711/sensor.py @@ -2,7 +2,8 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins from esphome.components import sensor -from esphome.const import CONF_CLK_PIN, CONF_GAIN, CONF_ID, ICON_SCALE +from esphome.const import CONF_CLK_PIN, CONF_GAIN, CONF_ID, DEVICE_CLASS_EMPTY, ICON_SCALE, \ + UNIT_EMPTY hx711_ns = cg.esphome_ns.namespace('hx711') HX711Sensor = hx711_ns.class_('HX711Sensor', sensor.Sensor, cg.PollingComponent) @@ -16,7 +17,7 @@ GAINS = { 64: HX711Gain.HX711_GAIN_64, } -CONFIG_SCHEMA = sensor.sensor_schema('', ICON_SCALE, 0).extend({ +CONFIG_SCHEMA = sensor.sensor_schema(UNIT_EMPTY, ICON_SCALE, 0, DEVICE_CLASS_EMPTY).extend({ cv.GenerateID(): cv.declare_id(HX711Sensor), cv.Required(CONF_DOUT_PIN): pins.gpio_input_pin_schema, cv.Required(CONF_CLK_PIN): pins.gpio_output_pin_schema, diff --git a/esphome/components/ina219/sensor.py b/esphome/components/ina219/sensor.py index 6a61e16226..6229dad0d8 100644 --- a/esphome/components/ina219/sensor.py +++ b/esphome/components/ina219/sensor.py @@ -1,9 +1,9 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import CONF_BUS_VOLTAGE, CONF_CURRENT, CONF_ID, \ - CONF_MAX_CURRENT, CONF_MAX_VOLTAGE, CONF_POWER, CONF_SHUNT_RESISTANCE, \ - CONF_SHUNT_VOLTAGE, ICON_FLASH, UNIT_VOLT, UNIT_AMPERE, UNIT_WATT +from esphome.const import CONF_BUS_VOLTAGE, CONF_CURRENT, CONF_ID, CONF_MAX_CURRENT, \ + CONF_MAX_VOLTAGE, CONF_POWER, CONF_SHUNT_RESISTANCE, CONF_SHUNT_VOLTAGE, DEVICE_CLASS_CURRENT, \ + DEVICE_CLASS_POWER, DEVICE_CLASS_VOLTAGE, ICON_EMPTY, UNIT_VOLT, UNIT_AMPERE, UNIT_WATT DEPENDENCIES = ['i2c'] @@ -12,10 +12,13 @@ INA219Component = ina219_ns.class_('INA219Component', cg.PollingComponent, i2c.I CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(INA219Component), - cv.Optional(CONF_BUS_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 2), - cv.Optional(CONF_SHUNT_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 2), - cv.Optional(CONF_CURRENT): sensor.sensor_schema(UNIT_AMPERE, ICON_FLASH, 3), - cv.Optional(CONF_POWER): sensor.sensor_schema(UNIT_WATT, ICON_FLASH, 2), + cv.Optional(CONF_BUS_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_EMPTY, 2, + DEVICE_CLASS_VOLTAGE), + cv.Optional(CONF_SHUNT_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_EMPTY, 2, + DEVICE_CLASS_VOLTAGE), + cv.Optional(CONF_CURRENT): sensor.sensor_schema(UNIT_AMPERE, ICON_EMPTY, 3, + DEVICE_CLASS_CURRENT), + cv.Optional(CONF_POWER): sensor.sensor_schema(UNIT_WATT, ICON_EMPTY, 2, DEVICE_CLASS_POWER), cv.Optional(CONF_SHUNT_RESISTANCE, default=0.1): cv.All(cv.resistance, cv.Range(min=0.0, max=32.0)), cv.Optional(CONF_MAX_VOLTAGE, default=32.0): cv.All(cv.voltage, cv.Range(min=0.0, max=32.0)), diff --git a/esphome/components/ina226/sensor.py b/esphome/components/ina226/sensor.py index 066363b3d4..b36a74755f 100644 --- a/esphome/components/ina226/sensor.py +++ b/esphome/components/ina226/sensor.py @@ -1,9 +1,9 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import CONF_BUS_VOLTAGE, CONF_CURRENT, CONF_ID, \ - CONF_MAX_CURRENT, CONF_POWER, CONF_SHUNT_RESISTANCE, \ - CONF_SHUNT_VOLTAGE, ICON_FLASH, UNIT_VOLT, UNIT_AMPERE, UNIT_WATT +from esphome.const import CONF_BUS_VOLTAGE, CONF_CURRENT, CONF_ID, CONF_MAX_CURRENT, CONF_POWER, \ + CONF_SHUNT_RESISTANCE, CONF_SHUNT_VOLTAGE, DEVICE_CLASS_VOLTAGE, DEVICE_CLASS_CURRENT, \ + DEVICE_CLASS_POWER, ICON_EMPTY, UNIT_VOLT, UNIT_AMPERE, UNIT_WATT DEPENDENCIES = ['i2c'] @@ -12,10 +12,13 @@ INA226Component = ina226_ns.class_('INA226Component', cg.PollingComponent, i2c.I CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(INA226Component), - cv.Optional(CONF_BUS_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 2), - cv.Optional(CONF_SHUNT_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 2), - cv.Optional(CONF_CURRENT): sensor.sensor_schema(UNIT_AMPERE, ICON_FLASH, 3), - cv.Optional(CONF_POWER): sensor.sensor_schema(UNIT_WATT, ICON_FLASH, 2), + cv.Optional(CONF_BUS_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_EMPTY, 2, + DEVICE_CLASS_VOLTAGE), + cv.Optional(CONF_SHUNT_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_EMPTY, 2, + DEVICE_CLASS_VOLTAGE), + cv.Optional(CONF_CURRENT): sensor.sensor_schema(UNIT_AMPERE, ICON_EMPTY, 3, + DEVICE_CLASS_CURRENT), + cv.Optional(CONF_POWER): sensor.sensor_schema(UNIT_WATT, ICON_EMPTY, 2, DEVICE_CLASS_POWER), cv.Optional(CONF_SHUNT_RESISTANCE, default=0.1): cv.All(cv.resistance, cv.Range(min=0.0)), cv.Optional(CONF_MAX_CURRENT, default=3.2): cv.All(cv.current, cv.Range(min=0.0)), }).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x40)) diff --git a/esphome/components/ina3221/sensor.py b/esphome/components/ina3221/sensor.py index 1c26533cc4..e71f9f5d9b 100644 --- a/esphome/components/ina3221/sensor.py +++ b/esphome/components/ina3221/sensor.py @@ -2,8 +2,8 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor from esphome.const import CONF_BUS_VOLTAGE, CONF_CURRENT, CONF_ID, CONF_POWER, \ - CONF_SHUNT_RESISTANCE, CONF_SHUNT_VOLTAGE, ICON_FLASH, \ - UNIT_VOLT, UNIT_AMPERE, UNIT_WATT + CONF_SHUNT_RESISTANCE, CONF_SHUNT_VOLTAGE, DEVICE_CLASS_VOLTAGE, DEVICE_CLASS_CURRENT, \ + DEVICE_CLASS_POWER, ICON_EMPTY, UNIT_VOLT, UNIT_AMPERE, UNIT_WATT DEPENDENCIES = ['i2c'] @@ -15,10 +15,13 @@ ina3221_ns = cg.esphome_ns.namespace('ina3221') INA3221Component = ina3221_ns.class_('INA3221Component', cg.PollingComponent, i2c.I2CDevice) INA3221_CHANNEL_SCHEMA = cv.Schema({ - cv.Optional(CONF_BUS_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 2), - cv.Optional(CONF_SHUNT_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 2), - cv.Optional(CONF_CURRENT): sensor.sensor_schema(UNIT_AMPERE, ICON_FLASH, 2), - cv.Optional(CONF_POWER): sensor.sensor_schema(UNIT_WATT, ICON_FLASH, 2), + cv.Optional(CONF_BUS_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_EMPTY, 2, + DEVICE_CLASS_VOLTAGE), + cv.Optional(CONF_SHUNT_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_EMPTY, 2, + DEVICE_CLASS_VOLTAGE), + cv.Optional(CONF_CURRENT): sensor.sensor_schema(UNIT_AMPERE, ICON_EMPTY, 2, + DEVICE_CLASS_CURRENT), + cv.Optional(CONF_POWER): sensor.sensor_schema(UNIT_WATT, ICON_EMPTY, 2, DEVICE_CLASS_POWER), cv.Optional(CONF_SHUNT_RESISTANCE, default=0.1): cv.All(cv.resistance, cv.Range(min=0.0, max=32.0)), }) diff --git a/esphome/components/inkbird_ibsth1_mini/sensor.py b/esphome/components/inkbird_ibsth1_mini/sensor.py index 93d90d2e6f..57a16f2883 100644 --- a/esphome/components/inkbird_ibsth1_mini/sensor.py +++ b/esphome/components/inkbird_ibsth1_mini/sensor.py @@ -2,7 +2,8 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, esp32_ble_tracker from esphome.const import CONF_BATTERY_LEVEL, CONF_HUMIDITY, CONF_MAC_ADDRESS, CONF_TEMPERATURE, \ - UNIT_CELSIUS, ICON_THERMOMETER, UNIT_PERCENT, ICON_WATER_PERCENT, ICON_BATTERY, CONF_ID + DEVICE_CLASS_BATTERY, DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_TEMPERATURE, ICON_EMPTY, \ + UNIT_CELSIUS, UNIT_PERCENT, CONF_ID CODEOWNERS = ['@fkirill'] DEPENDENCIES = ['esp32_ble_tracker'] @@ -14,9 +15,12 @@ InkbirdUBSTH1_MINI = inkbird_ibsth1_mini_ns.class_( CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(InkbirdUBSTH1_MINI), cv.Required(CONF_MAC_ADDRESS): cv.mac_address, - cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1), - cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 1), - cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(UNIT_PERCENT, ICON_BATTERY, 0), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, + DEVICE_CLASS_TEMPERATURE), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 1, + DEVICE_CLASS_HUMIDITY), + cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 0, + DEVICE_CLASS_BATTERY), }).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) diff --git a/esphome/components/max31855/sensor.py b/esphome/components/max31855/sensor.py index d1b5649b3e..9f105272a2 100644 --- a/esphome/components/max31855/sensor.py +++ b/esphome/components/max31855/sensor.py @@ -1,16 +1,17 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, spi -from esphome.const import CONF_ID, CONF_REFERENCE_TEMPERATURE, ICON_THERMOMETER, UNIT_CELSIUS +from esphome.const import CONF_ID, CONF_REFERENCE_TEMPERATURE, DEVICE_CLASS_TEMPERATURE, \ + ICON_EMPTY, UNIT_CELSIUS max31855_ns = cg.esphome_ns.namespace('max31855') MAX31855Sensor = max31855_ns.class_('MAX31855Sensor', sensor.Sensor, cg.PollingComponent, spi.SPIDevice) -CONFIG_SCHEMA = sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1).extend({ +CONFIG_SCHEMA = sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE).extend({ cv.GenerateID(): cv.declare_id(MAX31855Sensor), cv.Optional(CONF_REFERENCE_TEMPERATURE): - sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 2), + sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 2, DEVICE_CLASS_TEMPERATURE), }).extend(cv.polling_component_schema('60s')).extend(spi.spi_device_schema()) diff --git a/esphome/components/max31856/sensor.py b/esphome/components/max31856/sensor.py index 4e1411a2a4..95da4e54ad 100644 --- a/esphome/components/max31856/sensor.py +++ b/esphome/components/max31856/sensor.py @@ -1,7 +1,8 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, spi -from esphome.const import CONF_ID, CONF_MAINS_FILTER, ICON_THERMOMETER, UNIT_CELSIUS +from esphome.const import CONF_ID, CONF_MAINS_FILTER, DEVICE_CLASS_TEMPERATURE, ICON_EMPTY, \ + UNIT_CELSIUS max31856_ns = cg.esphome_ns.namespace('max31856') MAX31856Sensor = max31856_ns.class_('MAX31856Sensor', sensor.Sensor, cg.PollingComponent, @@ -13,7 +14,7 @@ FILTER = { '60HZ': MAX31865ConfigFilter.FILTER_60HZ, } -CONFIG_SCHEMA = sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1).extend({ +CONFIG_SCHEMA = sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE).extend({ cv.GenerateID(): cv.declare_id(MAX31856Sensor), cv.Optional(CONF_MAINS_FILTER, default='60HZ'): cv.enum(FILTER, upper=True, space=''), }).extend(cv.polling_component_schema('60s')).extend(spi.spi_device_schema()) diff --git a/esphome/components/max31865/sensor.py b/esphome/components/max31865/sensor.py index 7df36dfde4..70913cc8f7 100644 --- a/esphome/components/max31865/sensor.py +++ b/esphome/components/max31865/sensor.py @@ -2,7 +2,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, spi from esphome.const import CONF_ID, CONF_MAINS_FILTER, CONF_REFERENCE_RESISTANCE, \ - CONF_RTD_NOMINAL_RESISTANCE, CONF_RTD_WIRES, ICON_THERMOMETER, UNIT_CELSIUS + CONF_RTD_NOMINAL_RESISTANCE, CONF_RTD_WIRES, DEVICE_CLASS_TEMPERATURE, ICON_EMPTY, UNIT_CELSIUS max31865_ns = cg.esphome_ns.namespace('max31865') MAX31865Sensor = max31865_ns.class_('MAX31865Sensor', sensor.Sensor, cg.PollingComponent, @@ -14,7 +14,7 @@ FILTER = { '60HZ': MAX31865ConfigFilter.FILTER_60HZ, } -CONFIG_SCHEMA = sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 2).extend({ +CONFIG_SCHEMA = sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 2, DEVICE_CLASS_TEMPERATURE).extend({ cv.GenerateID(): cv.declare_id(MAX31865Sensor), cv.Required(CONF_REFERENCE_RESISTANCE): cv.All(cv.resistance, cv.Range(min=100, max=10000)), cv.Required(CONF_RTD_NOMINAL_RESISTANCE): cv.All(cv.resistance, cv.Range(min=100, max=1000)), diff --git a/esphome/components/max6675/sensor.py b/esphome/components/max6675/sensor.py index 7f0c943399..f5b537ace9 100644 --- a/esphome/components/max6675/sensor.py +++ b/esphome/components/max6675/sensor.py @@ -1,13 +1,13 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, spi -from esphome.const import CONF_ID, ICON_THERMOMETER, UNIT_CELSIUS +from esphome.const import CONF_ID, DEVICE_CLASS_TEMPERATURE, ICON_EMPTY, UNIT_CELSIUS max6675_ns = cg.esphome_ns.namespace('max6675') MAX6675Sensor = max6675_ns.class_('MAX6675Sensor', sensor.Sensor, cg.PollingComponent, spi.SPIDevice) -CONFIG_SCHEMA = sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1).extend({ +CONFIG_SCHEMA = sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE).extend({ cv.GenerateID(): cv.declare_id(MAX6675Sensor), }).extend(cv.polling_component_schema('60s')).extend(spi.spi_device_schema()) diff --git a/esphome/components/mcp9808/sensor.py b/esphome/components/mcp9808/sensor.py index f1ccfadc54..0489dd5e1b 100644 --- a/esphome/components/mcp9808/sensor.py +++ b/esphome/components/mcp9808/sensor.py @@ -1,7 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import CONF_ID, ICON_THERMOMETER, UNIT_CELSIUS +from esphome.const import CONF_ID, DEVICE_CLASS_TEMPERATURE, ICON_EMPTY, UNIT_CELSIUS CODEOWNERS = ['@k7hpn'] DEPENDENCIES = ['i2c'] @@ -10,7 +10,7 @@ mcp9808_ns = cg.esphome_ns.namespace('mcp9808') MCP9808Sensor = mcp9808_ns.class_('MCP9808Sensor', sensor.Sensor, cg.PollingComponent, i2c.I2CDevice) -CONFIG_SCHEMA = sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1).extend({ +CONFIG_SCHEMA = sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE).extend({ cv.GenerateID(): cv.declare_id(MCP9808Sensor), }).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x18)) diff --git a/esphome/components/mhz19/sensor.py b/esphome/components/mhz19/sensor.py index a3e6946efd..aa7db6b775 100644 --- a/esphome/components/mhz19/sensor.py +++ b/esphome/components/mhz19/sensor.py @@ -3,8 +3,8 @@ import esphome.config_validation as cv from esphome import automation from esphome.automation import maybe_simple_id from esphome.components import sensor, uart -from esphome.const import CONF_CO2, CONF_ID, CONF_TEMPERATURE, ICON_MOLECULE_CO2, \ - UNIT_PARTS_PER_MILLION, UNIT_CELSIUS, ICON_EMPTY, DEVICE_CLASS_TEMPERATURE +from esphome.const import CONF_CO2, CONF_ID, CONF_TEMPERATURE, DEVICE_CLASS_EMPTY, \ + DEVICE_CLASS_TEMPERATURE, ICON_MOLECULE_CO2, UNIT_PARTS_PER_MILLION, UNIT_CELSIUS, ICON_EMPTY DEPENDENCIES = ['uart'] @@ -18,7 +18,8 @@ MHZ19ABCDisableAction = mhz19_ns.class_('MHZ19ABCDisableAction', automation.Acti CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(MHZ19Component), - cv.Required(CONF_CO2): sensor.sensor_schema(UNIT_PARTS_PER_MILLION, ICON_MOLECULE_CO2, 0), + cv.Required(CONF_CO2): sensor.sensor_schema( + UNIT_PARTS_PER_MILLION, ICON_MOLECULE_CO2, 0, DEVICE_CLASS_EMPTY), cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( UNIT_CELSIUS, ICON_EMPTY, 0, DEVICE_CLASS_TEMPERATURE), cv.Optional(CONF_AUTOMATIC_BASELINE_CALIBRATION): cv.boolean, diff --git a/esphome/components/mpu6050/sensor.py b/esphome/components/mpu6050/sensor.py index 73c78e7f16..1a02d2ce7c 100644 --- a/esphome/components/mpu6050/sensor.py +++ b/esphome/components/mpu6050/sensor.py @@ -1,9 +1,9 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import CONF_ID, CONF_TEMPERATURE, \ - ICON_BRIEFCASE_DOWNLOAD, UNIT_METER_PER_SECOND_SQUARED, \ - ICON_SCREEN_ROTATION, UNIT_DEGREE_PER_SECOND, ICON_THERMOMETER, UNIT_CELSIUS +from esphome.const import CONF_ID, CONF_TEMPERATURE, DEVICE_CLASS_EMPTY, DEVICE_CLASS_TEMPERATURE, \ + ICON_BRIEFCASE_DOWNLOAD, ICON_EMPTY, UNIT_METER_PER_SECOND_SQUARED, \ + ICON_SCREEN_ROTATION, UNIT_DEGREE_PER_SECOND, UNIT_CELSIUS DEPENDENCIES = ['i2c'] @@ -17,9 +17,11 @@ CONF_GYRO_Z = 'gyro_z' mpu6050_ns = cg.esphome_ns.namespace('mpu6050') MPU6050Component = mpu6050_ns.class_('MPU6050Component', cg.PollingComponent, i2c.I2CDevice) -accel_schema = sensor.sensor_schema(UNIT_METER_PER_SECOND_SQUARED, ICON_BRIEFCASE_DOWNLOAD, 2) -gyro_schema = sensor.sensor_schema(UNIT_DEGREE_PER_SECOND, ICON_SCREEN_ROTATION, 2) -temperature_schema = sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1) +accel_schema = sensor.sensor_schema(UNIT_METER_PER_SECOND_SQUARED, ICON_BRIEFCASE_DOWNLOAD, 2, + DEVICE_CLASS_EMPTY) +gyro_schema = sensor.sensor_schema(UNIT_DEGREE_PER_SECOND, ICON_SCREEN_ROTATION, 2, + DEVICE_CLASS_EMPTY) +temperature_schema = sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE) CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(MPU6050Component), diff --git a/esphome/components/mqtt_subscribe/sensor/__init__.py b/esphome/components/mqtt_subscribe/sensor/__init__.py index dedc2ac9a7..0ccad5c72d 100644 --- a/esphome/components/mqtt_subscribe/sensor/__init__.py +++ b/esphome/components/mqtt_subscribe/sensor/__init__.py @@ -1,7 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import mqtt, sensor -from esphome.const import CONF_ID, CONF_QOS, CONF_TOPIC, UNIT_EMPTY, ICON_EMPTY +from esphome.const import CONF_ID, CONF_QOS, CONF_TOPIC, UNIT_EMPTY, ICON_EMPTY, DEVICE_CLASS_EMPTY from .. import mqtt_subscribe_ns DEPENDENCIES = ['mqtt'] @@ -9,7 +9,7 @@ DEPENDENCIES = ['mqtt'] CONF_MQTT_PARENT_ID = 'mqtt_parent_id' MQTTSubscribeSensor = mqtt_subscribe_ns.class_('MQTTSubscribeSensor', sensor.Sensor, cg.Component) -CONFIG_SCHEMA = sensor.sensor_schema(UNIT_EMPTY, ICON_EMPTY, 1).extend({ +CONFIG_SCHEMA = sensor.sensor_schema(UNIT_EMPTY, ICON_EMPTY, 1, DEVICE_CLASS_EMPTY).extend({ cv.GenerateID(): cv.declare_id(MQTTSubscribeSensor), cv.GenerateID(CONF_MQTT_PARENT_ID): cv.use_id(mqtt.MQTTClientComponent), cv.Required(CONF_TOPIC): cv.subscribe_topic, diff --git a/esphome/components/ms5611/sensor.py b/esphome/components/ms5611/sensor.py index ab9aac6d5f..94ff6cbb0a 100644 --- a/esphome/components/ms5611/sensor.py +++ b/esphome/components/ms5611/sensor.py @@ -2,8 +2,8 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor from esphome.const import CONF_ID, CONF_PRESSURE, \ - CONF_TEMPERATURE, ICON_THERMOMETER, UNIT_CELSIUS, ICON_GAUGE, \ - UNIT_HECTOPASCAL + CONF_TEMPERATURE, DEVICE_CLASS_PRESSURE, DEVICE_CLASS_TEMPERATURE, ICON_EMPTY, UNIT_CELSIUS, \ + ICON_GAUGE, UNIT_HECTOPASCAL DEPENDENCIES = ['i2c'] @@ -12,8 +12,10 @@ MS5611Component = ms5611_ns.class_('MS5611Component', cg.PollingComponent, i2c.I CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(MS5611Component), - cv.Required(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1), - cv.Required(CONF_PRESSURE): sensor.sensor_schema(UNIT_HECTOPASCAL, ICON_GAUGE, 1), + cv.Required(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, + DEVICE_CLASS_TEMPERATURE), + cv.Required(CONF_PRESSURE): sensor.sensor_schema(UNIT_HECTOPASCAL, ICON_GAUGE, 1, + DEVICE_CLASS_PRESSURE), }).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x77)) diff --git a/esphome/components/ntc/sensor.py b/esphome/components/ntc/sensor.py index f2eb601ed2..5cafecdc5f 100644 --- a/esphome/components/ntc/sensor.py +++ b/esphome/components/ntc/sensor.py @@ -4,8 +4,8 @@ import esphome.config_validation as cv import esphome.codegen as cg from esphome.components import sensor from esphome.const import CONF_CALIBRATION, CONF_ID, CONF_REFERENCE_RESISTANCE, \ - CONF_REFERENCE_TEMPERATURE, CONF_SENSOR, CONF_TEMPERATURE, CONF_VALUE, ICON_THERMOMETER, \ - UNIT_CELSIUS + CONF_REFERENCE_TEMPERATURE, CONF_SENSOR, CONF_TEMPERATURE, CONF_VALUE, \ + DEVICE_CLASS_TEMPERATURE, ICON_EMPTY, UNIT_CELSIUS ntc_ns = cg.esphome_ns.namespace('ntc') NTC = ntc_ns.class_('NTC', cg.Component, sensor.Sensor) @@ -98,7 +98,7 @@ def process_calibration(value): } -CONFIG_SCHEMA = sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1).extend({ +CONFIG_SCHEMA = sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE).extend({ cv.GenerateID(): cv.declare_id(NTC), cv.Required(CONF_SENSOR): cv.use_id(sensor.Sensor), cv.Required(CONF_CALIBRATION): process_calibration, diff --git a/esphome/components/pid/sensor/__init__.py b/esphome/components/pid/sensor/__init__.py index ff8cf15eb5..e655480a46 100644 --- a/esphome/components/pid/sensor/__init__.py +++ b/esphome/components/pid/sensor/__init__.py @@ -1,7 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor -from esphome.const import CONF_ID, UNIT_PERCENT, ICON_GAUGE, CONF_TYPE +from esphome.const import CONF_ID, DEVICE_CLASS_EMPTY, UNIT_PERCENT, ICON_GAUGE, CONF_TYPE from ..climate import pid_ns, PIDClimate PIDClimateSensor = pid_ns.class_('PIDClimateSensor', sensor.Sensor, cg.Component) @@ -21,7 +21,7 @@ PID_CLIMATE_SENSOR_TYPES = { } CONF_CLIMATE_ID = 'climate_id' -CONFIG_SCHEMA = sensor.sensor_schema(UNIT_PERCENT, ICON_GAUGE, 1).extend({ +CONFIG_SCHEMA = sensor.sensor_schema(UNIT_PERCENT, ICON_GAUGE, 1, DEVICE_CLASS_EMPTY).extend({ cv.GenerateID(): cv.declare_id(PIDClimateSensor), cv.GenerateID(CONF_CLIMATE_ID): cv.use_id(PIDClimate), diff --git a/esphome/components/pmsx003/sensor.py b/esphome/components/pmsx003/sensor.py index 4dbe500d3d..aa1be62ad0 100644 --- a/esphome/components/pmsx003/sensor.py +++ b/esphome/components/pmsx003/sensor.py @@ -2,9 +2,9 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, uart from esphome.const import CONF_FORMALDEHYDE, CONF_HUMIDITY, CONF_ID, CONF_PM_10_0, \ - CONF_PM_1_0, CONF_PM_2_5, CONF_TEMPERATURE, CONF_TYPE, ICON_CHEMICAL_WEAPON, \ - UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_THERMOMETER, ICON_WATER_PERCENT, UNIT_CELSIUS, \ - UNIT_PERCENT + CONF_PM_1_0, CONF_PM_2_5, CONF_TEMPERATURE, CONF_TYPE, DEVICE_CLASS_EMPTY, \ + DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_TEMPERATURE, ICON_CHEMICAL_WEAPON, ICON_EMPTY, \ + UNIT_MICROGRAMS_PER_CUBIC_METER, UNIT_CELSIUS, UNIT_PERCENT DEPENDENCIES = ['uart'] @@ -45,17 +45,21 @@ CONFIG_SCHEMA = cv.Schema({ cv.Required(CONF_TYPE): cv.enum(PMSX003_TYPES, upper=True), cv.Optional(CONF_PM_1_0): - sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_CHEMICAL_WEAPON, 0), + sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_CHEMICAL_WEAPON, 0, + DEVICE_CLASS_EMPTY), cv.Optional(CONF_PM_2_5): - sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_CHEMICAL_WEAPON, 0), + sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_CHEMICAL_WEAPON, 0, + DEVICE_CLASS_EMPTY), cv.Optional(CONF_PM_10_0): - sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_CHEMICAL_WEAPON, 0), + sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_CHEMICAL_WEAPON, 0, + DEVICE_CLASS_EMPTY), cv.Optional(CONF_TEMPERATURE): - sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1), + sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE), cv.Optional(CONF_HUMIDITY): - sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 1), + sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 1, DEVICE_CLASS_HUMIDITY), cv.Optional(CONF_FORMALDEHYDE): - sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_CHEMICAL_WEAPON, 0), + sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_CHEMICAL_WEAPON, 0, + DEVICE_CLASS_EMPTY), }).extend(cv.COMPONENT_SCHEMA).extend(uart.UART_DEVICE_SCHEMA) diff --git a/esphome/components/pulse_counter/sensor.py b/esphome/components/pulse_counter/sensor.py index 7550d5693a..a6f54872d4 100644 --- a/esphome/components/pulse_counter/sensor.py +++ b/esphome/components/pulse_counter/sensor.py @@ -3,7 +3,7 @@ import esphome.config_validation as cv from esphome import pins from esphome.components import sensor from esphome.const import CONF_COUNT_MODE, CONF_FALLING_EDGE, CONF_ID, CONF_INTERNAL_FILTER, \ - CONF_PIN, CONF_RISING_EDGE, CONF_NUMBER, CONF_TOTAL, \ + CONF_PIN, CONF_RISING_EDGE, CONF_NUMBER, CONF_TOTAL, DEVICE_CLASS_EMPTY, \ ICON_PULSE, UNIT_PULSES_PER_MINUTE, UNIT_PULSES from esphome.core import CORE @@ -47,7 +47,9 @@ def validate_count_mode(value): return value -CONFIG_SCHEMA = sensor.sensor_schema(UNIT_PULSES_PER_MINUTE, ICON_PULSE, 2).extend({ +CONFIG_SCHEMA = sensor.sensor_schema( + UNIT_PULSES_PER_MINUTE, ICON_PULSE, 2, DEVICE_CLASS_EMPTY +).extend({ cv.GenerateID(): cv.declare_id(PulseCounterSensor), cv.Required(CONF_PIN): validate_pulse_counter_pin, cv.Optional(CONF_COUNT_MODE, default={ @@ -58,7 +60,7 @@ CONFIG_SCHEMA = sensor.sensor_schema(UNIT_PULSES_PER_MINUTE, ICON_PULSE, 2).exte cv.Required(CONF_FALLING_EDGE): COUNT_MODE_SCHEMA, }), validate_count_mode), cv.Optional(CONF_INTERNAL_FILTER, default='13us'): validate_internal_filter, - cv.Optional(CONF_TOTAL): sensor.sensor_schema(UNIT_PULSES, ICON_PULSE, 0), + cv.Optional(CONF_TOTAL): sensor.sensor_schema(UNIT_PULSES, ICON_PULSE, 0, DEVICE_CLASS_EMPTY), }).extend(cv.polling_component_schema('60s')) diff --git a/esphome/components/pulse_width/sensor.py b/esphome/components/pulse_width/sensor.py index 8328da2ac0..01a1c05247 100644 --- a/esphome/components/pulse_width/sensor.py +++ b/esphome/components/pulse_width/sensor.py @@ -2,13 +2,13 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins from esphome.components import sensor -from esphome.const import CONF_ID, CONF_PIN, UNIT_SECOND, ICON_TIMER +from esphome.const import CONF_ID, CONF_PIN, DEVICE_CLASS_EMPTY, UNIT_SECOND, ICON_TIMER pulse_width_ns = cg.esphome_ns.namespace('pulse_width') PulseWidthSensor = pulse_width_ns.class_('PulseWidthSensor', sensor.Sensor, cg.PollingComponent) -CONFIG_SCHEMA = sensor.sensor_schema(UNIT_SECOND, ICON_TIMER, 3).extend({ +CONFIG_SCHEMA = sensor.sensor_schema(UNIT_SECOND, ICON_TIMER, 3, DEVICE_CLASS_EMPTY).extend({ cv.GenerateID(): cv.declare_id(PulseWidthSensor), cv.Required(CONF_PIN): cv.All(pins.internal_gpio_input_pin_schema, pins.validate_has_interrupt), diff --git a/esphome/components/pzem004t/sensor.py b/esphome/components/pzem004t/sensor.py index b54ba4887c..ed7158c057 100644 --- a/esphome/components/pzem004t/sensor.py +++ b/esphome/components/pzem004t/sensor.py @@ -1,9 +1,9 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, uart -from esphome.const import CONF_CURRENT, CONF_ID, CONF_POWER, CONF_VOLTAGE, \ - CONF_ENERGY, UNIT_VOLT, ICON_FLASH, ICON_COUNTER, UNIT_AMPERE, UNIT_WATT, \ - UNIT_WATT_HOURS +from esphome.const import CONF_CURRENT, CONF_ID, CONF_POWER, CONF_VOLTAGE, CONF_ENERGY, \ + DEVICE_CLASS_CURRENT, DEVICE_CLASS_ENERGY, DEVICE_CLASS_POWER, DEVICE_CLASS_VOLTAGE, \ + ICON_EMPTY, UNIT_VOLT, UNIT_AMPERE, UNIT_WATT, UNIT_WATT_HOURS DEPENDENCIES = ['uart'] @@ -13,10 +13,12 @@ PZEM004T = pzem004t_ns.class_('PZEM004T', cg.PollingComponent, uart.UARTDevice) CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(PZEM004T), - cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 1), - cv.Optional(CONF_CURRENT): sensor.sensor_schema(UNIT_AMPERE, ICON_FLASH, 2), - cv.Optional(CONF_POWER): sensor.sensor_schema(UNIT_WATT, ICON_FLASH, 0), - cv.Optional(CONF_ENERGY): sensor.sensor_schema(UNIT_WATT_HOURS, ICON_COUNTER, 0) + cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE), + cv.Optional(CONF_CURRENT): sensor.sensor_schema(UNIT_AMPERE, ICON_EMPTY, 2, + DEVICE_CLASS_CURRENT), + cv.Optional(CONF_POWER): sensor.sensor_schema(UNIT_WATT, ICON_EMPTY, 0, DEVICE_CLASS_POWER), + cv.Optional(CONF_ENERGY): sensor.sensor_schema(UNIT_WATT_HOURS, ICON_EMPTY, 0, + DEVICE_CLASS_ENERGY) }).extend(cv.polling_component_schema('60s')).extend(uart.UART_DEVICE_SCHEMA) diff --git a/esphome/components/pzemac/sensor.py b/esphome/components/pzemac/sensor.py index e3d2b90742..1004d49172 100644 --- a/esphome/components/pzemac/sensor.py +++ b/esphome/components/pzemac/sensor.py @@ -1,10 +1,11 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, modbus -from esphome.const import CONF_CURRENT, CONF_ID, CONF_POWER, CONF_VOLTAGE, \ - CONF_FREQUENCY, UNIT_VOLT, ICON_FLASH, UNIT_AMPERE, UNIT_WATT, UNIT_EMPTY, \ - ICON_POWER, CONF_POWER_FACTOR, ICON_CURRENT_AC, UNIT_HERTZ, \ - CONF_ENERGY, UNIT_WATT_HOURS, ICON_COUNTER +from esphome.const import CONF_CURRENT, CONF_ENERGY, CONF_ID, CONF_POWER, CONF_VOLTAGE, \ + CONF_FREQUENCY, CONF_POWER_FACTOR, DEVICE_CLASS_EMPTY, DEVICE_CLASS_POWER_FACTOR, \ + DEVICE_CLASS_VOLTAGE, DEVICE_CLASS_CURRENT, DEVICE_CLASS_POWER, DEVICE_CLASS_ENERGY, \ + ICON_EMPTY, ICON_CURRENT_AC, UNIT_HERTZ, UNIT_VOLT, UNIT_AMPERE, UNIT_WATT, UNIT_EMPTY, \ + UNIT_WATT_HOURS AUTO_LOAD = ['modbus'] @@ -13,12 +14,16 @@ PZEMAC = pzemac_ns.class_('PZEMAC', cg.PollingComponent, modbus.ModbusDevice) CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(PZEMAC), - cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 1), - cv.Optional(CONF_CURRENT): sensor.sensor_schema(UNIT_AMPERE, ICON_CURRENT_AC, 3), - cv.Optional(CONF_POWER): sensor.sensor_schema(UNIT_WATT, ICON_POWER, 1), - cv.Optional(CONF_ENERGY): sensor.sensor_schema(UNIT_WATT_HOURS, ICON_COUNTER, 0), - cv.Optional(CONF_FREQUENCY): sensor.sensor_schema(UNIT_HERTZ, ICON_CURRENT_AC, 1), - cv.Optional(CONF_POWER_FACTOR): sensor.sensor_schema(UNIT_EMPTY, ICON_FLASH, 2), + cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE), + cv.Optional(CONF_CURRENT): sensor.sensor_schema(UNIT_AMPERE, ICON_EMPTY, 3, + DEVICE_CLASS_CURRENT), + cv.Optional(CONF_POWER): sensor.sensor_schema(UNIT_WATT, ICON_EMPTY, 2, DEVICE_CLASS_POWER), + cv.Optional(CONF_ENERGY): sensor.sensor_schema(UNIT_WATT_HOURS, ICON_EMPTY, 0, + DEVICE_CLASS_ENERGY), + cv.Optional(CONF_FREQUENCY): sensor.sensor_schema(UNIT_HERTZ, ICON_CURRENT_AC, 1, + DEVICE_CLASS_EMPTY), + cv.Optional(CONF_POWER_FACTOR): sensor.sensor_schema(UNIT_EMPTY, ICON_EMPTY, 2, + DEVICE_CLASS_POWER_FACTOR), }).extend(cv.polling_component_schema('60s')).extend(modbus.modbus_device_schema(0x01)) diff --git a/esphome/components/pzemdc/sensor.py b/esphome/components/pzemdc/sensor.py index 8c6fd08868..a4fc9ab26f 100644 --- a/esphome/components/pzemdc/sensor.py +++ b/esphome/components/pzemdc/sensor.py @@ -1,8 +1,8 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, modbus -from esphome.const import CONF_CURRENT, CONF_ID, CONF_POWER, CONF_VOLTAGE, \ - UNIT_VOLT, ICON_FLASH, UNIT_AMPERE, UNIT_WATT, ICON_POWER, ICON_CURRENT_AC +from esphome.const import CONF_CURRENT, CONF_ID, CONF_POWER, CONF_VOLTAGE, DEVICE_CLASS_CURRENT, \ + DEVICE_CLASS_POWER, DEVICE_CLASS_VOLTAGE, ICON_EMPTY, UNIT_VOLT, UNIT_AMPERE, UNIT_WATT AUTO_LOAD = ['modbus'] @@ -11,9 +11,11 @@ PZEMDC = pzemdc_ns.class_('PZEMDC', cg.PollingComponent, modbus.ModbusDevice) CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(PZEMDC), - cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 1), - cv.Optional(CONF_CURRENT): sensor.sensor_schema(UNIT_AMPERE, ICON_CURRENT_AC, 3), - cv.Optional(CONF_POWER): sensor.sensor_schema(UNIT_WATT, ICON_POWER, 1), + cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE), + cv.Optional(CONF_CURRENT): sensor.sensor_schema(UNIT_AMPERE, ICON_EMPTY, 3, + DEVICE_CLASS_CURRENT), + cv.Optional(CONF_POWER): sensor.sensor_schema(UNIT_WATT, ICON_EMPTY, 1, + DEVICE_CLASS_POWER), }).extend(cv.polling_component_schema('60s')).extend(modbus.modbus_device_schema(0x01)) diff --git a/esphome/components/qmc5883l/sensor.py b/esphome/components/qmc5883l/sensor.py index 8a2952f54f..3beaa8bd7b 100644 --- a/esphome/components/qmc5883l/sensor.py +++ b/esphome/components/qmc5883l/sensor.py @@ -1,9 +1,9 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import (CONF_ADDRESS, CONF_ID, CONF_OVERSAMPLING, CONF_RANGE, ICON_MAGNET, - UNIT_MICROTESLA, UNIT_DEGREES, ICON_SCREEN_ROTATION, - CONF_UPDATE_INTERVAL) +from esphome.const import CONF_ADDRESS, CONF_ID, CONF_OVERSAMPLING, CONF_RANGE, \ + DEVICE_CLASS_EMPTY, ICON_MAGNET, UNIT_MICROTESLA, UNIT_DEGREES, ICON_SCREEN_ROTATION, \ + CONF_UPDATE_INTERVAL DEPENDENCIES = ['i2c'] @@ -57,8 +57,8 @@ def validate_enum(enum_values, units=None, int=True): return validate_enum_bound -field_strength_schema = sensor.sensor_schema(UNIT_MICROTESLA, ICON_MAGNET, 1) -heading_schema = sensor.sensor_schema(UNIT_DEGREES, ICON_SCREEN_ROTATION, 1) +field_strength_schema = sensor.sensor_schema(UNIT_MICROTESLA, ICON_MAGNET, 1, DEVICE_CLASS_EMPTY) +heading_schema = sensor.sensor_schema(UNIT_DEGREES, ICON_SCREEN_ROTATION, 1, DEVICE_CLASS_EMPTY) CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(QMC5883LComponent), diff --git a/esphome/components/resistance/sensor.py b/esphome/components/resistance/sensor.py index fb245bcdf0..373fa3a8ae 100644 --- a/esphome/components/resistance/sensor.py +++ b/esphome/components/resistance/sensor.py @@ -1,7 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor -from esphome.const import CONF_SENSOR, UNIT_OHM, ICON_FLASH, CONF_ID +from esphome.const import CONF_SENSOR, DEVICE_CLASS_EMPTY, UNIT_OHM, ICON_FLASH, CONF_ID resistance_ns = cg.esphome_ns.namespace('resistance') ResistanceSensor = resistance_ns.class_('ResistanceSensor', cg.Component, sensor.Sensor) @@ -16,7 +16,7 @@ CONFIGURATIONS = { 'UPSTREAM': ResistanceConfiguration.UPSTREAM, } -CONFIG_SCHEMA = sensor.sensor_schema(UNIT_OHM, ICON_FLASH, 1).extend({ +CONFIG_SCHEMA = sensor.sensor_schema(UNIT_OHM, ICON_FLASH, 1, DEVICE_CLASS_EMPTY).extend({ cv.GenerateID(): cv.declare_id(ResistanceSensor), cv.Required(CONF_SENSOR): cv.use_id(sensor.Sensor), cv.Required(CONF_CONFIGURATION): cv.enum(CONFIGURATIONS, upper=True), diff --git a/esphome/components/rotary_encoder/sensor.py b/esphome/components/rotary_encoder/sensor.py index c518982bc6..4064cdea81 100644 --- a/esphome/components/rotary_encoder/sensor.py +++ b/esphome/components/rotary_encoder/sensor.py @@ -2,8 +2,9 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins, automation from esphome.components import sensor -from esphome.const import CONF_ID, CONF_RESOLUTION, CONF_MIN_VALUE, CONF_MAX_VALUE, UNIT_STEPS, \ - ICON_ROTATE_RIGHT, CONF_VALUE, CONF_PIN_A, CONF_PIN_B, CONF_TRIGGER_ID +from esphome.const import CONF_ID, CONF_RESOLUTION, CONF_MIN_VALUE, CONF_MAX_VALUE, \ + DEVICE_CLASS_EMPTY, UNIT_STEPS, ICON_ROTATE_RIGHT, CONF_VALUE, CONF_PIN_A, CONF_PIN_B, \ + CONF_TRIGGER_ID rotary_encoder_ns = cg.esphome_ns.namespace('rotary_encoder') RotaryEncoderResolution = rotary_encoder_ns.enum('RotaryEncoderResolution') @@ -37,7 +38,9 @@ def validate_min_max_value(config): return config -CONFIG_SCHEMA = cv.All(sensor.sensor_schema(UNIT_STEPS, ICON_ROTATE_RIGHT, 0).extend({ +CONFIG_SCHEMA = cv.All(sensor.sensor_schema( + UNIT_STEPS, ICON_ROTATE_RIGHT, 0, DEVICE_CLASS_EMPTY +).extend({ cv.GenerateID(): cv.declare_id(RotaryEncoderSensor), cv.Required(CONF_PIN_A): cv.All(pins.internal_gpio_input_pin_schema, pins.validate_has_interrupt), diff --git a/esphome/components/ruuvitag/sensor.py b/esphome/components/ruuvitag/sensor.py index 3d9c724e7b..e992652266 100644 --- a/esphome/components/ruuvitag/sensor.py +++ b/esphome/components/ruuvitag/sensor.py @@ -1,14 +1,14 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, esp32_ble_tracker -from esphome.const import CONF_HUMIDITY, CONF_MAC_ADDRESS, CONF_TEMPERATURE, \ - CONF_PRESSURE, CONF_ACCELERATION, CONF_ACCELERATION_X, CONF_ACCELERATION_Y, \ - CONF_ACCELERATION_Z, CONF_BATTERY_VOLTAGE, CONF_TX_POWER, \ - CONF_MEASUREMENT_SEQUENCE_NUMBER, CONF_MOVEMENT_COUNTER, UNIT_CELSIUS, \ - ICON_THERMOMETER, UNIT_PERCENT, UNIT_VOLT, UNIT_HECTOPASCAL, UNIT_G, \ - UNIT_DECIBEL_MILLIWATT, UNIT_EMPTY, ICON_WATER_PERCENT, ICON_BATTERY, \ - ICON_GAUGE, ICON_ACCELERATION, ICON_ACCELERATION_X, ICON_ACCELERATION_Y, \ - ICON_ACCELERATION_Z, ICON_SIGNAL, CONF_ID +from esphome.const import CONF_HUMIDITY, CONF_MAC_ADDRESS, CONF_TEMPERATURE, CONF_PRESSURE, \ + CONF_ACCELERATION, CONF_ACCELERATION_X, CONF_ACCELERATION_Y, CONF_ACCELERATION_Z, \ + CONF_BATTERY_VOLTAGE, CONF_TX_POWER, CONF_MEASUREMENT_SEQUENCE_NUMBER, CONF_MOVEMENT_COUNTER, \ + DEVICE_CLASS_EMPTY, DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_PRESSURE, \ + DEVICE_CLASS_SIGNAL_STRENGTH, DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_VOLTAGE, ICON_EMPTY, \ + UNIT_CELSIUS, UNIT_PERCENT, UNIT_VOLT, UNIT_HECTOPASCAL, UNIT_G, UNIT_DECIBEL_MILLIWATT, \ + UNIT_EMPTY, ICON_GAUGE, ICON_ACCELERATION, ICON_ACCELERATION_X, ICON_ACCELERATION_Y, \ + ICON_ACCELERATION_Z, CONF_ID DEPENDENCIES = ['esp32_ble_tracker'] AUTO_LOAD = ['ruuvi_ble'] @@ -20,17 +20,28 @@ RuuviTag = ruuvitag_ns.class_( CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(RuuviTag), cv.Required(CONF_MAC_ADDRESS): cv.mac_address, - cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 2), - cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 2), - cv.Optional(CONF_PRESSURE): sensor.sensor_schema(UNIT_HECTOPASCAL, ICON_GAUGE, 2), - cv.Optional(CONF_ACCELERATION): sensor.sensor_schema(UNIT_G, ICON_ACCELERATION, 3), - cv.Optional(CONF_ACCELERATION_X): sensor.sensor_schema(UNIT_G, ICON_ACCELERATION_X, 3), - cv.Optional(CONF_ACCELERATION_Y): sensor.sensor_schema(UNIT_G, ICON_ACCELERATION_Y, 3), - cv.Optional(CONF_ACCELERATION_Z): sensor.sensor_schema(UNIT_G, ICON_ACCELERATION_Z, 3), - cv.Optional(CONF_BATTERY_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_BATTERY, 3), - cv.Optional(CONF_TX_POWER): sensor.sensor_schema(UNIT_DECIBEL_MILLIWATT, ICON_SIGNAL, 0), - cv.Optional(CONF_MOVEMENT_COUNTER): sensor.sensor_schema(UNIT_EMPTY, ICON_GAUGE, 0), - cv.Optional(CONF_MEASUREMENT_SEQUENCE_NUMBER): sensor.sensor_schema(UNIT_EMPTY, ICON_GAUGE, 0), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 2, + DEVICE_CLASS_TEMPERATURE), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 2, + DEVICE_CLASS_HUMIDITY), + cv.Optional(CONF_PRESSURE): sensor.sensor_schema(UNIT_HECTOPASCAL, ICON_EMPTY, 2, + DEVICE_CLASS_PRESSURE), + cv.Optional(CONF_ACCELERATION): sensor.sensor_schema(UNIT_G, ICON_ACCELERATION, 3, + DEVICE_CLASS_EMPTY), + cv.Optional(CONF_ACCELERATION_X): sensor.sensor_schema(UNIT_G, ICON_ACCELERATION_X, 3, + DEVICE_CLASS_EMPTY), + cv.Optional(CONF_ACCELERATION_Y): sensor.sensor_schema(UNIT_G, ICON_ACCELERATION_Y, 3, + DEVICE_CLASS_EMPTY), + cv.Optional(CONF_ACCELERATION_Z): sensor.sensor_schema(UNIT_G, ICON_ACCELERATION_Z, 3, + DEVICE_CLASS_EMPTY), + cv.Optional(CONF_BATTERY_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_EMPTY, 3, + DEVICE_CLASS_VOLTAGE), + cv.Optional(CONF_TX_POWER): sensor.sensor_schema(UNIT_DECIBEL_MILLIWATT, ICON_EMPTY, 0, + DEVICE_CLASS_SIGNAL_STRENGTH), + cv.Optional(CONF_MOVEMENT_COUNTER): sensor.sensor_schema(UNIT_EMPTY, ICON_GAUGE, 0, + DEVICE_CLASS_EMPTY), + cv.Optional(CONF_MEASUREMENT_SEQUENCE_NUMBER): sensor.sensor_schema(UNIT_EMPTY, ICON_GAUGE, 0, + DEVICE_CLASS_EMPTY), }).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) diff --git a/esphome/components/scd30/sensor.py b/esphome/components/scd30/sensor.py index eef6f44390..5cbc55fbd6 100644 --- a/esphome/components/scd30/sensor.py +++ b/esphome/components/scd30/sensor.py @@ -2,9 +2,9 @@ import re import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import CONF_ID, UNIT_PARTS_PER_MILLION, \ - CONF_HUMIDITY, CONF_TEMPERATURE, ICON_MOLECULE_CO2, \ - UNIT_CELSIUS, ICON_THERMOMETER, ICON_WATER_PERCENT, UNIT_PERCENT, CONF_CO2 +from esphome.const import CONF_ID, DEVICE_CLASS_EMPTY, CONF_HUMIDITY, CONF_TEMPERATURE, CONF_CO2, \ + DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_TEMPERATURE, ICON_EMPTY, UNIT_PARTS_PER_MILLION, \ + ICON_MOLECULE_CO2, UNIT_CELSIUS, UNIT_PERCENT DEPENDENCIES = ['i2c'] @@ -24,9 +24,11 @@ def remove_altitude_suffix(value): CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(SCD30Component), cv.Optional(CONF_CO2): sensor.sensor_schema(UNIT_PARTS_PER_MILLION, - ICON_MOLECULE_CO2, 0), - cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1), - cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 1), + ICON_MOLECULE_CO2, 0, DEVICE_CLASS_EMPTY), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, + DEVICE_CLASS_TEMPERATURE), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 1, + DEVICE_CLASS_HUMIDITY), cv.Optional(CONF_AUTOMATIC_SELF_CALIBRATION, default=True): cv.boolean, cv.Optional(CONF_ALTITUDE_COMPENSATION): cv.All(remove_altitude_suffix, cv.int_range(min=0, max=0xFFFF, diff --git a/esphome/components/sds011/sensor.py b/esphome/components/sds011/sensor.py index 0f750810a6..7b16f0412f 100644 --- a/esphome/components/sds011/sensor.py +++ b/esphome/components/sds011/sensor.py @@ -1,9 +1,8 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, uart -from esphome.const import (CONF_ID, CONF_PM_10_0, CONF_PM_2_5, CONF_RX_ONLY, - CONF_UPDATE_INTERVAL, UNIT_MICROGRAMS_PER_CUBIC_METER, - ICON_CHEMICAL_WEAPON) +from esphome.const import CONF_ID, CONF_PM_10_0, CONF_PM_2_5, CONF_RX_ONLY, CONF_UPDATE_INTERVAL, \ + DEVICE_CLASS_EMPTY, UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_CHEMICAL_WEAPON DEPENDENCIES = ['uart'] @@ -28,9 +27,11 @@ CONFIG_SCHEMA = cv.All(cv.Schema({ cv.GenerateID(): cv.declare_id(SDS011Component), cv.Optional(CONF_PM_2_5): - sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_CHEMICAL_WEAPON, 1), + sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_CHEMICAL_WEAPON, 1, + DEVICE_CLASS_EMPTY), cv.Optional(CONF_PM_10_0): - sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_CHEMICAL_WEAPON, 1), + sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_CHEMICAL_WEAPON, 1, + DEVICE_CLASS_EMPTY), cv.Optional(CONF_RX_ONLY, default=False): cv.boolean, cv.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_minutes, diff --git a/esphome/components/senseair/sensor.py b/esphome/components/senseair/sensor.py index c015871156..8ce06686be 100644 --- a/esphome/components/senseair/sensor.py +++ b/esphome/components/senseair/sensor.py @@ -1,7 +1,8 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, uart -from esphome.const import CONF_CO2, CONF_ID, ICON_MOLECULE_CO2, UNIT_PARTS_PER_MILLION +from esphome.const import CONF_CO2, CONF_ID, DEVICE_CLASS_EMPTY, ICON_MOLECULE_CO2, \ + UNIT_PARTS_PER_MILLION DEPENDENCIES = ['uart'] @@ -10,7 +11,8 @@ SenseAirComponent = senseair_ns.class_('SenseAirComponent', cg.PollingComponent, CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(SenseAirComponent), - cv.Required(CONF_CO2): sensor.sensor_schema(UNIT_PARTS_PER_MILLION, ICON_MOLECULE_CO2, 0), + cv.Required(CONF_CO2): sensor.sensor_schema(UNIT_PARTS_PER_MILLION, ICON_MOLECULE_CO2, 0, + DEVICE_CLASS_EMPTY), }).extend(cv.polling_component_schema('60s')).extend(uart.UART_DEVICE_SCHEMA) diff --git a/esphome/components/sensor/__init__.py b/esphome/components/sensor/__init__.py index 7def03c23e..c22edf0855 100644 --- a/esphome/components/sensor/__init__.py +++ b/esphome/components/sensor/__init__.py @@ -117,8 +117,7 @@ SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend({ }) -def sensor_schema(unit_of_measurement_=UNIT_EMPTY, icon_=ICON_EMPTY, accuracy_decimals_=0, - device_class_=DEVICE_CLASS_EMPTY): +def sensor_schema(unit_of_measurement_, icon_, accuracy_decimals_, device_class_): # type: (str, str, int, str) -> cv.Schema schema = SENSOR_SCHEMA if unit_of_measurement_ != UNIT_EMPTY: diff --git a/esphome/components/sgp30/sensor.py b/esphome/components/sgp30/sensor.py index a8963fef7b..f28c0f0e71 100644 --- a/esphome/components/sgp30/sensor.py +++ b/esphome/components/sgp30/sensor.py @@ -1,7 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import CONF_ID, ICON_RADIATOR, UNIT_PARTS_PER_MILLION, \ +from esphome.const import CONF_ID, DEVICE_CLASS_EMPTY, ICON_RADIATOR, UNIT_PARTS_PER_MILLION, \ UNIT_PARTS_PER_BILLION, ICON_MOLECULE_CO2 DEPENDENCIES = ['i2c'] @@ -22,8 +22,9 @@ CONF_TEMPERATURE_SOURCE = 'temperature_source' CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(SGP30Component), cv.Required(CONF_ECO2): sensor.sensor_schema(UNIT_PARTS_PER_MILLION, - ICON_MOLECULE_CO2, 0), - cv.Required(CONF_TVOC): sensor.sensor_schema(UNIT_PARTS_PER_BILLION, ICON_RADIATOR, 0), + ICON_MOLECULE_CO2, 0, DEVICE_CLASS_EMPTY), + cv.Required(CONF_TVOC): sensor.sensor_schema(UNIT_PARTS_PER_BILLION, ICON_RADIATOR, 0, + DEVICE_CLASS_EMPTY), cv.Optional(CONF_BASELINE): cv.Schema({ cv.Required(CONF_ECO2_BASELINE): cv.hex_uint16_t, cv.Required(CONF_TVOC_BASELINE): cv.hex_uint16_t, diff --git a/esphome/components/sht3xd/sensor.py b/esphome/components/sht3xd/sensor.py index 9bbdc47eec..45ede0b106 100644 --- a/esphome/components/sht3xd/sensor.py +++ b/esphome/components/sht3xd/sensor.py @@ -1,8 +1,8 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import CONF_HUMIDITY, CONF_ID, CONF_TEMPERATURE, ICON_WATER_PERCENT, \ - ICON_THERMOMETER, UNIT_CELSIUS, UNIT_PERCENT +from esphome.const import CONF_HUMIDITY, CONF_ID, CONF_TEMPERATURE, DEVICE_CLASS_HUMIDITY, \ + DEVICE_CLASS_TEMPERATURE, ICON_EMPTY, UNIT_CELSIUS, UNIT_PERCENT DEPENDENCIES = ['i2c'] @@ -11,8 +11,10 @@ SHT3XDComponent = sht3xd_ns.class_('SHT3XDComponent', cg.PollingComponent, i2c.I CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(SHT3XDComponent), - cv.Required(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1), - cv.Required(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 1), + cv.Required(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, + DEVICE_CLASS_TEMPERATURE), + cv.Required(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 1, + DEVICE_CLASS_HUMIDITY), }).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x44)) diff --git a/esphome/components/shtcx/sensor.py b/esphome/components/shtcx/sensor.py index eb215078e7..788dd0021a 100644 --- a/esphome/components/shtcx/sensor.py +++ b/esphome/components/shtcx/sensor.py @@ -1,8 +1,8 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import CONF_HUMIDITY, CONF_ID, CONF_TEMPERATURE, ICON_WATER_PERCENT, \ - ICON_THERMOMETER, UNIT_CELSIUS, UNIT_PERCENT +from esphome.const import CONF_HUMIDITY, CONF_ID, CONF_TEMPERATURE, DEVICE_CLASS_HUMIDITY, \ + DEVICE_CLASS_TEMPERATURE, ICON_EMPTY, UNIT_CELSIUS, UNIT_PERCENT DEPENDENCIES = ['i2c'] @@ -13,8 +13,10 @@ SHTCXType = shtcx_ns.enum('SHTCXType') CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(SHTCXComponent), - cv.Required(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1), - cv.Required(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 1), + cv.Required(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, + DEVICE_CLASS_TEMPERATURE), + cv.Required(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 1, + DEVICE_CLASS_HUMIDITY), }).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x70)) diff --git a/esphome/components/sps30/sensor.py b/esphome/components/sps30/sensor.py index c45758be4e..9f139aa262 100644 --- a/esphome/components/sps30/sensor.py +++ b/esphome/components/sps30/sensor.py @@ -3,8 +3,8 @@ import esphome.config_validation as cv from esphome.components import i2c, sensor from esphome.const import CONF_ID, CONF_PM_1_0, CONF_PM_2_5, CONF_PM_4_0, CONF_PM_10_0, \ CONF_PMC_0_5, CONF_PMC_1_0, CONF_PMC_2_5, CONF_PMC_4_0, CONF_PMC_10_0, CONF_PM_SIZE, \ - UNIT_MICROGRAMS_PER_CUBIC_METER, UNIT_COUNTS_PER_CUBIC_METER, UNIT_MICROMETER, \ - ICON_CHEMICAL_WEAPON, ICON_COUNTER, ICON_RULER + DEVICE_CLASS_EMPTY, UNIT_MICROGRAMS_PER_CUBIC_METER, UNIT_COUNTS_PER_CUBIC_METER, \ + UNIT_MICROMETER, ICON_CHEMICAL_WEAPON, ICON_COUNTER, ICON_RULER DEPENDENCIES = ['i2c'] @@ -14,25 +14,25 @@ SPS30Component = sps30_ns.class_('SPS30Component', cg.PollingComponent, i2c.I2CD CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(SPS30Component), cv.Optional(CONF_PM_1_0): sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, - ICON_CHEMICAL_WEAPON, 2), + ICON_CHEMICAL_WEAPON, 2, DEVICE_CLASS_EMPTY), cv.Optional(CONF_PM_2_5): sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, - ICON_CHEMICAL_WEAPON, 2), + ICON_CHEMICAL_WEAPON, 2, DEVICE_CLASS_EMPTY), cv.Optional(CONF_PM_4_0): sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, - ICON_CHEMICAL_WEAPON, 2), + ICON_CHEMICAL_WEAPON, 2, DEVICE_CLASS_EMPTY), cv.Optional(CONF_PM_10_0): sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, - ICON_CHEMICAL_WEAPON, 2), + ICON_CHEMICAL_WEAPON, 2, DEVICE_CLASS_EMPTY), cv.Optional(CONF_PMC_0_5): sensor.sensor_schema(UNIT_COUNTS_PER_CUBIC_METER, - ICON_COUNTER, 2), + ICON_COUNTER, 2, DEVICE_CLASS_EMPTY), cv.Optional(CONF_PMC_1_0): sensor.sensor_schema(UNIT_COUNTS_PER_CUBIC_METER, - ICON_COUNTER, 2), + ICON_COUNTER, 2, DEVICE_CLASS_EMPTY), cv.Optional(CONF_PMC_2_5): sensor.sensor_schema(UNIT_COUNTS_PER_CUBIC_METER, - ICON_COUNTER, 2), + ICON_COUNTER, 2, DEVICE_CLASS_EMPTY), cv.Optional(CONF_PMC_4_0): sensor.sensor_schema(UNIT_COUNTS_PER_CUBIC_METER, - ICON_COUNTER, 2), + ICON_COUNTER, 2, DEVICE_CLASS_EMPTY), cv.Optional(CONF_PMC_10_0): sensor.sensor_schema(UNIT_COUNTS_PER_CUBIC_METER, - ICON_COUNTER, 2), + ICON_COUNTER, 2, DEVICE_CLASS_EMPTY), cv.Optional(CONF_PM_SIZE): sensor.sensor_schema(UNIT_MICROMETER, - ICON_RULER, 0), + ICON_RULER, 0, DEVICE_CLASS_EMPTY), }).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x69)) diff --git a/esphome/components/sts3x/sensor.py b/esphome/components/sts3x/sensor.py index f48deeeae5..f13ddf3978 100644 --- a/esphome/components/sts3x/sensor.py +++ b/esphome/components/sts3x/sensor.py @@ -1,7 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import CONF_ID, ICON_THERMOMETER, UNIT_CELSIUS +from esphome.const import CONF_ID, DEVICE_CLASS_TEMPERATURE, ICON_EMPTY, UNIT_CELSIUS DEPENDENCIES = ['i2c'] @@ -10,7 +10,7 @@ sts3x_ns = cg.esphome_ns.namespace('sts3x') STS3XComponent = sts3x_ns.class_('STS3XComponent', sensor.Sensor, cg.PollingComponent, i2c.I2CDevice) -CONFIG_SCHEMA = sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1).extend({ +CONFIG_SCHEMA = sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE).extend({ cv.GenerateID(): cv.declare_id(STS3XComponent), }).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x4A)) diff --git a/esphome/components/sun/sensor/__init__.py b/esphome/components/sun/sensor/__init__.py index 5ca315888d..880e271fc5 100644 --- a/esphome/components/sun/sensor/__init__.py +++ b/esphome/components/sun/sensor/__init__.py @@ -1,7 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor -from esphome.const import UNIT_DEGREES, ICON_WEATHER_SUNSET, CONF_ID, CONF_TYPE +from esphome.const import DEVICE_CLASS_EMPTY, UNIT_DEGREES, ICON_WEATHER_SUNSET, CONF_ID, CONF_TYPE from .. import sun_ns, CONF_SUN_ID, Sun DEPENDENCIES = ['sun'] @@ -13,7 +13,9 @@ TYPES = { 'azimuth': SensorType.SUN_SENSOR_AZIMUTH, } -CONFIG_SCHEMA = sensor.sensor_schema(UNIT_DEGREES, ICON_WEATHER_SUNSET, 1).extend({ +CONFIG_SCHEMA = sensor.sensor_schema( + UNIT_DEGREES, ICON_WEATHER_SUNSET, 1, DEVICE_CLASS_EMPTY +).extend({ cv.GenerateID(): cv.declare_id(SunSensor), cv.GenerateID(CONF_SUN_ID): cv.use_id(Sun), cv.Required(CONF_TYPE): cv.enum(TYPES, lower=True), diff --git a/esphome/components/tcs34725/sensor.py b/esphome/components/tcs34725/sensor.py index 287b2e441c..acd484db3c 100644 --- a/esphome/components/tcs34725/sensor.py +++ b/esphome/components/tcs34725/sensor.py @@ -1,9 +1,9 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import CONF_COLOR_TEMPERATURE, CONF_GAIN, CONF_ID, \ - CONF_ILLUMINANCE, CONF_INTEGRATION_TIME, ICON_LIGHTBULB, \ - UNIT_PERCENT, ICON_THERMOMETER, UNIT_KELVIN, ICON_BRIGHTNESS_5, UNIT_LUX +from esphome.const import CONF_COLOR_TEMPERATURE, CONF_GAIN, CONF_ID, CONF_ILLUMINANCE, \ + CONF_INTEGRATION_TIME, DEVICE_CLASS_EMPTY, DEVICE_CLASS_ILLUMINANCE, ICON_EMPTY, \ + ICON_LIGHTBULB, UNIT_PERCENT, ICON_THERMOMETER, UNIT_KELVIN, UNIT_LUX DEPENDENCIES = ['i2c'] @@ -33,9 +33,10 @@ TCS34725_GAINS = { '60X': TCS34725Gain.TCS34725_GAIN_60X, } -color_channel_schema = sensor.sensor_schema(UNIT_PERCENT, ICON_LIGHTBULB, 1) -color_temperature_schema = sensor.sensor_schema(UNIT_KELVIN, ICON_THERMOMETER, 1) -illuminance_schema = sensor.sensor_schema(UNIT_LUX, ICON_BRIGHTNESS_5, 1) +color_channel_schema = sensor.sensor_schema(UNIT_PERCENT, ICON_LIGHTBULB, 1, DEVICE_CLASS_EMPTY) +color_temperature_schema = sensor.sensor_schema(UNIT_KELVIN, ICON_THERMOMETER, 1, + DEVICE_CLASS_EMPTY) +illuminance_schema = sensor.sensor_schema(UNIT_LUX, ICON_EMPTY, 1, DEVICE_CLASS_ILLUMINANCE) CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(TCS34725Component), diff --git a/esphome/components/teleinfo/sensor.py b/esphome/components/teleinfo/sensor.py index 54b50a9921..600464aa2b 100644 --- a/esphome/components/teleinfo/sensor.py +++ b/esphome/components/teleinfo/sensor.py @@ -1,7 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, uart -from esphome.const import CONF_ID, CONF_SENSOR, ICON_FLASH, UNIT_WATT_HOURS +from esphome.const import CONF_ID, CONF_SENSOR, DEVICE_CLASS_POWER, ICON_EMPTY, UNIT_WATT_HOURS DEPENDENCIES = ['uart'] @@ -11,7 +11,8 @@ TeleInfo = teleinfo_ns.class_('TeleInfo', cg.PollingComponent, uart.UARTDevice) CONF_TAG_NAME = "tag_name" TELEINFO_TAG_SCHEMA = cv.Schema({ cv.Required(CONF_TAG_NAME): cv.string, - cv.Required(CONF_SENSOR): sensor.sensor_schema(UNIT_WATT_HOURS, ICON_FLASH, 0) + cv.Required(CONF_SENSOR): sensor.sensor_schema(UNIT_WATT_HOURS, ICON_EMPTY, 0, + DEVICE_CLASS_POWER) }) CONF_TAGS = "tags" diff --git a/esphome/components/template/sensor/__init__.py b/esphome/components/template/sensor/__init__.py index 3fc71cf9de..7ebfc1047a 100644 --- a/esphome/components/template/sensor/__init__.py +++ b/esphome/components/template/sensor/__init__.py @@ -2,12 +2,13 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import automation from esphome.components import sensor -from esphome.const import CONF_ID, CONF_LAMBDA, CONF_STATE, UNIT_EMPTY, ICON_EMPTY +from esphome.const import CONF_ID, CONF_LAMBDA, CONF_STATE, DEVICE_CLASS_EMPTY, UNIT_EMPTY, \ + ICON_EMPTY from .. import template_ns TemplateSensor = template_ns.class_('TemplateSensor', sensor.Sensor, cg.PollingComponent) -CONFIG_SCHEMA = sensor.sensor_schema(UNIT_EMPTY, ICON_EMPTY, 1).extend({ +CONFIG_SCHEMA = sensor.sensor_schema(UNIT_EMPTY, ICON_EMPTY, 1, DEVICE_CLASS_EMPTY).extend({ cv.GenerateID(): cv.declare_id(TemplateSensor), cv.Optional(CONF_LAMBDA): cv.returning_lambda, }).extend(cv.polling_component_schema('60s')) diff --git a/esphome/components/tmp102/sensor.py b/esphome/components/tmp102/sensor.py index d60f0a8646..05f33c43ab 100644 --- a/esphome/components/tmp102/sensor.py +++ b/esphome/components/tmp102/sensor.py @@ -10,7 +10,7 @@ https://www.sparkfun.com/datasheets/Sensors/Temperature/tmp102.pdf import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import CONF_ID, UNIT_CELSIUS, ICON_THERMOMETER +from esphome.const import CONF_ID, DEVICE_CLASS_TEMPERATURE, ICON_EMPTY, UNIT_CELSIUS CODEOWNERS = ['@timsavage'] DEPENDENCIES = ['i2c'] @@ -19,7 +19,7 @@ tmp102_ns = cg.esphome_ns.namespace('tmp102') TMP102Component = tmp102_ns.class_("TMP102Component", cg.PollingComponent, i2c.I2CDevice, sensor.Sensor) -CONFIG_SCHEMA = sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1).extend({ +CONFIG_SCHEMA = sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE).extend({ cv.GenerateID(): cv.declare_id(TMP102Component), }).extend(cv.polling_component_schema("60s")).extend(i2c.i2c_device_schema(0x48)) diff --git a/esphome/components/tmp117/sensor.py b/esphome/components/tmp117/sensor.py index ddca3eeb64..4d2e662b53 100644 --- a/esphome/components/tmp117/sensor.py +++ b/esphome/components/tmp117/sensor.py @@ -1,8 +1,8 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import CONF_ID, CONF_UPDATE_INTERVAL, \ - UNIT_CELSIUS, ICON_THERMOMETER +from esphome.const import CONF_ID, CONF_UPDATE_INTERVAL, DEVICE_CLASS_TEMPERATURE, ICON_EMPTY, \ + UNIT_CELSIUS DEPENDENCIES = ['i2c'] @@ -10,7 +10,9 @@ tmp117_ns = cg.esphome_ns.namespace('tmp117') TMP117Component = tmp117_ns.class_('TMP117Component', cg.PollingComponent, i2c.I2CDevice, sensor.Sensor) -CONFIG_SCHEMA = cv.All(sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1).extend({ +CONFIG_SCHEMA = cv.All(sensor.sensor_schema( + UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE +).extend({ cv.GenerateID(): cv.declare_id(TMP117Component), }).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x48))) diff --git a/esphome/components/tsl2561/sensor.py b/esphome/components/tsl2561/sensor.py index d51ece09d5..92ad64e2ee 100644 --- a/esphome/components/tsl2561/sensor.py +++ b/esphome/components/tsl2561/sensor.py @@ -1,7 +1,8 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import CONF_GAIN, CONF_ID, CONF_INTEGRATION_TIME, UNIT_LUX, ICON_BRIGHTNESS_5 +from esphome.const import CONF_GAIN, CONF_ID, CONF_INTEGRATION_TIME, DEVICE_CLASS_ILLUMINANCE, \ + ICON_EMPTY, UNIT_LUX DEPENDENCIES = ['i2c'] @@ -30,7 +31,7 @@ def validate_integration_time(value): TSL2561Sensor = tsl2561_ns.class_('TSL2561Sensor', sensor.Sensor, cg.PollingComponent, i2c.I2CDevice) -CONFIG_SCHEMA = sensor.sensor_schema(UNIT_LUX, ICON_BRIGHTNESS_5, 1).extend({ +CONFIG_SCHEMA = sensor.sensor_schema(UNIT_LUX, ICON_EMPTY, 1, DEVICE_CLASS_ILLUMINANCE).extend({ cv.GenerateID(): cv.declare_id(TSL2561Sensor), cv.Optional(CONF_INTEGRATION_TIME, default='402ms'): validate_integration_time, cv.Optional(CONF_GAIN, default='1X'): cv.enum(GAINS, upper=True), diff --git a/esphome/components/tx20/sensor.py b/esphome/components/tx20/sensor.py index 3547cdf50c..060bd233be 100644 --- a/esphome/components/tx20/sensor.py +++ b/esphome/components/tx20/sensor.py @@ -3,7 +3,7 @@ import esphome.config_validation as cv from esphome import pins from esphome.components import sensor from esphome.const import CONF_ID, CONF_WIND_SPEED, CONF_PIN, \ - CONF_WIND_DIRECTION_DEGREES, UNIT_KILOMETER_PER_HOUR, \ + CONF_WIND_DIRECTION_DEGREES, DEVICE_CLASS_EMPTY, UNIT_KILOMETER_PER_HOUR, \ ICON_WEATHER_WINDY, ICON_SIGN_DIRECTION, UNIT_DEGREES tx20_ns = cg.esphome_ns.namespace('tx20') @@ -12,9 +12,9 @@ Tx20Component = tx20_ns.class_('Tx20Component', cg.Component) CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(Tx20Component), cv.Optional(CONF_WIND_SPEED): - sensor.sensor_schema(UNIT_KILOMETER_PER_HOUR, ICON_WEATHER_WINDY, 1), + sensor.sensor_schema(UNIT_KILOMETER_PER_HOUR, ICON_WEATHER_WINDY, 1, DEVICE_CLASS_EMPTY), cv.Optional(CONF_WIND_DIRECTION_DEGREES): - sensor.sensor_schema(UNIT_DEGREES, ICON_SIGN_DIRECTION, 1), + sensor.sensor_schema(UNIT_DEGREES, ICON_SIGN_DIRECTION, 1, DEVICE_CLASS_EMPTY), cv.Required(CONF_PIN): cv.All(pins.internal_gpio_input_pin_schema, pins.validate_has_interrupt), }).extend(cv.COMPONENT_SCHEMA) diff --git a/esphome/components/ultrasonic/sensor.py b/esphome/components/ultrasonic/sensor.py index e4364f271c..889044fc7a 100644 --- a/esphome/components/ultrasonic/sensor.py +++ b/esphome/components/ultrasonic/sensor.py @@ -3,7 +3,7 @@ import esphome.config_validation as cv from esphome import pins from esphome.components import sensor from esphome.const import CONF_ECHO_PIN, CONF_ID, CONF_TRIGGER_PIN, \ - CONF_TIMEOUT, UNIT_METER, ICON_ARROW_EXPAND_VERTICAL + CONF_TIMEOUT, DEVICE_CLASS_EMPTY, UNIT_METER, ICON_ARROW_EXPAND_VERTICAL CONF_PULSE_TIME = 'pulse_time' @@ -11,7 +11,9 @@ ultrasonic_ns = cg.esphome_ns.namespace('ultrasonic') UltrasonicSensorComponent = ultrasonic_ns.class_('UltrasonicSensorComponent', sensor.Sensor, cg.PollingComponent) -CONFIG_SCHEMA = sensor.sensor_schema(UNIT_METER, ICON_ARROW_EXPAND_VERTICAL, 2).extend({ +CONFIG_SCHEMA = sensor.sensor_schema( + UNIT_METER, ICON_ARROW_EXPAND_VERTICAL, 2, DEVICE_CLASS_EMPTY +).extend({ cv.GenerateID(): cv.declare_id(UltrasonicSensorComponent), cv.Required(CONF_TRIGGER_PIN): pins.gpio_output_pin_schema, cv.Required(CONF_ECHO_PIN): pins.internal_gpio_input_pin_schema, diff --git a/esphome/components/uptime/sensor.py b/esphome/components/uptime/sensor.py index 1dacc99653..94d259d4b0 100644 --- a/esphome/components/uptime/sensor.py +++ b/esphome/components/uptime/sensor.py @@ -1,12 +1,12 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor -from esphome.const import CONF_ID, UNIT_SECOND, ICON_TIMER +from esphome.const import CONF_ID, DEVICE_CLASS_EMPTY, UNIT_SECOND, ICON_TIMER uptime_ns = cg.esphome_ns.namespace('uptime') UptimeSensor = uptime_ns.class_('UptimeSensor', sensor.Sensor, cg.PollingComponent) -CONFIG_SCHEMA = sensor.sensor_schema(UNIT_SECOND, ICON_TIMER, 0).extend({ +CONFIG_SCHEMA = sensor.sensor_schema(UNIT_SECOND, ICON_TIMER, 0, DEVICE_CLASS_EMPTY).extend({ cv.GenerateID(): cv.declare_id(UptimeSensor), }).extend(cv.polling_component_schema('60s')) diff --git a/esphome/components/vl53l0x/sensor.py b/esphome/components/vl53l0x/sensor.py index 62208eef53..8fc6a0d88d 100644 --- a/esphome/components/vl53l0x/sensor.py +++ b/esphome/components/vl53l0x/sensor.py @@ -1,8 +1,8 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import (CONF_ID, UNIT_METER, ICON_ARROW_EXPAND_VERTICAL, CONF_ADDRESS, - CONF_TIMEOUT, CONF_ENABLE_PIN) +from esphome.const import (CONF_ID, DEVICE_CLASS_EMPTY, UNIT_METER, ICON_ARROW_EXPAND_VERTICAL, + CONF_ADDRESS, CONF_TIMEOUT, CONF_ENABLE_PIN) from esphome import pins DEPENDENCIES = ['i2c'] @@ -31,7 +31,8 @@ def check_timeout(value): return value -CONFIG_SCHEMA = cv.All(sensor.sensor_schema(UNIT_METER, ICON_ARROW_EXPAND_VERTICAL, 2).extend({ +CONFIG_SCHEMA = cv.All( + sensor.sensor_schema(UNIT_METER, ICON_ARROW_EXPAND_VERTICAL, 2, DEVICE_CLASS_EMPTY).extend({ cv.GenerateID(): cv.declare_id(VL53L0XSensor), cv.Optional(CONF_SIGNAL_RATE_LIMIT, default=0.25): cv.float_range( min=0.0, max=512.0, min_included=False, max_included=False), diff --git a/esphome/components/wifi_signal/sensor.py b/esphome/components/wifi_signal/sensor.py index 1cc58009af..c1174fdecd 100644 --- a/esphome/components/wifi_signal/sensor.py +++ b/esphome/components/wifi_signal/sensor.py @@ -1,13 +1,15 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor -from esphome.const import CONF_ID, ICON_WIFI, UNIT_DECIBEL +from esphome.const import CONF_ID, DEVICE_CLASS_SIGNAL_STRENGTH, ICON_EMPTY, UNIT_DECIBEL DEPENDENCIES = ['wifi'] wifi_signal_ns = cg.esphome_ns.namespace('wifi_signal') WiFiSignalSensor = wifi_signal_ns.class_('WiFiSignalSensor', sensor.Sensor, cg.PollingComponent) -CONFIG_SCHEMA = sensor.sensor_schema(UNIT_DECIBEL, ICON_WIFI, 0).extend({ +CONFIG_SCHEMA = sensor.sensor_schema( + UNIT_DECIBEL, ICON_EMPTY, 0, DEVICE_CLASS_SIGNAL_STRENGTH +).extend({ cv.GenerateID(): cv.declare_id(WiFiSignalSensor), }).extend(cv.polling_component_schema('60s')) diff --git a/esphome/components/xiaomi_cgd1/sensor.py b/esphome/components/xiaomi_cgd1/sensor.py index 401f6de7d2..c84c996504 100644 --- a/esphome/components/xiaomi_cgd1/sensor.py +++ b/esphome/components/xiaomi_cgd1/sensor.py @@ -2,8 +2,8 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, esp32_ble_tracker from esphome.const import CONF_BATTERY_LEVEL, CONF_HUMIDITY, CONF_MAC_ADDRESS, CONF_TEMPERATURE, \ - UNIT_CELSIUS, ICON_THERMOMETER, UNIT_PERCENT, ICON_WATER_PERCENT, ICON_BATTERY, CONF_ID, \ - CONF_BINDKEY + CONF_ID, DEVICE_CLASS_BATTERY, DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_TEMPERATURE, ICON_EMPTY, \ + UNIT_CELSIUS, UNIT_PERCENT, CONF_BINDKEY DEPENDENCIES = ['esp32_ble_tracker'] AUTO_LOAD = ['xiaomi_ble'] @@ -16,9 +16,12 @@ CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(XiaomiCGD1), cv.Required(CONF_BINDKEY): cv.bind_key, cv.Required(CONF_MAC_ADDRESS): cv.mac_address, - cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1), - cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 1), - cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(UNIT_PERCENT, ICON_BATTERY, 0), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, + DEVICE_CLASS_TEMPERATURE), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 1, + DEVICE_CLASS_HUMIDITY), + cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 0, + DEVICE_CLASS_BATTERY), }).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) diff --git a/esphome/components/xiaomi_cgg1/sensor.py b/esphome/components/xiaomi_cgg1/sensor.py index 897687c68a..e78c503c98 100644 --- a/esphome/components/xiaomi_cgg1/sensor.py +++ b/esphome/components/xiaomi_cgg1/sensor.py @@ -2,7 +2,8 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, esp32_ble_tracker from esphome.const import CONF_BATTERY_LEVEL, CONF_HUMIDITY, CONF_MAC_ADDRESS, CONF_TEMPERATURE, \ - UNIT_CELSIUS, ICON_THERMOMETER, UNIT_PERCENT, ICON_WATER_PERCENT, ICON_BATTERY, CONF_ID + CONF_ID, DEVICE_CLASS_BATTERY, DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_TEMPERATURE, ICON_EMPTY, \ + UNIT_CELSIUS, UNIT_PERCENT DEPENDENCIES = ['esp32_ble_tracker'] AUTO_LOAD = ['xiaomi_ble'] @@ -14,9 +15,12 @@ XiaomiCGG1 = xiaomi_cgg1_ns.class_( CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(XiaomiCGG1), cv.Required(CONF_MAC_ADDRESS): cv.mac_address, - cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1), - cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 1), - cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(UNIT_PERCENT, ICON_BATTERY, 0), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, + DEVICE_CLASS_TEMPERATURE), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 1, + DEVICE_CLASS_HUMIDITY), + cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 0, + DEVICE_CLASS_BATTERY), }).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) diff --git a/esphome/components/xiaomi_gcls002/sensor.py b/esphome/components/xiaomi_gcls002/sensor.py index 1822977c38..8f434b6a3b 100644 --- a/esphome/components/xiaomi_gcls002/sensor.py +++ b/esphome/components/xiaomi_gcls002/sensor.py @@ -1,10 +1,10 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, esp32_ble_tracker -from esphome.const import CONF_MAC_ADDRESS, CONF_TEMPERATURE, \ - UNIT_CELSIUS, ICON_THERMOMETER, UNIT_PERCENT, ICON_WATER_PERCENT, CONF_ID, \ - CONF_MOISTURE, CONF_ILLUMINANCE, ICON_BRIGHTNESS_5, UNIT_LUX, CONF_CONDUCTIVITY, \ - UNIT_MICROSIEMENS_PER_CENTIMETER, ICON_FLOWER +from esphome.const import CONF_MAC_ADDRESS, CONF_TEMPERATURE, DEVICE_CLASS_EMPTY, \ + DEVICE_CLASS_ILLUMINANCE, DEVICE_CLASS_TEMPERATURE, ICON_EMPTY, ICON_WATER_PERCENT, \ + UNIT_CELSIUS, UNIT_PERCENT, CONF_ID, CONF_MOISTURE, CONF_ILLUMINANCE, UNIT_LUX, \ + CONF_CONDUCTIVITY, UNIT_MICROSIEMENS_PER_CENTIMETER, ICON_FLOWER DEPENDENCIES = ['esp32_ble_tracker'] AUTO_LOAD = ['xiaomi_ble'] @@ -16,11 +16,14 @@ XiaomiGCLS002 = xiaomi_gcls002_ns.class_('XiaomiGCLS002', CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(XiaomiGCLS002), cv.Required(CONF_MAC_ADDRESS): cv.mac_address, - cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1), - cv.Optional(CONF_MOISTURE): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 0), - cv.Optional(CONF_ILLUMINANCE): sensor.sensor_schema(UNIT_LUX, ICON_BRIGHTNESS_5, 0), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, + DEVICE_CLASS_TEMPERATURE), + cv.Optional(CONF_MOISTURE): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 0, + DEVICE_CLASS_EMPTY), + cv.Optional(CONF_ILLUMINANCE): sensor.sensor_schema(UNIT_LUX, ICON_EMPTY, 0, + DEVICE_CLASS_ILLUMINANCE), cv.Optional(CONF_CONDUCTIVITY): - sensor.sensor_schema(UNIT_MICROSIEMENS_PER_CENTIMETER, ICON_FLOWER, 0), + sensor.sensor_schema(UNIT_MICROSIEMENS_PER_CENTIMETER, ICON_FLOWER, 0, DEVICE_CLASS_EMPTY), }).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) diff --git a/esphome/components/xiaomi_hhccjcy01/sensor.py b/esphome/components/xiaomi_hhccjcy01/sensor.py index 5e904c7eb6..960f652bba 100644 --- a/esphome/components/xiaomi_hhccjcy01/sensor.py +++ b/esphome/components/xiaomi_hhccjcy01/sensor.py @@ -1,10 +1,11 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, esp32_ble_tracker -from esphome.const import CONF_BATTERY_LEVEL, CONF_MAC_ADDRESS, CONF_TEMPERATURE, \ - UNIT_CELSIUS, ICON_THERMOMETER, UNIT_PERCENT, ICON_WATER_PERCENT, ICON_BATTERY, CONF_ID, \ - CONF_MOISTURE, CONF_ILLUMINANCE, ICON_BRIGHTNESS_5, UNIT_LUX, CONF_CONDUCTIVITY, \ - UNIT_MICROSIEMENS_PER_CENTIMETER, ICON_FLOWER +from esphome.const import CONF_MAC_ADDRESS, CONF_TEMPERATURE, DEVICE_CLASS_EMPTY, \ + DEVICE_CLASS_ILLUMINANCE, DEVICE_CLASS_TEMPERATURE, ICON_EMPTY, ICON_WATER_PERCENT, \ + UNIT_CELSIUS, UNIT_PERCENT, CONF_ID, CONF_MOISTURE, CONF_ILLUMINANCE, UNIT_LUX, \ + CONF_CONDUCTIVITY, UNIT_MICROSIEMENS_PER_CENTIMETER, ICON_FLOWER, DEVICE_CLASS_BATTERY, \ + CONF_BATTERY_LEVEL DEPENDENCIES = ['esp32_ble_tracker'] AUTO_LOAD = ['xiaomi_ble'] @@ -16,12 +17,16 @@ XiaomiHHCCJCY01 = xiaomi_hhccjcy01_ns.class_('XiaomiHHCCJCY01', CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(XiaomiHHCCJCY01), cv.Required(CONF_MAC_ADDRESS): cv.mac_address, - cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1), - cv.Optional(CONF_MOISTURE): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 0), - cv.Optional(CONF_ILLUMINANCE): sensor.sensor_schema(UNIT_LUX, ICON_BRIGHTNESS_5, 0), - cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(UNIT_PERCENT, ICON_BATTERY, 0), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, + DEVICE_CLASS_TEMPERATURE), + cv.Optional(CONF_MOISTURE): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 0, + DEVICE_CLASS_EMPTY), + cv.Optional(CONF_ILLUMINANCE): sensor.sensor_schema(UNIT_LUX, ICON_EMPTY, 0, + DEVICE_CLASS_ILLUMINANCE), cv.Optional(CONF_CONDUCTIVITY): - sensor.sensor_schema(UNIT_MICROSIEMENS_PER_CENTIMETER, ICON_FLOWER, 0), + sensor.sensor_schema(UNIT_MICROSIEMENS_PER_CENTIMETER, ICON_FLOWER, 0, DEVICE_CLASS_EMPTY), + cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 0, + DEVICE_CLASS_BATTERY), }).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) diff --git a/esphome/components/xiaomi_hhccpot002/sensor.py b/esphome/components/xiaomi_hhccpot002/sensor.py index 33cd96252e..ad8f2184b3 100644 --- a/esphome/components/xiaomi_hhccpot002/sensor.py +++ b/esphome/components/xiaomi_hhccpot002/sensor.py @@ -1,8 +1,8 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, esp32_ble_tracker -from esphome.const import CONF_MAC_ADDRESS, UNIT_PERCENT, ICON_WATER_PERCENT, CONF_ID, \ - CONF_MOISTURE, CONF_CONDUCTIVITY, UNIT_MICROSIEMENS_PER_CENTIMETER, ICON_FLOWER +from esphome.const import CONF_MAC_ADDRESS, DEVICE_CLASS_EMPTY, UNIT_PERCENT, ICON_WATER_PERCENT, \ + CONF_ID, CONF_MOISTURE, CONF_CONDUCTIVITY, UNIT_MICROSIEMENS_PER_CENTIMETER, ICON_FLOWER DEPENDENCIES = ['esp32_ble_tracker'] AUTO_LOAD = ['xiaomi_ble'] @@ -14,9 +14,10 @@ XiaomiHHCCPOT002 = xiaomi_hhccpot002_ns.class_('XiaomiHHCCPOT002', CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(XiaomiHHCCPOT002), cv.Required(CONF_MAC_ADDRESS): cv.mac_address, - cv.Optional(CONF_MOISTURE): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 0), + cv.Optional(CONF_MOISTURE): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 0, + DEVICE_CLASS_EMPTY), cv.Optional(CONF_CONDUCTIVITY): - sensor.sensor_schema(UNIT_MICROSIEMENS_PER_CENTIMETER, ICON_FLOWER, 0), + sensor.sensor_schema(UNIT_MICROSIEMENS_PER_CENTIMETER, ICON_FLOWER, 0, DEVICE_CLASS_EMPTY), }).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) diff --git a/esphome/components/xiaomi_jqjcy01ym/sensor.py b/esphome/components/xiaomi_jqjcy01ym/sensor.py index 2bd397e829..c7451af5c4 100644 --- a/esphome/components/xiaomi_jqjcy01ym/sensor.py +++ b/esphome/components/xiaomi_jqjcy01ym/sensor.py @@ -1,9 +1,10 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, esp32_ble_tracker -from esphome.const import CONF_BATTERY_LEVEL, CONF_MAC_ADDRESS, CONF_TEMPERATURE, \ - UNIT_CELSIUS, ICON_THERMOMETER, UNIT_PERCENT, ICON_WATER_PERCENT, ICON_BATTERY, CONF_ID, \ - CONF_HUMIDITY, UNIT_MILLIGRAMS_PER_CUBIC_METER, ICON_FLASK_OUTLINE, CONF_FORMALDEHYDE +from esphome.const import CONF_BATTERY_LEVEL, CONF_MAC_ADDRESS, CONF_TEMPERATURE, CONF_ID, \ + DEVICE_CLASS_BATTERY, DEVICE_CLASS_EMPTY, DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_TEMPERATURE, \ + ICON_EMPTY, UNIT_CELSIUS, UNIT_PERCENT, CONF_HUMIDITY, UNIT_MILLIGRAMS_PER_CUBIC_METER, \ + ICON_FLASK_OUTLINE, CONF_FORMALDEHYDE DEPENDENCIES = ['esp32_ble_tracker'] AUTO_LOAD = ['xiaomi_ble'] @@ -15,11 +16,15 @@ XiaomiJQJCY01YM = xiaomi_jqjcy01ym_ns.class_('XiaomiJQJCY01YM', CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(XiaomiJQJCY01YM), cv.Required(CONF_MAC_ADDRESS): cv.mac_address, - cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1), - cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 0), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, + DEVICE_CLASS_TEMPERATURE), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 0, + DEVICE_CLASS_HUMIDITY), cv.Optional(CONF_FORMALDEHYDE): - sensor.sensor_schema(UNIT_MILLIGRAMS_PER_CUBIC_METER, ICON_FLASK_OUTLINE, 2), - cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(UNIT_PERCENT, ICON_BATTERY, 0), + sensor.sensor_schema(UNIT_MILLIGRAMS_PER_CUBIC_METER, ICON_FLASK_OUTLINE, 2, + DEVICE_CLASS_EMPTY), + cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 0, + DEVICE_CLASS_BATTERY), }).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) diff --git a/esphome/components/xiaomi_lywsd02/sensor.py b/esphome/components/xiaomi_lywsd02/sensor.py index 97eff0cf79..ced9875b55 100644 --- a/esphome/components/xiaomi_lywsd02/sensor.py +++ b/esphome/components/xiaomi_lywsd02/sensor.py @@ -2,7 +2,8 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, esp32_ble_tracker from esphome.const import CONF_BATTERY_LEVEL, CONF_HUMIDITY, CONF_MAC_ADDRESS, CONF_TEMPERATURE, \ - UNIT_CELSIUS, ICON_THERMOMETER, UNIT_PERCENT, ICON_WATER_PERCENT, ICON_BATTERY, CONF_ID + DEVICE_CLASS_TEMPERATURE, UNIT_CELSIUS, ICON_EMPTY, UNIT_PERCENT, DEVICE_CLASS_HUMIDITY, \ + DEVICE_CLASS_BATTERY, CONF_ID DEPENDENCIES = ['esp32_ble_tracker'] AUTO_LOAD = ['xiaomi_ble'] @@ -14,9 +15,12 @@ XiaomiLYWSD02 = xiaomi_lywsd02_ns.class_('XiaomiLYWSD02', esp32_ble_tracker.ESPB CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(XiaomiLYWSD02), cv.Required(CONF_MAC_ADDRESS): cv.mac_address, - cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1), - cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 1), - cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(UNIT_PERCENT, ICON_BATTERY, 0), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, + DEVICE_CLASS_TEMPERATURE), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 1, + DEVICE_CLASS_HUMIDITY), + cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 0, + DEVICE_CLASS_BATTERY), }).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) diff --git a/esphome/components/xiaomi_lywsd03mmc/sensor.py b/esphome/components/xiaomi_lywsd03mmc/sensor.py index a1983825fe..de3b4a4f9c 100644 --- a/esphome/components/xiaomi_lywsd03mmc/sensor.py +++ b/esphome/components/xiaomi_lywsd03mmc/sensor.py @@ -2,8 +2,8 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, esp32_ble_tracker from esphome.const import CONF_BATTERY_LEVEL, CONF_HUMIDITY, CONF_MAC_ADDRESS, CONF_TEMPERATURE, \ - UNIT_CELSIUS, ICON_THERMOMETER, UNIT_PERCENT, ICON_WATER_PERCENT, ICON_BATTERY, CONF_ID, \ - CONF_BINDKEY + UNIT_CELSIUS, ICON_EMPTY, UNIT_PERCENT, DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_HUMIDITY, \ + CONF_ID, CONF_BINDKEY, DEVICE_CLASS_BATTERY CODEOWNERS = ['@ahpohl'] @@ -19,9 +19,12 @@ CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(XiaomiLYWSD03MMC), cv.Required(CONF_BINDKEY): cv.bind_key, cv.Required(CONF_MAC_ADDRESS): cv.mac_address, - cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1), - cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 0), - cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(UNIT_PERCENT, ICON_BATTERY, 0), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, + DEVICE_CLASS_TEMPERATURE), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 0, + DEVICE_CLASS_HUMIDITY), + cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 0, + DEVICE_CLASS_BATTERY), }).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) diff --git a/esphome/components/xiaomi_lywsdcgq/sensor.py b/esphome/components/xiaomi_lywsdcgq/sensor.py index e13c860464..3bff2fca78 100644 --- a/esphome/components/xiaomi_lywsdcgq/sensor.py +++ b/esphome/components/xiaomi_lywsdcgq/sensor.py @@ -2,7 +2,8 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, esp32_ble_tracker from esphome.const import CONF_BATTERY_LEVEL, CONF_HUMIDITY, CONF_MAC_ADDRESS, CONF_TEMPERATURE, \ - UNIT_CELSIUS, ICON_THERMOMETER, UNIT_PERCENT, ICON_WATER_PERCENT, ICON_BATTERY, CONF_ID + UNIT_CELSIUS, ICON_EMPTY, UNIT_PERCENT, DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_HUMIDITY, \ + DEVICE_CLASS_BATTERY, CONF_ID DEPENDENCIES = ['esp32_ble_tracker'] AUTO_LOAD = ['xiaomi_ble'] @@ -14,9 +15,12 @@ XiaomiLYWSDCGQ = xiaomi_lywsdcgq_ns.class_('XiaomiLYWSDCGQ', esp32_ble_tracker.E CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(XiaomiLYWSDCGQ), cv.Required(CONF_MAC_ADDRESS): cv.mac_address, - cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1), - cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 1), - cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(UNIT_PERCENT, ICON_BATTERY, 0), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, + DEVICE_CLASS_TEMPERATURE), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 1, + DEVICE_CLASS_HUMIDITY), + cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 0, + DEVICE_CLASS_BATTERY), }).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) diff --git a/esphome/components/xiaomi_mhoc401/sensor.py b/esphome/components/xiaomi_mhoc401/sensor.py index 579ca0619a..301c8914a1 100644 --- a/esphome/components/xiaomi_mhoc401/sensor.py +++ b/esphome/components/xiaomi_mhoc401/sensor.py @@ -2,8 +2,8 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, esp32_ble_tracker from esphome.const import CONF_BATTERY_LEVEL, CONF_HUMIDITY, CONF_MAC_ADDRESS, CONF_TEMPERATURE, \ - UNIT_CELSIUS, ICON_THERMOMETER, UNIT_PERCENT, ICON_WATER_PERCENT, ICON_BATTERY, CONF_ID, \ - CONF_BINDKEY + UNIT_CELSIUS, ICON_EMPTY, UNIT_PERCENT, DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_HUMIDITY, \ + CONF_ID, CONF_BINDKEY, DEVICE_CLASS_BATTERY CODEOWNERS = ['@vevsvevs'] DEPENDENCIES = ['esp32_ble_tracker'] @@ -18,9 +18,12 @@ CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(XiaomiMHOC401), cv.Required(CONF_BINDKEY): cv.bind_key, cv.Required(CONF_MAC_ADDRESS): cv.mac_address, - cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1), - cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 0), - cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(UNIT_PERCENT, ICON_BATTERY, 0), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, + DEVICE_CLASS_TEMPERATURE), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 0, + DEVICE_CLASS_HUMIDITY), + cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 0, + DEVICE_CLASS_BATTERY), }).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) diff --git a/esphome/components/xiaomi_miscale/sensor.py b/esphome/components/xiaomi_miscale/sensor.py index a5e91a9178..8fcdad96af 100644 --- a/esphome/components/xiaomi_miscale/sensor.py +++ b/esphome/components/xiaomi_miscale/sensor.py @@ -2,7 +2,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, esp32_ble_tracker from esphome.const import CONF_MAC_ADDRESS, CONF_ID, CONF_WEIGHT, UNIT_KILOGRAM, \ - ICON_SCALE_BATHROOM + ICON_SCALE_BATHROOM, DEVICE_CLASS_EMPTY DEPENDENCIES = ['esp32_ble_tracker'] @@ -14,7 +14,8 @@ XiaomiMiscale = xiaomi_miscale_ns.class_('XiaomiMiscale', CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(XiaomiMiscale), cv.Required(CONF_MAC_ADDRESS): cv.mac_address, - cv.Optional(CONF_WEIGHT): sensor.sensor_schema(UNIT_KILOGRAM, ICON_SCALE_BATHROOM, 2), + cv.Optional(CONF_WEIGHT): sensor.sensor_schema( + UNIT_KILOGRAM, ICON_SCALE_BATHROOM, 2, DEVICE_CLASS_EMPTY), }).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) diff --git a/esphome/components/xiaomi_miscale2/sensor.py b/esphome/components/xiaomi_miscale2/sensor.py index 9f32385d54..84244cfc58 100644 --- a/esphome/components/xiaomi_miscale2/sensor.py +++ b/esphome/components/xiaomi_miscale2/sensor.py @@ -2,7 +2,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, esp32_ble_tracker from esphome.const import CONF_MAC_ADDRESS, CONF_ID, CONF_WEIGHT, UNIT_KILOGRAM, \ - ICON_SCALE_BATHROOM, UNIT_OHM, CONF_IMPEDANCE, ICON_OMEGA + ICON_SCALE_BATHROOM, UNIT_OHM, CONF_IMPEDANCE, ICON_OMEGA, DEVICE_CLASS_EMPTY DEPENDENCIES = ['esp32_ble_tracker'] @@ -14,8 +14,10 @@ XiaomiMiscale2 = xiaomi_miscale2_ns.class_('XiaomiMiscale2', CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(XiaomiMiscale2), cv.Required(CONF_MAC_ADDRESS): cv.mac_address, - cv.Optional(CONF_WEIGHT): sensor.sensor_schema(UNIT_KILOGRAM, ICON_SCALE_BATHROOM, 2), - cv.Optional(CONF_IMPEDANCE): sensor.sensor_schema(UNIT_OHM, ICON_OMEGA, 0), + cv.Optional(CONF_WEIGHT): sensor.sensor_schema( + UNIT_KILOGRAM, ICON_SCALE_BATHROOM, 2, DEVICE_CLASS_EMPTY), + cv.Optional(CONF_IMPEDANCE): sensor.sensor_schema( + UNIT_OHM, ICON_OMEGA, 0, DEVICE_CLASS_EMPTY), }).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) diff --git a/esphome/components/xiaomi_mjyd02yla/binary_sensor.py b/esphome/components/xiaomi_mjyd02yla/binary_sensor.py index e13dd77d13..a50f507b49 100644 --- a/esphome/components/xiaomi_mjyd02yla/binary_sensor.py +++ b/esphome/components/xiaomi_mjyd02yla/binary_sensor.py @@ -1,9 +1,10 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, binary_sensor, esp32_ble_tracker -from esphome.const import CONF_MAC_ADDRESS, CONF_ID, CONF_BINDKEY, \ - CONF_DEVICE_CLASS, CONF_LIGHT, CONF_BATTERY_LEVEL, UNIT_PERCENT, ICON_BATTERY, \ - CONF_IDLE_TIME, CONF_ILLUMINANCE, UNIT_MINUTE, UNIT_LUX, ICON_TIMELAPSE, ICON_BRIGHTNESS_5 +from esphome.const import CONF_MAC_ADDRESS, CONF_ID, CONF_BINDKEY, CONF_DEVICE_CLASS, CONF_LIGHT, \ + CONF_BATTERY_LEVEL, DEVICE_CLASS_BATTERY, DEVICE_CLASS_EMPTY, DEVICE_CLASS_ILLUMINANCE, \ + ICON_EMPTY, UNIT_PERCENT, CONF_IDLE_TIME, CONF_ILLUMINANCE, UNIT_MINUTE, UNIT_LUX, \ + ICON_TIMELAPSE DEPENDENCIES = ['esp32_ble_tracker'] AUTO_LOAD = ['xiaomi_ble'] @@ -17,9 +18,12 @@ CONFIG_SCHEMA = cv.All(binary_sensor.BINARY_SENSOR_SCHEMA.extend({ cv.Required(CONF_MAC_ADDRESS): cv.mac_address, cv.Required(CONF_BINDKEY): cv.bind_key, cv.Optional(CONF_DEVICE_CLASS, default='motion'): binary_sensor.device_class, - cv.Optional(CONF_IDLE_TIME): sensor.sensor_schema(UNIT_MINUTE, ICON_TIMELAPSE, 0), - cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(UNIT_PERCENT, ICON_BATTERY, 0), - cv.Optional(CONF_ILLUMINANCE): sensor.sensor_schema(UNIT_LUX, ICON_BRIGHTNESS_5, 0), + cv.Optional(CONF_IDLE_TIME): sensor.sensor_schema(UNIT_MINUTE, ICON_TIMELAPSE, 0, + DEVICE_CLASS_EMPTY), + cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 0, + DEVICE_CLASS_BATTERY), + cv.Optional(CONF_ILLUMINANCE): sensor.sensor_schema(UNIT_LUX, ICON_EMPTY, 0, + DEVICE_CLASS_ILLUMINANCE), cv.Optional(CONF_LIGHT): binary_sensor.BINARY_SENSOR_SCHEMA.extend({ cv.Optional(CONF_DEVICE_CLASS, default='light'): binary_sensor.device_class, }), diff --git a/esphome/components/xiaomi_wx08zm/binary_sensor.py b/esphome/components/xiaomi_wx08zm/binary_sensor.py index 1d60dbf5e0..becdce05c5 100644 --- a/esphome/components/xiaomi_wx08zm/binary_sensor.py +++ b/esphome/components/xiaomi_wx08zm/binary_sensor.py @@ -1,8 +1,8 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, binary_sensor, esp32_ble_tracker -from esphome.const import CONF_BATTERY_LEVEL, CONF_MAC_ADDRESS, CONF_TABLET, \ - UNIT_PERCENT, ICON_BUG, ICON_BATTERY, CONF_ID +from esphome.const import CONF_BATTERY_LEVEL, CONF_MAC_ADDRESS, CONF_TABLET, DEVICE_CLASS_BATTERY, \ + DEVICE_CLASS_EMPTY, ICON_EMPTY, UNIT_PERCENT, ICON_BUG, CONF_ID DEPENDENCIES = ['esp32_ble_tracker'] @@ -15,8 +15,9 @@ XiaomiWX08ZM = xiaomi_wx08zm_ns.class_('XiaomiWX08ZM', binary_sensor.BinarySenso CONFIG_SCHEMA = cv.All(binary_sensor.BINARY_SENSOR_SCHEMA.extend({ cv.GenerateID(): cv.declare_id(XiaomiWX08ZM), cv.Required(CONF_MAC_ADDRESS): cv.mac_address, - cv.Optional(CONF_TABLET): sensor.sensor_schema(UNIT_PERCENT, ICON_BUG, 0), - cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(UNIT_PERCENT, ICON_BATTERY, 0), + cv.Optional(CONF_TABLET): sensor.sensor_schema(UNIT_PERCENT, ICON_BUG, 0, DEVICE_CLASS_EMPTY), + cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 0, + DEVICE_CLASS_BATTERY), }).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA)) diff --git a/esphome/components/zyaura/sensor.py b/esphome/components/zyaura/sensor.py index df263974e8..4517ed9b2e 100644 --- a/esphome/components/zyaura/sensor.py +++ b/esphome/components/zyaura/sensor.py @@ -2,10 +2,9 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins from esphome.components import sensor -from esphome.const import CONF_ID, CONF_CLOCK_PIN, CONF_DATA_PIN, \ - CONF_CO2, CONF_TEMPERATURE, CONF_HUMIDITY, \ - UNIT_PARTS_PER_MILLION, UNIT_CELSIUS, UNIT_PERCENT, \ - ICON_MOLECULE_CO2, ICON_THERMOMETER, ICON_WATER_PERCENT +from esphome.const import CONF_ID, CONF_CLOCK_PIN, CONF_DATA_PIN, CONF_CO2, CONF_TEMPERATURE, \ + CONF_HUMIDITY, DEVICE_CLASS_EMPTY, DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_TEMPERATURE, \ + ICON_EMPTY, UNIT_PARTS_PER_MILLION, UNIT_CELSIUS, UNIT_PERCENT, ICON_MOLECULE_CO2 from esphome.cpp_helpers import gpio_pin_expression zyaura_ns = cg.esphome_ns.namespace('zyaura') @@ -17,9 +16,12 @@ CONFIG_SCHEMA = cv.Schema({ pins.validate_has_interrupt), cv.Required(CONF_DATA_PIN): cv.All(pins.internal_gpio_input_pin_schema, pins.validate_has_interrupt), - cv.Optional(CONF_CO2): sensor.sensor_schema(UNIT_PARTS_PER_MILLION, ICON_MOLECULE_CO2, 0), - cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1), - cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 1), + cv.Optional(CONF_CO2): sensor.sensor_schema(UNIT_PARTS_PER_MILLION, ICON_MOLECULE_CO2, 0, + DEVICE_CLASS_EMPTY), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, + DEVICE_CLASS_TEMPERATURE), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 1, + DEVICE_CLASS_HUMIDITY), }).extend(cv.polling_component_schema('60s')) From 08ecca86bcc82d31b220843b367b46b28e9431c0 Mon Sep 17 00:00:00 2001 From: Gabe Cook Date: Sat, 27 Feb 2021 16:53:53 -0600 Subject: [PATCH 058/111] Add send_every to uart switch for recurring data (#1514) --- esphome/components/uart/switch/__init__.py | 6 +++- .../components/uart/switch/uart_switch.cpp | 32 ++++++++++++++++--- esphome/components/uart/switch/uart_switch.h | 6 ++++ tests/test1.yaml | 4 +++ 4 files changed, 43 insertions(+), 5 deletions(-) diff --git a/esphome/components/uart/switch/__init__.py b/esphome/components/uart/switch/__init__.py index 6cc11d8bbe..0c9ebe56f7 100644 --- a/esphome/components/uart/switch/__init__.py +++ b/esphome/components/uart/switch/__init__.py @@ -1,7 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import switch, uart -from esphome.const import CONF_DATA, CONF_ID, CONF_INVERTED +from esphome.const import CONF_DATA, CONF_ID, CONF_INVERTED, CONF_SEND_EVERY from esphome.core import HexInt from .. import uart_ns, validate_raw_data @@ -14,6 +14,7 @@ CONFIG_SCHEMA = switch.SWITCH_SCHEMA.extend({ cv.GenerateID(): cv.declare_id(UARTSwitch), cv.Required(CONF_DATA): validate_raw_data, cv.Optional(CONF_INVERTED): cv.invalid("UART switches do not support inverted mode!"), + cv.Optional(CONF_SEND_EVERY): cv.positive_time_period_milliseconds, }).extend(uart.UART_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) @@ -27,3 +28,6 @@ def to_code(config): if isinstance(data, bytes): data = [HexInt(x) for x in data] cg.add(var.set_data(data)) + + if CONF_SEND_EVERY in config: + cg.add(var.set_send_every(config[CONF_SEND_EVERY])) diff --git a/esphome/components/uart/switch/uart_switch.cpp b/esphome/components/uart/switch/uart_switch.cpp index 9974ee1179..a1ebb6dbb9 100644 --- a/esphome/components/uart/switch/uart_switch.cpp +++ b/esphome/components/uart/switch/uart_switch.cpp @@ -6,6 +6,21 @@ namespace uart { static const char *TAG = "uart.switch"; +void UARTSwitch::loop() { + if (this->state && this->send_every_) { + const uint32_t now = millis(); + if (now - this->last_transmission_ > this->send_every_) { + this->write_command_(); + this->last_transmission_ = now; + } + } +} + +void UARTSwitch::write_command_() { + ESP_LOGD(TAG, "'%s': Sending data...", this->get_name().c_str()); + this->write_array(this->data_.data(), this->data_.size()); +} + void UARTSwitch::write_state(bool state) { if (!state) { this->publish_state(false); @@ -13,11 +28,20 @@ void UARTSwitch::write_state(bool state) { } this->publish_state(true); - ESP_LOGD(TAG, "'%s': Sending data...", this->get_name().c_str()); - this->write_array(this->data_.data(), this->data_.size()); - this->publish_state(false); + this->write_command_(); + + if (this->send_every_ == 0) { + this->publish_state(false); + } else { + this->last_transmission_ = millis(); + } +} +void UARTSwitch::dump_config() { + LOG_SWITCH("", "UART Switch", this); + if (this->send_every_) { + ESP_LOGCONFIG(TAG, " Send Every: %u", this->send_every_); + } } -void UARTSwitch::dump_config() { LOG_SWITCH("", "UART Switch", this); } } // namespace uart } // namespace esphome diff --git a/esphome/components/uart/switch/uart_switch.h b/esphome/components/uart/switch/uart_switch.h index c8a1b0d8c5..4c82d5680a 100644 --- a/esphome/components/uart/switch/uart_switch.h +++ b/esphome/components/uart/switch/uart_switch.h @@ -9,13 +9,19 @@ namespace uart { class UARTSwitch : public switch_::Switch, public UARTDevice, public Component { public: + void loop() override; + void set_data(const std::vector &data) { data_ = data; } + void set_send_every(uint32_t send_every) { this->send_every_ = send_every; } void dump_config() override; protected: + void write_command_(); void write_state(bool state) override; std::vector data_; + uint32_t send_every_; + uint32_t last_transmission_; }; } // namespace uart diff --git a/tests/test1.yaml b/tests/test1.yaml index 5da2ee03fe..e76d1d24c0 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -1617,6 +1617,10 @@ switch: - platform: uart name: 'UART Bytes Output' data: [0xDE, 0xAD, 0xBE, 0xEF] + - platform: uart + name: 'UART Recurring Output' + data: [0xDE, 0xAD, 0xBE, 0xEF] + send_every: 1s - platform: template assumed_state: yes name: Stepper Switch From bdf004867da39a1cc2d39041a09b7db1baa5b488 Mon Sep 17 00:00:00 2001 From: Seppel Hardt Date: Sun, 28 Feb 2021 00:16:27 +0100 Subject: [PATCH 059/111] Added samsung36 ir protocol (#1438) * Added samsung36 ir protocol * Make linter happy * Added test and fixed python failure * Sorry for the commits but linter was still not happy :) * Okay have to run script/clang-format -i Co-authored-by: tuxBurner --- esphome/components/remote_base/__init__.py | 36 ++++++ .../remote_base/samsung36_protocol.cpp | 103 ++++++++++++++++++ .../remote_base/samsung36_protocol.h | 39 +++++++ tests/test1.yaml | 6 + 4 files changed, 184 insertions(+) create mode 100644 esphome/components/remote_base/samsung36_protocol.cpp create mode 100644 esphome/components/remote_base/samsung36_protocol.h diff --git a/esphome/components/remote_base/__init__.py b/esphome/components/remote_base/__init__.py index 770394faec..cc48ffc5b2 100644 --- a/esphome/components/remote_base/__init__.py +++ b/esphome/components/remote_base/__init__.py @@ -686,6 +686,42 @@ def samsung_action(var, config, args): cg.add(var.set_data(template_)) +# Samsung36 +(Samsung36Data, Samsung36BinarySensor, Samsung36Trigger, Samsung36Action, + Samsung36Dumper) = declare_protocol('Samsung36') +SAMSUNG36_SCHEMA = cv.Schema({ + cv.Required(CONF_ADDRESS): cv.hex_uint16_t, + cv.Required(CONF_COMMAND): cv.hex_uint32_t, +}) + + +@register_binary_sensor('samsung36', Samsung36BinarySensor, SAMSUNG36_SCHEMA) +def samsung36_binary_sensor(var, config): + cg.add(var.set_data(cg.StructInitializer( + Samsung36Data, + ('address', config[CONF_ADDRESS]), + ('command', config[CONF_COMMAND]), + ))) + + +@register_trigger('samsung36', Samsung36Trigger, Samsung36Data) +def samsung36_trigger(var, config): + pass + + +@register_dumper('samsung36', Samsung36Dumper) +def samsung36_dumper(var, config): + pass + + +@register_action('samsung36', Samsung36Action, SAMSUNG36_SCHEMA) +def samsung36_action(var, config, args): + template_ = yield cg.templatable(config[CONF_ADDRESS], args, cg.uint16) + cg.add(var.set_address(template_)) + template_ = yield cg.templatable(config[CONF_COMMAND], args, cg.uint32) + cg.add(var.set_command(template_)) + + # Panasonic (PanasonicData, PanasonicBinarySensor, PanasonicTrigger, PanasonicAction, PanasonicDumper) = declare_protocol('Panasonic') diff --git a/esphome/components/remote_base/samsung36_protocol.cpp b/esphome/components/remote_base/samsung36_protocol.cpp new file mode 100644 index 0000000000..22b158fd8a --- /dev/null +++ b/esphome/components/remote_base/samsung36_protocol.cpp @@ -0,0 +1,103 @@ +#include "samsung36_protocol.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace remote_base { + +static const char *TAG = "remote.samsung36"; + +static const uint8_t NBITS = 78; + +static const uint32_t HEADER_HIGH_US = 4500; +static const uint32_t HEADER_LOW_US = 4500; +static const uint32_t BIT_HIGH_US = 500; +static const uint32_t BIT_ONE_LOW_US = 1500; +static const uint32_t BIT_ZERO_LOW_US = 500; +static const uint32_t MIDDLE_HIGH_US = 500; +static const uint32_t MIDDLE_LOW_US = 4500; +static const uint32_t FOOTER_HIGH_US = 500; +static const uint32_t FOOTER_LOW_US = 59000; + +void Samsung36Protocol::encode(RemoteTransmitData *dst, const Samsung36Data &data) { + dst->set_carrier_frequency(38000); + dst->reserve(NBITS); + + // send header + dst->item(HEADER_HIGH_US, HEADER_LOW_US); + + // send first 16 bits + for (uint32_t mask = 1UL << 15; mask != 0; mask >>= 1) { + if (data.address & mask) { + dst->item(BIT_HIGH_US, BIT_ONE_LOW_US); + } else { + dst->item(BIT_HIGH_US, BIT_ZERO_LOW_US); + } + } + + // send middle header + dst->item(MIDDLE_HIGH_US, MIDDLE_LOW_US); + + // send last 20 bits + for (uint32_t mask = 1UL << 19; mask != 0; mask >>= 1) { + if (data.command & mask) { + dst->item(BIT_HIGH_US, BIT_ONE_LOW_US); + } else { + dst->item(BIT_HIGH_US, BIT_ZERO_LOW_US); + } + } + + // footer + dst->item(FOOTER_HIGH_US, FOOTER_LOW_US); +} + +optional Samsung36Protocol::decode(RemoteReceiveData src) { + Samsung36Data out{ + .address = 0, + .command = 0, + }; + + // check if header matches + if (!src.expect_item(HEADER_HIGH_US, HEADER_LOW_US)) + return {}; + + // check if we have enough bits + if (src.size() != NBITS) + return {}; + + // get the first 16 bits + for (uint8_t i = 0; i < 16; i++) { + out.address <<= 1UL; + if (src.expect_item(BIT_HIGH_US, BIT_ONE_LOW_US)) { + out.address |= 1UL; + } else if (src.expect_item(BIT_HIGH_US, BIT_ZERO_LOW_US)) { + out.address |= 0UL; + } else { + return {}; + } + } + + // check if the middle mark matches + if (!src.expect_item(MIDDLE_HIGH_US, MIDDLE_LOW_US)) { + return {}; + } + + // get the last 20 bits + for (uint8_t i = 0; i < 20; i++) { + out.command <<= 1UL; + if (src.expect_item(BIT_HIGH_US, BIT_ONE_LOW_US)) { + out.command |= 1UL; + } else if (src.expect_item(BIT_HIGH_US, BIT_ZERO_LOW_US)) { + out.command |= 0UL; + } else { + return {}; + } + } + + return out; +} +void Samsung36Protocol::dump(const Samsung36Data &data) { + ESP_LOGD(TAG, "Received Samsung36: address=0x%04X, command=0x%08X", data.address, data.command); +} + +} // namespace remote_base +} // namespace esphome diff --git a/esphome/components/remote_base/samsung36_protocol.h b/esphome/components/remote_base/samsung36_protocol.h new file mode 100644 index 0000000000..4ba6226edd --- /dev/null +++ b/esphome/components/remote_base/samsung36_protocol.h @@ -0,0 +1,39 @@ +#pragma once + +#include "esphome/core/component.h" +#include "remote_base.h" + +namespace esphome { +namespace remote_base { + +struct Samsung36Data { + uint16_t address; + uint32_t command; + + bool operator==(const Samsung36Data &rhs) const { return address == rhs.address && command == rhs.command; } +}; + +class Samsung36Protocol : public RemoteProtocol { + public: + void encode(RemoteTransmitData *dst, const Samsung36Data &data) override; + optional decode(RemoteReceiveData src) override; + void dump(const Samsung36Data &data) override; +}; + +DECLARE_REMOTE_PROTOCOL(Samsung36) + +template class Samsung36Action : public RemoteTransmitterActionBase { + public: + TEMPLATABLE_VALUE(uint16_t, address) + TEMPLATABLE_VALUE(uint32_t, command) + + void encode(RemoteTransmitData *dst, Ts... x) override { + Samsung36Data data{}; + data.address = this->address_.value(x...); + data.command = this->command_.value(x...); + Samsung36Protocol().encode(dst, data); + } +}; + +} // namespace remote_base +} // namespace esphome diff --git a/tests/test1.yaml b/tests/test1.yaml index e76d1d24c0..59c2bf96c4 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -1478,6 +1478,12 @@ switch: turn_on_action: remote_transmitter.transmit_samsung: data: 0xABCDEF + - platform: template + name: Samsung36 + turn_on_action: + remote_transmitter.transmit_samsung36: + address: 0x0400 + command: 0x000E00FF - platform: template name: Sony turn_on_action: From 34d37961c37be9048215466007b1e4c93f73440c Mon Sep 17 00:00:00 2001 From: Christian Ferbar <5595808+ferbar@users.noreply.github.com> Date: Sun, 28 Feb 2021 18:02:02 +0100 Subject: [PATCH 060/111] ADC fix: GPIO0 not usable as output if ADC_VCC is used (#1557) --- esphome/components/adc/adc_sensor.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/esphome/components/adc/adc_sensor.cpp b/esphome/components/adc/adc_sensor.cpp index 9b1f452131..c6e2141725 100644 --- a/esphome/components/adc/adc_sensor.cpp +++ b/esphome/components/adc/adc_sensor.cpp @@ -16,7 +16,9 @@ void ADCSensor::set_attenuation(adc_attenuation_t attenuation) { this->attenuati void ADCSensor::setup() { ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str()); +#ifndef USE_ADC_SENSOR_VCC GPIOPin(this->pin_, INPUT).setup(); +#endif #ifdef ARDUINO_ARCH_ESP32 analogSetPinAttenuation(this->pin_, this->attenuation_); From 422f0ad7a92bfac1f8c6989f65fc8a65d687d73c Mon Sep 17 00:00:00 2001 From: marecabo <23156476+marecabo@users.noreply.github.com> Date: Mon, 1 Mar 2021 00:55:32 +0100 Subject: [PATCH 061/111] Add constants for device classes of binary_sensor (#1549) --- esphome/components/binary_sensor/__init__.py | 20 +++++++++++---- esphome/const.py | 26 ++++++++++++++++++-- 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/esphome/components/binary_sensor/__init__.py b/esphome/components/binary_sensor/__init__.py index 753010310c..da1dc697a2 100644 --- a/esphome/components/binary_sensor/__init__.py +++ b/esphome/components/binary_sensor/__init__.py @@ -7,16 +7,26 @@ from esphome.const import CONF_DEVICE_CLASS, CONF_FILTERS, \ CONF_ID, CONF_INTERNAL, CONF_INVALID_COOLDOWN, CONF_INVERTED, \ CONF_MAX_LENGTH, CONF_MIN_LENGTH, CONF_ON_CLICK, \ CONF_ON_DOUBLE_CLICK, CONF_ON_MULTI_CLICK, CONF_ON_PRESS, CONF_ON_RELEASE, CONF_ON_STATE, \ - CONF_STATE, CONF_TIMING, CONF_TRIGGER_ID, CONF_FOR, CONF_NAME, CONF_MQTT_ID + CONF_STATE, CONF_TIMING, CONF_TRIGGER_ID, CONF_FOR, CONF_NAME, CONF_MQTT_ID, \ + DEVICE_CLASS_EMPTY, DEVICE_CLASS_BATTERY, DEVICE_CLASS_BATTERY_CHARGING, DEVICE_CLASS_COLD, \ + DEVICE_CLASS_CONNECTIVITY, DEVICE_CLASS_DOOR, DEVICE_CLASS_GARAGE_DOOR, DEVICE_CLASS_GAS, \ + DEVICE_CLASS_HEAT, DEVICE_CLASS_LIGHT, DEVICE_CLASS_LOCK, DEVICE_CLASS_MOISTURE, \ + DEVICE_CLASS_MOTION, DEVICE_CLASS_MOVING, DEVICE_CLASS_OCCUPANCY, DEVICE_CLASS_OPENING, \ + DEVICE_CLASS_PLUG, DEVICE_CLASS_POWER, DEVICE_CLASS_PRESENCE, DEVICE_CLASS_PROBLEM, \ + DEVICE_CLASS_SAFETY, DEVICE_CLASS_SMOKE, DEVICE_CLASS_SOUND, DEVICE_CLASS_VIBRATION, \ + DEVICE_CLASS_WINDOW from esphome.core import CORE, coroutine, coroutine_with_priority from esphome.util import Registry CODEOWNERS = ['@esphome/core'] DEVICE_CLASSES = [ - '', 'battery', 'cold', 'connectivity', 'door', 'garage_door', 'gas', - 'heat', 'light', 'lock', 'moisture', 'motion', 'moving', 'occupancy', - 'opening', 'plug', 'power', 'presence', 'problem', 'safety', 'smoke', - 'sound', 'vibration', 'window' + DEVICE_CLASS_EMPTY, DEVICE_CLASS_BATTERY, DEVICE_CLASS_BATTERY_CHARGING, DEVICE_CLASS_COLD, + DEVICE_CLASS_CONNECTIVITY, DEVICE_CLASS_DOOR, DEVICE_CLASS_GARAGE_DOOR, DEVICE_CLASS_GAS, + DEVICE_CLASS_HEAT, DEVICE_CLASS_LIGHT, DEVICE_CLASS_LOCK, DEVICE_CLASS_MOISTURE, + DEVICE_CLASS_MOTION, DEVICE_CLASS_MOVING, DEVICE_CLASS_OCCUPANCY, DEVICE_CLASS_OPENING, + DEVICE_CLASS_PLUG, DEVICE_CLASS_POWER, DEVICE_CLASS_PRESENCE, DEVICE_CLASS_PROBLEM, + DEVICE_CLASS_SAFETY, DEVICE_CLASS_SMOKE, DEVICE_CLASS_SOUND, DEVICE_CLASS_VIBRATION, + DEVICE_CLASS_WINDOW ] IS_PLATFORM_COMPONENT = True diff --git a/esphome/const.py b/esphome/const.py index 345cac89dd..f652cc1b6d 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -666,18 +666,40 @@ UNIT_VOLT_AMPS_REACTIVE = 'VAR' UNIT_WATT = 'W' UNIT_WATT_HOURS = 'Wh' +# device classes of binary_sensor component +DEVICE_CLASS_BATTERY_CHARGING = 'battery_charging' +DEVICE_CLASS_COLD = 'cold' DEVICE_CLASS_CONNECTIVITY = 'connectivity' +DEVICE_CLASS_DOOR = 'door' +DEVICE_CLASS_GARAGE_DOOR = 'garage_door' +DEVICE_CLASS_GAS = 'gas' +DEVICE_CLASS_HEAT = 'heat' +DEVICE_CLASS_LIGHT = 'light' +DEVICE_CLASS_LOCK = 'lock' +DEVICE_CLASS_MOISTURE = 'moisture' +DEVICE_CLASS_MOTION = 'motion' DEVICE_CLASS_MOVING = 'moving' - +DEVICE_CLASS_OCCUPANCY = 'occupancy' +DEVICE_CLASS_OPENING = 'opening' +DEVICE_CLASS_PLUG = 'plug' +DEVICE_CLASS_PRESENCE = 'presence' +DEVICE_CLASS_PROBLEM = 'problem' +DEVICE_CLASS_SAFETY = 'safety' +DEVICE_CLASS_SMOKE = 'smoke' +DEVICE_CLASS_SOUND = 'sound' +DEVICE_CLASS_VIBRATION = 'vibration' +DEVICE_CLASS_WINDOW = 'window' +# device classes of both binary_sensor and sensor component DEVICE_CLASS_EMPTY = '' DEVICE_CLASS_BATTERY = 'battery' +DEVICE_CLASS_POWER = 'power' +# device classes of sensor component DEVICE_CLASS_CURRENT = 'current' DEVICE_CLASS_ENERGY = 'energy' DEVICE_CLASS_HUMIDITY = 'humidity' DEVICE_CLASS_ILLUMINANCE = 'illuminance' DEVICE_CLASS_SIGNAL_STRENGTH = 'signal_strength' DEVICE_CLASS_TEMPERATURE = 'temperature' -DEVICE_CLASS_POWER = 'power' DEVICE_CLASS_POWER_FACTOR = 'power_factor' DEVICE_CLASS_PRESSURE = 'pressure' DEVICE_CLASS_TIMESTAMP = 'timestamp' From b17e0c298ec19476a9c741389b81f00d8571b524 Mon Sep 17 00:00:00 2001 From: Guillermo Ruffino Date: Mon, 1 Mar 2021 22:16:36 -0300 Subject: [PATCH 062/111] fix path on windows escape (#1573) --- esphome/core.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/esphome/core.py b/esphome/core.py index 57b065c488..2295e42753 100644 --- a/esphome/core.py +++ b/esphome/core.py @@ -339,7 +339,8 @@ class DocumentLocation: @property def as_line_directive(self): - return f'#line {self.line + 1} "{self.document}"' + document_path = str(self.document).replace('\\', '\\\\') + return f'#line {self.line + 1} "{document_path}"' class DocumentRange: From ac25b138f55c56bdba9a7dd0937e8858cdef18b3 Mon Sep 17 00:00:00 2001 From: SenexCrenshaw <35600301+SenexCrenshaw@users.noreply.github.com> Date: Tue, 2 Mar 2021 09:08:57 -0500 Subject: [PATCH 063/111] Migrate ESPColor to Color (#1551) * Migrate ESPColor to Color * color.h constructor fix * Updated componets to use Color Added a using for ESPColor * Lint fixes * Fixed value error * Update display components to use colorutil * Updated to latest PR comments * Fixed COLOR_WHITE * Moved esp_scale to color_utils * Rename color_utils to display_color_utils --- .../adalight/adalight_light_effect.cpp | 6 +- .../adalight/adalight_light_effect.h | 2 +- esphome/components/color/__init__.py | 60 ++++-- esphome/components/display/display_buffer.h | 2 +- .../components/display/display_color_utils.h | 110 +++++++++++ .../e131/e131_addressable_light_effect.cpp | 10 +- .../e131/e131_addressable_light_effect.h | 2 +- .../components/ili9341/ili9341_display.cpp | 6 +- .../components/light/addressable_light.cpp | 17 +- esphome/components/light/addressable_light.h | 183 ++---------------- .../light/addressable_light_effect.h | 46 ++--- esphome/components/light/effects.py | 5 +- esphome/components/light/types.py | 2 +- .../neopixelbus/neopixelbus_light.h | 3 +- .../components/ssd1322_base/ssd1322_base.cpp | 4 +- .../components/ssd1325_base/ssd1325_base.cpp | 4 +- .../components/ssd1327_base/ssd1327_base.cpp | 4 +- .../components/ssd1331_base/ssd1331_base.cpp | 4 +- .../components/ssd1351_base/ssd1351_base.cpp | 4 +- esphome/components/st7735/st7735.cpp | 12 +- esphome/components/st7789v/st7789v.cpp | 2 +- esphome/components/wled/wled_light_effect.cpp | 12 +- esphome/components/wled/wled_light_effect.h | 2 +- esphome/core/color.h | 137 ++----------- tests/test1.yaml | 2 +- 25 files changed, 272 insertions(+), 369 deletions(-) create mode 100644 esphome/components/display/display_color_utils.h diff --git a/esphome/components/adalight/adalight_light_effect.cpp b/esphome/components/adalight/adalight_light_effect.cpp index 1bf357e308..d162c90721 100644 --- a/esphome/components/adalight/adalight_light_effect.cpp +++ b/esphome/components/adalight/adalight_light_effect.cpp @@ -42,11 +42,11 @@ void AdalightLightEffect::reset_frame_(light::AddressableLight &it) { void AdalightLightEffect::blank_all_leds_(light::AddressableLight &it) { for (int led = it.size(); led-- > 0;) { - it[led].set(light::ESPColor::BLACK); + it[led].set(COLOR_BLACK); } } -void AdalightLightEffect::apply(light::AddressableLight &it, const light::ESPColor ¤t_color) { +void AdalightLightEffect::apply(light::AddressableLight &it, const Color ¤t_color) { const uint32_t now = millis(); if (now - this->last_ack_ >= ADALIGHT_ACK_INTERVAL) { @@ -130,7 +130,7 @@ AdalightLightEffect::Frame AdalightLightEffect::parse_frame_(light::AddressableL for (int led = 0; led < accepted_led_count; led++, led_data += 3) { auto white = std::min(std::min(led_data[0], led_data[1]), led_data[2]); - it[led].set(light::ESPColor(led_data[0], led_data[1], led_data[2], white)); + it[led].set(Color(led_data[0], led_data[1], led_data[2], white)); } return CONSUMED; diff --git a/esphome/components/adalight/adalight_light_effect.h b/esphome/components/adalight/adalight_light_effect.h index 4f77394ebc..c1df55659b 100644 --- a/esphome/components/adalight/adalight_light_effect.h +++ b/esphome/components/adalight/adalight_light_effect.h @@ -16,7 +16,7 @@ class AdalightLightEffect : public light::AddressableLightEffect, public uart::U public: void start() override; void stop() override; - void apply(light::AddressableLight &it, const light::ESPColor ¤t_color) override; + void apply(light::AddressableLight &it, const Color ¤t_color) override; protected: enum Frame { diff --git a/esphome/components/color/__init__.py b/esphome/components/color/__init__.py index 3e2e7b2c07..db2fc6c093 100644 --- a/esphome/components/color/__init__.py +++ b/esphome/components/color/__init__.py @@ -2,22 +2,58 @@ from esphome import config_validation as cv from esphome import codegen as cg from esphome.const import CONF_BLUE, CONF_GREEN, CONF_ID, CONF_RED, CONF_WHITE -ColorStruct = cg.esphome_ns.struct('Color') +ColorStruct = cg.esphome_ns.struct("Color") MULTI_CONF = True -CONFIG_SCHEMA = cv.Schema({ - cv.Required(CONF_ID): cv.declare_id(ColorStruct), - cv.Optional(CONF_RED, default=0.0): cv.percentage, - cv.Optional(CONF_GREEN, default=0.0): cv.percentage, - cv.Optional(CONF_BLUE, default=0.0): cv.percentage, - cv.Optional(CONF_WHITE, default=0.0): cv.percentage, -}).extend(cv.COMPONENT_SCHEMA) + +CONF_RED_INT = "red_int" +CONF_GREEN_INT = "green_int" +CONF_BLUE_INT = "blue_int" +CONF_WHITE_INT = "white_int" + +CONFIG_SCHEMA = cv.Schema( + { + cv.Required(CONF_ID): cv.declare_id(ColorStruct), + cv.Exclusive(CONF_RED, 'red'): cv.percentage, + cv.Exclusive(CONF_RED_INT, 'red'): cv.uint8_t, + cv.Exclusive(CONF_GREEN, 'green'): cv.percentage, + cv.Exclusive(CONF_GREEN_INT, 'green'): cv.uint8_t, + cv.Exclusive(CONF_BLUE, 'blue'): cv.percentage, + cv.Exclusive(CONF_BLUE_INT, 'blue'): cv.uint8_t, + cv.Exclusive(CONF_WHITE, 'white'): cv.percentage, + cv.Exclusive(CONF_WHITE_INT, 'white'): cv.uint8_t, + } +).extend(cv.COMPONENT_SCHEMA) def to_code(config): + r = 0 + if CONF_RED in config: + r = int(config[CONF_RED]*255) + elif CONF_RED_INT in config: + r = config[CONF_RED_INT] + + g = 0 + if CONF_GREEN in config: + g = int(config[CONF_GREEN]*255) + elif CONF_GREEN_INT in config: + g = config[CONF_GREEN_INT] + + b = 0 + if CONF_BLUE in config: + b = int(config[CONF_BLUE]*255) + elif CONF_BLUE_INT in config: + b = config[CONF_BLUE_INT] + + w = 0 + if CONF_WHITE in config: + w = int(config[CONF_WHITE]*255) + elif CONF_WHITE_INT in config: + w = config[CONF_WHITE_INT] + cg.variable(config[CONF_ID], cg.StructInitializer( ColorStruct, - ('r', config[CONF_RED]), - ('g', config[CONF_GREEN]), - ('b', config[CONF_BLUE]), - ('w', config[CONF_WHITE]))) + ('r', r), + ('g', g), + ('b', b), + ('w', w))) diff --git a/esphome/components/display/display_buffer.h b/esphome/components/display/display_buffer.h index 235224d42e..5a63441e2d 100644 --- a/esphome/components/display/display_buffer.h +++ b/esphome/components/display/display_buffer.h @@ -3,7 +3,7 @@ #include "esphome/core/component.h" #include "esphome/core/defines.h" #include "esphome/core/automation.h" -#include "esphome/core/color.h" +#include "display_color_utils.h" #ifdef USE_TIME #include "esphome/components/time/real_time_clock.h" diff --git a/esphome/components/display/display_color_utils.h b/esphome/components/display/display_color_utils.h new file mode 100644 index 0000000000..8fc3b0adb9 --- /dev/null +++ b/esphome/components/display/display_color_utils.h @@ -0,0 +1,110 @@ +#pragma once +#include "esphome/core/color.h" + +namespace esphome { +namespace display { +enum ColorOrder : uint8_t { COLOR_ORDER_RGB = 0, COLOR_ORDER_BGR = 1, COLOR_ORDER_GRB = 2 }; +enum ColorBitness : uint8_t { COLOR_BITNESS_888 = 0, COLOR_BITNESS_565 = 1, COLOR_BITNESS_332 = 2 }; +inline static uint8_t esp_scale(uint8_t i, uint8_t scale, uint8_t max_value = 255) { return (max_value * i / scale); } + +class ColorUtil { + public: + static Color to_color(uint32_t colorcode, ColorOrder color_order, + ColorBitness color_bitness = ColorBitness::COLOR_BITNESS_888, bool right_bit_aligned = true) { + uint8_t first_color, second_color, third_color; + uint8_t first_bits = 0; + uint8_t second_bits = 0; + uint8_t third_bits = 0; + + switch (color_bitness) { + case COLOR_BITNESS_888: + first_bits = 8; + second_bits = 8; + third_bits = 8; + break; + case COLOR_BITNESS_565: + first_bits = 5; + second_bits = 6; + third_bits = 5; + break; + case COLOR_BITNESS_332: + first_bits = 3; + second_bits = 3; + third_bits = 2; + break; + } + + first_color = right_bit_aligned ? esp_scale(((colorcode >> (second_bits + third_bits)) & ((1 << first_bits) - 1)), + ((1 << first_bits) - 1)) + : esp_scale(((colorcode >> 16) & 0xFF), (1 << first_bits) - 1); + + second_color = right_bit_aligned + ? esp_scale(((colorcode >> third_bits) & ((1 << second_bits) - 1)), ((1 << second_bits) - 1)) + : esp_scale(((colorcode >> 8) & 0xFF), ((1 << second_bits) - 1)); + + third_color = (right_bit_aligned ? esp_scale(((colorcode >> 0) & 0xFF), ((1 << third_bits) - 1)) + : esp_scale(((colorcode >> 0) & 0xFF), (1 << third_bits) - 1)); + + Color color_return; + + switch (color_order) { + case COLOR_ORDER_RGB: + color_return.r = first_color; + color_return.g = second_color; + color_return.b = third_color; + break; + case COLOR_ORDER_BGR: + color_return.b = first_color; + color_return.g = second_color; + color_return.r = third_color; + break; + case COLOR_ORDER_GRB: + color_return.g = first_color; + color_return.r = second_color; + color_return.b = third_color; + break; + } + return color_return; + } + static uint8_t color_to_332(Color color, ColorOrder color_order = ColorOrder::COLOR_ORDER_RGB) { + uint16_t red_color, green_color, blue_color; + + red_color = esp_scale8(color.red, ((1 << 3) - 1)); + green_color = esp_scale8(color.green, ((1 << 3) - 1)); + blue_color = esp_scale8(color.blue, (1 << 2) - 1); + + switch (color_order) { + case COLOR_ORDER_RGB: + return red_color << 5 | green_color << 2 | blue_color; + case COLOR_ORDER_BGR: + return blue_color << 6 | green_color << 3 | red_color; + case COLOR_ORDER_GRB: + return green_color << 5 | red_color << 2 | blue_color; + } + return 0; + } + static uint16_t color_to_565(Color color, ColorOrder color_order = ColorOrder::COLOR_ORDER_RGB) { + uint16_t red_color, green_color, blue_color; + + red_color = esp_scale8(color.red, ((1 << 5) - 1)); + green_color = esp_scale8(color.green, ((1 << 6) - 1)); + blue_color = esp_scale8(color.blue, (1 << 5) - 1); + + switch (color_order) { + case COLOR_ORDER_RGB: + return red_color << 11 | green_color << 5 | blue_color; + case COLOR_ORDER_BGR: + return blue_color << 11 | green_color << 5 | red_color; + case COLOR_ORDER_GRB: + return green_color << 10 | red_color << 5 | blue_color; + } + return 0; + } + + static uint32_t color_to_grayscale4(Color color) { + uint32_t gs4 = esp_scale8(color.white, 15); + return gs4; + } +}; +} // namespace display +} // namespace esphome diff --git a/esphome/components/e131/e131_addressable_light_effect.cpp b/esphome/components/e131/e131_addressable_light_effect.cpp index 8657d828c5..3283f71931 100644 --- a/esphome/components/e131/e131_addressable_light_effect.cpp +++ b/esphome/components/e131/e131_addressable_light_effect.cpp @@ -40,7 +40,7 @@ void E131AddressableLightEffect::stop() { AddressableLightEffect::stop(); } -void E131AddressableLightEffect::apply(light::AddressableLight &it, const light::ESPColor ¤t_color) { +void E131AddressableLightEffect::apply(light::AddressableLight &it, const Color ¤t_color) { // ignore, it is run by `E131Component::update()` } @@ -63,22 +63,22 @@ bool E131AddressableLightEffect::process_(int universe, const E131Packet &packet case E131_MONO: for (; output_offset < output_end; output_offset++, input_data++) { auto output = (*it)[output_offset]; - output.set(light::ESPColor(input_data[0], input_data[0], input_data[0], input_data[0])); + output.set(Color(input_data[0], input_data[0], input_data[0], input_data[0])); } break; case E131_RGB: for (; output_offset < output_end; output_offset++, input_data += 3) { auto output = (*it)[output_offset]; - output.set(light::ESPColor(input_data[0], input_data[1], input_data[2], - (input_data[0] + input_data[1] + input_data[2]) / 3)); + output.set( + Color(input_data[0], input_data[1], input_data[2], (input_data[0] + input_data[1] + input_data[2]) / 3)); } break; case E131_RGBW: for (; output_offset < output_end; output_offset++, input_data += 4) { auto output = (*it)[output_offset]; - output.set(light::ESPColor(input_data[0], input_data[1], input_data[2], input_data[3])); + output.set(Color(input_data[0], input_data[1], input_data[2], input_data[3])); } break; } diff --git a/esphome/components/e131/e131_addressable_light_effect.h b/esphome/components/e131/e131_addressable_light_effect.h index 85af4fe7a9..1ab5d43164 100644 --- a/esphome/components/e131/e131_addressable_light_effect.h +++ b/esphome/components/e131/e131_addressable_light_effect.h @@ -18,7 +18,7 @@ class E131AddressableLightEffect : public light::AddressableLightEffect { public: void start() override; void stop() override; - void apply(light::AddressableLight &it, const light::ESPColor ¤t_color) override; + void apply(light::AddressableLight &it, const Color ¤t_color) override; public: int get_data_per_universe() const; diff --git a/esphome/components/ili9341/ili9341_display.cpp b/esphome/components/ili9341/ili9341_display.cpp index c0e7873284..48a95f4df0 100644 --- a/esphome/components/ili9341/ili9341_display.cpp +++ b/esphome/components/ili9341/ili9341_display.cpp @@ -130,7 +130,7 @@ uint8_t ILI9341Display::convert_to_8bit_color_(uint16_t color_16bit) { } void ILI9341Display::fill(Color color) { - auto color565 = color.to_rgb_565(); + auto color565 = display::ColorUtil::color_to_565(color); memset(this->buffer_, convert_to_8bit_color_(color565), this->get_buffer_length_()); this->x_low_ = 0; this->y_low_ = 0; @@ -142,7 +142,7 @@ void ILI9341Display::fill_internal_(Color color) { this->set_addr_window_(0, 0, this->get_width_internal(), this->get_height_internal()); this->start_data_(); - auto color565 = color.to_rgb_565(); + auto color565 = display::ColorUtil::color_to_565(color); for (uint32_t i = 0; i < (this->get_width_internal()) * (this->get_height_internal()); i++) { this->write_byte(color565 >> 8); this->write_byte(color565); @@ -162,7 +162,7 @@ void HOT ILI9341Display::draw_absolute_pixel_internal(int x, int y, Color color) this->y_high_ = (y > this->y_high_) ? y : this->y_high_; uint32_t pos = (y * width_) + x; - auto color565 = color.to_rgb_565(); + auto color565 = display::ColorUtil::color_to_565(color); buffer_[pos] = convert_to_8bit_color_(color565); } diff --git a/esphome/components/light/addressable_light.cpp b/esphome/components/light/addressable_light.cpp index b5dc70a083..236e5cede6 100644 --- a/esphome/components/light/addressable_light.cpp +++ b/esphome/components/light/addressable_light.cpp @@ -6,10 +6,7 @@ namespace light { static const char *TAG = "light.addressable"; -const ESPColor ESPColor::BLACK = ESPColor(0, 0, 0, 0); -const ESPColor ESPColor::WHITE = ESPColor(255, 255, 255, 255); - -ESPColor ESPHSVColor::to_rgb() const { +Color ESPHSVColor::to_rgb() const { // based on FastLED's hsv rainbow to rgb const uint8_t hue = this->hue; const uint8_t sat = this->saturation; @@ -19,7 +16,7 @@ ESPColor ESPHSVColor::to_rgb() const { // third of the offset, 255/3 = 85 (actually only up to 82; 164) const uint8_t third = esp_scale8(offset8, 85); const uint8_t two_thirds = esp_scale8(offset8, 170); - ESPColor rgb(255, 255, 255, 0); + Color rgb(255, 255, 255, 0); switch (hue >> 5) { case 0b000: rgb.r = 255 - third; @@ -76,7 +73,7 @@ ESPColor ESPHSVColor::to_rgb() const { return rgb; } -void ESPRangeView::set(const ESPColor &color) { +void ESPRangeView::set(const Color &color) { for (int32_t i = this->begin_; i < this->end_; i++) { (*this->parent_)[i] = color; } @@ -179,12 +176,12 @@ void AddressableLight::call_setup() { #endif } -ESPColor esp_color_from_light_color_values(LightColorValues val) { +Color esp_color_from_light_color_values(LightColorValues val) { auto r = static_cast(roundf(val.get_red() * 255.0f)); auto g = static_cast(roundf(val.get_green() * 255.0f)); auto b = static_cast(roundf(val.get_blue() * 255.0f)); auto w = static_cast(roundf(val.get_white() * val.get_state() * 255.0f)); - return ESPColor(r, g, b, w); + return Color(r, g, b, w); } void AddressableLight::write_state(LightState *state) { @@ -219,7 +216,7 @@ void AddressableLight::write_state(LightState *state) { this->last_transition_progress_ = new_progress; auto end_values = state->transformer_->get_end_values(); - ESPColor target_color = esp_color_from_light_color_values(end_values); + Color target_color = esp_color_from_light_color_values(end_values); // our transition will handle brightness, disable brightness in correction. this->correction_.set_local_brightness(255); @@ -247,7 +244,7 @@ void AddressableLight::write_state(LightState *state) { if (alpha8 != 0) { uint8_t inv_alpha8 = 255 - alpha8; - ESPColor add = target_color * alpha8; + Color add = target_color * alpha8; for (auto led : *this) led = add + led.get() * inv_alpha8; diff --git a/esphome/components/light/addressable_light.h b/esphome/components/light/addressable_light.h index 4e7ec4b931..39bd905c65 100644 --- a/esphome/components/light/addressable_light.h +++ b/esphome/components/light/addressable_light.h @@ -2,6 +2,7 @@ #include "esphome/core/component.h" #include "esphome/core/defines.h" +#include "esphome/core/color.h" #include "light_output.h" #include "light_state.h" @@ -12,151 +13,7 @@ namespace esphome { namespace light { -inline static uint8_t esp_scale8(uint8_t i, uint8_t scale) { return (uint16_t(i) * (1 + uint16_t(scale))) / 256; } - -struct ESPColor { - union { - struct { - union { - uint8_t r; - uint8_t red; - }; - union { - uint8_t g; - uint8_t green; - }; - union { - uint8_t b; - uint8_t blue; - }; - union { - uint8_t w; - uint8_t white; - }; - }; - uint8_t raw[4]; - uint32_t raw_32; - }; - inline ESPColor() ALWAYS_INLINE : r(0), g(0), b(0), w(0) {} // NOLINT - inline ESPColor(uint8_t red, uint8_t green, uint8_t blue, uint8_t white) ALWAYS_INLINE : r(red), - g(green), - b(blue), - w(white) {} - inline ESPColor(uint8_t red, uint8_t green, uint8_t blue) ALWAYS_INLINE : r(red), g(green), b(blue), w(0) {} - inline ESPColor(uint32_t colorcode) ALWAYS_INLINE : r((colorcode >> 16) & 0xFF), - g((colorcode >> 8) & 0xFF), - b((colorcode >> 0) & 0xFF), - w((colorcode >> 24) & 0xFF) {} - inline ESPColor(const ESPColor &rhs) ALWAYS_INLINE { - this->r = rhs.r; - this->g = rhs.g; - this->b = rhs.b; - this->w = rhs.w; - } - inline bool is_on() ALWAYS_INLINE { return this->raw_32 != 0; } - inline ESPColor &operator=(const ESPColor &rhs) ALWAYS_INLINE { - this->r = rhs.r; - this->g = rhs.g; - this->b = rhs.b; - this->w = rhs.w; - return *this; - } - inline ESPColor &operator=(uint32_t colorcode) ALWAYS_INLINE { - this->w = (colorcode >> 24) & 0xFF; - this->r = (colorcode >> 16) & 0xFF; - this->g = (colorcode >> 8) & 0xFF; - this->b = (colorcode >> 0) & 0xFF; - return *this; - } - inline uint8_t &operator[](uint8_t x) ALWAYS_INLINE { return this->raw[x]; } - inline ESPColor operator*(uint8_t scale) const ALWAYS_INLINE { - return ESPColor(esp_scale8(this->red, scale), esp_scale8(this->green, scale), esp_scale8(this->blue, scale), - esp_scale8(this->white, scale)); - } - inline ESPColor &operator*=(uint8_t scale) ALWAYS_INLINE { - this->red = esp_scale8(this->red, scale); - this->green = esp_scale8(this->green, scale); - this->blue = esp_scale8(this->blue, scale); - this->white = esp_scale8(this->white, scale); - return *this; - } - inline ESPColor operator*(const ESPColor &scale) const ALWAYS_INLINE { - return ESPColor(esp_scale8(this->red, scale.red), esp_scale8(this->green, scale.green), - esp_scale8(this->blue, scale.blue), esp_scale8(this->white, scale.white)); - } - inline ESPColor &operator*=(const ESPColor &scale) ALWAYS_INLINE { - this->red = esp_scale8(this->red, scale.red); - this->green = esp_scale8(this->green, scale.green); - this->blue = esp_scale8(this->blue, scale.blue); - this->white = esp_scale8(this->white, scale.white); - return *this; - } - inline ESPColor operator+(const ESPColor &add) const ALWAYS_INLINE { - ESPColor ret; - if (uint8_t(add.r + this->r) < this->r) - ret.r = 255; - else - ret.r = this->r + add.r; - if (uint8_t(add.g + this->g) < this->g) - ret.g = 255; - else - ret.g = this->g + add.g; - if (uint8_t(add.b + this->b) < this->b) - ret.b = 255; - else - ret.b = this->b + add.b; - if (uint8_t(add.w + this->w) < this->w) - ret.w = 255; - else - ret.w = this->w + add.w; - return ret; - } - inline ESPColor &operator+=(const ESPColor &add) ALWAYS_INLINE { return *this = (*this) + add; } - inline ESPColor operator+(uint8_t add) const ALWAYS_INLINE { return (*this) + ESPColor(add, add, add, add); } - inline ESPColor &operator+=(uint8_t add) ALWAYS_INLINE { return *this = (*this) + add; } - inline ESPColor operator-(const ESPColor &subtract) const ALWAYS_INLINE { - ESPColor ret; - if (subtract.r > this->r) - ret.r = 0; - else - ret.r = this->r - subtract.r; - if (subtract.g > this->g) - ret.g = 0; - else - ret.g = this->g - subtract.g; - if (subtract.b > this->b) - ret.b = 0; - else - ret.b = this->b - subtract.b; - if (subtract.w > this->w) - ret.w = 0; - else - ret.w = this->w - subtract.w; - return ret; - } - inline ESPColor &operator-=(const ESPColor &subtract) ALWAYS_INLINE { return *this = (*this) - subtract; } - inline ESPColor operator-(uint8_t subtract) const ALWAYS_INLINE { - return (*this) - ESPColor(subtract, subtract, subtract, subtract); - } - inline ESPColor &operator-=(uint8_t subtract) ALWAYS_INLINE { return *this = (*this) - subtract; } - static ESPColor random_color() { - uint32_t rand = random_uint32(); - uint8_t w = rand >> 24; - uint8_t r = rand >> 16; - uint8_t g = rand >> 8; - uint8_t b = rand >> 0; - const uint16_t max_rgb = std::max(r, std::max(g, b)); - return ESPColor(uint8_t((uint16_t(r) * 255U / max_rgb)), uint8_t((uint16_t(g) * 255U / max_rgb)), - uint8_t((uint16_t(b) * 255U / max_rgb)), w); - } - ESPColor fade_to_white(uint8_t amnt) const { return ESPColor(255, 255, 255, 255) - (*this * amnt); } - ESPColor fade_to_black(uint8_t amnt) const { return *this * amnt; } - ESPColor lighten(uint8_t delta) const { return *this + delta; } - ESPColor darken(uint8_t delta) const { return *this - delta; } - - static const ESPColor BLACK; - static const ESPColor WHITE; -}; +using ESPColor = Color; struct ESPHSVColor { union { @@ -181,19 +38,19 @@ struct ESPHSVColor { inline ESPHSVColor(uint8_t hue, uint8_t saturation, uint8_t value) ALWAYS_INLINE : hue(hue), saturation(saturation), value(value) {} - ESPColor to_rgb() const; + Color to_rgb() const; }; class ESPColorCorrection { public: ESPColorCorrection() : max_brightness_(255, 255, 255, 255) {} - void set_max_brightness(const ESPColor &max_brightness) { this->max_brightness_ = max_brightness; } + void set_max_brightness(const Color &max_brightness) { this->max_brightness_ = max_brightness; } void set_local_brightness(uint8_t local_brightness) { this->local_brightness_ = local_brightness; } void calculate_gamma_table(float gamma); - inline ESPColor color_correct(ESPColor color) const ALWAYS_INLINE { + inline Color color_correct(Color color) const ALWAYS_INLINE { // corrected = (uncorrected * max_brightness * local_brightness) ^ gamma - return ESPColor(this->color_correct_red(color.red), this->color_correct_green(color.green), - this->color_correct_blue(color.blue), this->color_correct_white(color.white)); + return Color(this->color_correct_red(color.red), this->color_correct_green(color.green), + this->color_correct_blue(color.blue), this->color_correct_white(color.white)); } inline uint8_t color_correct_red(uint8_t red) const ALWAYS_INLINE { uint8_t res = esp_scale8(esp_scale8(red, this->max_brightness_.red), this->local_brightness_); @@ -212,10 +69,10 @@ class ESPColorCorrection { uint8_t res = esp_scale8(white, this->max_brightness_.white); return this->gamma_table_[res]; } - inline ESPColor color_uncorrect(ESPColor color) const ALWAYS_INLINE { + inline Color color_uncorrect(Color color) const ALWAYS_INLINE { // uncorrected = corrected^(1/gamma) / (max_brightness * local_brightness) - return ESPColor(this->color_uncorrect_red(color.red), this->color_uncorrect_green(color.green), - this->color_uncorrect_blue(color.blue), this->color_uncorrect_white(color.white)); + return Color(this->color_uncorrect_red(color.red), this->color_uncorrect_green(color.green), + this->color_uncorrect_blue(color.blue), this->color_uncorrect_white(color.white)); } inline uint8_t color_uncorrect_red(uint8_t red) const ALWAYS_INLINE { if (this->max_brightness_.red == 0 || this->local_brightness_ == 0) @@ -249,13 +106,13 @@ class ESPColorCorrection { protected: uint8_t gamma_table_[256]; uint8_t gamma_reverse_table_[256]; - ESPColor max_brightness_; + Color max_brightness_; uint8_t local_brightness_{255}; }; class ESPColorSettable { public: - virtual void set(const ESPColor &color) = 0; + virtual void set(const Color &color) = 0; virtual void set_red(uint8_t red) = 0; virtual void set_green(uint8_t green) = 0; virtual void set_blue(uint8_t blue) = 0; @@ -267,7 +124,7 @@ class ESPColorSettable { virtual void darken(uint8_t delta) = 0; void set(const ESPHSVColor &color) { this->set_hsv(color); } void set_hsv(const ESPHSVColor &color) { - ESPColor rgb = color.to_rgb(); + Color rgb = color.to_rgb(); this->set_rgb(rgb.r, rgb.g, rgb.b); } void set_rgb(uint8_t red, uint8_t green, uint8_t blue) { @@ -291,7 +148,7 @@ class ESPColorView : public ESPColorSettable { white_(white), effect_data_(effect_data), color_correction_(color_correction) {} - ESPColorView &operator=(const ESPColor &rhs) { + ESPColorView &operator=(const Color &rhs) { this->set(rhs); return *this; } @@ -299,7 +156,7 @@ class ESPColorView : public ESPColorSettable { this->set_hsv(rhs); return *this; } - void set(const ESPColor &color) override { this->set_rgbw(color.r, color.g, color.b, color.w); } + void set(const Color &color) override { this->set_rgbw(color.r, color.g, color.b, color.w); } void set_red(uint8_t red) override { *this->red_ = this->color_correction_->color_correct_red(red); } void set_green(uint8_t green) override { *this->green_ = this->color_correction_->color_correct_green(green); } void set_blue(uint8_t blue) override { *this->blue_ = this->color_correction_->color_correct_blue(blue); } @@ -317,7 +174,7 @@ class ESPColorView : public ESPColorSettable { void fade_to_black(uint8_t amnt) override { this->set(this->get().fade_to_black(amnt)); } void lighten(uint8_t delta) override { this->set(this->get().lighten(delta)); } void darken(uint8_t delta) override { this->set(this->get().darken(delta)); } - ESPColor get() const { return ESPColor(this->get_red(), this->get_green(), this->get_blue(), this->get_white()); } + Color get() const { return Color(this->get_red(), this->get_green(), this->get_blue(), this->get_white()); } uint8_t get_red() const { return this->color_correction_->color_uncorrect_red(*this->red_); } uint8_t get_red_raw() const { return *this->red_; } uint8_t get_green() const { return this->color_correction_->color_uncorrect_green(*this->green_); } @@ -370,8 +227,8 @@ class ESPRangeView : public ESPColorSettable { ESPRangeIterator begin(); ESPRangeIterator end(); - void set(const ESPColor &color) override; - ESPRangeView &operator=(const ESPColor &rhs) { + void set(const Color &color) override; + ESPRangeView &operator=(const Color &rhs) { this->set(rhs); return *this; } @@ -454,8 +311,8 @@ class AddressableLight : public LightOutput, public Component { void set_effect_active(bool effect_active) { this->effect_active_ = effect_active; } void write_state(LightState *state) override; void set_correction(float red, float green, float blue, float white = 1.0f) { - this->correction_.set_max_brightness(ESPColor(uint8_t(roundf(red * 255.0f)), uint8_t(roundf(green * 255.0f)), - uint8_t(roundf(blue * 255.0f)), uint8_t(roundf(white * 255.0f)))); + this->correction_.set_max_brightness(Color(uint8_t(roundf(red * 255.0f)), uint8_t(roundf(green * 255.0f)), + uint8_t(roundf(blue * 255.0f)), uint8_t(roundf(white * 255.0f)))); } void setup_state(LightState *state) override { this->correction_.calculate_gamma_table(state->get_gamma_correct()); diff --git a/esphome/components/light/addressable_light_effect.h b/esphome/components/light/addressable_light_effect.h index e6528fcd8a..8d4d37ec34 100644 --- a/esphome/components/light/addressable_light_effect.h +++ b/esphome/components/light/addressable_light_effect.h @@ -34,13 +34,13 @@ class AddressableLightEffect : public LightEffect { this->start(); } void stop() override { this->get_addressable_()->set_effect_active(false); } - virtual void apply(AddressableLight &it, const ESPColor ¤t_color) = 0; + virtual void apply(AddressableLight &it, const Color ¤t_color) = 0; void apply() override { LightColorValues color = this->state_->remote_values; // not using any color correction etc. that will be handled by the addressable layer - ESPColor current_color = - ESPColor(static_cast(color.get_red() * 255), static_cast(color.get_green() * 255), - static_cast(color.get_blue() * 255), static_cast(color.get_white() * 255)); + Color current_color = + Color(static_cast(color.get_red() * 255), static_cast(color.get_green() * 255), + static_cast(color.get_blue() * 255), static_cast(color.get_white() * 255)); this->apply(*this->get_addressable_(), current_color); } @@ -51,11 +51,11 @@ class AddressableLightEffect : public LightEffect { class AddressableLambdaLightEffect : public AddressableLightEffect { public: AddressableLambdaLightEffect(const std::string &name, - const std::function &f, + const std::function &f, uint32_t update_interval) : AddressableLightEffect(name), f_(f), update_interval_(update_interval) {} void start() override { this->initial_run_ = true; } - void apply(AddressableLight &it, const ESPColor ¤t_color) override { + void apply(AddressableLight &it, const Color ¤t_color) override { const uint32_t now = millis(); if (now - this->last_run_ >= this->update_interval_) { this->last_run_ = now; @@ -65,7 +65,7 @@ class AddressableLambdaLightEffect : public AddressableLightEffect { } protected: - std::function f_; + std::function f_; uint32_t update_interval_; uint32_t last_run_{0}; bool initial_run_; @@ -74,7 +74,7 @@ class AddressableLambdaLightEffect : public AddressableLightEffect { class AddressableRainbowLightEffect : public AddressableLightEffect { public: explicit AddressableRainbowLightEffect(const std::string &name) : AddressableLightEffect(name) {} - void apply(AddressableLight &it, const ESPColor ¤t_color) override { + void apply(AddressableLight &it, const Color ¤t_color) override { ESPHSVColor hsv; hsv.value = 255; hsv.saturation = 240; @@ -106,7 +106,7 @@ class AddressableColorWipeEffect : public AddressableLightEffect { void set_colors(const std::vector &colors) { this->colors_ = colors; } void set_add_led_interval(uint32_t add_led_interval) { this->add_led_interval_ = add_led_interval; } void set_reverse(bool reverse) { this->reverse_ = reverse; } - void apply(AddressableLight &it, const ESPColor ¤t_color) override { + void apply(AddressableLight &it, const Color ¤t_color) override { const uint32_t now = millis(); if (now - this->last_add_ < this->add_led_interval_) return; @@ -116,7 +116,7 @@ class AddressableColorWipeEffect : public AddressableLightEffect { else it.shift_right(1); const AddressableColorWipeEffectColor color = this->colors_[this->at_color_]; - const ESPColor esp_color = ESPColor(color.r, color.g, color.b, color.w); + const Color esp_color = Color(color.r, color.g, color.b, color.w); if (this->reverse_) it[-1] = esp_color; else @@ -126,7 +126,7 @@ class AddressableColorWipeEffect : public AddressableLightEffect { this->at_color_ = (this->at_color_ + 1) % this->colors_.size(); AddressableColorWipeEffectColor &new_color = this->colors_[this->at_color_]; if (new_color.random) { - ESPColor c = ESPColor::random_color(); + Color c = Color::random_color(); new_color.r = c.r; new_color.g = c.g; new_color.b = c.b; @@ -148,8 +148,8 @@ class AddressableScanEffect : public AddressableLightEffect { explicit AddressableScanEffect(const std::string &name) : AddressableLightEffect(name) {} void set_move_interval(uint32_t move_interval) { this->move_interval_ = move_interval; } void set_scan_width(uint32_t scan_width) { this->scan_width_ = scan_width; } - void apply(AddressableLight &it, const ESPColor ¤t_color) override { - it.all() = ESPColor::BLACK; + void apply(AddressableLight &it, const Color ¤t_color) override { + it.all() = COLOR_BLACK; for (auto i = 0; i < this->scan_width_; i++) { it[this->at_led_ + i] = current_color; @@ -181,7 +181,7 @@ class AddressableScanEffect : public AddressableLightEffect { class AddressableTwinkleEffect : public AddressableLightEffect { public: explicit AddressableTwinkleEffect(const std::string &name) : AddressableLightEffect(name) {} - void apply(AddressableLight &addressable, const ESPColor ¤t_color) override { + void apply(AddressableLight &addressable, const Color ¤t_color) override { const uint32_t now = millis(); uint8_t pos_add = 0; if (now - this->last_progress_ > this->progress_interval_) { @@ -199,7 +199,7 @@ class AddressableTwinkleEffect : public AddressableLightEffect { else view.set_effect_data(new_pos); } else { - view = ESPColor::BLACK; + view = COLOR_BLACK; } } while (random_float() < this->twinkle_probability_) { @@ -221,7 +221,7 @@ class AddressableTwinkleEffect : public AddressableLightEffect { class AddressableRandomTwinkleEffect : public AddressableLightEffect { public: explicit AddressableRandomTwinkleEffect(const std::string &name) : AddressableLightEffect(name) {} - void apply(AddressableLight &it, const ESPColor ¤t_color) override { + void apply(AddressableLight &it, const Color ¤t_color) override { const uint32_t now = millis(); uint8_t pos_add = 0; if (now - this->last_progress_ > this->progress_interval_) { @@ -237,7 +237,7 @@ class AddressableRandomTwinkleEffect : public AddressableLightEffect { if (color == 0) { view = current_color * sine; } else { - view = ESPColor(((color >> 2) & 1) * sine, ((color >> 1) & 1) * sine, ((color >> 0) & 1) * sine); + view = Color(((color >> 2) & 1) * sine, ((color >> 1) & 1) * sine, ((color >> 0) & 1) * sine); } const uint8_t new_x = x + pos_add; if (new_x > 0b11111) @@ -245,7 +245,7 @@ class AddressableRandomTwinkleEffect : public AddressableLightEffect { else view.set_effect_data((new_x << 3) | color); } else { - view = ESPColor(0, 0, 0, 0); + view = Color(0, 0, 0, 0); } } while (random_float() < this->twinkle_probability_) { @@ -270,9 +270,9 @@ class AddressableFireworksEffect : public AddressableLightEffect { explicit AddressableFireworksEffect(const std::string &name) : AddressableLightEffect(name) {} void start() override { auto &it = *this->get_addressable_(); - it.all() = ESPColor::BLACK; + it.all() = COLOR_BLACK; } - void apply(AddressableLight &it, const ESPColor ¤t_color) override { + void apply(AddressableLight &it, const Color ¤t_color) override { const uint32_t now = millis(); if (now - this->last_update_ < this->update_interval_) return; @@ -280,7 +280,7 @@ class AddressableFireworksEffect : public AddressableLightEffect { // "invert" the fade out parameter so that higher values make fade out faster const uint8_t fade_out_mult = 255u - this->fade_out_rate_; for (auto view : it) { - ESPColor target = view.get() * fade_out_mult; + Color target = view.get() * fade_out_mult; if (target.r < 64) target *= 170; view = target; @@ -294,7 +294,7 @@ class AddressableFireworksEffect : public AddressableLightEffect { if (random_float() < this->spark_probability_) { const size_t pos = random_uint32() % it.size(); if (this->use_random_color_) { - it[pos] = ESPColor::random_color(); + it[pos] = Color::random_color(); } else { it[pos] = current_color; } @@ -316,7 +316,7 @@ class AddressableFireworksEffect : public AddressableLightEffect { class AddressableFlickerEffect : public AddressableLightEffect { public: explicit AddressableFlickerEffect(const std::string &name) : AddressableLightEffect(name) {} - void apply(AddressableLight &it, const ESPColor ¤t_color) override { + void apply(AddressableLight &it, const Color ¤t_color) override { const uint32_t now = millis(); const uint8_t intensity = this->intensity_; const uint8_t inv_intensity = 255 - intensity; diff --git a/esphome/components/light/effects.py b/esphome/components/light/effects.py index d8c709b8ad..edb7a39ed9 100644 --- a/esphome/components/light/effects.py +++ b/esphome/components/light/effects.py @@ -1,6 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import automation + from esphome.const import CONF_NAME, CONF_LAMBDA, CONF_UPDATE_INTERVAL, CONF_TRANSITION_LENGTH, \ CONF_COLORS, CONF_STATE, CONF_DURATION, CONF_BRIGHTNESS, CONF_RED, CONF_GREEN, CONF_BLUE, \ CONF_WHITE, CONF_ALPHA, CONF_INTENSITY, CONF_SPEED, CONF_WIDTH, CONF_NUM_LEDS, CONF_RANDOM, \ @@ -11,7 +12,7 @@ from .types import LambdaLightEffect, RandomLightEffect, StrobeLightEffect, \ FlickerLightEffect, AddressableRainbowLightEffect, AddressableColorWipeEffect, \ AddressableColorWipeEffectColor, AddressableScanEffect, AddressableTwinkleEffect, \ AddressableRandomTwinkleEffect, AddressableFireworksEffect, AddressableFlickerEffect, \ - AutomationLightEffect, ESPColor + AutomationLightEffect, Color CONF_ADD_LED_INTERVAL = 'add_led_interval' CONF_REVERSE = 'reverse' @@ -162,7 +163,7 @@ def flicker_effect_to_code(config, effect_id): } ) def addressable_lambda_effect_to_code(config, effect_id): - args = [(AddressableLightRef, 'it'), (ESPColor, 'current_color'), (bool, 'initial_run')] + args = [(AddressableLightRef, 'it'), (Color, 'current_color'), (bool, 'initial_run')] lambda_ = yield cg.process_lambda(config[CONF_LAMBDA], args, return_type=cg.void) var = cg.new_Pvariable(effect_id, config[CONF_NAME], lambda_, config[CONF_UPDATE_INTERVAL]) diff --git a/esphome/components/light/types.py b/esphome/components/light/types.py index d32ef0214c..6bcfd5fdb4 100644 --- a/esphome/components/light/types.py +++ b/esphome/components/light/types.py @@ -10,7 +10,7 @@ LightOutput = light_ns.class_('LightOutput') AddressableLight = light_ns.class_('AddressableLight', cg.Component) AddressableLightRef = AddressableLight.operator('ref') -ESPColor = light_ns.class_('ESPColor') +Color = cg.esphome_ns.class_('Color') LightColorValues = light_ns.class_('LightColorValues') # Actions diff --git a/esphome/components/neopixelbus/neopixelbus_light.h b/esphome/components/neopixelbus/neopixelbus_light.h index 5e8097187e..46601d8345 100644 --- a/esphome/components/neopixelbus/neopixelbus_light.h +++ b/esphome/components/neopixelbus/neopixelbus_light.h @@ -2,6 +2,7 @@ #include "esphome/core/component.h" #include "esphome/core/helpers.h" +#include "esphome/core/color.h" #include "esphome/components/light/light_output.h" #include "esphome/components/light/addressable_light.h" @@ -73,7 +74,7 @@ class NeoPixelBusLightOutputBase : public light::AddressableLight { // ========== INTERNAL METHODS ========== void setup() override { for (int i = 0; i < this->size(); i++) { - (*this)[i] = light::ESPColor(0, 0, 0, 0); + (*this)[i] = Color(0, 0, 0, 0); } this->effect_data_ = new uint8_t[this->size()]; diff --git a/esphome/components/ssd1322_base/ssd1322_base.cpp b/esphome/components/ssd1322_base/ssd1322_base.cpp index 9f382190a6..703f53fa93 100644 --- a/esphome/components/ssd1322_base/ssd1322_base.cpp +++ b/esphome/components/ssd1322_base/ssd1322_base.cpp @@ -162,7 +162,7 @@ size_t SSD1322::get_buffer_length_() { void HOT SSD1322::draw_absolute_pixel_internal(int x, int y, Color color) { if (x >= this->get_width_internal() || x < 0 || y >= this->get_height_internal() || y < 0) return; - uint32_t color4 = color.to_grayscale4(); + uint32_t color4 = display::ColorUtil::color_to_grayscale4(color); // where should the bits go in the big buffer array? math... uint16_t pos = (x / SSD1322_PIXELSPERBYTE) + (y * this->get_width_internal() / SSD1322_PIXELSPERBYTE); uint8_t shift = (1u - (x % SSD1322_PIXELSPERBYTE)) * SSD1322_COLORSHIFT; @@ -174,7 +174,7 @@ void HOT SSD1322::draw_absolute_pixel_internal(int x, int y, Color color) { this->buffer_[pos] |= color4; } void SSD1322::fill(Color color) { - const uint32_t color4 = color.to_grayscale4(); + const uint32_t color4 = display::ColorUtil::color_to_grayscale4(color); uint8_t fill = (color4 & SSD1322_COLORMASK) | ((color4 & SSD1322_COLORMASK) << SSD1322_COLORSHIFT); for (uint32_t i = 0; i < this->get_buffer_length_(); i++) this->buffer_[i] = fill; diff --git a/esphome/components/ssd1325_base/ssd1325_base.cpp b/esphome/components/ssd1325_base/ssd1325_base.cpp index dfb1ef00ee..9818880505 100644 --- a/esphome/components/ssd1325_base/ssd1325_base.cpp +++ b/esphome/components/ssd1325_base/ssd1325_base.cpp @@ -192,7 +192,7 @@ size_t SSD1325::get_buffer_length_() { void HOT SSD1325::draw_absolute_pixel_internal(int x, int y, Color color) { if (x >= this->get_width_internal() || x < 0 || y >= this->get_height_internal() || y < 0) return; - uint32_t color4 = color.to_grayscale4(); + uint32_t color4 = display::ColorUtil::color_to_grayscale4(color); // where should the bits go in the big buffer array? math... uint16_t pos = (x / SSD1325_PIXELSPERBYTE) + (y * this->get_width_internal() / SSD1325_PIXELSPERBYTE); uint8_t shift = (x % SSD1325_PIXELSPERBYTE) * SSD1325_COLORSHIFT; @@ -204,7 +204,7 @@ void HOT SSD1325::draw_absolute_pixel_internal(int x, int y, Color color) { this->buffer_[pos] |= color4; } void SSD1325::fill(Color color) { - const uint32_t color4 = color.to_grayscale4(); + const uint32_t color4 = display::ColorUtil::color_to_grayscale4(color); uint8_t fill = (color4 & SSD1325_COLORMASK) | ((color4 & SSD1325_COLORMASK) << SSD1325_COLORSHIFT); for (uint32_t i = 0; i < this->get_buffer_length_(); i++) this->buffer_[i] = fill; diff --git a/esphome/components/ssd1327_base/ssd1327_base.cpp b/esphome/components/ssd1327_base/ssd1327_base.cpp index debe2455ff..398712c083 100644 --- a/esphome/components/ssd1327_base/ssd1327_base.cpp +++ b/esphome/components/ssd1327_base/ssd1327_base.cpp @@ -136,7 +136,7 @@ size_t SSD1327::get_buffer_length_() { void HOT SSD1327::draw_absolute_pixel_internal(int x, int y, Color color) { if (x >= this->get_width_internal() || x < 0 || y >= this->get_height_internal() || y < 0) return; - uint32_t color4 = color.to_grayscale4(); + uint32_t color4 = display::ColorUtil::color_to_grayscale4(color); // where should the bits go in the big buffer array? math... uint16_t pos = (x / SSD1327_PIXELSPERBYTE) + (y * this->get_width_internal() / SSD1327_PIXELSPERBYTE); uint8_t shift = (x % SSD1327_PIXELSPERBYTE) * SSD1327_COLORSHIFT; @@ -148,7 +148,7 @@ void HOT SSD1327::draw_absolute_pixel_internal(int x, int y, Color color) { this->buffer_[pos] |= color4; } void SSD1327::fill(Color color) { - const uint32_t color4 = color.to_grayscale4(); + const uint32_t color4 = display::ColorUtil::color_to_grayscale4(color); uint8_t fill = (color4 & SSD1327_COLORMASK) | ((color4 & SSD1327_COLORMASK) << SSD1327_COLORSHIFT); for (uint32_t i = 0; i < this->get_buffer_length_(); i++) this->buffer_[i] = fill; diff --git a/esphome/components/ssd1331_base/ssd1331_base.cpp b/esphome/components/ssd1331_base/ssd1331_base.cpp index 1405184177..9db5581044 100644 --- a/esphome/components/ssd1331_base/ssd1331_base.cpp +++ b/esphome/components/ssd1331_base/ssd1331_base.cpp @@ -123,14 +123,14 @@ size_t SSD1331::get_buffer_length_() { void HOT SSD1331::draw_absolute_pixel_internal(int x, int y, Color color) { if (x >= this->get_width_internal() || x < 0 || y >= this->get_height_internal() || y < 0) return; - const uint32_t color565 = color.to_rgb_565(); + const uint32_t color565 = display::ColorUtil::color_to_565(color); // where should the bits go in the big buffer array? math... uint16_t pos = (x + y * this->get_width_internal()) * SSD1331_BYTESPERPIXEL; this->buffer_[pos++] = (color565 >> 8) & 0xff; this->buffer_[pos] = color565 & 0xff; } void SSD1331::fill(Color color) { - const uint32_t color565 = color.to_rgb_565(); + const uint32_t color565 = display::ColorUtil::color_to_565(color); for (uint32_t i = 0; i < this->get_buffer_length_(); i++) if (i & 1) { this->buffer_[i] = color565 & 0xff; diff --git a/esphome/components/ssd1351_base/ssd1351_base.cpp b/esphome/components/ssd1351_base/ssd1351_base.cpp index fded8e3482..42407a13de 100644 --- a/esphome/components/ssd1351_base/ssd1351_base.cpp +++ b/esphome/components/ssd1351_base/ssd1351_base.cpp @@ -151,14 +151,14 @@ size_t SSD1351::get_buffer_length_() { void HOT SSD1351::draw_absolute_pixel_internal(int x, int y, Color color) { if (x >= this->get_width_internal() || x < 0 || y >= this->get_height_internal() || y < 0) return; - const uint32_t color565 = color.to_rgb_565(); + const uint32_t color565 = display::ColorUtil::color_to_565(color); // where should the bits go in the big buffer array? math... uint16_t pos = (x + y * this->get_width_internal()) * SSD1351_BYTESPERPIXEL; this->buffer_[pos++] = (color565 >> 8) & 0xff; this->buffer_[pos] = color565 & 0xff; } void SSD1351::fill(Color color) { - const uint32_t color565 = color.to_rgb_565(); + const uint32_t color565 = display::ColorUtil::color_to_565(color); for (uint32_t i = 0; i < this->get_buffer_length_(); i++) if (i & 1) { this->buffer_[i] = color565 & 0xff; diff --git a/esphome/components/st7735/st7735.cpp b/esphome/components/st7735/st7735.cpp index 5cd79da2cb..201098f3cf 100644 --- a/esphome/components/st7735/st7735.cpp +++ b/esphome/components/st7735/st7735.cpp @@ -308,11 +308,11 @@ void HOT ST7735::draw_absolute_pixel_internal(int x, int y, Color color) { return; if (this->eightbitcolor_) { - const uint32_t color332 = color.to_332(); + const uint32_t color332 = display::ColorUtil::color_to_332(color); uint16_t pos = (x + y * this->get_width_internal()); this->buffer_[pos] = color332; } else { - const uint32_t color565 = color.to_565(); + const uint32_t color565 = display::ColorUtil::color_to_565(color); uint16_t pos = (x + y * this->get_width_internal()) * 2; this->buffer_[pos++] = (color565 >> 8) & 0xff; this->buffer_[pos] = color565 & 0xff; @@ -444,9 +444,11 @@ void HOT ST7735::write_display_data_() { if (this->eightbitcolor_) { for (int line = 0; line < this->get_buffer_length(); line = line + this->get_width_internal()) { for (int index = 0; index < this->get_width_internal(); ++index) { - auto color = Color(this->buffer_[index + line], Color::ColorOrder::COLOR_ORDER_RGB, - Color::ColorBitness::COLOR_BITNESS_332, true) - .to_565(); + auto color332 = display::ColorUtil::to_color(this->buffer_[index + line], display::ColorOrder::COLOR_ORDER_RGB, + display::ColorBitness::COLOR_BITNESS_332, true); + + auto color = display::ColorUtil::color_to_565(color332); + this->write_byte((color >> 8) & 0xff); this->write_byte(color & 0xff); } diff --git a/esphome/components/st7789v/st7789v.cpp b/esphome/components/st7789v/st7789v.cpp index 284f2342fc..3a6374263a 100644 --- a/esphome/components/st7789v/st7789v.cpp +++ b/esphome/components/st7789v/st7789v.cpp @@ -263,7 +263,7 @@ void HOT ST7789V::draw_absolute_pixel_internal(int x, int y, Color color) { if (x >= this->get_width_internal() || x < 0 || y >= this->get_height_internal() || y < 0) return; - auto color565 = color.to_rgb_565(); + auto color565 = display::ColorUtil::color_to_565(color); uint16_t pos = (x + y * this->get_width_internal()) * 2; this->buffer_[pos++] = (color565 >> 8) & 0xff; diff --git a/esphome/components/wled/wled_light_effect.cpp b/esphome/components/wled/wled_light_effect.cpp index e06a23aacf..3c26beeed4 100644 --- a/esphome/components/wled/wled_light_effect.cpp +++ b/esphome/components/wled/wled_light_effect.cpp @@ -40,11 +40,11 @@ void WLEDLightEffect::stop() { void WLEDLightEffect::blank_all_leds_(light::AddressableLight &it) { for (int led = it.size(); led-- > 0;) { - it[led].set(light::ESPColor::BLACK); + it[led].set(COLOR_BLACK); } } -void WLEDLightEffect::apply(light::AddressableLight &it, const light::ESPColor ¤t_color) { +void WLEDLightEffect::apply(light::AddressableLight &it, const Color ¤t_color) { // Init UDP lazily if (!udp_) { udp_.reset(new WiFiUDP()); @@ -152,7 +152,7 @@ bool WLEDLightEffect::parse_warls_frame_(light::AddressableLight &it, const uint uint8_t b = payload[3]; if (led < max_leds) { - it[led].set(light::ESPColor(r, g, b)); + it[led].set(Color(r, g, b)); } } @@ -174,7 +174,7 @@ bool WLEDLightEffect::parse_drgb_frame_(light::AddressableLight &it, const uint8 uint8_t b = payload[2]; if (led < max_leds) { - it[led].set(light::ESPColor(r, g, b)); + it[led].set(Color(r, g, b)); } } @@ -197,7 +197,7 @@ bool WLEDLightEffect::parse_drgbw_frame_(light::AddressableLight &it, const uint uint8_t w = payload[3]; if (led < max_leds) { - it[led].set(light::ESPColor(r, g, b, w)); + it[led].set(Color(r, g, b, w)); } } @@ -228,7 +228,7 @@ bool WLEDLightEffect::parse_dnrgb_frame_(light::AddressableLight &it, const uint uint8_t b = payload[2]; if (led < max_leds) { - it[led].set(light::ESPColor(r, g, b)); + it[led].set(Color(r, g, b)); } } diff --git a/esphome/components/wled/wled_light_effect.h b/esphome/components/wled/wled_light_effect.h index f1d27b06c7..2a7654ec27 100644 --- a/esphome/components/wled/wled_light_effect.h +++ b/esphome/components/wled/wled_light_effect.h @@ -18,7 +18,7 @@ class WLEDLightEffect : public light::AddressableLightEffect { public: void start() override; void stop() override; - void apply(light::AddressableLight &it, const light::ESPColor ¤t_color) override; + void apply(light::AddressableLight &it, const Color ¤t_color) override; void set_port(uint16_t port) { this->port_ = port; } protected: diff --git a/esphome/core/color.h b/esphome/core/color.h index 3120e48064..d2f225dc3a 100644 --- a/esphome/core/color.h +++ b/esphome/core/color.h @@ -6,7 +6,6 @@ namespace esphome { inline static uint8_t esp_scale8(uint8_t i, uint8_t scale) { return (uint16_t(i) * (1 + uint16_t(scale))) / 256; } -inline static uint8_t esp_scale(uint8_t i, uint8_t scale, uint8_t max_value = 255) { return (max_value * i / scale); } struct Color { union { @@ -31,75 +30,19 @@ struct Color { uint8_t raw[4]; uint32_t raw_32; }; - enum ColorOrder : uint8_t { COLOR_ORDER_RGB = 0, COLOR_ORDER_BGR = 1, COLOR_ORDER_GRB = 2 }; - enum ColorBitness : uint8_t { COLOR_BITNESS_888 = 0, COLOR_BITNESS_565 = 1, COLOR_BITNESS_332 = 2 }; + inline Color() ALWAYS_INLINE : r(0), g(0), b(0), w(0) {} // NOLINT - inline Color(float red, float green, float blue) ALWAYS_INLINE : r(uint8_t(red * 255)), - g(uint8_t(green * 255)), - b(uint8_t(blue * 255)), - w(0) {} - inline Color(float red, float green, float blue, float white) ALWAYS_INLINE : r(uint8_t(red * 255)), - g(uint8_t(green * 255)), - b(uint8_t(blue * 255)), - w(uint8_t(white * 255)) {} + inline Color(uint8_t red, uint8_t green, uint8_t blue) ALWAYS_INLINE : r(red), g(green), b(blue), w(0) {} + + inline Color(uint8_t red, uint8_t green, uint8_t blue, uint8_t white) ALWAYS_INLINE : r(red), + g(green), + b(blue), + w(white) {} inline Color(uint32_t colorcode) ALWAYS_INLINE : r((colorcode >> 16) & 0xFF), g((colorcode >> 8) & 0xFF), b((colorcode >> 0) & 0xFF), w((colorcode >> 24) & 0xFF) {} - inline Color(uint32_t colorcode, ColorOrder color_order, ColorBitness color_bitness = ColorBitness::COLOR_BITNESS_888, - bool right_bit_aligned = true) { - uint8_t first_color, second_color, third_color; - uint8_t first_bits = 0; - uint8_t second_bits = 0; - uint8_t third_bits = 0; - switch (color_bitness) { - case COLOR_BITNESS_888: - first_bits = 8; - second_bits = 8; - third_bits = 8; - break; - case COLOR_BITNESS_565: - first_bits = 5; - second_bits = 6; - third_bits = 5; - break; - case COLOR_BITNESS_332: - first_bits = 3; - second_bits = 3; - third_bits = 2; - break; - } - - first_color = right_bit_aligned ? esp_scale(((colorcode >> (second_bits + third_bits)) & ((1 << first_bits) - 1)), - ((1 << first_bits) - 1)) - : esp_scale(((colorcode >> 16) & 0xFF), (1 << first_bits) - 1); - - second_color = right_bit_aligned - ? esp_scale(((colorcode >> third_bits) & ((1 << second_bits) - 1)), ((1 << second_bits) - 1)) - : esp_scale(((colorcode >> 8) & 0xFF), ((1 << second_bits) - 1)); - - third_color = (right_bit_aligned ? esp_scale(((colorcode >> 0) & 0xFF), ((1 << third_bits) - 1)) - : esp_scale(((colorcode >> 0) & 0xFF), (1 << third_bits) - 1)); - - switch (color_order) { - case COLOR_ORDER_RGB: - this->r = first_color; - this->g = second_color; - this->b = third_color; - break; - case COLOR_ORDER_BGR: - this->b = first_color; - this->g = second_color; - this->r = third_color; - break; - case COLOR_ORDER_GRB: - this->g = first_color; - this->r = second_color; - this->b = third_color; - break; - } - } inline bool is_on() ALWAYS_INLINE { return this->raw_32 != 0; } inline Color &operator=(const Color &rhs) ALWAYS_INLINE { this->r = rhs.r; @@ -187,65 +130,21 @@ struct Color { } inline Color &operator-=(uint8_t subtract) ALWAYS_INLINE { return *this = (*this) - subtract; } static Color random_color() { - float r = float(random_uint32()) / float(UINT32_MAX); - float g = float(random_uint32()) / float(UINT32_MAX); - float b = float(random_uint32()) / float(UINT32_MAX); - float w = float(random_uint32()) / float(UINT32_MAX); - return Color(r, g, b, w); + uint32_t rand = random_uint32(); + uint8_t w = rand >> 24; + uint8_t r = rand >> 16; + uint8_t g = rand >> 8; + uint8_t b = rand >> 0; + const uint16_t max_rgb = std::max(r, std::max(g, b)); + return Color(uint8_t((uint16_t(r) * 255U / max_rgb)), uint8_t((uint16_t(g) * 255U / max_rgb)), + uint8_t((uint16_t(b) * 255U / max_rgb)), w); } - Color fade_to_white(uint8_t amnt) { return Color(1, 1, 1, 1) - (*this * amnt); } + Color fade_to_white(uint8_t amnt) { return Color(255, 255, 255, 255) - (*this * amnt); } Color fade_to_black(uint8_t amnt) { return *this * amnt; } Color lighten(uint8_t delta) { return *this + delta; } Color darken(uint8_t delta) { return *this - delta; } - uint8_t to_332(ColorOrder color_order = ColorOrder::COLOR_ORDER_RGB) const { - uint16_t red_color, green_color, blue_color; - - red_color = esp_scale8(this->red, ((1 << 3) - 1)); - green_color = esp_scale8(this->green, ((1 << 3) - 1)); - blue_color = esp_scale8(this->blue, (1 << 2) - 1); - - switch (color_order) { - case COLOR_ORDER_RGB: - return red_color << 5 | green_color << 2 | blue_color; - case COLOR_ORDER_BGR: - return blue_color << 6 | green_color << 3 | red_color; - case COLOR_ORDER_GRB: - return green_color << 5 | red_color << 2 | blue_color; - } - return 0; - } - uint16_t to_565(ColorOrder color_order = ColorOrder::COLOR_ORDER_RGB) const { - uint16_t red_color, green_color, blue_color; - - red_color = esp_scale8(this->red, ((1 << 5) - 1)); - green_color = esp_scale8(this->green, ((1 << 6) - 1)); - blue_color = esp_scale8(this->blue, (1 << 5) - 1); - - switch (color_order) { - case COLOR_ORDER_RGB: - return red_color << 11 | green_color << 5 | blue_color; - case COLOR_ORDER_BGR: - return blue_color << 11 | green_color << 5 | red_color; - case COLOR_ORDER_GRB: - return green_color << 10 | red_color << 5 | blue_color; - } - return 0; - } - uint32_t to_rgb_565() const { - uint32_t color565 = - (esp_scale8(this->red, 31) << 11) | (esp_scale8(this->green, 63) << 5) | (esp_scale8(this->blue, 31) << 0); - return color565; - } - uint32_t to_bgr_565() const { - uint32_t color565 = - (esp_scale8(this->blue, 31) << 11) | (esp_scale8(this->green, 63) << 5) | (esp_scale8(this->red, 31) << 0); - return color565; - } - uint32_t to_grayscale4() const { - uint32_t gs4 = esp_scale8(this->white, 15); - return gs4; - } }; + static const Color COLOR_BLACK(0, 0, 0); -static const Color COLOR_WHITE(1, 1, 1); +static const Color COLOR_WHITE(255, 255, 255, 255); }; // namespace esphome diff --git a/tests/test1.yaml b/tests/test1.yaml index 59c2bf96c4..1b1d3799a0 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -1693,7 +1693,7 @@ interval: color: - id: kbx_red red: 100% - green: 1% + green_int: 123 blue: 2% - id: kbx_blue red: 0% From 8e93735861c785f5883a908022d4418cc413f1de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20Gl=C3=B6ckl?= Date: Wed, 3 Mar 2021 01:54:52 +0100 Subject: [PATCH 064/111] Add support for the SM300D2 7-in-1 sensor module (#1524) * Added support for SM300D2 sensor module * Fixed lint errors due to added tvoc config * add device class Co-authored-by: Guillermo Ruffino --- esphome/components/ccs811/sensor.py | 3 +- esphome/components/sgp30/sensor.py | 3 +- esphome/components/sm300d2/__init__.py | 0 esphome/components/sm300d2/sensor.py | 61 ++++++++++++++++ esphome/components/sm300d2/sm300d2.cpp | 99 ++++++++++++++++++++++++++ esphome/components/sm300d2/sm300d2.h | 38 ++++++++++ esphome/const.py | 3 + tests/test1.yaml | 16 +++++ 8 files changed, 219 insertions(+), 4 deletions(-) create mode 100644 esphome/components/sm300d2/__init__.py create mode 100644 esphome/components/sm300d2/sensor.py create mode 100644 esphome/components/sm300d2/sm300d2.cpp create mode 100644 esphome/components/sm300d2/sm300d2.h diff --git a/esphome/components/ccs811/sensor.py b/esphome/components/ccs811/sensor.py index f205121e11..869a49dcdc 100644 --- a/esphome/components/ccs811/sensor.py +++ b/esphome/components/ccs811/sensor.py @@ -2,7 +2,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor from esphome.const import CONF_ID, DEVICE_CLASS_EMPTY, ICON_RADIATOR, UNIT_PARTS_PER_MILLION, \ - UNIT_PARTS_PER_BILLION, CONF_TEMPERATURE, CONF_HUMIDITY, ICON_MOLECULE_CO2 + UNIT_PARTS_PER_BILLION, CONF_TEMPERATURE, CONF_TVOC, CONF_HUMIDITY, ICON_MOLECULE_CO2 DEPENDENCIES = ['i2c'] @@ -10,7 +10,6 @@ ccs811_ns = cg.esphome_ns.namespace('ccs811') CCS811Component = ccs811_ns.class_('CCS811Component', cg.PollingComponent, i2c.I2CDevice) CONF_ECO2 = 'eco2' -CONF_TVOC = 'tvoc' CONF_BASELINE = 'baseline' CONFIG_SCHEMA = cv.Schema({ diff --git a/esphome/components/sgp30/sensor.py b/esphome/components/sgp30/sensor.py index f28c0f0e71..a3ce1013e0 100644 --- a/esphome/components/sgp30/sensor.py +++ b/esphome/components/sgp30/sensor.py @@ -2,7 +2,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor from esphome.const import CONF_ID, DEVICE_CLASS_EMPTY, ICON_RADIATOR, UNIT_PARTS_PER_MILLION, \ - UNIT_PARTS_PER_BILLION, ICON_MOLECULE_CO2 + UNIT_PARTS_PER_BILLION, ICON_MOLECULE_CO2, CONF_TVOC DEPENDENCIES = ['i2c'] @@ -10,7 +10,6 @@ sgp30_ns = cg.esphome_ns.namespace('sgp30') SGP30Component = sgp30_ns.class_('SGP30Component', cg.PollingComponent, i2c.I2CDevice) CONF_ECO2 = 'eco2' -CONF_TVOC = 'tvoc' CONF_BASELINE = 'baseline' CONF_ECO2_BASELINE = 'eco2_baseline' CONF_TVOC_BASELINE = 'tvoc_baseline' diff --git a/esphome/components/sm300d2/__init__.py b/esphome/components/sm300d2/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/sm300d2/sensor.py b/esphome/components/sm300d2/sensor.py new file mode 100644 index 0000000000..2191143ec2 --- /dev/null +++ b/esphome/components/sm300d2/sensor.py @@ -0,0 +1,61 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import sensor, uart +from esphome.const import CONF_ID, CONF_CO2, CONF_FORMALDEHYDE, CONF_TVOC, CONF_PM_2_5, \ + CONF_PM_10_0, CONF_TEMPERATURE, CONF_HUMIDITY, DEVICE_CLASS_EMPTY, DEVICE_CLASS_TEMPERATURE, \ + DEVICE_CLASS_HUMIDITY, UNIT_PARTS_PER_MILLION, UNIT_MICROGRAMS_PER_CUBIC_METER, UNIT_CELSIUS, \ + UNIT_PERCENT, ICON_EMPTY, ICON_MOLECULE_CO2, ICON_FLASK, ICON_CHEMICAL_WEAPON, ICON_GRAIN + +DEPENDENCIES = ['uart'] + +sm300d2_ns = cg.esphome_ns.namespace('sm300d2') +SM300D2Sensor = sm300d2_ns.class_('SM300D2Sensor', cg.PollingComponent, uart.UARTDevice) + +CONFIG_SCHEMA = cv.All(cv.Schema({ + cv.GenerateID(): cv.declare_id(SM300D2Sensor), + + cv.Optional(CONF_CO2): + sensor.sensor_schema(UNIT_PARTS_PER_MILLION, ICON_MOLECULE_CO2, 0, DEVICE_CLASS_EMPTY), + cv.Optional(CONF_FORMALDEHYDE): + sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_FLASK, 0, DEVICE_CLASS_EMPTY), + cv.Optional(CONF_TVOC): + sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_CHEMICAL_WEAPON, 0, + DEVICE_CLASS_EMPTY), + cv.Optional(CONF_PM_2_5): + sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_GRAIN, 0, DEVICE_CLASS_EMPTY), + cv.Optional(CONF_PM_10_0): + sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_GRAIN, 0, DEVICE_CLASS_EMPTY), + cv.Optional(CONF_TEMPERATURE): + sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 0, DEVICE_CLASS_TEMPERATURE), + cv.Optional(CONF_HUMIDITY): + sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 0, DEVICE_CLASS_HUMIDITY), + +}).extend(cv.polling_component_schema('60s')).extend(uart.UART_DEVICE_SCHEMA)) + + +def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + yield cg.register_component(var, config) + yield uart.register_uart_device(var, config) + + if CONF_CO2 in config: + sens = yield sensor.new_sensor(config[CONF_CO2]) + cg.add(var.set_co2_sensor(sens)) + if CONF_FORMALDEHYDE in config: + sens = yield sensor.new_sensor(config[CONF_FORMALDEHYDE]) + cg.add(var.set_formaldehyde_sensor(sens)) + if CONF_TVOC in config: + sens = yield sensor.new_sensor(config[CONF_TVOC]) + cg.add(var.set_tvoc_sensor(sens)) + if CONF_PM_2_5 in config: + sens = yield sensor.new_sensor(config[CONF_PM_2_5]) + cg.add(var.set_pm_2_5_sensor(sens)) + if CONF_PM_10_0 in config: + sens = yield sensor.new_sensor(config[CONF_PM_10_0]) + cg.add(var.set_pm_10_0_sensor(sens)) + if CONF_TEMPERATURE in config: + sens = yield sensor.new_sensor(config[CONF_TEMPERATURE]) + cg.add(var.set_temperature_sensor(sens)) + if CONF_HUMIDITY in config: + sens = yield sensor.new_sensor(config[CONF_HUMIDITY]) + cg.add(var.set_humidity_sensor(sens)) diff --git a/esphome/components/sm300d2/sm300d2.cpp b/esphome/components/sm300d2/sm300d2.cpp new file mode 100644 index 0000000000..25abc82f3c --- /dev/null +++ b/esphome/components/sm300d2/sm300d2.cpp @@ -0,0 +1,99 @@ +#include "sm300d2.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace sm300d2 { + +static const char *TAG = "sm300d2"; +static const uint8_t SM300D2_RESPONSE_LENGTH = 17; + +void SM300D2Sensor::update() { + uint8_t response[SM300D2_RESPONSE_LENGTH]; + + flush(); + bool read_success = read_array(response, SM300D2_RESPONSE_LENGTH); + flush(); + + if (!read_success) { + ESP_LOGW(TAG, "Reading data from SM300D2 failed!"); + status_set_warning(); + return; + } + + if (response[0] != 0x3C || response[1] != 0x02) { + ESP_LOGW(TAG, "Invalid preamble for SM300D2 response!"); + this->status_set_warning(); + return; + } + + uint16_t calculated_checksum = this->sm300d2_checksum_(response); + if (calculated_checksum != response[SM300D2_RESPONSE_LENGTH - 1]) { + ESP_LOGW(TAG, "SM300D2 Checksum doesn't match: 0x%02X!=0x%02X", response[SM300D2_RESPONSE_LENGTH - 1], + calculated_checksum); + this->status_set_warning(); + return; + } + + this->status_clear_warning(); + + ESP_LOGW(TAG, "Successfully read SM300D2 data"); + + const uint16_t co2 = (response[2] * 256) + response[3]; + const uint16_t formaldehyde = (response[4] * 256) + response[5]; + const uint16_t tvoc = (response[6] * 256) + response[7]; + const uint16_t pm_2_5 = (response[8] * 256) + response[9]; + const uint16_t pm_10_0 = (response[10] * 256) + response[11]; + const float temperature = response[12] + (response[13] * 0.1); + const float humidity = response[14] + (response[15] * 0.1); + + ESP_LOGD(TAG, "Received CO₂: %u ppm", co2); + if (this->co2_sensor_ != nullptr) + this->co2_sensor_->publish_state(co2); + + ESP_LOGD(TAG, "Received Formaldehyde: %u µg/m³", formaldehyde); + if (this->formaldehyde_sensor_ != nullptr) + this->formaldehyde_sensor_->publish_state(formaldehyde); + + ESP_LOGD(TAG, "Received TVOC: %u µg/m³", tvoc); + if (this->tvoc_sensor_ != nullptr) + this->tvoc_sensor_->publish_state(tvoc); + + ESP_LOGD(TAG, "Received PM2.5: %u µg/m³", pm_2_5); + if (this->pm_2_5_sensor_ != nullptr) + this->pm_2_5_sensor_->publish_state(pm_2_5); + + ESP_LOGD(TAG, "Received pm_10_0: %u µg/m³", pm_10_0); + if (this->pm_10_0_sensor_ != nullptr) + this->pm_10_0_sensor_->publish_state(pm_10_0); + + ESP_LOGD(TAG, "Received Temperature: %.2f °C", temperature); + if (this->temperature_sensor_ != nullptr) + this->temperature_sensor_->publish_state(temperature); + + ESP_LOGD(TAG, "Received Humidity: %.2f percent", humidity); + if (this->humidity_sensor_ != nullptr) + this->humidity_sensor_->publish_state(humidity); +} + +uint16_t SM300D2Sensor::sm300d2_checksum_(uint8_t *ptr) { + uint8_t sum = 0; + for (int i = 0; i < (SM300D2_RESPONSE_LENGTH - 1); i++) { + sum += *ptr++; + } + return sum; +} + +void SM300D2Sensor::dump_config() { + ESP_LOGCONFIG(TAG, "SM300D2:"); + LOG_SENSOR(" ", "CO2", this->co2_sensor_); + LOG_SENSOR(" ", "Formaldehyde", this->formaldehyde_sensor_); + LOG_SENSOR(" ", "TVOC", this->tvoc_sensor_); + LOG_SENSOR(" ", "PM2.5", this->pm_2_5_sensor_); + LOG_SENSOR(" ", "PM10", this->pm_10_0_sensor_); + LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); + LOG_SENSOR(" ", "Humidity", this->humidity_sensor_); + this->check_uart_settings(9600); +} + +} // namespace sm300d2 +} // namespace esphome diff --git a/esphome/components/sm300d2/sm300d2.h b/esphome/components/sm300d2/sm300d2.h new file mode 100644 index 0000000000..88c04e9813 --- /dev/null +++ b/esphome/components/sm300d2/sm300d2.h @@ -0,0 +1,38 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/uart/uart.h" + +namespace esphome { +namespace sm300d2 { + +class SM300D2Sensor : public PollingComponent, public uart::UARTDevice { + public: + float get_setup_priority() const override { return setup_priority::DATA; } + + void set_co2_sensor(sensor::Sensor *co2_sensor) { co2_sensor_ = co2_sensor; } + void set_formaldehyde_sensor(sensor::Sensor *formaldehyde_sensor) { formaldehyde_sensor_ = formaldehyde_sensor; } + void set_tvoc_sensor(sensor::Sensor *tvoc_sensor) { tvoc_sensor_ = tvoc_sensor; } + void set_pm_2_5_sensor(sensor::Sensor *pm_2_5_sensor) { pm_2_5_sensor_ = pm_2_5_sensor; } + void set_pm_10_0_sensor(sensor::Sensor *pm_10_0_sensor) { pm_10_0_sensor_ = pm_10_0_sensor; } + void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; } + void set_humidity_sensor(sensor::Sensor *humidity_sensor) { humidity_sensor_ = humidity_sensor; } + + void update() override; + void dump_config() override; + + protected: + uint16_t sm300d2_checksum_(uint8_t *ptr); + + sensor::Sensor *co2_sensor_{nullptr}; + sensor::Sensor *formaldehyde_sensor_{nullptr}; + sensor::Sensor *tvoc_sensor_{nullptr}; + sensor::Sensor *pm_2_5_sensor_{nullptr}; + sensor::Sensor *pm_10_0_sensor_{nullptr}; + sensor::Sensor *temperature_sensor_{nullptr}; + sensor::Sensor *humidity_sensor_{nullptr}; +}; + +} // namespace sm300d2 +} // namespace esphome diff --git a/esphome/const.py b/esphome/const.py index f652cc1b6d..e431784985 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -538,6 +538,7 @@ CONF_TRIGGER_ID = 'trigger_id' CONF_TRIGGER_PIN = 'trigger_pin' CONF_TURN_OFF_ACTION = 'turn_off_action' CONF_TURN_ON_ACTION = 'turn_on_action' +CONF_TVOC = 'tvoc' CONF_TX_BUFFER_SIZE = 'tx_buffer_size' CONF_TX_PIN = 'tx_pin' CONF_TX_POWER = 'tx_power' @@ -595,10 +596,12 @@ ICON_COUNTER = 'mdi:counter' ICON_CURRENT_AC = 'mdi:current-ac' ICON_EMPTY = '' ICON_FLASH = 'mdi:flash' +ICON_FLASK = 'mdi:flask' ICON_FLASK_OUTLINE = 'mdi:flask-outline' ICON_FLOWER = 'mdi:flower' ICON_GAS_CYLINDER = 'mdi:gas-cylinder' ICON_GAUGE = 'mdi:gauge' +ICON_GRAIN = 'mdi:grain' ICON_LIGHTBULB = 'mdi:lightbulb' ICON_MAGNET = 'mdi:magnet' ICON_MOLECULE_CO2 = 'mdi:molecule-co2' diff --git a/tests/test1.yaml b/tests/test1.yaml index 1b1d3799a0..ff7971d901 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -642,6 +642,22 @@ sensor: co2: name: 'SenseAir CO2 Value' update_interval: 15s + - platform: sm300d2 + co2: + name: "SM300D2 CO2 Value" + formaldehyde: + name: "SM300D2 Formaldehyde Value" + tvoc: + name: "SM300D2 TVOC Value" + pm_2_5: + name: "SM300D2 PM2.5 Value" + pm_10_0: + name: "SM300D2 PM10 Value" + temperature: + name: "SM300D2 Temperature Value" + humidity: + name: "SM300D2 Humidity Value" + update_interval: 60s - platform: sht3xd temperature: name: 'Living Room Temperature 8' From c5dc736c5300eb43680e85a148a21a54549d6f97 Mon Sep 17 00:00:00 2001 From: Cody James <33857557+codyjamestechnical@users.noreply.github.com> Date: Tue, 2 Mar 2021 21:41:15 -0500 Subject: [PATCH 065/111] changed color temp from float to int (#1522) * changed color temp from float to int Changed cold_white_temperature and warm_white temperature from a float to an integer. This makes home assisting show it correctly in the color temperature slider. If it is set to float home assistant show something like 470.8390000283847829304845 in the slider which provides an ugly ui and color temperature to as a decimal is invalid anyway. * Update esphome/components/rgbww/light.py * Update esphome/components/rgbww/light.py Co-authored-by: Guillermo Ruffino --- esphome/components/rgbww/rgbww_light_output.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/esphome/components/rgbww/rgbww_light_output.h b/esphome/components/rgbww/rgbww_light_output.h index 152766970e..e14b967530 100644 --- a/esphome/components/rgbww/rgbww_light_output.h +++ b/esphome/components/rgbww/rgbww_light_output.h @@ -14,8 +14,8 @@ class RGBWWLightOutput : public light::LightOutput { void set_blue(output::FloatOutput *blue) { blue_ = blue; } void set_cold_white(output::FloatOutput *cold_white) { cold_white_ = cold_white; } void set_warm_white(output::FloatOutput *warm_white) { warm_white_ = warm_white; } - void set_cold_white_temperature(float cold_white_temperature) { cold_white_temperature_ = cold_white_temperature; } - void set_warm_white_temperature(float warm_white_temperature) { warm_white_temperature_ = warm_white_temperature; } + void set_cold_white_temperature(int cold_white_temperature) { cold_white_temperature_ = cold_white_temperature; } + void set_warm_white_temperature(int warm_white_temperature) { warm_white_temperature_ = warm_white_temperature; } void set_constant_brightness(bool constant_brightness) { constant_brightness_ = constant_brightness; } void set_color_interlock(bool color_interlock) { color_interlock_ = color_interlock; } light::LightTraits get_traits() override { @@ -46,8 +46,8 @@ class RGBWWLightOutput : public light::LightOutput { output::FloatOutput *blue_; output::FloatOutput *cold_white_; output::FloatOutput *warm_white_; - float cold_white_temperature_; - float warm_white_temperature_; + int cold_white_temperature_; + int warm_white_temperature_; bool constant_brightness_; bool color_interlock_{false}; }; From 2c77d121daa6f8a6640d454faf7e8e7614d1e588 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Mar 2021 23:41:51 -0300 Subject: [PATCH 066/111] Bump pytest-cov from 2.10.1 to 2.11.1 (#1483) Bumps [pytest-cov](https://github.com/pytest-dev/pytest-cov) from 2.10.1 to 2.11.1. - [Release notes](https://github.com/pytest-dev/pytest-cov/releases) - [Changelog](https://github.com/pytest-dev/pytest-cov/blob/master/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest-cov/compare/v2.10.1...v2.11.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements_test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements_test.txt b/requirements_test.txt index 864dfe1a72..9b5dd6682f 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -6,7 +6,7 @@ pexpect==4.8.0 # Unit tests pytest==6.2.1 -pytest-cov==2.10.1 +pytest-cov==2.11.1 pytest-mock==3.5.1 asyncmock==0.4.2 hypothesis==5.21.0 From 521dfe08f25911f7c16a74ec8398bbfc5f370b43 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Mar 2021 23:42:41 -0300 Subject: [PATCH 067/111] Bump colorlog from 4.6.2 to 4.7.2 (#1473) Bumps [colorlog](https://github.com/borntyping/python-colorlog) from 4.6.2 to 4.7.2. - [Release notes](https://github.com/borntyping/python-colorlog/releases) - [Commits](https://github.com/borntyping/python-colorlog/compare/v4.6.2...v4.7.2) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index ee73c4c747..4dfba3239e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ voluptuous==0.12.1 PyYAML==5.3.1 paho-mqtt==1.5.1 colorama==0.4.4 -colorlog==4.6.2 +colorlog==4.7.2 tornado==6.1 protobuf==3.13.0 tzlocal==2.1 From 811c13d7d1f04087ac4a8d670114724ebbe86dd8 Mon Sep 17 00:00:00 2001 From: Andrew Zaborowski Date: Wed, 3 Mar 2021 19:40:25 +0100 Subject: [PATCH 068/111] pins: Add three new boards (#1576) Add the Lolin32 Lite and TTGO T7 board names so that esphome can be built for those boards. There's nothing special about them. The espressif/arduino-esp32 and platformio/platform-espressif32 projects have both merged definitions for those boards recently. --- esphome/pins.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/esphome/pins.py b/esphome/pins.py index b6fde85250..2a154c4dbf 100644 --- a/esphome/pins.py +++ b/esphome/pins.py @@ -182,6 +182,7 @@ ESP32_BOARD_PINS = { 'iotbusio': {}, 'iotbusproteus': {}, 'lolin32': {'LED': 5}, + 'lolin32-lite': {'LED': 22}, 'lolin_d32': {'LED': 5, '_VBAT': 35}, 'lolin_d32_pro': {'LED': 5, '_VBAT': 35}, 'lopy': {'A1': 37, 'A2': 38, 'LED': 0, 'MISO': 37, 'MOSI': 22, 'SCK': 13, 'SCL': 13, @@ -239,6 +240,8 @@ ESP32_BOARD_PINS = { 'ttgo-t-beam': {'BUTTON': 39, 'LED': 14, 'MOSI': 27, 'SCK': 5, 'SS': 18}, 'ttgo-t-watch': {'BUTTON': 36, 'MISO': 2, 'MOSI': 15, 'SCK': 14, 'SS': 13}, 'ttgo-t1': {'LED': 22, 'MISO': 2, 'MOSI': 15, 'SCK': 14, 'SCL': 23, 'SS': 13}, + 'ttgo-t7-v13-mini32': {'LED': 22}, + 'ttgo-t7-v14-mini32': {'LED': 19}, 'turta_iot_node': {}, 'vintlabs-devkit-v1': {'LED': 2, 'PWM0': 12, 'PWM1': 13, 'PWM2': 14, 'PWM3': 15, 'PWM4': 16, 'PWM5': 17, 'PWM6': 18, 'PWM7': 19}, From f3fe2bde38b2e58892849cf5d3f33797f2c264ae Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Mar 2021 15:41:27 -0300 Subject: [PATCH 069/111] Bump pytest from 6.2.1 to 6.2.2 (#1574) Bumps [pytest](https://github.com/pytest-dev/pytest) from 6.2.1 to 6.2.2. - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/master/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/6.2.1...6.2.2) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements_test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements_test.txt b/requirements_test.txt index 9b5dd6682f..6e08cecd93 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -5,7 +5,7 @@ cryptography>=2.0.0,<4 pexpect==4.8.0 # Unit tests -pytest==6.2.1 +pytest==6.2.2 pytest-cov==2.11.1 pytest-mock==3.5.1 asyncmock==0.4.2 From 1883ce18762f99164c0f83c3318d66149e41fa56 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Mar 2021 15:42:19 -0300 Subject: [PATCH 070/111] Bump pytz from 2020.5 to 2021.1 (#1575) Bumps [pytz](https://github.com/stub42/pytz) from 2020.5 to 2021.1. - [Release notes](https://github.com/stub42/pytz/releases) - [Commits](https://github.com/stub42/pytz/compare/release_2020.5...release_2021.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 4dfba3239e..280027af36 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,7 +6,7 @@ colorlog==4.7.2 tornado==6.1 protobuf==3.13.0 tzlocal==2.1 -pytz==2020.5 +pytz==2021.1 pyserial==3.5 ifaddr==0.1.7 platformio==5.0.4 From fa290fbce83009c0c5313b00fed7ad783fa98cf7 Mon Sep 17 00:00:00 2001 From: nikito7 <45373783+nikito7@users.noreply.github.com> Date: Thu, 4 Mar 2021 00:50:27 +0000 Subject: [PATCH 071/111] Fix for waveshare 2.13in-ttgo-b73 (#1543) --- esphome/components/waveshare_epaper/waveshare_epaper.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.cpp b/esphome/components/waveshare_epaper/waveshare_epaper.cpp index fa0cf6aa4f..adb748d308 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.cpp +++ b/esphome/components/waveshare_epaper/waveshare_epaper.cpp @@ -223,7 +223,7 @@ void HOT WaveshareEPaperTypeA::display() { return; } - if (this->full_update_every_ >= 2) { + if (this->full_update_every_ >= 1) { if (full_update != prev_full_update) { if (this->model_ == TTGO_EPAPER_2_13_IN) { this->write_lut_(full_update ? FULL_UPDATE_LUT_TTGO : PARTIAL_UPDATE_LUT_TTGO, LUT_SIZE_TTGO); @@ -271,6 +271,8 @@ void HOT WaveshareEPaperTypeA::display() { this->command(0x22); if (this->model_ == WAVESHARE_EPAPER_2_9_IN_V2) { this->data(full_update ? 0xF7 : 0xFF); + } else if (this->model_ == TTGO_EPAPER_2_13_IN_B73) { + this->data(0xC7); } else { this->data(0xC4); } From 88b46b748756cda0bd6cac5a780c087a2526b07f Mon Sep 17 00:00:00 2001 From: Gabe Cook Date: Fri, 5 Mar 2021 07:10:06 -0600 Subject: [PATCH 072/111] Add min/max filters (#1569) --- esphome/components/sensor/__init__.py | 28 ++++++++++++ esphome/components/sensor/filter.cpp | 62 +++++++++++++++++++++++++++ esphome/components/sensor/filter.h | 60 ++++++++++++++++++++++++++ tests/test1.yaml | 8 ++++ 4 files changed, 158 insertions(+) diff --git a/esphome/components/sensor/__init__.py b/esphome/components/sensor/__init__.py index c22edf0855..a1de877d78 100644 --- a/esphome/components/sensor/__init__.py +++ b/esphome/components/sensor/__init__.py @@ -72,6 +72,8 @@ SensorPublishAction = sensor_ns.class_('SensorPublishAction', automation.Action) # Filters Filter = sensor_ns.class_('Filter') MedianFilter = sensor_ns.class_('MedianFilter', Filter) +MinFilter = sensor_ns.class_('MinFilter', Filter) +MaxFilter = sensor_ns.class_('MaxFilter', Filter) SlidingWindowMovingAverageFilter = sensor_ns.class_('SlidingWindowMovingAverageFilter', Filter) ExponentialMovingAverageFilter = sensor_ns.class_('ExponentialMovingAverageFilter', Filter) LambdaFilter = sensor_ns.class_('LambdaFilter', Filter) @@ -165,6 +167,32 @@ def median_filter_to_code(config, filter_id): config[CONF_SEND_FIRST_AT]) +MIN_SCHEMA = cv.All(cv.Schema({ + cv.Optional(CONF_WINDOW_SIZE, default=5): cv.positive_not_null_int, + cv.Optional(CONF_SEND_EVERY, default=5): cv.positive_not_null_int, + cv.Optional(CONF_SEND_FIRST_AT, default=1): cv.positive_not_null_int, +}), validate_send_first_at) + + +@FILTER_REGISTRY.register('min', MinFilter, MIN_SCHEMA) +def min_filter_to_code(config, filter_id): + yield cg.new_Pvariable(filter_id, config[CONF_WINDOW_SIZE], config[CONF_SEND_EVERY], + config[CONF_SEND_FIRST_AT]) + + +MAX_SCHEMA = cv.All(cv.Schema({ + cv.Optional(CONF_WINDOW_SIZE, default=5): cv.positive_not_null_int, + cv.Optional(CONF_SEND_EVERY, default=5): cv.positive_not_null_int, + cv.Optional(CONF_SEND_FIRST_AT, default=1): cv.positive_not_null_int, +}), validate_send_first_at) + + +@FILTER_REGISTRY.register('max', MaxFilter, MAX_SCHEMA) +def max_filter_to_code(config, filter_id): + yield cg.new_Pvariable(filter_id, config[CONF_WINDOW_SIZE], config[CONF_SEND_EVERY], + config[CONF_SEND_FIRST_AT]) + + SLIDING_AVERAGE_SCHEMA = cv.All(cv.Schema({ cv.Optional(CONF_WINDOW_SIZE, default=15): cv.positive_not_null_int, cv.Optional(CONF_SEND_EVERY, default=15): cv.positive_not_null_int, diff --git a/esphome/components/sensor/filter.cpp b/esphome/components/sensor/filter.cpp index f7a5b5d7ad..6dfb11b9c9 100644 --- a/esphome/components/sensor/filter.cpp +++ b/esphome/components/sensor/filter.cpp @@ -77,6 +77,68 @@ optional MedianFilter::new_value(float value) { uint32_t MedianFilter::expected_interval(uint32_t input) { return input * this->send_every_; } +// MinFilter +MinFilter::MinFilter(size_t window_size, size_t send_every, size_t send_first_at) + : send_every_(send_every), send_at_(send_every - send_first_at), window_size_(window_size) {} +void MinFilter::set_send_every(size_t send_every) { this->send_every_ = send_every; } +void MinFilter::set_window_size(size_t window_size) { this->window_size_ = window_size; } +optional MinFilter::new_value(float value) { + if (!isnan(value)) { + while (this->queue_.size() >= this->window_size_) { + this->queue_.pop_front(); + } + this->queue_.push_back(value); + ESP_LOGVV(TAG, "MinFilter(%p)::new_value(%f)", this, value); + } + + if (++this->send_at_ >= this->send_every_) { + this->send_at_ = 0; + + float min = 0.0f; + if (!this->queue_.empty()) { + std::deque::iterator it = std::min_element(queue_.begin(), queue_.end()); + min = *it; + } + + ESP_LOGVV(TAG, "MinFilter(%p)::new_value(%f) SENDING", this, min); + return min; + } + return {}; +} + +uint32_t MinFilter::expected_interval(uint32_t input) { return input * this->send_every_; } + +// MaxFilter +MaxFilter::MaxFilter(size_t window_size, size_t send_every, size_t send_first_at) + : send_every_(send_every), send_at_(send_every - send_first_at), window_size_(window_size) {} +void MaxFilter::set_send_every(size_t send_every) { this->send_every_ = send_every; } +void MaxFilter::set_window_size(size_t window_size) { this->window_size_ = window_size; } +optional MaxFilter::new_value(float value) { + if (!isnan(value)) { + while (this->queue_.size() >= this->window_size_) { + this->queue_.pop_front(); + } + this->queue_.push_back(value); + ESP_LOGVV(TAG, "MaxFilter(%p)::new_value(%f)", this, value); + } + + if (++this->send_at_ >= this->send_every_) { + this->send_at_ = 0; + + float max = 0.0f; + if (!this->queue_.empty()) { + std::deque::iterator it = std::max_element(queue_.begin(), queue_.end()); + max = *it; + } + + ESP_LOGVV(TAG, "MaxFilter(%p)::new_value(%f) SENDING", this, max); + return max; + } + return {}; +} + +uint32_t MaxFilter::expected_interval(uint32_t input) { return input * this->send_every_; } + // SlidingWindowMovingAverageFilter SlidingWindowMovingAverageFilter::SlidingWindowMovingAverageFilter(size_t window_size, size_t send_every, size_t send_first_at) diff --git a/esphome/components/sensor/filter.h b/esphome/components/sensor/filter.h index 4c61d4c0a2..651d2a8986 100644 --- a/esphome/components/sensor/filter.h +++ b/esphome/components/sensor/filter.h @@ -76,6 +76,66 @@ class MedianFilter : public Filter { size_t window_size_; }; +/** Simple min filter. + * + * Takes the min of the last values and pushes it out every . + */ +class MinFilter : public Filter { + public: + /** Construct a MinFilter. + * + * @param window_size The number of values that the min should be returned from. + * @param send_every After how many sensor values should a new one be pushed out. + * @param send_first_at After how many values to forward the very first value. Defaults to the first value + * on startup being published on the first *raw* value, so with no filter applied. Must be less than or equal to + * send_every. + */ + explicit MinFilter(size_t window_size, size_t send_every, size_t send_first_at); + + optional new_value(float value) override; + + void set_send_every(size_t send_every); + void set_window_size(size_t window_size); + + uint32_t expected_interval(uint32_t input) override; + + protected: + std::deque queue_; + size_t send_every_; + size_t send_at_; + size_t window_size_; +}; + +/** Simple max filter. + * + * Takes the max of the last values and pushes it out every . + */ +class MaxFilter : public Filter { + public: + /** Construct a MaxFilter. + * + * @param window_size The number of values that the max should be returned from. + * @param send_every After how many sensor values should a new one be pushed out. + * @param send_first_at After how many values to forward the very first value. Defaults to the first value + * on startup being published on the first *raw* value, so with no filter applied. Must be less than or equal to + * send_every. + */ + explicit MaxFilter(size_t window_size, size_t send_every, size_t send_first_at); + + optional new_value(float value) override; + + void set_send_every(size_t send_every); + void set_window_size(size_t window_size); + + uint32_t expected_interval(uint32_t input) override; + + protected: + std::deque queue_; + size_t send_every_; + size_t send_at_; + size_t window_size_; +}; + /** Simple sliding window moving average filter. * * Essentially just takes takes the average of the last window_size values and pushes them out diff --git a/tests/test1.yaml b/tests/test1.yaml index ff7971d901..8842b4b3b3 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -262,6 +262,14 @@ sensor: window_size: 5 send_every: 5 send_first_at: 3 + - min: + window_size: 5 + send_every: 5 + send_first_at: 3 + - max: + window_size: 5 + send_every: 5 + send_first_at: 3 - sliding_window_moving_average: window_size: 15 send_every: 15 From e62bf333a286c2453903fd7a96d87c84b392ea51 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 5 Mar 2021 10:10:58 -0300 Subject: [PATCH 073/111] Bump pylint from 2.6.0 to 2.7.2 (#1582) Bumps [pylint](https://github.com/PyCQA/pylint) from 2.6.0 to 2.7.2. - [Release notes](https://github.com/PyCQA/pylint/releases) - [Changelog](https://github.com/PyCQA/pylint/blob/master/ChangeLog) - [Commits](https://github.com/PyCQA/pylint/compare/pylint-2.6.0...pylint-2.7.2) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements_test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements_test.txt b/requirements_test.txt index 6e08cecd93..d7f8bf50af 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,4 +1,4 @@ -pylint==2.6.0 +pylint==2.7.2 flake8==3.8.4 pillow>4.0.0 cryptography>=2.0.0,<4 From 0f151a8f6bcf94d741b876d795c4a30648f924fe Mon Sep 17 00:00:00 2001 From: needspeed Date: Sat, 6 Mar 2021 14:25:07 +0100 Subject: [PATCH 074/111] Extend 'uart:' with 'invert:' for esp32 (#1586) --- esphome/components/uart/__init__.py | 6 +++++- esphome/components/uart/uart.h | 6 ++++++ esphome/components/uart/uart_esp32.cpp | 2 +- tests/test1.yaml | 1 + 4 files changed, 13 insertions(+), 2 deletions(-) diff --git a/esphome/components/uart/__init__.py b/esphome/components/uart/__init__.py index b83aedad9f..b3f94a4718 100644 --- a/esphome/components/uart/__init__.py +++ b/esphome/components/uart/__init__.py @@ -2,7 +2,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins, automation from esphome.const import CONF_BAUD_RATE, CONF_ID, CONF_RX_PIN, CONF_TX_PIN, CONF_UART_ID, \ - CONF_DATA, CONF_RX_BUFFER_SIZE + CONF_DATA, CONF_RX_BUFFER_SIZE, CONF_INVERT from esphome.core import CORE, coroutine CODEOWNERS = ['@esphome/core'] @@ -47,6 +47,8 @@ CONFIG_SCHEMA = cv.All(cv.Schema({ cv.Optional(CONF_TX_PIN): pins.output_pin, cv.Optional(CONF_RX_PIN): validate_rx_pin, cv.Optional(CONF_RX_BUFFER_SIZE, default=256): cv.validate_bytes, + cv.SplitDefault(CONF_INVERT, esp32=False): cv.All(cv.only_on_esp32, + cv.boolean), cv.Optional(CONF_STOP_BITS, default=1): cv.one_of(1, 2, int=True), cv.Optional(CONF_DATA_BITS, default=8): cv.int_range(min=5, max=8), cv.Optional(CONF_PARITY, default="NONE"): cv.enum(UART_PARITY_OPTIONS, upper=True) @@ -65,6 +67,8 @@ def to_code(config): if CONF_RX_PIN in config: cg.add(var.set_rx_pin(config[CONF_RX_PIN])) cg.add(var.set_rx_buffer_size(config[CONF_RX_BUFFER_SIZE])) + if CONF_INVERT in config: + cg.add(var.set_invert(config[CONF_INVERT])) cg.add(var.set_stop_bits(config[CONF_STOP_BITS])) cg.add(var.set_data_bits(config[CONF_DATA_BITS])) cg.add(var.set_parity(config[CONF_PARITY])) diff --git a/esphome/components/uart/uart.h b/esphome/components/uart/uart.h index 7430a4ee05..6dd62070da 100644 --- a/esphome/components/uart/uart.h +++ b/esphome/components/uart/uart.h @@ -90,6 +90,9 @@ class UARTComponent : public Component, public Stream { void set_tx_pin(uint8_t tx_pin) { this->tx_pin_ = tx_pin; } void set_rx_pin(uint8_t rx_pin) { this->rx_pin_ = rx_pin; } void set_rx_buffer_size(size_t rx_buffer_size) { this->rx_buffer_size_ = rx_buffer_size; } +#ifdef ARDUINO_ARCH_ESP32 + void set_invert(bool invert) { this->invert_ = invert; } +#endif void set_stop_bits(uint8_t stop_bits) { this->stop_bits_ = stop_bits; } void set_data_bits(uint8_t data_bits) { this->data_bits_ = data_bits; } void set_parity(UARTParityOptions parity) { this->parity_ = parity; } @@ -106,6 +109,9 @@ class UARTComponent : public Component, public Stream { optional tx_pin_; optional rx_pin_; size_t rx_buffer_size_; +#ifdef ARDUINO_ARCH_ESP32 + bool invert_; +#endif uint32_t baud_rate_; uint8_t stop_bits_; uint8_t data_bits_; diff --git a/esphome/components/uart/uart_esp32.cpp b/esphome/components/uart/uart_esp32.cpp index f7af85cf7b..920fe660be 100644 --- a/esphome/components/uart/uart_esp32.cpp +++ b/esphome/components/uart/uart_esp32.cpp @@ -80,7 +80,7 @@ void UARTComponent::setup() { } int8_t tx = this->tx_pin_.has_value() ? *this->tx_pin_ : -1; int8_t rx = this->rx_pin_.has_value() ? *this->rx_pin_ : -1; - this->hw_serial_->begin(this->baud_rate_, get_config(), rx, tx); + this->hw_serial_->begin(this->baud_rate_, get_config(), rx, tx, this->invert_); this->hw_serial_->setRxBufferSize(this->rx_buffer_size_); } diff --git a/tests/test1.yaml b/tests/test1.yaml index 8842b4b3b3..4dd3173d47 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -169,6 +169,7 @@ uart: data_bits: 8 stop_bits: 1 rx_buffer_size: 512 + invert: false - id: adalight_uart tx_pin: GPIO25 From c17624adab770a7611c46ffda94cb885d71be6b7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 6 Mar 2021 18:41:19 -0300 Subject: [PATCH 075/111] Bump platformio from 5.0.4 to 5.1.0 (#1581) Bumps [platformio](https://github.com/platformio/platformio) from 5.0.4 to 5.1.0. - [Release notes](https://github.com/platformio/platformio/releases) - [Changelog](https://github.com/platformio/platformio-core/blob/develop/HISTORY.rst) - [Commits](https://github.com/platformio/platformio/compare/v5.0.4...v5.1.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 280027af36..be7c46f98b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,6 +9,6 @@ tzlocal==2.1 pytz==2021.1 pyserial==3.5 ifaddr==0.1.7 -platformio==5.0.4 +platformio==5.1.0 esptool==2.8 click==7.1.2 From 2b60b0f1fa7ef0649f81c0f6da56b4ff04152a93 Mon Sep 17 00:00:00 2001 From: Guillermo Ruffino Date: Sat, 6 Mar 2021 19:59:06 -0300 Subject: [PATCH 076/111] fix servo warning (#1591) --- esphome/components/servo/servo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/servo/servo.cpp b/esphome/components/servo/servo.cpp index 5d2a9ee236..6935c34653 100644 --- a/esphome/components/servo/servo.cpp +++ b/esphome/components/servo/servo.cpp @@ -31,7 +31,7 @@ void Servo::loop() { if (this->transition_length_) { float new_value; float travel_diff = this->target_value_ - this->source_value_; - uint32_t target_runtime = target_runtime = abs((int) ((travel_diff) * this->transition_length_ * 1.0f / 2.0f)); + uint32_t target_runtime = abs((int) ((travel_diff) * this->transition_length_ * 1.0f / 2.0f)); uint32_t current_runtime = millis() - this->start_millis_; float percentage_run = current_runtime * 1.0f / target_runtime * 1.0f; if (percentage_run > 1.0f) { From 69879920eb21d92a8e06a16d559a5729a538456a Mon Sep 17 00:00:00 2001 From: Guillermo Ruffino Date: Sun, 7 Mar 2021 16:03:16 -0300 Subject: [PATCH 077/111] add-black (#1593) * Add black Update pre commit Update pre commit add empty line * Format with black --- .pre-commit-config.yaml | 30 +- esphome/__main__.py | 436 +- esphome/api/api_pb2.py | 5858 +++++++++++------ esphome/api/client.py | 73 +- esphome/automation.py | 167 +- esphome/codegen.py | 78 +- esphome/components/a4988/stepper.py | 18 +- esphome/components/ac_dimmer/output.py | 38 +- esphome/components/adalight/__init__.py | 16 +- esphome/components/adc/__init__.py | 2 +- esphome/components/adc/sensor.py | 52 +- esphome/components/ade7953/sensor.py | 78 +- esphome/components/ads1115/__init__.py | 24 +- esphome/components/ads1115/sensor.py | 71 +- esphome/components/aht10/sensor.py | 40 +- esphome/components/am2320/sensor.py | 42 +- esphome/components/animation/__init__.py | 54 +- esphome/components/apds9960/__init__.py | 22 +- esphome/components/apds9960/binary_sensor.py | 24 +- esphome/components/apds9960/sensor.py | 24 +- esphome/components/api/__init__.py | 176 +- esphome/components/as3935/__init__.py | 52 +- esphome/components/as3935/binary_sensor.py | 10 +- esphome/components/as3935/sensor.py | 31 +- esphome/components/as3935_i2c/__init__.py | 20 +- esphome/components/as3935_spi/__init__.py | 20 +- esphome/components/async_tcp/__init__.py | 6 +- .../components/atc_mithermometer/sensor.py | 66 +- esphome/components/atm90e32/sensor.py | 138 +- esphome/components/bang_bang/__init__.py | 2 +- esphome/components/bang_bang/climate.py | 68 +- esphome/components/bh1750/sensor.py | 41 +- esphome/components/binary/__init__.py | 2 +- esphome/components/binary/fan/__init__.py | 24 +- esphome/components/binary/light/__init__.py | 12 +- esphome/components/binary_sensor/__init__.py | 378 +- .../components/binary_sensor_map/sensor.py | 46 +- .../components/ble_presence/binary_sensor.py | 42 +- esphome/components/ble_rssi/sensor.py | 50 +- esphome/components/ble_scanner/text_sensor.py | 25 +- esphome/components/bme280/sensor.py | 110 +- esphome/components/bme680/sensor.py | 151 +- esphome/components/bmp085/sensor.py | 42 +- esphome/components/bmp280/sensor.py | 93 +- esphome/components/canbus/__init__.py | 121 +- esphome/components/captive_portal/__init__.py | 24 +- esphome/components/ccs811/sensor.py | 54 +- esphome/components/climate/__init__.py | 141 +- esphome/components/climate_ir/__init__.py | 45 +- esphome/components/climate_ir_lg/climate.py | 44 +- esphome/components/color/__init__.py | 34 +- esphome/components/coolix/climate.py | 16 +- esphome/components/cover/__init__.py | 106 +- esphome/components/cse7766/sensor.py | 49 +- esphome/components/ct_clamp/sensor.py | 36 +- esphome/components/custom/__init__.py | 2 +- .../custom/binary_sensor/__init__.py | 21 +- esphome/components/custom/climate/__init__.py | 22 +- esphome/components/custom/cover/__init__.py | 22 +- esphome/components/custom/light/__init__.py | 22 +- esphome/components/custom/output/__init__.py | 62 +- esphome/components/custom/sensor/__init__.py | 17 +- esphome/components/custom/switch/__init__.py | 26 +- .../components/custom/text_sensor/__init__.py | 28 +- .../components/custom_component/__init__.py | 25 +- esphome/components/cwww/light.py | 33 +- esphome/components/daikin/climate.py | 14 +- esphome/components/dallas/__init__.py | 22 +- esphome/components/dallas/sensor.py | 36 +- esphome/components/debug/__init__.py | 16 +- esphome/components/deep_sleep/__init__.py | 127 +- esphome/components/dfplayer/__init__.py | 277 +- esphome/components/dht/__init__.py | 2 +- esphome/components/dht/sensor.py | 58 +- esphome/components/dht12/sensor.py | 40 +- esphome/components/display/__init__.py | 91 +- esphome/components/ds1307/time.py | 44 +- esphome/components/duty_cycle/sensor.py | 31 +- esphome/components/e131/__init__.py | 48 +- esphome/components/endstop/cover.py | 57 +- .../components/esp32_ble_beacon/__init__.py | 30 +- .../components/esp32_ble_tracker/__init__.py | 183 +- esphome/components/esp32_camera/__init__.py | 199 +- esphome/components/esp32_dac/output.py | 16 +- esphome/components/esp32_hall/sensor.py | 27 +- esphome/components/esp32_touch/__init__.py | 111 +- .../components/esp32_touch/binary_sensor.py | 38 +- esphome/components/esp8266_pwm/output.py | 42 +- esphome/components/ethernet/__init__.py | 117 +- .../exposure_notifications/__init__.py | 36 +- esphome/components/ezo/sensor.py | 22 +- esphome/components/fan/__init__.py | 114 +- esphome/components/fastled_base/__init__.py | 44 +- esphome/components/fastled_clockless/light.py | 70 +- esphome/components/fastled_spi/light.py | 56 +- esphome/components/font/__init__.py | 64 +- esphome/components/fujitsu_general/climate.py | 16 +- esphome/components/globals/__init__.py | 49 +- esphome/components/gpio/__init__.py | 4 +- .../components/gpio/binary_sensor/__init__.py | 14 +- esphome/components/gpio/output/__init__.py | 13 +- esphome/components/gpio/switch/__init__.py | 35 +- esphome/components/gps/__init__.py | 24 +- esphome/components/gps/time/__init__.py | 16 +- esphome/components/hbridge/light.py | 18 +- esphome/components/hdc1080/sensor.py | 42 +- esphome/components/hitachi_ac344/climate.py | 14 +- esphome/components/hlw8012/sensor.py | 89 +- esphome/components/hm3301/sensor.py | 87 +- esphome/components/hmc5883l/sensor.py | 80 +- esphome/components/homeassistant/__init__.py | 4 +- .../homeassistant/binary_sensor/__init__.py | 18 +- .../homeassistant/sensor/__init__.py | 27 +- .../homeassistant/text_sensor/__init__.py | 17 +- .../components/homeassistant/time/__init__.py | 14 +- esphome/components/http_request/__init__.py | 173 +- esphome/components/htu21d/sensor.py | 42 +- esphome/components/hx711/sensor.py | 36 +- esphome/components/i2c/__init__.py | 40 +- esphome/components/ili9341/display.py | 62 +- esphome/components/image/__init__.py | 59 +- esphome/components/ina219/sensor.py | 72 +- esphome/components/ina226/sensor.py | 66 +- esphome/components/ina3221/sensor.py | 80 +- .../components/inkbird_ibsth1_mini/sensor.py | 54 +- esphome/components/inkplate6/__init__.py | 2 +- esphome/components/inkplate6/display.py | 142 +- esphome/components/integration/__init__.py | 2 +- esphome/components/integration/sensor.py | 63 +- esphome/components/interval/__init__.py | 21 +- esphome/components/json/__init__.py | 8 +- esphome/components/lcd_base/__init__.py | 12 +- esphome/components/lcd_gpio/display.py | 48 +- esphome/components/lcd_pcf8574/display.py | 26 +- esphome/components/ledc/__init__.py | 2 +- esphome/components/ledc/output.py | 69 +- esphome/components/light/__init__.py | 140 +- esphome/components/light/automation.py | 214 +- esphome/components/light/effects.py | 425 +- esphome/components/light/types.py | 87 +- esphome/components/logger/__init__.py | 220 +- esphome/components/max31855/sensor.py | 34 +- esphome/components/max31856/sensor.py | 39 +- esphome/components/max31865/sensor.py | 52 +- esphome/components/max6675/sensor.py | 20 +- esphome/components/max7219/display.py | 36 +- esphome/components/mcp23008/__init__.py | 81 +- esphome/components/mcp23016/__init__.py | 69 +- esphome/components/mcp23017/__init__.py | 81 +- esphome/components/mcp23s08/__init__.py | 75 +- esphome/components/mcp23s17/__init__.py | 75 +- esphome/components/mcp2515/canbus.py | 38 +- esphome/components/mcp3008/__init__.py | 18 +- esphome/components/mcp3008/sensor.py | 36 +- esphome/components/mcp4725/output.py | 18 +- esphome/components/mcp9808/sensor.py | 24 +- esphome/components/mhz19/sensor.py | 76 +- esphome/components/mitsubishi/climate.py | 14 +- esphome/components/modbus/__init__.py | 22 +- esphome/components/monochromatic/light.py | 16 +- esphome/components/mpr121/__init__.py | 34 +- esphome/components/mpr121/binary_sensor.py | 29 +- esphome/components/mpu6050/sensor.py | 85 +- esphome/components/mqtt/__init__.py | 318 +- esphome/components/mqtt_subscribe/__init__.py | 2 +- .../mqtt_subscribe/sensor/__init__.py | 35 +- .../mqtt_subscribe/text_sensor/__init__.py | 23 +- esphome/components/ms5611/sensor.py | 44 +- esphome/components/my9231/__init__.py | 34 +- esphome/components/my9231/output.py | 19 +- esphome/components/neopixelbus/light.py | 176 +- esphome/components/network/__init__.py | 2 +- esphome/components/nextion/__init__.py | 2 +- esphome/components/nextion/binary_sensor.py | 23 +- esphome/components/nextion/display.py | 27 +- esphome/components/nfc/__init__.py | 6 +- esphome/components/ntc/sensor.py | 109 +- esphome/components/ota/__init__.py | 45 +- esphome/components/output/__init__.py | 76 +- esphome/components/output/switch/__init__.py | 12 +- esphome/components/packages/__init__.py | 7 +- esphome/components/partition/light.py | 45 +- esphome/components/pca9685/__init__.py | 23 +- esphome/components/pca9685/output.py | 19 +- esphome/components/pcd8544/display.py | 43 +- esphome/components/pcf8574/__init__.py | 76 +- esphome/components/pid/__init__.py | 2 +- esphome/components/pid/climate.py | 121 +- esphome/components/pid/sensor/__init__.py | 51 +- esphome/components/pmsx003/sensor.py | 103 +- esphome/components/pn532/__init__.py | 75 +- esphome/components/pn532/binary_sensor.py | 34 +- esphome/components/pn532_i2c/__init__.py | 20 +- esphome/components/pn532_spi/__init__.py | 20 +- esphome/components/power_supply/__init__.py | 26 +- esphome/components/prometheus/__init__.py | 20 +- esphome/components/pulse_counter/sensor.py | 89 +- esphome/components/pulse_width/sensor.py | 23 +- esphome/components/pzem004t/sensor.py | 56 +- esphome/components/pzemac/sensor.py | 74 +- esphome/components/pzemdc/sensor.py | 47 +- esphome/components/qmc5883l/sensor.py | 79 +- esphome/components/rc522/__init__.py | 34 +- esphome/components/rc522/binary_sensor.py | 34 +- esphome/components/rc522_i2c/__init__.py | 20 +- esphome/components/rc522_spi/__init__.py | 20 +- esphome/components/rc522_spi/binary_sensor.py | 2 +- esphome/components/rdm6300/__init__.py | 34 +- esphome/components/rdm6300/binary_sensor.py | 20 +- esphome/components/remote_base/__init__.py | 820 ++- .../components/remote_receiver/__init__.py | 57 +- .../remote_receiver/binary_sensor.py | 2 +- .../components/remote_transmitter/__init__.py | 24 +- .../components/remote_transmitter/switch.py | 27 +- esphome/components/resistance/sensor.py | 36 +- esphome/components/restart/__init__.py | 2 +- esphome/components/restart/switch.py | 19 +- esphome/components/rf_bridge/__init__.py | 131 +- esphome/components/rgb/light.py | 18 +- esphome/components/rgbw/light.py | 24 +- esphome/components/rgbww/light.py | 47 +- esphome/components/rotary_encoder/sensor.py | 119 +- esphome/components/rtttl/__init__.py | 78 +- esphome/components/ruuvi_ble/__init__.py | 16 +- esphome/components/ruuvitag/sensor.py | 120 +- esphome/components/scd30/sensor.py | 79 +- esphome/components/script/__init__.py | 93 +- esphome/components/sds011/sensor.py | 61 +- esphome/components/senseair/sensor.py | 34 +- esphome/components/sensor/__init__.py | 470 +- esphome/components/servo/__init__.py | 74 +- esphome/components/sgp30/sensor.py | 75 +- esphome/components/sht3xd/sensor.py | 42 +- esphome/components/shtcx/sensor.py | 42 +- esphome/components/shutdown/__init__.py | 2 +- esphome/components/shutdown/switch.py | 20 +- esphome/components/sim800l/__init__.py | 80 +- esphome/components/sm16716/__init__.py | 31 +- esphome/components/sm16716/output.py | 18 +- esphome/components/sm300d2/sensor.py | 85 +- esphome/components/sn74hc595/__init__.py | 57 +- esphome/components/sntp/time.py | 23 +- esphome/components/speed/__init__.py | 2 +- esphome/components/speed/fan/__init__.py | 40 +- esphome/components/spi/__init__.py | 35 +- esphome/components/sps30/sensor.py | 104 +- esphome/components/ssd1306_base/__init__.py | 48 +- esphome/components/ssd1306_i2c/display.py | 22 +- esphome/components/ssd1306_spi/display.py | 24 +- esphome/components/ssd1322_base/__init__.py | 36 +- esphome/components/ssd1322_spi/display.py | 26 +- esphome/components/ssd1325_base/__init__.py | 44 +- esphome/components/ssd1325_spi/display.py | 26 +- esphome/components/ssd1327_base/__init__.py | 25 +- esphome/components/ssd1327_i2c/display.py | 24 +- esphome/components/ssd1327_spi/display.py | 26 +- esphome/components/ssd1331_base/__init__.py | 19 +- esphome/components/ssd1331_spi/display.py | 26 +- esphome/components/ssd1351_base/__init__.py | 27 +- esphome/components/ssd1351_spi/display.py | 26 +- esphome/components/st7735/__init__.py | 3 +- esphome/components/st7789v/__init__.py | 2 +- esphome/components/st7789v/display.py | 47 +- esphome/components/status/binary_sensor.py | 20 +- esphome/components/status_led/__init__.py | 16 +- esphome/components/stepper/__init__.py | 87 +- esphome/components/sts3x/sensor.py | 22 +- esphome/components/substitutions/__init__.py | 45 +- esphome/components/sun/__init__.py | 105 +- esphome/components/sun/sensor/__init__.py | 36 +- .../components/sun/text_sensor/__init__.py | 43 +- esphome/components/switch/__init__.py | 88 +- esphome/components/sx1509/__init__.py | 108 +- .../sx1509/binary_sensor/__init__.py | 22 +- esphome/components/sx1509/output/__init__.py | 19 +- esphome/components/tcl112/climate.py | 16 +- esphome/components/tcs34725/sensor.py | 102 +- esphome/components/teleinfo/__init__.py | 2 +- esphome/components/teleinfo/sensor.py | 43 +- esphome/components/template/__init__.py | 2 +- .../template/binary_sensor/__init__.py | 36 +- esphome/components/template/cover/__init__.py | 123 +- .../components/template/output/__init__.py | 49 +- .../components/template/sensor/__init__.py | 48 +- .../components/template/switch/__init__.py | 63 +- .../template/text_sensor/__init__.py | 34 +- esphome/components/text_sensor/__init__.py | 67 +- esphome/components/thermostat/climate.py | 408 +- esphome/components/time/__init__.py | 269 +- esphome/components/time_based/cover.py | 54 +- esphome/components/tlc59208f/__init__.py | 18 +- esphome/components/tlc59208f/output.py | 19 +- esphome/components/tm1637/display.py | 38 +- esphome/components/tm1651/__init__.py | 96 +- esphome/components/tmp102/sensor.py | 24 +- esphome/components/tmp117/sensor.py | 33 +- esphome/components/toshiba/climate.py | 14 +- .../components/total_daily_energy/sensor.py | 22 +- esphome/components/tsl2561/sensor.py | 50 +- esphome/components/ttp229_bsf/__init__.py | 22 +- .../components/ttp229_bsf/binary_sensor.py | 16 +- esphome/components/ttp229_lsf/__init__.py | 24 +- .../components/ttp229_lsf/binary_sensor.py | 16 +- esphome/components/tuya/__init__.py | 26 +- .../components/tuya/binary_sensor/__init__.py | 20 +- esphome/components/tuya/climate/__init__.py | 111 +- esphome/components/tuya/fan/__init__.py | 24 +- esphome/components/tuya/light/__init__.py | 49 +- esphome/components/tuya/sensor/__init__.py | 18 +- esphome/components/tuya/switch/__init__.py | 18 +- esphome/components/tx20/sensor.py | 41 +- esphome/components/uart/__init__.py | 97 +- esphome/components/uart/switch/__init__.py | 24 +- esphome/components/uln2003/stepper.py | 45 +- esphome/components/ultrasonic/__init__.py | 2 +- esphome/components/ultrasonic/sensor.py | 57 +- esphome/components/uptime/sensor.py | 16 +- esphome/components/version/__init__.py | 2 +- esphome/components/version/text_sensor.py | 18 +- esphome/components/vl53l0x/sensor.py | 48 +- .../components/voltage_sampler/__init__.py | 4 +- .../components/waveshare_epaper/display.py | 120 +- esphome/components/web_server/__init__.py | 61 +- .../components/web_server_base/__init__.py | 24 +- esphome/components/whirlpool/climate.py | 24 +- esphome/components/wifi/__init__.py | 242 +- esphome/components/wifi/wpa2_eap.py | 49 +- esphome/components/wifi_info/text_sensor.py | 64 +- esphome/components/wifi_signal/sensor.py | 29 +- esphome/components/wled/__init__.py | 15 +- esphome/components/xiaomi_ble/__init__.py | 16 +- esphome/components/xiaomi_cgd1/sensor.py | 59 +- esphome/components/xiaomi_cgg1/sensor.py | 54 +- esphome/components/xiaomi_gcls002/sensor.py | 67 +- esphome/components/xiaomi_hhccjcy01/sensor.py | 75 +- .../components/xiaomi_hhccpot002/sensor.py | 48 +- esphome/components/xiaomi_jqjcy01ym/sensor.py | 70 +- esphome/components/xiaomi_lywsd02/sensor.py | 56 +- .../components/xiaomi_lywsd03mmc/sensor.py | 62 +- esphome/components/xiaomi_lywsdcgq/sensor.py | 56 +- esphome/components/xiaomi_mhoc401/sensor.py | 62 +- esphome/components/xiaomi_miscale/sensor.py | 39 +- esphome/components/xiaomi_miscale2/sensor.py | 47 +- .../xiaomi_mjyd02yla/binary_sensor.py | 81 +- .../xiaomi_mue4094rt/binary_sensor.py | 36 +- .../components/xiaomi_wx08zm/binary_sensor.py | 50 +- esphome/components/yashima/climate.py | 26 +- esphome/components/zyaura/sensor.py | 56 +- esphome/config.py | 290 +- esphome/config_helpers.py | 21 +- esphome/config_validation.py | 713 +- esphome/const.py | 1360 ++-- esphome/core.py | 207 +- esphome/core_config.py | 246 +- esphome/cpp_generator.py | 237 +- esphome/cpp_helpers.py | 20 +- esphome/cpp_types.py | 56 +- esphome/dashboard/dashboard.py | 327 +- esphome/espota2.py | 117 +- esphome/helpers.py | 79 +- esphome/legacy.py | 2 +- esphome/mqtt.py | 66 +- esphome/pins.py | 1215 +++- esphome/platformio_api.py | 123 +- esphome/storage_json.py | 133 +- esphome/util.py | 60 +- esphome/voluptuous_schema.py | 53 +- esphome/vscode.py | 46 +- esphome/wizard.py | 243 +- esphome/writer.py | 233 +- esphome/yaml_util.py | 144 +- esphome/zeroconf.py | 144 +- pylintrc | 1 + pyproject.toml | 3 + requirements_test.txt | 2 + script/api_protobuf/api_options_pb2.py | 300 +- script/api_protobuf/api_protobuf.py | 542 +- script/build_codeowners.py | 37 +- script/build_compile_commands.py | 2 +- script/bump-docker-base-version.py | 20 +- script/bump-version.py | 29 +- script/ci-custom.py | 463 +- script/helpers.py | 95 +- script/lint-python | 46 +- script/setup | 2 + setup.cfg | 39 + .../binary_sensor/test_binary_sensor.py | 16 +- tests/component_tests/sensor/test_sensor.py | 4 +- tests/unit_tests/conftest.py | 1 - tests/unit_tests/strategies.py | 4 +- tests/unit_tests/test_codegen.py | 88 +- tests/unit_tests/test_config_validation.py | 24 +- tests/unit_tests/test_core.py | 371 +- tests/unit_tests/test_cpp_generator.py | 150 +- tests/unit_tests/test_cpp_helpers.py | 19 +- tests/unit_tests/test_helpers.py | 139 +- tests/unit_tests/test_pins.py | 87 +- tests/unit_tests/test_wizard.py | 42 +- 398 files changed, 21624 insertions(+), 12644 deletions(-) create mode 100644 pyproject.toml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d57da791fd..1e56aa17cf 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,11 +1,27 @@ # See https://pre-commit.com for more information # See https://pre-commit.com/hooks.html for more hooks repos: -- repo: https://github.com/pre-commit/pre-commit-hooks - rev: v2.4.0 + - repo: https://github.com/ambv/black + rev: 20.8b1 hooks: - - id: trailing-whitespace - - id: end-of-file-fixer - - id: check-yaml - - id: check-added-large-files - - id: flake8 + - id: black + args: + - --safe + - --quiet + files: ^((esphome|script|tests)/.+)?[^/]+\.py$ + - repo: https://gitlab.com/pycqa/flake8 + rev: 3.8.4 + hooks: + - id: flake8 + additional_dependencies: + - flake8-docstrings==1.5.0 + - pydocstyle==5.1.1 + files: ^(esphome|tests)/.+\.py$ + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v3.4.0 + hooks: + - id: no-commit-to-branch + args: + - --branch=dev + - --branch=master + - --branch=beta diff --git a/esphome/__main__.py b/esphome/__main__.py index 200ab2d7d7..2acd16cf95 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -8,21 +8,36 @@ from datetime import datetime from esphome import const, writer, yaml_util import esphome.codegen as cg from esphome.config import iter_components, read_config, strip_default_ids -from esphome.const import CONF_BAUD_RATE, CONF_BROKER, CONF_LOGGER, CONF_OTA, \ - CONF_PASSWORD, CONF_PORT, CONF_ESPHOME, CONF_PLATFORMIO_OPTIONS +from esphome.const import ( + CONF_BAUD_RATE, + CONF_BROKER, + CONF_LOGGER, + CONF_OTA, + CONF_PASSWORD, + CONF_PORT, + CONF_ESPHOME, + CONF_PLATFORMIO_OPTIONS, +) from esphome.core import CORE, EsphomeError, coroutine, coroutine_with_priority from esphome.helpers import color, indent -from esphome.util import run_external_command, run_external_process, safe_print, list_yaml_files, \ - get_serial_ports +from esphome.util import ( + run_external_command, + run_external_process, + safe_print, + list_yaml_files, + get_serial_ports, +) _LOGGER = logging.getLogger(__name__) def choose_prompt(options): if not options: - raise EsphomeError("Found no valid options for upload/logging, please make sure relevant " - "sections (ota, api, mqtt, ...) are in your configuration and/or the " - "device is plugged in.") + raise EsphomeError( + "Found no valid options for upload/logging, please make sure relevant " + "sections (ota, api, mqtt, ...) are in your configuration and/or the " + "device is plugged in." + ) if len(options) == 1: return options[0][1] @@ -32,7 +47,7 @@ def choose_prompt(options): safe_print(f" [{i+1}] {desc}") while True: - opt = input('(number): ') + opt = input("(number): ") if opt in options: opt = options.index(opt) break @@ -42,7 +57,7 @@ def choose_prompt(options): raise ValueError break except ValueError: - safe_print(color('red', f"Invalid option: '{opt}'")) + safe_print(color("red", f"Invalid option: '{opt}'")) return options[opt - 1][1] @@ -50,14 +65,14 @@ def choose_upload_log_host(default, check_default, show_ota, show_mqtt, show_api options = [] for port in get_serial_ports(): options.append((f"{port.path} ({port.description})", port.path)) - if (show_ota and 'ota' in CORE.config) or (show_api and 'api' in CORE.config): + if (show_ota and "ota" in CORE.config) or (show_api and "api" in CORE.config): options.append((f"Over The Air ({CORE.address})", CORE.address)) - if default == 'OTA': + if default == "OTA": return CORE.address - if show_mqtt and 'mqtt' in CORE.config: - options.append(("MQTT ({})".format(CORE.config['mqtt'][CONF_BROKER]), 'MQTT')) - if default == 'OTA': - return 'MQTT' + if show_mqtt and "mqtt" in CORE.config: + options.append(("MQTT ({})".format(CORE.config["mqtt"][CONF_BROKER]), "MQTT")) + if default == "OTA": + return "MQTT" if default is not None: return default if check_default is not None and check_default in [opt[1] for opt in options]: @@ -66,11 +81,11 @@ def choose_upload_log_host(default, check_default, show_ota, show_mqtt, show_api def get_port_type(port): - if port.startswith('/') or port.startswith('COM'): - return 'SERIAL' - if port == 'MQTT': - return 'MQTT' - return 'NETWORK' + if port.startswith("/") or port.startswith("COM"): + return "SERIAL" + if port == "MQTT": + return "MQTT" + return "NETWORK" def run_miniterm(config, port): @@ -80,7 +95,7 @@ def run_miniterm(config, port): if CONF_LOGGER not in config: _LOGGER.info("Logger is not enabled. Not starting UART logs.") return - baud_rate = config['logger'][CONF_BAUD_RATE] + baud_rate = config["logger"][CONF_BAUD_RATE] if baud_rate == 0: _LOGGER.info("UART logging is disabled (baud_rate=0). Not starting UART logs.") _LOGGER.info("Starting log output from %s with baud rate %s", port, baud_rate) @@ -93,13 +108,18 @@ def run_miniterm(config, port): except serial.SerialException: _LOGGER.error("Serial port closed!") return - line = raw.replace(b'\r', b'').replace(b'\n', b'').decode('utf8', 'backslashreplace') - time = datetime.now().time().strftime('[%H:%M:%S]') + line = ( + raw.replace(b"\r", b"") + .replace(b"\n", b"") + .decode("utf8", "backslashreplace") + ) + time = datetime.now().time().strftime("[%H:%M:%S]") message = time + line safe_print(message) backtrace_state = platformio_api.process_stacktrace( - config, line, backtrace_state=backtrace_state) + config, line, backtrace_state=backtrace_state + ) def wrap_to_code(name, comp): @@ -111,7 +131,7 @@ def wrap_to_code(name, comp): cg.add(cg.LineComment(f"{name}:")) if comp.config_schema is not None: conf_str = yaml_util.dump(conf) - conf_str = conf_str.replace('//', '') + conf_str = conf_str.replace("//", "") cg.add(cg.LineComment(indent(conf_str))) yield coro(conf) @@ -151,15 +171,31 @@ def compile_program(args, config): def upload_using_esptool(config, port): path = CORE.firmware_bin - first_baudrate = config[CONF_ESPHOME][CONF_PLATFORMIO_OPTIONS].get('upload_speed', 460800) + first_baudrate = config[CONF_ESPHOME][CONF_PLATFORMIO_OPTIONS].get( + "upload_speed", 460800 + ) def run_esptool(baud_rate): - cmd = ['esptool.py', '--before', 'default_reset', '--after', 'hard_reset', - '--baud', str(baud_rate), - '--chip', 'esp8266', '--port', port, 'write_flash', '0x0', path] + cmd = [ + "esptool.py", + "--before", + "default_reset", + "--after", + "hard_reset", + "--baud", + str(baud_rate), + "--chip", + "esp8266", + "--port", + port, + "write_flash", + "0x0", + path, + ] - if os.environ.get('ESPHOME_USE_SUBPROCESS') is None: + if os.environ.get("ESPHOME_USE_SUBPROCESS") is None: import esptool + # pylint: disable=protected-access return run_external_command(esptool._main, *cmd) @@ -169,14 +205,16 @@ def upload_using_esptool(config, port): if rc == 0 or first_baudrate == 115200: return rc # Try with 115200 baud rate, with some serial chips the faster baud rates do not work well - _LOGGER.info("Upload with baud rate %s failed. Trying again with baud rate 115200.", - first_baudrate) + _LOGGER.info( + "Upload with baud rate %s failed. Trying again with baud rate 115200.", + first_baudrate, + ) return run_esptool(115200) def upload_program(config, args, host): # if upload is to a serial port use platformio, otherwise assume ota - if get_port_type(host) == 'SERIAL': + if get_port_type(host) == "SERIAL": from esphome import platformio_api if CORE.is_esp8266: @@ -186,8 +224,10 @@ def upload_program(config, args, host): from esphome import espota2 if CONF_OTA not in config: - raise EsphomeError("Cannot upload Over the Air as the config does not include the ota: " - "component") + raise EsphomeError( + "Cannot upload Over the Air as the config does not include the ota: " + "component" + ) ota_conf = config[CONF_OTA] remote_port = ota_conf[CONF_PORT] @@ -196,19 +236,21 @@ def upload_program(config, args, host): def show_logs(config, args, port): - if 'logger' not in config: + if "logger" not in config: raise EsphomeError("Logger is not configured!") - if get_port_type(port) == 'SERIAL': + if get_port_type(port) == "SERIAL": run_miniterm(config, port) return 0 - if get_port_type(port) == 'NETWORK' and 'api' in config: + if get_port_type(port) == "NETWORK" and "api" in config: from esphome.api.client import run_logs return run_logs(config, port) - if get_port_type(port) == 'MQTT' and 'mqtt' in config: + if get_port_type(port) == "MQTT" and "mqtt" in config: from esphome import mqtt - return mqtt.show_logs(config, args.topic, args.username, args.password, args.client_id) + return mqtt.show_logs( + config, args.topic, args.username, args.password, args.client_id + ) raise EsphomeError("No remote or local logging method configured (api/mqtt/logger)") @@ -216,7 +258,9 @@ def show_logs(config, args, port): def clean_mqtt(config, args): from esphome import mqtt - return mqtt.clear_topic(config, args.topic, args.username, args.password, args.client_id) + return mqtt.clear_topic( + config, args.topic, args.username, args.password, args.client_id + ) def setup_log(debug=False, quiet=False): @@ -230,27 +274,31 @@ def setup_log(debug=False, quiet=False): logging.basicConfig(level=log_level) fmt = "%(levelname)s %(message)s" colorfmt = f"%(log_color)s{fmt}%(reset)s" - datefmt = '%H:%M:%S' + datefmt = "%H:%M:%S" - logging.getLogger('urllib3').setLevel(logging.WARNING) + logging.getLogger("urllib3").setLevel(logging.WARNING) try: import colorama + colorama.init(strip=True) from colorlog import ColoredFormatter - logging.getLogger().handlers[0].setFormatter(ColoredFormatter( - colorfmt, - datefmt=datefmt, - reset=True, - log_colors={ - 'DEBUG': 'cyan', - 'INFO': 'green', - 'WARNING': 'yellow', - 'ERROR': 'red', - 'CRITICAL': 'red', - } - )) + + logging.getLogger().handlers[0].setFormatter( + ColoredFormatter( + colorfmt, + datefmt=datefmt, + reset=True, + log_colors={ + "DEBUG": "cyan", + "INFO": "green", + "WARNING": "yellow", + "ERROR": "red", + "CRITICAL": "red", + }, + ) + ) except ImportError: pass @@ -291,8 +339,13 @@ def command_compile(args, config): def command_upload(args, config): - port = choose_upload_log_host(default=args.upload_port, check_default=None, - show_ota=True, show_mqtt=False, show_api=False) + port = choose_upload_log_host( + default=args.upload_port, + check_default=None, + show_ota=True, + show_mqtt=False, + show_api=False, + ) exit_code = upload_program(config, args, port) if exit_code != 0: return exit_code @@ -301,8 +354,13 @@ def command_upload(args, config): def command_logs(args, config): - port = choose_upload_log_host(default=args.serial_port, check_default=None, - show_ota=False, show_mqtt=True, show_api=True) + port = choose_upload_log_host( + default=args.serial_port, + check_default=None, + show_ota=False, + show_mqtt=True, + show_api=True, + ) return show_logs(config, args, port) @@ -314,16 +372,26 @@ def command_run(args, config): if exit_code != 0: return exit_code _LOGGER.info("Successfully compiled program.") - port = choose_upload_log_host(default=args.upload_port, check_default=None, - show_ota=True, show_mqtt=False, show_api=True) + port = choose_upload_log_host( + default=args.upload_port, + check_default=None, + show_ota=True, + show_mqtt=False, + show_api=True, + ) exit_code = upload_program(config, args, port) if exit_code != 0: return exit_code _LOGGER.info("Successfully uploaded program.") if args.no_logs: return 0 - port = choose_upload_log_host(default=args.upload_port, check_default=port, - show_ota=False, show_mqtt=True, show_api=True) + port = choose_upload_log_host( + default=args.upload_port, + check_default=port, + show_ota=False, + show_mqtt=True, + show_api=True, + ) return show_logs(config, args, port) @@ -372,137 +440,189 @@ def command_update_all(args): click.echo(f"{half_line}{middle_text}{half_line}") for f in files: - print("Updating {}".format(color('cyan', f))) - print('-' * twidth) + print("Updating {}".format(color("cyan", f))) + print("-" * twidth) print() - rc = run_external_process('esphome', '--dashboard', f, 'run', '--no-logs', '--upload-port', - 'OTA') + rc = run_external_process( + "esphome", "--dashboard", f, "run", "--no-logs", "--upload-port", "OTA" + ) if rc == 0: - print_bar("[{}] {}".format(color('bold_green', 'SUCCESS'), f)) + print_bar("[{}] {}".format(color("bold_green", "SUCCESS"), f)) success[f] = True else: - print_bar("[{}] {}".format(color('bold_red', 'ERROR'), f)) + print_bar("[{}] {}".format(color("bold_red", "ERROR"), f)) success[f] = False print() print() print() - print_bar('[{}]'.format(color('bold_white', 'SUMMARY'))) + print_bar("[{}]".format(color("bold_white", "SUMMARY"))) failed = 0 for f in files: if success[f]: - print(" - {}: {}".format(f, color('green', 'SUCCESS'))) + print(" - {}: {}".format(f, color("green", "SUCCESS"))) else: - print(" - {}: {}".format(f, color('bold_red', 'FAILED'))) + print(" - {}: {}".format(f, color("bold_red", "FAILED"))) failed += 1 return failed PRE_CONFIG_ACTIONS = { - 'wizard': command_wizard, - 'version': command_version, - 'dashboard': command_dashboard, - 'vscode': command_vscode, - 'update-all': command_update_all, + "wizard": command_wizard, + "version": command_version, + "dashboard": command_dashboard, + "vscode": command_vscode, + "update-all": command_update_all, } POST_CONFIG_ACTIONS = { - 'config': command_config, - 'compile': command_compile, - 'upload': command_upload, - 'logs': command_logs, - 'run': command_run, - 'clean-mqtt': command_clean_mqtt, - 'mqtt-fingerprint': command_mqtt_fingerprint, - 'clean': command_clean, + "config": command_config, + "compile": command_compile, + "upload": command_upload, + "logs": command_logs, + "run": command_run, + "clean-mqtt": command_clean_mqtt, + "mqtt-fingerprint": command_mqtt_fingerprint, + "clean": command_clean, } def parse_args(argv): - parser = argparse.ArgumentParser(description=f'ESPHome v{const.__version__}') - parser.add_argument('-v', '--verbose', help="Enable verbose esphome logs.", - action='store_true') - parser.add_argument('-q', '--quiet', help="Disable all esphome logs.", - action='store_true') - parser.add_argument('--dashboard', help=argparse.SUPPRESS, action='store_true') - parser.add_argument('-s', '--substitution', nargs=2, action='append', - help='Add a substitution', metavar=('key', 'value')) - parser.add_argument('configuration', help='Your YAML configuration file.', nargs='*') + parser = argparse.ArgumentParser(description=f"ESPHome v{const.__version__}") + parser.add_argument( + "-v", "--verbose", help="Enable verbose esphome logs.", action="store_true" + ) + parser.add_argument( + "-q", "--quiet", help="Disable all esphome logs.", action="store_true" + ) + parser.add_argument("--dashboard", help=argparse.SUPPRESS, action="store_true") + parser.add_argument( + "-s", + "--substitution", + nargs=2, + action="append", + help="Add a substitution", + metavar=("key", "value"), + ) + parser.add_argument( + "configuration", help="Your YAML configuration file.", nargs="*" + ) - subparsers = parser.add_subparsers(help='Commands', dest='command') + subparsers = parser.add_subparsers(help="Commands", dest="command") subparsers.required = True - subparsers.add_parser('config', help='Validate the configuration and spit it out.') + subparsers.add_parser("config", help="Validate the configuration and spit it out.") - parser_compile = subparsers.add_parser('compile', - help='Read the configuration and compile a program.') - parser_compile.add_argument('--only-generate', - help="Only generate source code, do not compile.", - action='store_true') + parser_compile = subparsers.add_parser( + "compile", help="Read the configuration and compile a program." + ) + parser_compile.add_argument( + "--only-generate", + help="Only generate source code, do not compile.", + action="store_true", + ) - parser_upload = subparsers.add_parser('upload', help='Validate the configuration ' - 'and upload the latest binary.') - parser_upload.add_argument('--upload-port', help="Manually specify the upload port to use. " - "For example /dev/cu.SLAB_USBtoUART.") + parser_upload = subparsers.add_parser( + "upload", help="Validate the configuration " "and upload the latest binary." + ) + parser_upload.add_argument( + "--upload-port", + help="Manually specify the upload port to use. " + "For example /dev/cu.SLAB_USBtoUART.", + ) - parser_logs = subparsers.add_parser('logs', help='Validate the configuration ' - 'and show all MQTT logs.') - parser_logs.add_argument('--topic', help='Manually set the topic to subscribe to.') - parser_logs.add_argument('--username', help='Manually set the username.') - parser_logs.add_argument('--password', help='Manually set the password.') - parser_logs.add_argument('--client-id', help='Manually set the client id.') - parser_logs.add_argument('--serial-port', help="Manually specify a serial port to use" - "For example /dev/cu.SLAB_USBtoUART.") + parser_logs = subparsers.add_parser( + "logs", help="Validate the configuration " "and show all MQTT logs." + ) + parser_logs.add_argument("--topic", help="Manually set the topic to subscribe to.") + parser_logs.add_argument("--username", help="Manually set the username.") + parser_logs.add_argument("--password", help="Manually set the password.") + parser_logs.add_argument("--client-id", help="Manually set the client id.") + parser_logs.add_argument( + "--serial-port", + help="Manually specify a serial port to use" + "For example /dev/cu.SLAB_USBtoUART.", + ) - parser_run = subparsers.add_parser('run', help='Validate the configuration, create a binary, ' - 'upload it, and start MQTT logs.') - parser_run.add_argument('--upload-port', help="Manually specify the upload port/ip to use. " - "For example /dev/cu.SLAB_USBtoUART.") - parser_run.add_argument('--no-logs', help='Disable starting MQTT logs.', - action='store_true') - parser_run.add_argument('--topic', help='Manually set the topic to subscribe to for logs.') - parser_run.add_argument('--username', help='Manually set the MQTT username for logs.') - parser_run.add_argument('--password', help='Manually set the MQTT password for logs.') - parser_run.add_argument('--client-id', help='Manually set the client id for logs.') + parser_run = subparsers.add_parser( + "run", + help="Validate the configuration, create a binary, " + "upload it, and start MQTT logs.", + ) + parser_run.add_argument( + "--upload-port", + help="Manually specify the upload port/ip to use. " + "For example /dev/cu.SLAB_USBtoUART.", + ) + parser_run.add_argument( + "--no-logs", help="Disable starting MQTT logs.", action="store_true" + ) + parser_run.add_argument( + "--topic", help="Manually set the topic to subscribe to for logs." + ) + parser_run.add_argument( + "--username", help="Manually set the MQTT username for logs." + ) + parser_run.add_argument( + "--password", help="Manually set the MQTT password for logs." + ) + parser_run.add_argument("--client-id", help="Manually set the client id for logs.") - parser_clean = subparsers.add_parser('clean-mqtt', help="Helper to clear an MQTT topic from " - "retain messages.") - parser_clean.add_argument('--topic', help='Manually set the topic to subscribe to.') - parser_clean.add_argument('--username', help='Manually set the username.') - parser_clean.add_argument('--password', help='Manually set the password.') - parser_clean.add_argument('--client-id', help='Manually set the client id.') + parser_clean = subparsers.add_parser( + "clean-mqtt", help="Helper to clear an MQTT topic from " "retain messages." + ) + parser_clean.add_argument("--topic", help="Manually set the topic to subscribe to.") + parser_clean.add_argument("--username", help="Manually set the username.") + parser_clean.add_argument("--password", help="Manually set the password.") + parser_clean.add_argument("--client-id", help="Manually set the client id.") - subparsers.add_parser('wizard', help="A helpful setup wizard that will guide " - "you through setting up esphome.") + subparsers.add_parser( + "wizard", + help="A helpful setup wizard that will guide " + "you through setting up esphome.", + ) - subparsers.add_parser('mqtt-fingerprint', help="Get the SSL fingerprint from a MQTT broker.") + subparsers.add_parser( + "mqtt-fingerprint", help="Get the SSL fingerprint from a MQTT broker." + ) - subparsers.add_parser('version', help="Print the esphome version and exit.") + subparsers.add_parser("version", help="Print the esphome version and exit.") - subparsers.add_parser('clean', help="Delete all temporary build files.") + subparsers.add_parser("clean", help="Delete all temporary build files.") - dashboard = subparsers.add_parser('dashboard', - help="Create a simple web server for a dashboard.") - dashboard.add_argument("--port", help="The HTTP port to open connections on. Defaults to 6052.", - type=int, default=6052) - dashboard.add_argument("--username", help="The optional username to require " - "for authentication.", - type=str, default='') - dashboard.add_argument("--password", help="The optional password to require " - "for authentication.", - type=str, default='') - dashboard.add_argument("--open-ui", help="Open the dashboard UI in a browser.", - action='store_true') - dashboard.add_argument("--hassio", - help=argparse.SUPPRESS, - action="store_true") - dashboard.add_argument("--socket", - help="Make the dashboard serve under a unix socket", type=str) + dashboard = subparsers.add_parser( + "dashboard", help="Create a simple web server for a dashboard." + ) + dashboard.add_argument( + "--port", + help="The HTTP port to open connections on. Defaults to 6052.", + type=int, + default=6052, + ) + dashboard.add_argument( + "--username", + help="The optional username to require " "for authentication.", + type=str, + default="", + ) + dashboard.add_argument( + "--password", + help="The optional password to require " "for authentication.", + type=str, + default="", + ) + dashboard.add_argument( + "--open-ui", help="Open the dashboard UI in a browser.", action="store_true" + ) + dashboard.add_argument("--hassio", help=argparse.SUPPRESS, action="store_true") + dashboard.add_argument( + "--socket", help="Make the dashboard serve under a unix socket", type=str + ) - vscode = subparsers.add_parser('vscode', help=argparse.SUPPRESS) - vscode.add_argument('--ace', action='store_true') + vscode = subparsers.add_parser("vscode", help=argparse.SUPPRESS) + vscode.add_argument("--ace", action="store_true") - subparsers.add_parser('update-all', help=argparse.SUPPRESS) + subparsers.add_parser("update-all", help=argparse.SUPPRESS) return parser.parse_args(argv[1:]) @@ -512,13 +632,15 @@ def run_esphome(argv): CORE.dashboard = args.dashboard setup_log(args.verbose, args.quiet) - if args.command != 'version' and not args.configuration: + if args.command != "version" and not args.configuration: _LOGGER.error("Missing configuration parameter, see esphome --help.") return 1 if sys.version_info < (3, 6, 0): - _LOGGER.error("You're running ESPHome with Python <3.6. ESPHome is no longer compatible " - "with this Python version. Please reinstall ESPHome with Python 3.6+") + _LOGGER.error( + "You're running ESPHome with Python <3.6. ESPHome is no longer compatible " + "with this Python version. Please reinstall ESPHome with Python 3.6+" + ) return 1 if args.command in PRE_CONFIG_ACTIONS: diff --git a/esphome/api/api_pb2.py b/esphome/api/api_pb2.py index c6c8741f01..6262b752c6 100644 --- a/esphome/api/api_pb2.py +++ b/esphome/api/api_pb2.py @@ -3,93 +3,85 @@ # source: api.proto import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) + +_b = sys.version_info[0] < 3 and (lambda x: x) or (lambda x: x.encode("latin1")) from google.protobuf.internal import enum_type_wrapper from google.protobuf import descriptor as _descriptor from google.protobuf import message as _message from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database + # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() - - DESCRIPTOR = _descriptor.FileDescriptor( - name='api.proto', - package='', - syntax='proto3', - serialized_options=None, - serialized_pb=_b('\n\tapi.proto\"#\n\x0cHelloRequest\x12\x13\n\x0b\x63lient_info\x18\x01 \x01(\t\"Z\n\rHelloResponse\x12\x19\n\x11\x61pi_version_major\x18\x01 \x01(\r\x12\x19\n\x11\x61pi_version_minor\x18\x02 \x01(\r\x12\x13\n\x0bserver_info\x18\x03 \x01(\t\"\"\n\x0e\x43onnectRequest\x12\x10\n\x08password\x18\x01 \x01(\t\"+\n\x0f\x43onnectResponse\x12\x18\n\x10invalid_password\x18\x01 \x01(\x08\"\x13\n\x11\x44isconnectRequest\"\x14\n\x12\x44isconnectResponse\"\r\n\x0bPingRequest\"\x0e\n\x0cPingResponse\"\x13\n\x11\x44\x65viceInfoRequest\"\xad\x01\n\x12\x44\x65viceInfoResponse\x12\x15\n\ruses_password\x18\x01 \x01(\x08\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x13\n\x0bmac_address\x18\x03 \x01(\t\x12\x1c\n\x14\x65sphome_core_version\x18\x04 \x01(\t\x12\x18\n\x10\x63ompilation_time\x18\x05 \x01(\t\x12\r\n\x05model\x18\x06 \x01(\t\x12\x16\n\x0ehas_deep_sleep\x18\x07 \x01(\x08\"\x15\n\x13ListEntitiesRequest\"\x9a\x01\n ListEntitiesBinarySensorResponse\x12\x11\n\tobject_id\x18\x01 \x01(\t\x12\x0b\n\x03key\x18\x02 \x01(\x07\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x11\n\tunique_id\x18\x04 \x01(\t\x12\x14\n\x0c\x64\x65vice_class\x18\x05 \x01(\t\x12\x1f\n\x17is_status_binary_sensor\x18\x06 \x01(\x08\"s\n\x19ListEntitiesCoverResponse\x12\x11\n\tobject_id\x18\x01 \x01(\t\x12\x0b\n\x03key\x18\x02 \x01(\x07\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x11\n\tunique_id\x18\x04 \x01(\t\x12\x15\n\ris_optimistic\x18\x05 \x01(\x08\"\x90\x01\n\x17ListEntitiesFanResponse\x12\x11\n\tobject_id\x18\x01 \x01(\t\x12\x0b\n\x03key\x18\x02 \x01(\x07\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x11\n\tunique_id\x18\x04 \x01(\t\x12\x1c\n\x14supports_oscillation\x18\x05 \x01(\x08\x12\x16\n\x0esupports_speed\x18\x06 \x01(\x08\"\x8a\x02\n\x19ListEntitiesLightResponse\x12\x11\n\tobject_id\x18\x01 \x01(\t\x12\x0b\n\x03key\x18\x02 \x01(\x07\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x11\n\tunique_id\x18\x04 \x01(\t\x12\x1b\n\x13supports_brightness\x18\x05 \x01(\x08\x12\x14\n\x0csupports_rgb\x18\x06 \x01(\x08\x12\x1c\n\x14supports_white_value\x18\x07 \x01(\x08\x12\"\n\x1asupports_color_temperature\x18\x08 \x01(\x08\x12\x12\n\nmin_mireds\x18\t \x01(\x02\x12\x12\n\nmax_mireds\x18\n \x01(\x02\x12\x0f\n\x07\x65\x66\x66\x65\x63ts\x18\x0b \x03(\t\"\xa3\x01\n\x1aListEntitiesSensorResponse\x12\x11\n\tobject_id\x18\x01 \x01(\t\x12\x0b\n\x03key\x18\x02 \x01(\x07\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x11\n\tunique_id\x18\x04 \x01(\t\x12\x0c\n\x04icon\x18\x05 \x01(\t\x12\x1b\n\x13unit_of_measurement\x18\x06 \x01(\t\x12\x19\n\x11\x61\x63\x63uracy_decimals\x18\x07 \x01(\x05\"\x7f\n\x1aListEntitiesSwitchResponse\x12\x11\n\tobject_id\x18\x01 \x01(\t\x12\x0b\n\x03key\x18\x02 \x01(\x07\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x11\n\tunique_id\x18\x04 \x01(\t\x12\x0c\n\x04icon\x18\x05 \x01(\t\x12\x12\n\noptimistic\x18\x06 \x01(\x08\"o\n\x1eListEntitiesTextSensorResponse\x12\x11\n\tobject_id\x18\x01 \x01(\t\x12\x0b\n\x03key\x18\x02 \x01(\x07\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x11\n\tunique_id\x18\x04 \x01(\t\x12\x0c\n\x04icon\x18\x05 \x01(\t\"\x1a\n\x18ListEntitiesDoneResponse\"\x18\n\x16SubscribeStatesRequest\"7\n\x19\x42inarySensorStateResponse\x12\x0b\n\x03key\x18\x01 \x01(\x07\x12\r\n\x05state\x18\x02 \x01(\x08\"t\n\x12\x43overStateResponse\x12\x0b\n\x03key\x18\x01 \x01(\x07\x12-\n\x05state\x18\x02 \x01(\x0e\x32\x1e.CoverStateResponse.CoverState\"\"\n\nCoverState\x12\x08\n\x04OPEN\x10\x00\x12\n\n\x06\x43LOSED\x10\x01\"]\n\x10\x46\x61nStateResponse\x12\x0b\n\x03key\x18\x01 \x01(\x07\x12\r\n\x05state\x18\x02 \x01(\x08\x12\x13\n\x0boscillating\x18\x03 \x01(\x08\x12\x18\n\x05speed\x18\x04 \x01(\x0e\x32\t.FanSpeed\"\xa8\x01\n\x12LightStateResponse\x12\x0b\n\x03key\x18\x01 \x01(\x07\x12\r\n\x05state\x18\x02 \x01(\x08\x12\x12\n\nbrightness\x18\x03 \x01(\x02\x12\x0b\n\x03red\x18\x04 \x01(\x02\x12\r\n\x05green\x18\x05 \x01(\x02\x12\x0c\n\x04\x62lue\x18\x06 \x01(\x02\x12\r\n\x05white\x18\x07 \x01(\x02\x12\x19\n\x11\x63olor_temperature\x18\x08 \x01(\x02\x12\x0e\n\x06\x65\x66\x66\x65\x63t\x18\t \x01(\t\"1\n\x13SensorStateResponse\x12\x0b\n\x03key\x18\x01 \x01(\x07\x12\r\n\x05state\x18\x02 \x01(\x02\"1\n\x13SwitchStateResponse\x12\x0b\n\x03key\x18\x01 \x01(\x07\x12\r\n\x05state\x18\x02 \x01(\x08\"5\n\x17TextSensorStateResponse\x12\x0b\n\x03key\x18\x01 \x01(\x07\x12\r\n\x05state\x18\x02 \x01(\t\"\x98\x01\n\x13\x43overCommandRequest\x12\x0b\n\x03key\x18\x01 \x01(\x07\x12\x11\n\thas_state\x18\x02 \x01(\x08\x12\x32\n\x07\x63ommand\x18\x03 \x01(\x0e\x32!.CoverCommandRequest.CoverCommand\"-\n\x0c\x43overCommand\x12\x08\n\x04OPEN\x10\x00\x12\t\n\x05\x43LOSE\x10\x01\x12\x08\n\x04STOP\x10\x02\"\x9d\x01\n\x11\x46\x61nCommandRequest\x12\x0b\n\x03key\x18\x01 \x01(\x07\x12\x11\n\thas_state\x18\x02 \x01(\x08\x12\r\n\x05state\x18\x03 \x01(\x08\x12\x11\n\thas_speed\x18\x04 \x01(\x08\x12\x18\n\x05speed\x18\x05 \x01(\x0e\x32\t.FanSpeed\x12\x17\n\x0fhas_oscillating\x18\x06 \x01(\x08\x12\x13\n\x0boscillating\x18\x07 \x01(\x08\"\x95\x03\n\x13LightCommandRequest\x12\x0b\n\x03key\x18\x01 \x01(\x07\x12\x11\n\thas_state\x18\x02 \x01(\x08\x12\r\n\x05state\x18\x03 \x01(\x08\x12\x16\n\x0ehas_brightness\x18\x04 \x01(\x08\x12\x12\n\nbrightness\x18\x05 \x01(\x02\x12\x0f\n\x07has_rgb\x18\x06 \x01(\x08\x12\x0b\n\x03red\x18\x07 \x01(\x02\x12\r\n\x05green\x18\x08 \x01(\x02\x12\x0c\n\x04\x62lue\x18\t \x01(\x02\x12\x11\n\thas_white\x18\n \x01(\x08\x12\r\n\x05white\x18\x0b \x01(\x02\x12\x1d\n\x15has_color_temperature\x18\x0c \x01(\x08\x12\x19\n\x11\x63olor_temperature\x18\r \x01(\x02\x12\x1d\n\x15has_transition_length\x18\x0e \x01(\x08\x12\x19\n\x11transition_length\x18\x0f \x01(\r\x12\x18\n\x10has_flash_length\x18\x10 \x01(\x08\x12\x14\n\x0c\x66lash_length\x18\x11 \x01(\r\x12\x12\n\nhas_effect\x18\x12 \x01(\x08\x12\x0e\n\x06\x65\x66\x66\x65\x63t\x18\x13 \x01(\t\"2\n\x14SwitchCommandRequest\x12\x0b\n\x03key\x18\x01 \x01(\x07\x12\r\n\x05state\x18\x02 \x01(\x08\"E\n\x14SubscribeLogsRequest\x12\x18\n\x05level\x18\x01 \x01(\x0e\x32\t.LogLevel\x12\x13\n\x0b\x64ump_config\x18\x02 \x01(\x08\"d\n\x15SubscribeLogsResponse\x12\x18\n\x05level\x18\x01 \x01(\x0e\x32\t.LogLevel\x12\x0b\n\x03tag\x18\x02 \x01(\t\x12\x0f\n\x07message\x18\x03 \x01(\t\x12\x13\n\x0bsend_failed\x18\x04 \x01(\x08\"\x1e\n\x1cSubscribeServiceCallsRequest\"\xdf\x02\n\x13ServiceCallResponse\x12\x0f\n\x07service\x18\x01 \x01(\t\x12,\n\x04\x64\x61ta\x18\x02 \x03(\x0b\x32\x1e.ServiceCallResponse.DataEntry\x12=\n\rdata_template\x18\x03 \x03(\x0b\x32&.ServiceCallResponse.DataTemplateEntry\x12\x36\n\tvariables\x18\x04 \x03(\x0b\x32#.ServiceCallResponse.VariablesEntry\x1a+\n\tDataEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a\x33\n\x11\x44\x61taTemplateEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a\x30\n\x0eVariablesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"%\n#SubscribeHomeAssistantStatesRequest\"8\n#SubscribeHomeAssistantStateResponse\x12\x11\n\tentity_id\x18\x01 \x01(\t\">\n\x1aHomeAssistantStateResponse\x12\x11\n\tentity_id\x18\x01 \x01(\t\x12\r\n\x05state\x18\x02 \x01(\t\"\x10\n\x0eGetTimeRequest\"(\n\x0fGetTimeResponse\x12\x15\n\repoch_seconds\x18\x01 \x01(\x07*)\n\x08\x46\x61nSpeed\x12\x07\n\x03LOW\x10\x00\x12\n\n\x06MEDIUM\x10\x01\x12\x08\n\x04HIGH\x10\x02*]\n\x08LogLevel\x12\x08\n\x04NONE\x10\x00\x12\t\n\x05\x45RROR\x10\x01\x12\x08\n\x04WARN\x10\x02\x12\x08\n\x04INFO\x10\x03\x12\t\n\x05\x44\x45\x42UG\x10\x04\x12\x0b\n\x07VERBOSE\x10\x05\x12\x10\n\x0cVERY_VERBOSE\x10\x06\x62\x06proto3') + name="api.proto", + package="", + syntax="proto3", + serialized_options=None, + serialized_pb=_b( + '\n\tapi.proto"#\n\x0cHelloRequest\x12\x13\n\x0b\x63lient_info\x18\x01 \x01(\t"Z\n\rHelloResponse\x12\x19\n\x11\x61pi_version_major\x18\x01 \x01(\r\x12\x19\n\x11\x61pi_version_minor\x18\x02 \x01(\r\x12\x13\n\x0bserver_info\x18\x03 \x01(\t""\n\x0e\x43onnectRequest\x12\x10\n\x08password\x18\x01 \x01(\t"+\n\x0f\x43onnectResponse\x12\x18\n\x10invalid_password\x18\x01 \x01(\x08"\x13\n\x11\x44isconnectRequest"\x14\n\x12\x44isconnectResponse"\r\n\x0bPingRequest"\x0e\n\x0cPingResponse"\x13\n\x11\x44\x65viceInfoRequest"\xad\x01\n\x12\x44\x65viceInfoResponse\x12\x15\n\ruses_password\x18\x01 \x01(\x08\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x13\n\x0bmac_address\x18\x03 \x01(\t\x12\x1c\n\x14\x65sphome_core_version\x18\x04 \x01(\t\x12\x18\n\x10\x63ompilation_time\x18\x05 \x01(\t\x12\r\n\x05model\x18\x06 \x01(\t\x12\x16\n\x0ehas_deep_sleep\x18\x07 \x01(\x08"\x15\n\x13ListEntitiesRequest"\x9a\x01\n ListEntitiesBinarySensorResponse\x12\x11\n\tobject_id\x18\x01 \x01(\t\x12\x0b\n\x03key\x18\x02 \x01(\x07\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x11\n\tunique_id\x18\x04 \x01(\t\x12\x14\n\x0c\x64\x65vice_class\x18\x05 \x01(\t\x12\x1f\n\x17is_status_binary_sensor\x18\x06 \x01(\x08"s\n\x19ListEntitiesCoverResponse\x12\x11\n\tobject_id\x18\x01 \x01(\t\x12\x0b\n\x03key\x18\x02 \x01(\x07\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x11\n\tunique_id\x18\x04 \x01(\t\x12\x15\n\ris_optimistic\x18\x05 \x01(\x08"\x90\x01\n\x17ListEntitiesFanResponse\x12\x11\n\tobject_id\x18\x01 \x01(\t\x12\x0b\n\x03key\x18\x02 \x01(\x07\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x11\n\tunique_id\x18\x04 \x01(\t\x12\x1c\n\x14supports_oscillation\x18\x05 \x01(\x08\x12\x16\n\x0esupports_speed\x18\x06 \x01(\x08"\x8a\x02\n\x19ListEntitiesLightResponse\x12\x11\n\tobject_id\x18\x01 \x01(\t\x12\x0b\n\x03key\x18\x02 \x01(\x07\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x11\n\tunique_id\x18\x04 \x01(\t\x12\x1b\n\x13supports_brightness\x18\x05 \x01(\x08\x12\x14\n\x0csupports_rgb\x18\x06 \x01(\x08\x12\x1c\n\x14supports_white_value\x18\x07 \x01(\x08\x12"\n\x1asupports_color_temperature\x18\x08 \x01(\x08\x12\x12\n\nmin_mireds\x18\t \x01(\x02\x12\x12\n\nmax_mireds\x18\n \x01(\x02\x12\x0f\n\x07\x65\x66\x66\x65\x63ts\x18\x0b \x03(\t"\xa3\x01\n\x1aListEntitiesSensorResponse\x12\x11\n\tobject_id\x18\x01 \x01(\t\x12\x0b\n\x03key\x18\x02 \x01(\x07\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x11\n\tunique_id\x18\x04 \x01(\t\x12\x0c\n\x04icon\x18\x05 \x01(\t\x12\x1b\n\x13unit_of_measurement\x18\x06 \x01(\t\x12\x19\n\x11\x61\x63\x63uracy_decimals\x18\x07 \x01(\x05"\x7f\n\x1aListEntitiesSwitchResponse\x12\x11\n\tobject_id\x18\x01 \x01(\t\x12\x0b\n\x03key\x18\x02 \x01(\x07\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x11\n\tunique_id\x18\x04 \x01(\t\x12\x0c\n\x04icon\x18\x05 \x01(\t\x12\x12\n\noptimistic\x18\x06 \x01(\x08"o\n\x1eListEntitiesTextSensorResponse\x12\x11\n\tobject_id\x18\x01 \x01(\t\x12\x0b\n\x03key\x18\x02 \x01(\x07\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x11\n\tunique_id\x18\x04 \x01(\t\x12\x0c\n\x04icon\x18\x05 \x01(\t"\x1a\n\x18ListEntitiesDoneResponse"\x18\n\x16SubscribeStatesRequest"7\n\x19\x42inarySensorStateResponse\x12\x0b\n\x03key\x18\x01 \x01(\x07\x12\r\n\x05state\x18\x02 \x01(\x08"t\n\x12\x43overStateResponse\x12\x0b\n\x03key\x18\x01 \x01(\x07\x12-\n\x05state\x18\x02 \x01(\x0e\x32\x1e.CoverStateResponse.CoverState""\n\nCoverState\x12\x08\n\x04OPEN\x10\x00\x12\n\n\x06\x43LOSED\x10\x01"]\n\x10\x46\x61nStateResponse\x12\x0b\n\x03key\x18\x01 \x01(\x07\x12\r\n\x05state\x18\x02 \x01(\x08\x12\x13\n\x0boscillating\x18\x03 \x01(\x08\x12\x18\n\x05speed\x18\x04 \x01(\x0e\x32\t.FanSpeed"\xa8\x01\n\x12LightStateResponse\x12\x0b\n\x03key\x18\x01 \x01(\x07\x12\r\n\x05state\x18\x02 \x01(\x08\x12\x12\n\nbrightness\x18\x03 \x01(\x02\x12\x0b\n\x03red\x18\x04 \x01(\x02\x12\r\n\x05green\x18\x05 \x01(\x02\x12\x0c\n\x04\x62lue\x18\x06 \x01(\x02\x12\r\n\x05white\x18\x07 \x01(\x02\x12\x19\n\x11\x63olor_temperature\x18\x08 \x01(\x02\x12\x0e\n\x06\x65\x66\x66\x65\x63t\x18\t \x01(\t"1\n\x13SensorStateResponse\x12\x0b\n\x03key\x18\x01 \x01(\x07\x12\r\n\x05state\x18\x02 \x01(\x02"1\n\x13SwitchStateResponse\x12\x0b\n\x03key\x18\x01 \x01(\x07\x12\r\n\x05state\x18\x02 \x01(\x08"5\n\x17TextSensorStateResponse\x12\x0b\n\x03key\x18\x01 \x01(\x07\x12\r\n\x05state\x18\x02 \x01(\t"\x98\x01\n\x13\x43overCommandRequest\x12\x0b\n\x03key\x18\x01 \x01(\x07\x12\x11\n\thas_state\x18\x02 \x01(\x08\x12\x32\n\x07\x63ommand\x18\x03 \x01(\x0e\x32!.CoverCommandRequest.CoverCommand"-\n\x0c\x43overCommand\x12\x08\n\x04OPEN\x10\x00\x12\t\n\x05\x43LOSE\x10\x01\x12\x08\n\x04STOP\x10\x02"\x9d\x01\n\x11\x46\x61nCommandRequest\x12\x0b\n\x03key\x18\x01 \x01(\x07\x12\x11\n\thas_state\x18\x02 \x01(\x08\x12\r\n\x05state\x18\x03 \x01(\x08\x12\x11\n\thas_speed\x18\x04 \x01(\x08\x12\x18\n\x05speed\x18\x05 \x01(\x0e\x32\t.FanSpeed\x12\x17\n\x0fhas_oscillating\x18\x06 \x01(\x08\x12\x13\n\x0boscillating\x18\x07 \x01(\x08"\x95\x03\n\x13LightCommandRequest\x12\x0b\n\x03key\x18\x01 \x01(\x07\x12\x11\n\thas_state\x18\x02 \x01(\x08\x12\r\n\x05state\x18\x03 \x01(\x08\x12\x16\n\x0ehas_brightness\x18\x04 \x01(\x08\x12\x12\n\nbrightness\x18\x05 \x01(\x02\x12\x0f\n\x07has_rgb\x18\x06 \x01(\x08\x12\x0b\n\x03red\x18\x07 \x01(\x02\x12\r\n\x05green\x18\x08 \x01(\x02\x12\x0c\n\x04\x62lue\x18\t \x01(\x02\x12\x11\n\thas_white\x18\n \x01(\x08\x12\r\n\x05white\x18\x0b \x01(\x02\x12\x1d\n\x15has_color_temperature\x18\x0c \x01(\x08\x12\x19\n\x11\x63olor_temperature\x18\r \x01(\x02\x12\x1d\n\x15has_transition_length\x18\x0e \x01(\x08\x12\x19\n\x11transition_length\x18\x0f \x01(\r\x12\x18\n\x10has_flash_length\x18\x10 \x01(\x08\x12\x14\n\x0c\x66lash_length\x18\x11 \x01(\r\x12\x12\n\nhas_effect\x18\x12 \x01(\x08\x12\x0e\n\x06\x65\x66\x66\x65\x63t\x18\x13 \x01(\t"2\n\x14SwitchCommandRequest\x12\x0b\n\x03key\x18\x01 \x01(\x07\x12\r\n\x05state\x18\x02 \x01(\x08"E\n\x14SubscribeLogsRequest\x12\x18\n\x05level\x18\x01 \x01(\x0e\x32\t.LogLevel\x12\x13\n\x0b\x64ump_config\x18\x02 \x01(\x08"d\n\x15SubscribeLogsResponse\x12\x18\n\x05level\x18\x01 \x01(\x0e\x32\t.LogLevel\x12\x0b\n\x03tag\x18\x02 \x01(\t\x12\x0f\n\x07message\x18\x03 \x01(\t\x12\x13\n\x0bsend_failed\x18\x04 \x01(\x08"\x1e\n\x1cSubscribeServiceCallsRequest"\xdf\x02\n\x13ServiceCallResponse\x12\x0f\n\x07service\x18\x01 \x01(\t\x12,\n\x04\x64\x61ta\x18\x02 \x03(\x0b\x32\x1e.ServiceCallResponse.DataEntry\x12=\n\rdata_template\x18\x03 \x03(\x0b\x32&.ServiceCallResponse.DataTemplateEntry\x12\x36\n\tvariables\x18\x04 \x03(\x0b\x32#.ServiceCallResponse.VariablesEntry\x1a+\n\tDataEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a\x33\n\x11\x44\x61taTemplateEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a\x30\n\x0eVariablesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01"%\n#SubscribeHomeAssistantStatesRequest"8\n#SubscribeHomeAssistantStateResponse\x12\x11\n\tentity_id\x18\x01 \x01(\t">\n\x1aHomeAssistantStateResponse\x12\x11\n\tentity_id\x18\x01 \x01(\t\x12\r\n\x05state\x18\x02 \x01(\t"\x10\n\x0eGetTimeRequest"(\n\x0fGetTimeResponse\x12\x15\n\repoch_seconds\x18\x01 \x01(\x07*)\n\x08\x46\x61nSpeed\x12\x07\n\x03LOW\x10\x00\x12\n\n\x06MEDIUM\x10\x01\x12\x08\n\x04HIGH\x10\x02*]\n\x08LogLevel\x12\x08\n\x04NONE\x10\x00\x12\t\n\x05\x45RROR\x10\x01\x12\x08\n\x04WARN\x10\x02\x12\x08\n\x04INFO\x10\x03\x12\t\n\x05\x44\x45\x42UG\x10\x04\x12\x0b\n\x07VERBOSE\x10\x05\x12\x10\n\x0cVERY_VERBOSE\x10\x06\x62\x06proto3' + ), ) _FANSPEED = _descriptor.EnumDescriptor( - name='FanSpeed', - full_name='FanSpeed', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='LOW', index=0, number=0, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='MEDIUM', index=1, number=1, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='HIGH', index=2, number=2, - serialized_options=None, - type=None), - ], - containing_type=None, - serialized_options=None, - serialized_start=3822, - serialized_end=3863, + name="FanSpeed", + full_name="FanSpeed", + filename=None, + file=DESCRIPTOR, + values=[ + _descriptor.EnumValueDescriptor( + name="LOW", index=0, number=0, serialized_options=None, type=None + ), + _descriptor.EnumValueDescriptor( + name="MEDIUM", index=1, number=1, serialized_options=None, type=None + ), + _descriptor.EnumValueDescriptor( + name="HIGH", index=2, number=2, serialized_options=None, type=None + ), + ], + containing_type=None, + serialized_options=None, + serialized_start=3822, + serialized_end=3863, ) _sym_db.RegisterEnumDescriptor(_FANSPEED) FanSpeed = enum_type_wrapper.EnumTypeWrapper(_FANSPEED) _LOGLEVEL = _descriptor.EnumDescriptor( - name='LogLevel', - full_name='LogLevel', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='NONE', index=0, number=0, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='ERROR', index=1, number=1, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='WARN', index=2, number=2, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='INFO', index=3, number=3, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='DEBUG', index=4, number=4, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='VERBOSE', index=5, number=5, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='VERY_VERBOSE', index=6, number=6, - serialized_options=None, - type=None), - ], - containing_type=None, - serialized_options=None, - serialized_start=3865, - serialized_end=3958, + name="LogLevel", + full_name="LogLevel", + filename=None, + file=DESCRIPTOR, + values=[ + _descriptor.EnumValueDescriptor( + name="NONE", index=0, number=0, serialized_options=None, type=None + ), + _descriptor.EnumValueDescriptor( + name="ERROR", index=1, number=1, serialized_options=None, type=None + ), + _descriptor.EnumValueDescriptor( + name="WARN", index=2, number=2, serialized_options=None, type=None + ), + _descriptor.EnumValueDescriptor( + name="INFO", index=3, number=3, serialized_options=None, type=None + ), + _descriptor.EnumValueDescriptor( + name="DEBUG", index=4, number=4, serialized_options=None, type=None + ), + _descriptor.EnumValueDescriptor( + name="VERBOSE", index=5, number=5, serialized_options=None, type=None + ), + _descriptor.EnumValueDescriptor( + name="VERY_VERBOSE", index=6, number=6, serialized_options=None, type=None + ), + ], + containing_type=None, + serialized_options=None, + serialized_start=3865, + serialized_end=3958, ) _sym_db.RegisterEnumDescriptor(_LOGLEVEL) @@ -107,2375 +99,3895 @@ VERY_VERBOSE = 6 _COVERSTATERESPONSE_COVERSTATE = _descriptor.EnumDescriptor( - name='CoverState', - full_name='CoverStateResponse.CoverState', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='OPEN', index=0, number=0, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CLOSED', index=1, number=1, - serialized_options=None, - type=None), - ], - containing_type=None, - serialized_options=None, - serialized_start=1808, - serialized_end=1842, + name="CoverState", + full_name="CoverStateResponse.CoverState", + filename=None, + file=DESCRIPTOR, + values=[ + _descriptor.EnumValueDescriptor( + name="OPEN", index=0, number=0, serialized_options=None, type=None + ), + _descriptor.EnumValueDescriptor( + name="CLOSED", index=1, number=1, serialized_options=None, type=None + ), + ], + containing_type=None, + serialized_options=None, + serialized_start=1808, + serialized_end=1842, ) _sym_db.RegisterEnumDescriptor(_COVERSTATERESPONSE_COVERSTATE) _COVERCOMMANDREQUEST_COVERCOMMAND = _descriptor.EnumDescriptor( - name='CoverCommand', - full_name='CoverCommandRequest.CoverCommand', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='OPEN', index=0, number=0, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='CLOSE', index=1, number=1, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='STOP', index=2, number=2, - serialized_options=None, - type=None), - ], - containing_type=None, - serialized_options=None, - serialized_start=2375, - serialized_end=2420, + name="CoverCommand", + full_name="CoverCommandRequest.CoverCommand", + filename=None, + file=DESCRIPTOR, + values=[ + _descriptor.EnumValueDescriptor( + name="OPEN", index=0, number=0, serialized_options=None, type=None + ), + _descriptor.EnumValueDescriptor( + name="CLOSE", index=1, number=1, serialized_options=None, type=None + ), + _descriptor.EnumValueDescriptor( + name="STOP", index=2, number=2, serialized_options=None, type=None + ), + ], + containing_type=None, + serialized_options=None, + serialized_start=2375, + serialized_end=2420, ) _sym_db.RegisterEnumDescriptor(_COVERCOMMANDREQUEST_COVERCOMMAND) _HELLOREQUEST = _descriptor.Descriptor( - name='HelloRequest', - full_name='HelloRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='client_info', full_name='HelloRequest.client_info', index=0, - number=1, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=13, - serialized_end=48, + name="HelloRequest", + full_name="HelloRequest", + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name="client_info", + full_name="HelloRequest.client_info", + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=_b("").decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=13, + serialized_end=48, ) _HELLORESPONSE = _descriptor.Descriptor( - name='HelloResponse', - full_name='HelloResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='api_version_major', full_name='HelloResponse.api_version_major', index=0, - number=1, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='api_version_minor', full_name='HelloResponse.api_version_minor', index=1, - number=2, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='server_info', full_name='HelloResponse.server_info', index=2, - number=3, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=50, - serialized_end=140, + name="HelloResponse", + full_name="HelloResponse", + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name="api_version_major", + full_name="HelloResponse.api_version_major", + index=0, + number=1, + type=13, + cpp_type=3, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="api_version_minor", + full_name="HelloResponse.api_version_minor", + index=1, + number=2, + type=13, + cpp_type=3, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="server_info", + full_name="HelloResponse.server_info", + index=2, + number=3, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=_b("").decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=50, + serialized_end=140, ) _CONNECTREQUEST = _descriptor.Descriptor( - name='ConnectRequest', - full_name='ConnectRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='password', full_name='ConnectRequest.password', index=0, - number=1, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=142, - serialized_end=176, + name="ConnectRequest", + full_name="ConnectRequest", + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name="password", + full_name="ConnectRequest.password", + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=_b("").decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=142, + serialized_end=176, ) _CONNECTRESPONSE = _descriptor.Descriptor( - name='ConnectResponse', - full_name='ConnectResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='invalid_password', full_name='ConnectResponse.invalid_password', index=0, - number=1, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=178, - serialized_end=221, + name="ConnectResponse", + full_name="ConnectResponse", + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name="invalid_password", + full_name="ConnectResponse.invalid_password", + index=0, + number=1, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=178, + serialized_end=221, ) _DISCONNECTREQUEST = _descriptor.Descriptor( - name='DisconnectRequest', - full_name='DisconnectRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=223, - serialized_end=242, + name="DisconnectRequest", + full_name="DisconnectRequest", + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=223, + serialized_end=242, ) _DISCONNECTRESPONSE = _descriptor.Descriptor( - name='DisconnectResponse', - full_name='DisconnectResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=244, - serialized_end=264, + name="DisconnectResponse", + full_name="DisconnectResponse", + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=244, + serialized_end=264, ) _PINGREQUEST = _descriptor.Descriptor( - name='PingRequest', - full_name='PingRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=266, - serialized_end=279, + name="PingRequest", + full_name="PingRequest", + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=266, + serialized_end=279, ) _PINGRESPONSE = _descriptor.Descriptor( - name='PingResponse', - full_name='PingResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=281, - serialized_end=295, + name="PingResponse", + full_name="PingResponse", + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=281, + serialized_end=295, ) _DEVICEINFOREQUEST = _descriptor.Descriptor( - name='DeviceInfoRequest', - full_name='DeviceInfoRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=297, - serialized_end=316, + name="DeviceInfoRequest", + full_name="DeviceInfoRequest", + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=297, + serialized_end=316, ) _DEVICEINFORESPONSE = _descriptor.Descriptor( - name='DeviceInfoResponse', - full_name='DeviceInfoResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='uses_password', full_name='DeviceInfoResponse.uses_password', index=0, - number=1, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='name', full_name='DeviceInfoResponse.name', index=1, - number=2, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='mac_address', full_name='DeviceInfoResponse.mac_address', index=2, - number=3, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='esphome_core_version', full_name='DeviceInfoResponse.esphome_core_version', index=3, - number=4, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='compilation_time', full_name='DeviceInfoResponse.compilation_time', index=4, - number=5, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='model', full_name='DeviceInfoResponse.model', index=5, - number=6, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='has_deep_sleep', full_name='DeviceInfoResponse.has_deep_sleep', index=6, - number=7, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=319, - serialized_end=492, + name="DeviceInfoResponse", + full_name="DeviceInfoResponse", + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name="uses_password", + full_name="DeviceInfoResponse.uses_password", + index=0, + number=1, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="name", + full_name="DeviceInfoResponse.name", + index=1, + number=2, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=_b("").decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="mac_address", + full_name="DeviceInfoResponse.mac_address", + index=2, + number=3, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=_b("").decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="esphome_core_version", + full_name="DeviceInfoResponse.esphome_core_version", + index=3, + number=4, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=_b("").decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="compilation_time", + full_name="DeviceInfoResponse.compilation_time", + index=4, + number=5, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=_b("").decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="model", + full_name="DeviceInfoResponse.model", + index=5, + number=6, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=_b("").decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="has_deep_sleep", + full_name="DeviceInfoResponse.has_deep_sleep", + index=6, + number=7, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=319, + serialized_end=492, ) _LISTENTITIESREQUEST = _descriptor.Descriptor( - name='ListEntitiesRequest', - full_name='ListEntitiesRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=494, - serialized_end=515, + name="ListEntitiesRequest", + full_name="ListEntitiesRequest", + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=494, + serialized_end=515, ) _LISTENTITIESBINARYSENSORRESPONSE = _descriptor.Descriptor( - name='ListEntitiesBinarySensorResponse', - full_name='ListEntitiesBinarySensorResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='object_id', full_name='ListEntitiesBinarySensorResponse.object_id', index=0, - number=1, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='key', full_name='ListEntitiesBinarySensorResponse.key', index=1, - number=2, type=7, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='name', full_name='ListEntitiesBinarySensorResponse.name', index=2, - number=3, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='unique_id', full_name='ListEntitiesBinarySensorResponse.unique_id', index=3, - number=4, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='device_class', full_name='ListEntitiesBinarySensorResponse.device_class', index=4, - number=5, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='is_status_binary_sensor', full_name='ListEntitiesBinarySensorResponse.is_status_binary_sensor', index=5, - number=6, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=518, - serialized_end=672, + name="ListEntitiesBinarySensorResponse", + full_name="ListEntitiesBinarySensorResponse", + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name="object_id", + full_name="ListEntitiesBinarySensorResponse.object_id", + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=_b("").decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="key", + full_name="ListEntitiesBinarySensorResponse.key", + index=1, + number=2, + type=7, + cpp_type=3, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="name", + full_name="ListEntitiesBinarySensorResponse.name", + index=2, + number=3, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=_b("").decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="unique_id", + full_name="ListEntitiesBinarySensorResponse.unique_id", + index=3, + number=4, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=_b("").decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="device_class", + full_name="ListEntitiesBinarySensorResponse.device_class", + index=4, + number=5, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=_b("").decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="is_status_binary_sensor", + full_name="ListEntitiesBinarySensorResponse.is_status_binary_sensor", + index=5, + number=6, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=518, + serialized_end=672, ) _LISTENTITIESCOVERRESPONSE = _descriptor.Descriptor( - name='ListEntitiesCoverResponse', - full_name='ListEntitiesCoverResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='object_id', full_name='ListEntitiesCoverResponse.object_id', index=0, - number=1, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='key', full_name='ListEntitiesCoverResponse.key', index=1, - number=2, type=7, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='name', full_name='ListEntitiesCoverResponse.name', index=2, - number=3, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='unique_id', full_name='ListEntitiesCoverResponse.unique_id', index=3, - number=4, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='is_optimistic', full_name='ListEntitiesCoverResponse.is_optimistic', index=4, - number=5, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=674, - serialized_end=789, + name="ListEntitiesCoverResponse", + full_name="ListEntitiesCoverResponse", + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name="object_id", + full_name="ListEntitiesCoverResponse.object_id", + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=_b("").decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="key", + full_name="ListEntitiesCoverResponse.key", + index=1, + number=2, + type=7, + cpp_type=3, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="name", + full_name="ListEntitiesCoverResponse.name", + index=2, + number=3, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=_b("").decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="unique_id", + full_name="ListEntitiesCoverResponse.unique_id", + index=3, + number=4, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=_b("").decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="is_optimistic", + full_name="ListEntitiesCoverResponse.is_optimistic", + index=4, + number=5, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=674, + serialized_end=789, ) _LISTENTITIESFANRESPONSE = _descriptor.Descriptor( - name='ListEntitiesFanResponse', - full_name='ListEntitiesFanResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='object_id', full_name='ListEntitiesFanResponse.object_id', index=0, - number=1, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='key', full_name='ListEntitiesFanResponse.key', index=1, - number=2, type=7, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='name', full_name='ListEntitiesFanResponse.name', index=2, - number=3, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='unique_id', full_name='ListEntitiesFanResponse.unique_id', index=3, - number=4, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='supports_oscillation', full_name='ListEntitiesFanResponse.supports_oscillation', index=4, - number=5, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='supports_speed', full_name='ListEntitiesFanResponse.supports_speed', index=5, - number=6, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=792, - serialized_end=936, + name="ListEntitiesFanResponse", + full_name="ListEntitiesFanResponse", + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name="object_id", + full_name="ListEntitiesFanResponse.object_id", + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=_b("").decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="key", + full_name="ListEntitiesFanResponse.key", + index=1, + number=2, + type=7, + cpp_type=3, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="name", + full_name="ListEntitiesFanResponse.name", + index=2, + number=3, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=_b("").decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="unique_id", + full_name="ListEntitiesFanResponse.unique_id", + index=3, + number=4, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=_b("").decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="supports_oscillation", + full_name="ListEntitiesFanResponse.supports_oscillation", + index=4, + number=5, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="supports_speed", + full_name="ListEntitiesFanResponse.supports_speed", + index=5, + number=6, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=792, + serialized_end=936, ) _LISTENTITIESLIGHTRESPONSE = _descriptor.Descriptor( - name='ListEntitiesLightResponse', - full_name='ListEntitiesLightResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='object_id', full_name='ListEntitiesLightResponse.object_id', index=0, - number=1, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='key', full_name='ListEntitiesLightResponse.key', index=1, - number=2, type=7, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='name', full_name='ListEntitiesLightResponse.name', index=2, - number=3, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='unique_id', full_name='ListEntitiesLightResponse.unique_id', index=3, - number=4, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='supports_brightness', full_name='ListEntitiesLightResponse.supports_brightness', index=4, - number=5, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='supports_rgb', full_name='ListEntitiesLightResponse.supports_rgb', index=5, - number=6, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='supports_white_value', full_name='ListEntitiesLightResponse.supports_white_value', index=6, - number=7, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='supports_color_temperature', full_name='ListEntitiesLightResponse.supports_color_temperature', index=7, - number=8, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='min_mireds', full_name='ListEntitiesLightResponse.min_mireds', index=8, - number=9, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='max_mireds', full_name='ListEntitiesLightResponse.max_mireds', index=9, - number=10, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='effects', full_name='ListEntitiesLightResponse.effects', index=10, - number=11, type=9, cpp_type=9, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=939, - serialized_end=1205, + name="ListEntitiesLightResponse", + full_name="ListEntitiesLightResponse", + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name="object_id", + full_name="ListEntitiesLightResponse.object_id", + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=_b("").decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="key", + full_name="ListEntitiesLightResponse.key", + index=1, + number=2, + type=7, + cpp_type=3, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="name", + full_name="ListEntitiesLightResponse.name", + index=2, + number=3, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=_b("").decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="unique_id", + full_name="ListEntitiesLightResponse.unique_id", + index=3, + number=4, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=_b("").decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="supports_brightness", + full_name="ListEntitiesLightResponse.supports_brightness", + index=4, + number=5, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="supports_rgb", + full_name="ListEntitiesLightResponse.supports_rgb", + index=5, + number=6, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="supports_white_value", + full_name="ListEntitiesLightResponse.supports_white_value", + index=6, + number=7, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="supports_color_temperature", + full_name="ListEntitiesLightResponse.supports_color_temperature", + index=7, + number=8, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="min_mireds", + full_name="ListEntitiesLightResponse.min_mireds", + index=8, + number=9, + type=2, + cpp_type=6, + label=1, + has_default_value=False, + default_value=float(0), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="max_mireds", + full_name="ListEntitiesLightResponse.max_mireds", + index=9, + number=10, + type=2, + cpp_type=6, + label=1, + has_default_value=False, + default_value=float(0), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="effects", + full_name="ListEntitiesLightResponse.effects", + index=10, + number=11, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=939, + serialized_end=1205, ) _LISTENTITIESSENSORRESPONSE = _descriptor.Descriptor( - name='ListEntitiesSensorResponse', - full_name='ListEntitiesSensorResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='object_id', full_name='ListEntitiesSensorResponse.object_id', index=0, - number=1, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='key', full_name='ListEntitiesSensorResponse.key', index=1, - number=2, type=7, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='name', full_name='ListEntitiesSensorResponse.name', index=2, - number=3, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='unique_id', full_name='ListEntitiesSensorResponse.unique_id', index=3, - number=4, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='icon', full_name='ListEntitiesSensorResponse.icon', index=4, - number=5, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='unit_of_measurement', full_name='ListEntitiesSensorResponse.unit_of_measurement', index=5, - number=6, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='accuracy_decimals', full_name='ListEntitiesSensorResponse.accuracy_decimals', index=6, - number=7, type=5, cpp_type=1, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1208, - serialized_end=1371, + name="ListEntitiesSensorResponse", + full_name="ListEntitiesSensorResponse", + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name="object_id", + full_name="ListEntitiesSensorResponse.object_id", + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=_b("").decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="key", + full_name="ListEntitiesSensorResponse.key", + index=1, + number=2, + type=7, + cpp_type=3, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="name", + full_name="ListEntitiesSensorResponse.name", + index=2, + number=3, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=_b("").decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="unique_id", + full_name="ListEntitiesSensorResponse.unique_id", + index=3, + number=4, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=_b("").decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="icon", + full_name="ListEntitiesSensorResponse.icon", + index=4, + number=5, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=_b("").decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="unit_of_measurement", + full_name="ListEntitiesSensorResponse.unit_of_measurement", + index=5, + number=6, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=_b("").decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="accuracy_decimals", + full_name="ListEntitiesSensorResponse.accuracy_decimals", + index=6, + number=7, + type=5, + cpp_type=1, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=1208, + serialized_end=1371, ) _LISTENTITIESSWITCHRESPONSE = _descriptor.Descriptor( - name='ListEntitiesSwitchResponse', - full_name='ListEntitiesSwitchResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='object_id', full_name='ListEntitiesSwitchResponse.object_id', index=0, - number=1, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='key', full_name='ListEntitiesSwitchResponse.key', index=1, - number=2, type=7, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='name', full_name='ListEntitiesSwitchResponse.name', index=2, - number=3, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='unique_id', full_name='ListEntitiesSwitchResponse.unique_id', index=3, - number=4, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='icon', full_name='ListEntitiesSwitchResponse.icon', index=4, - number=5, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='optimistic', full_name='ListEntitiesSwitchResponse.optimistic', index=5, - number=6, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1373, - serialized_end=1500, + name="ListEntitiesSwitchResponse", + full_name="ListEntitiesSwitchResponse", + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name="object_id", + full_name="ListEntitiesSwitchResponse.object_id", + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=_b("").decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="key", + full_name="ListEntitiesSwitchResponse.key", + index=1, + number=2, + type=7, + cpp_type=3, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="name", + full_name="ListEntitiesSwitchResponse.name", + index=2, + number=3, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=_b("").decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="unique_id", + full_name="ListEntitiesSwitchResponse.unique_id", + index=3, + number=4, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=_b("").decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="icon", + full_name="ListEntitiesSwitchResponse.icon", + index=4, + number=5, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=_b("").decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="optimistic", + full_name="ListEntitiesSwitchResponse.optimistic", + index=5, + number=6, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=1373, + serialized_end=1500, ) _LISTENTITIESTEXTSENSORRESPONSE = _descriptor.Descriptor( - name='ListEntitiesTextSensorResponse', - full_name='ListEntitiesTextSensorResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='object_id', full_name='ListEntitiesTextSensorResponse.object_id', index=0, - number=1, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='key', full_name='ListEntitiesTextSensorResponse.key', index=1, - number=2, type=7, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='name', full_name='ListEntitiesTextSensorResponse.name', index=2, - number=3, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='unique_id', full_name='ListEntitiesTextSensorResponse.unique_id', index=3, - number=4, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='icon', full_name='ListEntitiesTextSensorResponse.icon', index=4, - number=5, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1502, - serialized_end=1613, + name="ListEntitiesTextSensorResponse", + full_name="ListEntitiesTextSensorResponse", + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name="object_id", + full_name="ListEntitiesTextSensorResponse.object_id", + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=_b("").decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="key", + full_name="ListEntitiesTextSensorResponse.key", + index=1, + number=2, + type=7, + cpp_type=3, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="name", + full_name="ListEntitiesTextSensorResponse.name", + index=2, + number=3, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=_b("").decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="unique_id", + full_name="ListEntitiesTextSensorResponse.unique_id", + index=3, + number=4, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=_b("").decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="icon", + full_name="ListEntitiesTextSensorResponse.icon", + index=4, + number=5, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=_b("").decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=1502, + serialized_end=1613, ) _LISTENTITIESDONERESPONSE = _descriptor.Descriptor( - name='ListEntitiesDoneResponse', - full_name='ListEntitiesDoneResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1615, - serialized_end=1641, + name="ListEntitiesDoneResponse", + full_name="ListEntitiesDoneResponse", + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=1615, + serialized_end=1641, ) _SUBSCRIBESTATESREQUEST = _descriptor.Descriptor( - name='SubscribeStatesRequest', - full_name='SubscribeStatesRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1643, - serialized_end=1667, + name="SubscribeStatesRequest", + full_name="SubscribeStatesRequest", + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=1643, + serialized_end=1667, ) _BINARYSENSORSTATERESPONSE = _descriptor.Descriptor( - name='BinarySensorStateResponse', - full_name='BinarySensorStateResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='key', full_name='BinarySensorStateResponse.key', index=0, - number=1, type=7, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='state', full_name='BinarySensorStateResponse.state', index=1, - number=2, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1669, - serialized_end=1724, + name="BinarySensorStateResponse", + full_name="BinarySensorStateResponse", + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name="key", + full_name="BinarySensorStateResponse.key", + index=0, + number=1, + type=7, + cpp_type=3, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="state", + full_name="BinarySensorStateResponse.state", + index=1, + number=2, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=1669, + serialized_end=1724, ) _COVERSTATERESPONSE = _descriptor.Descriptor( - name='CoverStateResponse', - full_name='CoverStateResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='key', full_name='CoverStateResponse.key', index=0, - number=1, type=7, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='state', full_name='CoverStateResponse.state', index=1, - number=2, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - _COVERSTATERESPONSE_COVERSTATE, - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1726, - serialized_end=1842, + name="CoverStateResponse", + full_name="CoverStateResponse", + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name="key", + full_name="CoverStateResponse.key", + index=0, + number=1, + type=7, + cpp_type=3, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="state", + full_name="CoverStateResponse.state", + index=1, + number=2, + type=14, + cpp_type=8, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + ], + extensions=[], + nested_types=[], + enum_types=[ + _COVERSTATERESPONSE_COVERSTATE, + ], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=1726, + serialized_end=1842, ) _FANSTATERESPONSE = _descriptor.Descriptor( - name='FanStateResponse', - full_name='FanStateResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='key', full_name='FanStateResponse.key', index=0, - number=1, type=7, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='state', full_name='FanStateResponse.state', index=1, - number=2, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='oscillating', full_name='FanStateResponse.oscillating', index=2, - number=3, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='speed', full_name='FanStateResponse.speed', index=3, - number=4, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1844, - serialized_end=1937, + name="FanStateResponse", + full_name="FanStateResponse", + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name="key", + full_name="FanStateResponse.key", + index=0, + number=1, + type=7, + cpp_type=3, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="state", + full_name="FanStateResponse.state", + index=1, + number=2, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="oscillating", + full_name="FanStateResponse.oscillating", + index=2, + number=3, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="speed", + full_name="FanStateResponse.speed", + index=3, + number=4, + type=14, + cpp_type=8, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=1844, + serialized_end=1937, ) _LIGHTSTATERESPONSE = _descriptor.Descriptor( - name='LightStateResponse', - full_name='LightStateResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='key', full_name='LightStateResponse.key', index=0, - number=1, type=7, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='state', full_name='LightStateResponse.state', index=1, - number=2, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='brightness', full_name='LightStateResponse.brightness', index=2, - number=3, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='red', full_name='LightStateResponse.red', index=3, - number=4, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='green', full_name='LightStateResponse.green', index=4, - number=5, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='blue', full_name='LightStateResponse.blue', index=5, - number=6, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='white', full_name='LightStateResponse.white', index=6, - number=7, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='color_temperature', full_name='LightStateResponse.color_temperature', index=7, - number=8, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='effect', full_name='LightStateResponse.effect', index=8, - number=9, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1940, - serialized_end=2108, + name="LightStateResponse", + full_name="LightStateResponse", + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name="key", + full_name="LightStateResponse.key", + index=0, + number=1, + type=7, + cpp_type=3, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="state", + full_name="LightStateResponse.state", + index=1, + number=2, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="brightness", + full_name="LightStateResponse.brightness", + index=2, + number=3, + type=2, + cpp_type=6, + label=1, + has_default_value=False, + default_value=float(0), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="red", + full_name="LightStateResponse.red", + index=3, + number=4, + type=2, + cpp_type=6, + label=1, + has_default_value=False, + default_value=float(0), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="green", + full_name="LightStateResponse.green", + index=4, + number=5, + type=2, + cpp_type=6, + label=1, + has_default_value=False, + default_value=float(0), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="blue", + full_name="LightStateResponse.blue", + index=5, + number=6, + type=2, + cpp_type=6, + label=1, + has_default_value=False, + default_value=float(0), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="white", + full_name="LightStateResponse.white", + index=6, + number=7, + type=2, + cpp_type=6, + label=1, + has_default_value=False, + default_value=float(0), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="color_temperature", + full_name="LightStateResponse.color_temperature", + index=7, + number=8, + type=2, + cpp_type=6, + label=1, + has_default_value=False, + default_value=float(0), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="effect", + full_name="LightStateResponse.effect", + index=8, + number=9, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=_b("").decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=1940, + serialized_end=2108, ) _SENSORSTATERESPONSE = _descriptor.Descriptor( - name='SensorStateResponse', - full_name='SensorStateResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='key', full_name='SensorStateResponse.key', index=0, - number=1, type=7, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='state', full_name='SensorStateResponse.state', index=1, - number=2, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2110, - serialized_end=2159, + name="SensorStateResponse", + full_name="SensorStateResponse", + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name="key", + full_name="SensorStateResponse.key", + index=0, + number=1, + type=7, + cpp_type=3, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="state", + full_name="SensorStateResponse.state", + index=1, + number=2, + type=2, + cpp_type=6, + label=1, + has_default_value=False, + default_value=float(0), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=2110, + serialized_end=2159, ) _SWITCHSTATERESPONSE = _descriptor.Descriptor( - name='SwitchStateResponse', - full_name='SwitchStateResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='key', full_name='SwitchStateResponse.key', index=0, - number=1, type=7, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='state', full_name='SwitchStateResponse.state', index=1, - number=2, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2161, - serialized_end=2210, + name="SwitchStateResponse", + full_name="SwitchStateResponse", + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name="key", + full_name="SwitchStateResponse.key", + index=0, + number=1, + type=7, + cpp_type=3, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="state", + full_name="SwitchStateResponse.state", + index=1, + number=2, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=2161, + serialized_end=2210, ) _TEXTSENSORSTATERESPONSE = _descriptor.Descriptor( - name='TextSensorStateResponse', - full_name='TextSensorStateResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='key', full_name='TextSensorStateResponse.key', index=0, - number=1, type=7, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='state', full_name='TextSensorStateResponse.state', index=1, - number=2, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2212, - serialized_end=2265, + name="TextSensorStateResponse", + full_name="TextSensorStateResponse", + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name="key", + full_name="TextSensorStateResponse.key", + index=0, + number=1, + type=7, + cpp_type=3, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="state", + full_name="TextSensorStateResponse.state", + index=1, + number=2, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=_b("").decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=2212, + serialized_end=2265, ) _COVERCOMMANDREQUEST = _descriptor.Descriptor( - name='CoverCommandRequest', - full_name='CoverCommandRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='key', full_name='CoverCommandRequest.key', index=0, - number=1, type=7, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='has_state', full_name='CoverCommandRequest.has_state', index=1, - number=2, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='command', full_name='CoverCommandRequest.command', index=2, - number=3, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - _COVERCOMMANDREQUEST_COVERCOMMAND, - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2268, - serialized_end=2420, + name="CoverCommandRequest", + full_name="CoverCommandRequest", + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name="key", + full_name="CoverCommandRequest.key", + index=0, + number=1, + type=7, + cpp_type=3, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="has_state", + full_name="CoverCommandRequest.has_state", + index=1, + number=2, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="command", + full_name="CoverCommandRequest.command", + index=2, + number=3, + type=14, + cpp_type=8, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + ], + extensions=[], + nested_types=[], + enum_types=[ + _COVERCOMMANDREQUEST_COVERCOMMAND, + ], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=2268, + serialized_end=2420, ) _FANCOMMANDREQUEST = _descriptor.Descriptor( - name='FanCommandRequest', - full_name='FanCommandRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='key', full_name='FanCommandRequest.key', index=0, - number=1, type=7, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='has_state', full_name='FanCommandRequest.has_state', index=1, - number=2, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='state', full_name='FanCommandRequest.state', index=2, - number=3, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='has_speed', full_name='FanCommandRequest.has_speed', index=3, - number=4, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='speed', full_name='FanCommandRequest.speed', index=4, - number=5, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='has_oscillating', full_name='FanCommandRequest.has_oscillating', index=5, - number=6, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='oscillating', full_name='FanCommandRequest.oscillating', index=6, - number=7, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2423, - serialized_end=2580, + name="FanCommandRequest", + full_name="FanCommandRequest", + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name="key", + full_name="FanCommandRequest.key", + index=0, + number=1, + type=7, + cpp_type=3, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="has_state", + full_name="FanCommandRequest.has_state", + index=1, + number=2, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="state", + full_name="FanCommandRequest.state", + index=2, + number=3, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="has_speed", + full_name="FanCommandRequest.has_speed", + index=3, + number=4, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="speed", + full_name="FanCommandRequest.speed", + index=4, + number=5, + type=14, + cpp_type=8, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="has_oscillating", + full_name="FanCommandRequest.has_oscillating", + index=5, + number=6, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="oscillating", + full_name="FanCommandRequest.oscillating", + index=6, + number=7, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=2423, + serialized_end=2580, ) _LIGHTCOMMANDREQUEST = _descriptor.Descriptor( - name='LightCommandRequest', - full_name='LightCommandRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='key', full_name='LightCommandRequest.key', index=0, - number=1, type=7, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='has_state', full_name='LightCommandRequest.has_state', index=1, - number=2, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='state', full_name='LightCommandRequest.state', index=2, - number=3, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='has_brightness', full_name='LightCommandRequest.has_brightness', index=3, - number=4, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='brightness', full_name='LightCommandRequest.brightness', index=4, - number=5, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='has_rgb', full_name='LightCommandRequest.has_rgb', index=5, - number=6, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='red', full_name='LightCommandRequest.red', index=6, - number=7, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='green', full_name='LightCommandRequest.green', index=7, - number=8, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='blue', full_name='LightCommandRequest.blue', index=8, - number=9, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='has_white', full_name='LightCommandRequest.has_white', index=9, - number=10, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='white', full_name='LightCommandRequest.white', index=10, - number=11, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='has_color_temperature', full_name='LightCommandRequest.has_color_temperature', index=11, - number=12, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='color_temperature', full_name='LightCommandRequest.color_temperature', index=12, - number=13, type=2, cpp_type=6, label=1, - has_default_value=False, default_value=float(0), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='has_transition_length', full_name='LightCommandRequest.has_transition_length', index=13, - number=14, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='transition_length', full_name='LightCommandRequest.transition_length', index=14, - number=15, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='has_flash_length', full_name='LightCommandRequest.has_flash_length', index=15, - number=16, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='flash_length', full_name='LightCommandRequest.flash_length', index=16, - number=17, type=13, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='has_effect', full_name='LightCommandRequest.has_effect', index=17, - number=18, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='effect', full_name='LightCommandRequest.effect', index=18, - number=19, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2583, - serialized_end=2988, + name="LightCommandRequest", + full_name="LightCommandRequest", + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name="key", + full_name="LightCommandRequest.key", + index=0, + number=1, + type=7, + cpp_type=3, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="has_state", + full_name="LightCommandRequest.has_state", + index=1, + number=2, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="state", + full_name="LightCommandRequest.state", + index=2, + number=3, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="has_brightness", + full_name="LightCommandRequest.has_brightness", + index=3, + number=4, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="brightness", + full_name="LightCommandRequest.brightness", + index=4, + number=5, + type=2, + cpp_type=6, + label=1, + has_default_value=False, + default_value=float(0), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="has_rgb", + full_name="LightCommandRequest.has_rgb", + index=5, + number=6, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="red", + full_name="LightCommandRequest.red", + index=6, + number=7, + type=2, + cpp_type=6, + label=1, + has_default_value=False, + default_value=float(0), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="green", + full_name="LightCommandRequest.green", + index=7, + number=8, + type=2, + cpp_type=6, + label=1, + has_default_value=False, + default_value=float(0), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="blue", + full_name="LightCommandRequest.blue", + index=8, + number=9, + type=2, + cpp_type=6, + label=1, + has_default_value=False, + default_value=float(0), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="has_white", + full_name="LightCommandRequest.has_white", + index=9, + number=10, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="white", + full_name="LightCommandRequest.white", + index=10, + number=11, + type=2, + cpp_type=6, + label=1, + has_default_value=False, + default_value=float(0), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="has_color_temperature", + full_name="LightCommandRequest.has_color_temperature", + index=11, + number=12, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="color_temperature", + full_name="LightCommandRequest.color_temperature", + index=12, + number=13, + type=2, + cpp_type=6, + label=1, + has_default_value=False, + default_value=float(0), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="has_transition_length", + full_name="LightCommandRequest.has_transition_length", + index=13, + number=14, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="transition_length", + full_name="LightCommandRequest.transition_length", + index=14, + number=15, + type=13, + cpp_type=3, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="has_flash_length", + full_name="LightCommandRequest.has_flash_length", + index=15, + number=16, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="flash_length", + full_name="LightCommandRequest.flash_length", + index=16, + number=17, + type=13, + cpp_type=3, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="has_effect", + full_name="LightCommandRequest.has_effect", + index=17, + number=18, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="effect", + full_name="LightCommandRequest.effect", + index=18, + number=19, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=_b("").decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=2583, + serialized_end=2988, ) _SWITCHCOMMANDREQUEST = _descriptor.Descriptor( - name='SwitchCommandRequest', - full_name='SwitchCommandRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='key', full_name='SwitchCommandRequest.key', index=0, - number=1, type=7, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='state', full_name='SwitchCommandRequest.state', index=1, - number=2, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2990, - serialized_end=3040, + name="SwitchCommandRequest", + full_name="SwitchCommandRequest", + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name="key", + full_name="SwitchCommandRequest.key", + index=0, + number=1, + type=7, + cpp_type=3, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="state", + full_name="SwitchCommandRequest.state", + index=1, + number=2, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=2990, + serialized_end=3040, ) _SUBSCRIBELOGSREQUEST = _descriptor.Descriptor( - name='SubscribeLogsRequest', - full_name='SubscribeLogsRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='level', full_name='SubscribeLogsRequest.level', index=0, - number=1, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='dump_config', full_name='SubscribeLogsRequest.dump_config', index=1, - number=2, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3042, - serialized_end=3111, + name="SubscribeLogsRequest", + full_name="SubscribeLogsRequest", + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name="level", + full_name="SubscribeLogsRequest.level", + index=0, + number=1, + type=14, + cpp_type=8, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="dump_config", + full_name="SubscribeLogsRequest.dump_config", + index=1, + number=2, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=3042, + serialized_end=3111, ) _SUBSCRIBELOGSRESPONSE = _descriptor.Descriptor( - name='SubscribeLogsResponse', - full_name='SubscribeLogsResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='level', full_name='SubscribeLogsResponse.level', index=0, - number=1, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='tag', full_name='SubscribeLogsResponse.tag', index=1, - number=2, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='message', full_name='SubscribeLogsResponse.message', index=2, - number=3, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='send_failed', full_name='SubscribeLogsResponse.send_failed', index=3, - number=4, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3113, - serialized_end=3213, + name="SubscribeLogsResponse", + full_name="SubscribeLogsResponse", + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name="level", + full_name="SubscribeLogsResponse.level", + index=0, + number=1, + type=14, + cpp_type=8, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="tag", + full_name="SubscribeLogsResponse.tag", + index=1, + number=2, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=_b("").decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="message", + full_name="SubscribeLogsResponse.message", + index=2, + number=3, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=_b("").decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="send_failed", + full_name="SubscribeLogsResponse.send_failed", + index=3, + number=4, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=3113, + serialized_end=3213, ) _SUBSCRIBESERVICECALLSREQUEST = _descriptor.Descriptor( - name='SubscribeServiceCallsRequest', - full_name='SubscribeServiceCallsRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3215, - serialized_end=3245, + name="SubscribeServiceCallsRequest", + full_name="SubscribeServiceCallsRequest", + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=3215, + serialized_end=3245, ) _SERVICECALLRESPONSE_DATAENTRY = _descriptor.Descriptor( - name='DataEntry', - full_name='ServiceCallResponse.DataEntry', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='key', full_name='ServiceCallResponse.DataEntry.key', index=0, - number=1, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='value', full_name='ServiceCallResponse.DataEntry.value', index=1, - number=2, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=_b('8\001'), - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3453, - serialized_end=3496, + name="DataEntry", + full_name="ServiceCallResponse.DataEntry", + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name="key", + full_name="ServiceCallResponse.DataEntry.key", + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=_b("").decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="value", + full_name="ServiceCallResponse.DataEntry.value", + index=1, + number=2, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=_b("").decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=_b("8\001"), + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=3453, + serialized_end=3496, ) _SERVICECALLRESPONSE_DATATEMPLATEENTRY = _descriptor.Descriptor( - name='DataTemplateEntry', - full_name='ServiceCallResponse.DataTemplateEntry', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='key', full_name='ServiceCallResponse.DataTemplateEntry.key', index=0, - number=1, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='value', full_name='ServiceCallResponse.DataTemplateEntry.value', index=1, - number=2, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=_b('8\001'), - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3498, - serialized_end=3549, + name="DataTemplateEntry", + full_name="ServiceCallResponse.DataTemplateEntry", + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name="key", + full_name="ServiceCallResponse.DataTemplateEntry.key", + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=_b("").decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="value", + full_name="ServiceCallResponse.DataTemplateEntry.value", + index=1, + number=2, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=_b("").decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=_b("8\001"), + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=3498, + serialized_end=3549, ) _SERVICECALLRESPONSE_VARIABLESENTRY = _descriptor.Descriptor( - name='VariablesEntry', - full_name='ServiceCallResponse.VariablesEntry', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='key', full_name='ServiceCallResponse.VariablesEntry.key', index=0, - number=1, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='value', full_name='ServiceCallResponse.VariablesEntry.value', index=1, - number=2, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=_b('8\001'), - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3551, - serialized_end=3599, + name="VariablesEntry", + full_name="ServiceCallResponse.VariablesEntry", + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name="key", + full_name="ServiceCallResponse.VariablesEntry.key", + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=_b("").decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="value", + full_name="ServiceCallResponse.VariablesEntry.value", + index=1, + number=2, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=_b("").decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=_b("8\001"), + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=3551, + serialized_end=3599, ) _SERVICECALLRESPONSE = _descriptor.Descriptor( - name='ServiceCallResponse', - full_name='ServiceCallResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='service', full_name='ServiceCallResponse.service', index=0, - number=1, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='data', full_name='ServiceCallResponse.data', index=1, - number=2, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='data_template', full_name='ServiceCallResponse.data_template', index=2, - number=3, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='variables', full_name='ServiceCallResponse.variables', index=3, - number=4, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[_SERVICECALLRESPONSE_DATAENTRY, _SERVICECALLRESPONSE_DATATEMPLATEENTRY, _SERVICECALLRESPONSE_VARIABLESENTRY, ], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3248, - serialized_end=3599, + name="ServiceCallResponse", + full_name="ServiceCallResponse", + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name="service", + full_name="ServiceCallResponse.service", + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=_b("").decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="data", + full_name="ServiceCallResponse.data", + index=1, + number=2, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="data_template", + full_name="ServiceCallResponse.data_template", + index=2, + number=3, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="variables", + full_name="ServiceCallResponse.variables", + index=3, + number=4, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + ], + extensions=[], + nested_types=[ + _SERVICECALLRESPONSE_DATAENTRY, + _SERVICECALLRESPONSE_DATATEMPLATEENTRY, + _SERVICECALLRESPONSE_VARIABLESENTRY, + ], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=3248, + serialized_end=3599, ) _SUBSCRIBEHOMEASSISTANTSTATESREQUEST = _descriptor.Descriptor( - name='SubscribeHomeAssistantStatesRequest', - full_name='SubscribeHomeAssistantStatesRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3601, - serialized_end=3638, + name="SubscribeHomeAssistantStatesRequest", + full_name="SubscribeHomeAssistantStatesRequest", + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=3601, + serialized_end=3638, ) _SUBSCRIBEHOMEASSISTANTSTATERESPONSE = _descriptor.Descriptor( - name='SubscribeHomeAssistantStateResponse', - full_name='SubscribeHomeAssistantStateResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='entity_id', full_name='SubscribeHomeAssistantStateResponse.entity_id', index=0, - number=1, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3640, - serialized_end=3696, + name="SubscribeHomeAssistantStateResponse", + full_name="SubscribeHomeAssistantStateResponse", + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name="entity_id", + full_name="SubscribeHomeAssistantStateResponse.entity_id", + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=_b("").decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=3640, + serialized_end=3696, ) _HOMEASSISTANTSTATERESPONSE = _descriptor.Descriptor( - name='HomeAssistantStateResponse', - full_name='HomeAssistantStateResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='entity_id', full_name='HomeAssistantStateResponse.entity_id', index=0, - number=1, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='state', full_name='HomeAssistantStateResponse.state', index=1, - number=2, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3698, - serialized_end=3760, + name="HomeAssistantStateResponse", + full_name="HomeAssistantStateResponse", + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name="entity_id", + full_name="HomeAssistantStateResponse.entity_id", + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=_b("").decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + _descriptor.FieldDescriptor( + name="state", + full_name="HomeAssistantStateResponse.state", + index=1, + number=2, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=_b("").decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=3698, + serialized_end=3760, ) _GETTIMEREQUEST = _descriptor.Descriptor( - name='GetTimeRequest', - full_name='GetTimeRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3762, - serialized_end=3778, + name="GetTimeRequest", + full_name="GetTimeRequest", + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=3762, + serialized_end=3778, ) _GETTIMERESPONSE = _descriptor.Descriptor( - name='GetTimeResponse', - full_name='GetTimeResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='epoch_seconds', full_name='GetTimeResponse.epoch_seconds', index=0, - number=1, type=7, cpp_type=3, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - serialized_options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=3780, - serialized_end=3820, + name="GetTimeResponse", + full_name="GetTimeResponse", + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name="epoch_seconds", + full_name="GetTimeResponse.epoch_seconds", + index=0, + number=1, + type=7, + cpp_type=3, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=3780, + serialized_end=3820, ) -_COVERSTATERESPONSE.fields_by_name['state'].enum_type = _COVERSTATERESPONSE_COVERSTATE +_COVERSTATERESPONSE.fields_by_name["state"].enum_type = _COVERSTATERESPONSE_COVERSTATE _COVERSTATERESPONSE_COVERSTATE.containing_type = _COVERSTATERESPONSE -_FANSTATERESPONSE.fields_by_name['speed'].enum_type = _FANSPEED -_COVERCOMMANDREQUEST.fields_by_name['command'].enum_type = _COVERCOMMANDREQUEST_COVERCOMMAND +_FANSTATERESPONSE.fields_by_name["speed"].enum_type = _FANSPEED +_COVERCOMMANDREQUEST.fields_by_name[ + "command" +].enum_type = _COVERCOMMANDREQUEST_COVERCOMMAND _COVERCOMMANDREQUEST_COVERCOMMAND.containing_type = _COVERCOMMANDREQUEST -_FANCOMMANDREQUEST.fields_by_name['speed'].enum_type = _FANSPEED -_SUBSCRIBELOGSREQUEST.fields_by_name['level'].enum_type = _LOGLEVEL -_SUBSCRIBELOGSRESPONSE.fields_by_name['level'].enum_type = _LOGLEVEL +_FANCOMMANDREQUEST.fields_by_name["speed"].enum_type = _FANSPEED +_SUBSCRIBELOGSREQUEST.fields_by_name["level"].enum_type = _LOGLEVEL +_SUBSCRIBELOGSRESPONSE.fields_by_name["level"].enum_type = _LOGLEVEL _SERVICECALLRESPONSE_DATAENTRY.containing_type = _SERVICECALLRESPONSE _SERVICECALLRESPONSE_DATATEMPLATEENTRY.containing_type = _SERVICECALLRESPONSE _SERVICECALLRESPONSE_VARIABLESENTRY.containing_type = _SERVICECALLRESPONSE -_SERVICECALLRESPONSE.fields_by_name['data'].message_type = _SERVICECALLRESPONSE_DATAENTRY -_SERVICECALLRESPONSE.fields_by_name['data_template'].message_type = _SERVICECALLRESPONSE_DATATEMPLATEENTRY -_SERVICECALLRESPONSE.fields_by_name['variables'].message_type = _SERVICECALLRESPONSE_VARIABLESENTRY -DESCRIPTOR.message_types_by_name['HelloRequest'] = _HELLOREQUEST -DESCRIPTOR.message_types_by_name['HelloResponse'] = _HELLORESPONSE -DESCRIPTOR.message_types_by_name['ConnectRequest'] = _CONNECTREQUEST -DESCRIPTOR.message_types_by_name['ConnectResponse'] = _CONNECTRESPONSE -DESCRIPTOR.message_types_by_name['DisconnectRequest'] = _DISCONNECTREQUEST -DESCRIPTOR.message_types_by_name['DisconnectResponse'] = _DISCONNECTRESPONSE -DESCRIPTOR.message_types_by_name['PingRequest'] = _PINGREQUEST -DESCRIPTOR.message_types_by_name['PingResponse'] = _PINGRESPONSE -DESCRIPTOR.message_types_by_name['DeviceInfoRequest'] = _DEVICEINFOREQUEST -DESCRIPTOR.message_types_by_name['DeviceInfoResponse'] = _DEVICEINFORESPONSE -DESCRIPTOR.message_types_by_name['ListEntitiesRequest'] = _LISTENTITIESREQUEST -DESCRIPTOR.message_types_by_name['ListEntitiesBinarySensorResponse'] = _LISTENTITIESBINARYSENSORRESPONSE -DESCRIPTOR.message_types_by_name['ListEntitiesCoverResponse'] = _LISTENTITIESCOVERRESPONSE -DESCRIPTOR.message_types_by_name['ListEntitiesFanResponse'] = _LISTENTITIESFANRESPONSE -DESCRIPTOR.message_types_by_name['ListEntitiesLightResponse'] = _LISTENTITIESLIGHTRESPONSE -DESCRIPTOR.message_types_by_name['ListEntitiesSensorResponse'] = _LISTENTITIESSENSORRESPONSE -DESCRIPTOR.message_types_by_name['ListEntitiesSwitchResponse'] = _LISTENTITIESSWITCHRESPONSE -DESCRIPTOR.message_types_by_name['ListEntitiesTextSensorResponse'] = _LISTENTITIESTEXTSENSORRESPONSE -DESCRIPTOR.message_types_by_name['ListEntitiesDoneResponse'] = _LISTENTITIESDONERESPONSE -DESCRIPTOR.message_types_by_name['SubscribeStatesRequest'] = _SUBSCRIBESTATESREQUEST -DESCRIPTOR.message_types_by_name['BinarySensorStateResponse'] = _BINARYSENSORSTATERESPONSE -DESCRIPTOR.message_types_by_name['CoverStateResponse'] = _COVERSTATERESPONSE -DESCRIPTOR.message_types_by_name['FanStateResponse'] = _FANSTATERESPONSE -DESCRIPTOR.message_types_by_name['LightStateResponse'] = _LIGHTSTATERESPONSE -DESCRIPTOR.message_types_by_name['SensorStateResponse'] = _SENSORSTATERESPONSE -DESCRIPTOR.message_types_by_name['SwitchStateResponse'] = _SWITCHSTATERESPONSE -DESCRIPTOR.message_types_by_name['TextSensorStateResponse'] = _TEXTSENSORSTATERESPONSE -DESCRIPTOR.message_types_by_name['CoverCommandRequest'] = _COVERCOMMANDREQUEST -DESCRIPTOR.message_types_by_name['FanCommandRequest'] = _FANCOMMANDREQUEST -DESCRIPTOR.message_types_by_name['LightCommandRequest'] = _LIGHTCOMMANDREQUEST -DESCRIPTOR.message_types_by_name['SwitchCommandRequest'] = _SWITCHCOMMANDREQUEST -DESCRIPTOR.message_types_by_name['SubscribeLogsRequest'] = _SUBSCRIBELOGSREQUEST -DESCRIPTOR.message_types_by_name['SubscribeLogsResponse'] = _SUBSCRIBELOGSRESPONSE -DESCRIPTOR.message_types_by_name['SubscribeServiceCallsRequest'] = _SUBSCRIBESERVICECALLSREQUEST -DESCRIPTOR.message_types_by_name['ServiceCallResponse'] = _SERVICECALLRESPONSE -DESCRIPTOR.message_types_by_name['SubscribeHomeAssistantStatesRequest'] = _SUBSCRIBEHOMEASSISTANTSTATESREQUEST -DESCRIPTOR.message_types_by_name['SubscribeHomeAssistantStateResponse'] = _SUBSCRIBEHOMEASSISTANTSTATERESPONSE -DESCRIPTOR.message_types_by_name['HomeAssistantStateResponse'] = _HOMEASSISTANTSTATERESPONSE -DESCRIPTOR.message_types_by_name['GetTimeRequest'] = _GETTIMEREQUEST -DESCRIPTOR.message_types_by_name['GetTimeResponse'] = _GETTIMERESPONSE -DESCRIPTOR.enum_types_by_name['FanSpeed'] = _FANSPEED -DESCRIPTOR.enum_types_by_name['LogLevel'] = _LOGLEVEL +_SERVICECALLRESPONSE.fields_by_name[ + "data" +].message_type = _SERVICECALLRESPONSE_DATAENTRY +_SERVICECALLRESPONSE.fields_by_name[ + "data_template" +].message_type = _SERVICECALLRESPONSE_DATATEMPLATEENTRY +_SERVICECALLRESPONSE.fields_by_name[ + "variables" +].message_type = _SERVICECALLRESPONSE_VARIABLESENTRY +DESCRIPTOR.message_types_by_name["HelloRequest"] = _HELLOREQUEST +DESCRIPTOR.message_types_by_name["HelloResponse"] = _HELLORESPONSE +DESCRIPTOR.message_types_by_name["ConnectRequest"] = _CONNECTREQUEST +DESCRIPTOR.message_types_by_name["ConnectResponse"] = _CONNECTRESPONSE +DESCRIPTOR.message_types_by_name["DisconnectRequest"] = _DISCONNECTREQUEST +DESCRIPTOR.message_types_by_name["DisconnectResponse"] = _DISCONNECTRESPONSE +DESCRIPTOR.message_types_by_name["PingRequest"] = _PINGREQUEST +DESCRIPTOR.message_types_by_name["PingResponse"] = _PINGRESPONSE +DESCRIPTOR.message_types_by_name["DeviceInfoRequest"] = _DEVICEINFOREQUEST +DESCRIPTOR.message_types_by_name["DeviceInfoResponse"] = _DEVICEINFORESPONSE +DESCRIPTOR.message_types_by_name["ListEntitiesRequest"] = _LISTENTITIESREQUEST +DESCRIPTOR.message_types_by_name[ + "ListEntitiesBinarySensorResponse" +] = _LISTENTITIESBINARYSENSORRESPONSE +DESCRIPTOR.message_types_by_name[ + "ListEntitiesCoverResponse" +] = _LISTENTITIESCOVERRESPONSE +DESCRIPTOR.message_types_by_name["ListEntitiesFanResponse"] = _LISTENTITIESFANRESPONSE +DESCRIPTOR.message_types_by_name[ + "ListEntitiesLightResponse" +] = _LISTENTITIESLIGHTRESPONSE +DESCRIPTOR.message_types_by_name[ + "ListEntitiesSensorResponse" +] = _LISTENTITIESSENSORRESPONSE +DESCRIPTOR.message_types_by_name[ + "ListEntitiesSwitchResponse" +] = _LISTENTITIESSWITCHRESPONSE +DESCRIPTOR.message_types_by_name[ + "ListEntitiesTextSensorResponse" +] = _LISTENTITIESTEXTSENSORRESPONSE +DESCRIPTOR.message_types_by_name["ListEntitiesDoneResponse"] = _LISTENTITIESDONERESPONSE +DESCRIPTOR.message_types_by_name["SubscribeStatesRequest"] = _SUBSCRIBESTATESREQUEST +DESCRIPTOR.message_types_by_name[ + "BinarySensorStateResponse" +] = _BINARYSENSORSTATERESPONSE +DESCRIPTOR.message_types_by_name["CoverStateResponse"] = _COVERSTATERESPONSE +DESCRIPTOR.message_types_by_name["FanStateResponse"] = _FANSTATERESPONSE +DESCRIPTOR.message_types_by_name["LightStateResponse"] = _LIGHTSTATERESPONSE +DESCRIPTOR.message_types_by_name["SensorStateResponse"] = _SENSORSTATERESPONSE +DESCRIPTOR.message_types_by_name["SwitchStateResponse"] = _SWITCHSTATERESPONSE +DESCRIPTOR.message_types_by_name["TextSensorStateResponse"] = _TEXTSENSORSTATERESPONSE +DESCRIPTOR.message_types_by_name["CoverCommandRequest"] = _COVERCOMMANDREQUEST +DESCRIPTOR.message_types_by_name["FanCommandRequest"] = _FANCOMMANDREQUEST +DESCRIPTOR.message_types_by_name["LightCommandRequest"] = _LIGHTCOMMANDREQUEST +DESCRIPTOR.message_types_by_name["SwitchCommandRequest"] = _SWITCHCOMMANDREQUEST +DESCRIPTOR.message_types_by_name["SubscribeLogsRequest"] = _SUBSCRIBELOGSREQUEST +DESCRIPTOR.message_types_by_name["SubscribeLogsResponse"] = _SUBSCRIBELOGSRESPONSE +DESCRIPTOR.message_types_by_name[ + "SubscribeServiceCallsRequest" +] = _SUBSCRIBESERVICECALLSREQUEST +DESCRIPTOR.message_types_by_name["ServiceCallResponse"] = _SERVICECALLRESPONSE +DESCRIPTOR.message_types_by_name[ + "SubscribeHomeAssistantStatesRequest" +] = _SUBSCRIBEHOMEASSISTANTSTATESREQUEST +DESCRIPTOR.message_types_by_name[ + "SubscribeHomeAssistantStateResponse" +] = _SUBSCRIBEHOMEASSISTANTSTATERESPONSE +DESCRIPTOR.message_types_by_name[ + "HomeAssistantStateResponse" +] = _HOMEASSISTANTSTATERESPONSE +DESCRIPTOR.message_types_by_name["GetTimeRequest"] = _GETTIMEREQUEST +DESCRIPTOR.message_types_by_name["GetTimeResponse"] = _GETTIMERESPONSE +DESCRIPTOR.enum_types_by_name["FanSpeed"] = _FANSPEED +DESCRIPTOR.enum_types_by_name["LogLevel"] = _LOGLEVEL _sym_db.RegisterFileDescriptor(DESCRIPTOR) -HelloRequest = _reflection.GeneratedProtocolMessageType('HelloRequest', (_message.Message,), dict( - DESCRIPTOR = _HELLOREQUEST, - __module__ = 'api_pb2' - # @@protoc_insertion_point(class_scope:HelloRequest) - )) +HelloRequest = _reflection.GeneratedProtocolMessageType( + "HelloRequest", + (_message.Message,), + dict( + DESCRIPTOR=_HELLOREQUEST, + __module__="api_pb2" + # @@protoc_insertion_point(class_scope:HelloRequest) + ), +) _sym_db.RegisterMessage(HelloRequest) -HelloResponse = _reflection.GeneratedProtocolMessageType('HelloResponse', (_message.Message,), dict( - DESCRIPTOR = _HELLORESPONSE, - __module__ = 'api_pb2' - # @@protoc_insertion_point(class_scope:HelloResponse) - )) +HelloResponse = _reflection.GeneratedProtocolMessageType( + "HelloResponse", + (_message.Message,), + dict( + DESCRIPTOR=_HELLORESPONSE, + __module__="api_pb2" + # @@protoc_insertion_point(class_scope:HelloResponse) + ), +) _sym_db.RegisterMessage(HelloResponse) -ConnectRequest = _reflection.GeneratedProtocolMessageType('ConnectRequest', (_message.Message,), dict( - DESCRIPTOR = _CONNECTREQUEST, - __module__ = 'api_pb2' - # @@protoc_insertion_point(class_scope:ConnectRequest) - )) +ConnectRequest = _reflection.GeneratedProtocolMessageType( + "ConnectRequest", + (_message.Message,), + dict( + DESCRIPTOR=_CONNECTREQUEST, + __module__="api_pb2" + # @@protoc_insertion_point(class_scope:ConnectRequest) + ), +) _sym_db.RegisterMessage(ConnectRequest) -ConnectResponse = _reflection.GeneratedProtocolMessageType('ConnectResponse', (_message.Message,), dict( - DESCRIPTOR = _CONNECTRESPONSE, - __module__ = 'api_pb2' - # @@protoc_insertion_point(class_scope:ConnectResponse) - )) +ConnectResponse = _reflection.GeneratedProtocolMessageType( + "ConnectResponse", + (_message.Message,), + dict( + DESCRIPTOR=_CONNECTRESPONSE, + __module__="api_pb2" + # @@protoc_insertion_point(class_scope:ConnectResponse) + ), +) _sym_db.RegisterMessage(ConnectResponse) -DisconnectRequest = _reflection.GeneratedProtocolMessageType('DisconnectRequest', (_message.Message,), dict( - DESCRIPTOR = _DISCONNECTREQUEST, - __module__ = 'api_pb2' - # @@protoc_insertion_point(class_scope:DisconnectRequest) - )) +DisconnectRequest = _reflection.GeneratedProtocolMessageType( + "DisconnectRequest", + (_message.Message,), + dict( + DESCRIPTOR=_DISCONNECTREQUEST, + __module__="api_pb2" + # @@protoc_insertion_point(class_scope:DisconnectRequest) + ), +) _sym_db.RegisterMessage(DisconnectRequest) -DisconnectResponse = _reflection.GeneratedProtocolMessageType('DisconnectResponse', (_message.Message,), dict( - DESCRIPTOR = _DISCONNECTRESPONSE, - __module__ = 'api_pb2' - # @@protoc_insertion_point(class_scope:DisconnectResponse) - )) +DisconnectResponse = _reflection.GeneratedProtocolMessageType( + "DisconnectResponse", + (_message.Message,), + dict( + DESCRIPTOR=_DISCONNECTRESPONSE, + __module__="api_pb2" + # @@protoc_insertion_point(class_scope:DisconnectResponse) + ), +) _sym_db.RegisterMessage(DisconnectResponse) -PingRequest = _reflection.GeneratedProtocolMessageType('PingRequest', (_message.Message,), dict( - DESCRIPTOR = _PINGREQUEST, - __module__ = 'api_pb2' - # @@protoc_insertion_point(class_scope:PingRequest) - )) +PingRequest = _reflection.GeneratedProtocolMessageType( + "PingRequest", + (_message.Message,), + dict( + DESCRIPTOR=_PINGREQUEST, + __module__="api_pb2" + # @@protoc_insertion_point(class_scope:PingRequest) + ), +) _sym_db.RegisterMessage(PingRequest) -PingResponse = _reflection.GeneratedProtocolMessageType('PingResponse', (_message.Message,), dict( - DESCRIPTOR = _PINGRESPONSE, - __module__ = 'api_pb2' - # @@protoc_insertion_point(class_scope:PingResponse) - )) +PingResponse = _reflection.GeneratedProtocolMessageType( + "PingResponse", + (_message.Message,), + dict( + DESCRIPTOR=_PINGRESPONSE, + __module__="api_pb2" + # @@protoc_insertion_point(class_scope:PingResponse) + ), +) _sym_db.RegisterMessage(PingResponse) -DeviceInfoRequest = _reflection.GeneratedProtocolMessageType('DeviceInfoRequest', (_message.Message,), dict( - DESCRIPTOR = _DEVICEINFOREQUEST, - __module__ = 'api_pb2' - # @@protoc_insertion_point(class_scope:DeviceInfoRequest) - )) +DeviceInfoRequest = _reflection.GeneratedProtocolMessageType( + "DeviceInfoRequest", + (_message.Message,), + dict( + DESCRIPTOR=_DEVICEINFOREQUEST, + __module__="api_pb2" + # @@protoc_insertion_point(class_scope:DeviceInfoRequest) + ), +) _sym_db.RegisterMessage(DeviceInfoRequest) -DeviceInfoResponse = _reflection.GeneratedProtocolMessageType('DeviceInfoResponse', (_message.Message,), dict( - DESCRIPTOR = _DEVICEINFORESPONSE, - __module__ = 'api_pb2' - # @@protoc_insertion_point(class_scope:DeviceInfoResponse) - )) +DeviceInfoResponse = _reflection.GeneratedProtocolMessageType( + "DeviceInfoResponse", + (_message.Message,), + dict( + DESCRIPTOR=_DEVICEINFORESPONSE, + __module__="api_pb2" + # @@protoc_insertion_point(class_scope:DeviceInfoResponse) + ), +) _sym_db.RegisterMessage(DeviceInfoResponse) -ListEntitiesRequest = _reflection.GeneratedProtocolMessageType('ListEntitiesRequest', (_message.Message,), dict( - DESCRIPTOR = _LISTENTITIESREQUEST, - __module__ = 'api_pb2' - # @@protoc_insertion_point(class_scope:ListEntitiesRequest) - )) +ListEntitiesRequest = _reflection.GeneratedProtocolMessageType( + "ListEntitiesRequest", + (_message.Message,), + dict( + DESCRIPTOR=_LISTENTITIESREQUEST, + __module__="api_pb2" + # @@protoc_insertion_point(class_scope:ListEntitiesRequest) + ), +) _sym_db.RegisterMessage(ListEntitiesRequest) -ListEntitiesBinarySensorResponse = _reflection.GeneratedProtocolMessageType('ListEntitiesBinarySensorResponse', (_message.Message,), dict( - DESCRIPTOR = _LISTENTITIESBINARYSENSORRESPONSE, - __module__ = 'api_pb2' - # @@protoc_insertion_point(class_scope:ListEntitiesBinarySensorResponse) - )) +ListEntitiesBinarySensorResponse = _reflection.GeneratedProtocolMessageType( + "ListEntitiesBinarySensorResponse", + (_message.Message,), + dict( + DESCRIPTOR=_LISTENTITIESBINARYSENSORRESPONSE, + __module__="api_pb2" + # @@protoc_insertion_point(class_scope:ListEntitiesBinarySensorResponse) + ), +) _sym_db.RegisterMessage(ListEntitiesBinarySensorResponse) -ListEntitiesCoverResponse = _reflection.GeneratedProtocolMessageType('ListEntitiesCoverResponse', (_message.Message,), dict( - DESCRIPTOR = _LISTENTITIESCOVERRESPONSE, - __module__ = 'api_pb2' - # @@protoc_insertion_point(class_scope:ListEntitiesCoverResponse) - )) +ListEntitiesCoverResponse = _reflection.GeneratedProtocolMessageType( + "ListEntitiesCoverResponse", + (_message.Message,), + dict( + DESCRIPTOR=_LISTENTITIESCOVERRESPONSE, + __module__="api_pb2" + # @@protoc_insertion_point(class_scope:ListEntitiesCoverResponse) + ), +) _sym_db.RegisterMessage(ListEntitiesCoverResponse) -ListEntitiesFanResponse = _reflection.GeneratedProtocolMessageType('ListEntitiesFanResponse', (_message.Message,), dict( - DESCRIPTOR = _LISTENTITIESFANRESPONSE, - __module__ = 'api_pb2' - # @@protoc_insertion_point(class_scope:ListEntitiesFanResponse) - )) +ListEntitiesFanResponse = _reflection.GeneratedProtocolMessageType( + "ListEntitiesFanResponse", + (_message.Message,), + dict( + DESCRIPTOR=_LISTENTITIESFANRESPONSE, + __module__="api_pb2" + # @@protoc_insertion_point(class_scope:ListEntitiesFanResponse) + ), +) _sym_db.RegisterMessage(ListEntitiesFanResponse) -ListEntitiesLightResponse = _reflection.GeneratedProtocolMessageType('ListEntitiesLightResponse', (_message.Message,), dict( - DESCRIPTOR = _LISTENTITIESLIGHTRESPONSE, - __module__ = 'api_pb2' - # @@protoc_insertion_point(class_scope:ListEntitiesLightResponse) - )) +ListEntitiesLightResponse = _reflection.GeneratedProtocolMessageType( + "ListEntitiesLightResponse", + (_message.Message,), + dict( + DESCRIPTOR=_LISTENTITIESLIGHTRESPONSE, + __module__="api_pb2" + # @@protoc_insertion_point(class_scope:ListEntitiesLightResponse) + ), +) _sym_db.RegisterMessage(ListEntitiesLightResponse) -ListEntitiesSensorResponse = _reflection.GeneratedProtocolMessageType('ListEntitiesSensorResponse', (_message.Message,), dict( - DESCRIPTOR = _LISTENTITIESSENSORRESPONSE, - __module__ = 'api_pb2' - # @@protoc_insertion_point(class_scope:ListEntitiesSensorResponse) - )) +ListEntitiesSensorResponse = _reflection.GeneratedProtocolMessageType( + "ListEntitiesSensorResponse", + (_message.Message,), + dict( + DESCRIPTOR=_LISTENTITIESSENSORRESPONSE, + __module__="api_pb2" + # @@protoc_insertion_point(class_scope:ListEntitiesSensorResponse) + ), +) _sym_db.RegisterMessage(ListEntitiesSensorResponse) -ListEntitiesSwitchResponse = _reflection.GeneratedProtocolMessageType('ListEntitiesSwitchResponse', (_message.Message,), dict( - DESCRIPTOR = _LISTENTITIESSWITCHRESPONSE, - __module__ = 'api_pb2' - # @@protoc_insertion_point(class_scope:ListEntitiesSwitchResponse) - )) +ListEntitiesSwitchResponse = _reflection.GeneratedProtocolMessageType( + "ListEntitiesSwitchResponse", + (_message.Message,), + dict( + DESCRIPTOR=_LISTENTITIESSWITCHRESPONSE, + __module__="api_pb2" + # @@protoc_insertion_point(class_scope:ListEntitiesSwitchResponse) + ), +) _sym_db.RegisterMessage(ListEntitiesSwitchResponse) -ListEntitiesTextSensorResponse = _reflection.GeneratedProtocolMessageType('ListEntitiesTextSensorResponse', (_message.Message,), dict( - DESCRIPTOR = _LISTENTITIESTEXTSENSORRESPONSE, - __module__ = 'api_pb2' - # @@protoc_insertion_point(class_scope:ListEntitiesTextSensorResponse) - )) +ListEntitiesTextSensorResponse = _reflection.GeneratedProtocolMessageType( + "ListEntitiesTextSensorResponse", + (_message.Message,), + dict( + DESCRIPTOR=_LISTENTITIESTEXTSENSORRESPONSE, + __module__="api_pb2" + # @@protoc_insertion_point(class_scope:ListEntitiesTextSensorResponse) + ), +) _sym_db.RegisterMessage(ListEntitiesTextSensorResponse) -ListEntitiesDoneResponse = _reflection.GeneratedProtocolMessageType('ListEntitiesDoneResponse', (_message.Message,), dict( - DESCRIPTOR = _LISTENTITIESDONERESPONSE, - __module__ = 'api_pb2' - # @@protoc_insertion_point(class_scope:ListEntitiesDoneResponse) - )) +ListEntitiesDoneResponse = _reflection.GeneratedProtocolMessageType( + "ListEntitiesDoneResponse", + (_message.Message,), + dict( + DESCRIPTOR=_LISTENTITIESDONERESPONSE, + __module__="api_pb2" + # @@protoc_insertion_point(class_scope:ListEntitiesDoneResponse) + ), +) _sym_db.RegisterMessage(ListEntitiesDoneResponse) -SubscribeStatesRequest = _reflection.GeneratedProtocolMessageType('SubscribeStatesRequest', (_message.Message,), dict( - DESCRIPTOR = _SUBSCRIBESTATESREQUEST, - __module__ = 'api_pb2' - # @@protoc_insertion_point(class_scope:SubscribeStatesRequest) - )) +SubscribeStatesRequest = _reflection.GeneratedProtocolMessageType( + "SubscribeStatesRequest", + (_message.Message,), + dict( + DESCRIPTOR=_SUBSCRIBESTATESREQUEST, + __module__="api_pb2" + # @@protoc_insertion_point(class_scope:SubscribeStatesRequest) + ), +) _sym_db.RegisterMessage(SubscribeStatesRequest) -BinarySensorStateResponse = _reflection.GeneratedProtocolMessageType('BinarySensorStateResponse', (_message.Message,), dict( - DESCRIPTOR = _BINARYSENSORSTATERESPONSE, - __module__ = 'api_pb2' - # @@protoc_insertion_point(class_scope:BinarySensorStateResponse) - )) +BinarySensorStateResponse = _reflection.GeneratedProtocolMessageType( + "BinarySensorStateResponse", + (_message.Message,), + dict( + DESCRIPTOR=_BINARYSENSORSTATERESPONSE, + __module__="api_pb2" + # @@protoc_insertion_point(class_scope:BinarySensorStateResponse) + ), +) _sym_db.RegisterMessage(BinarySensorStateResponse) -CoverStateResponse = _reflection.GeneratedProtocolMessageType('CoverStateResponse', (_message.Message,), dict( - DESCRIPTOR = _COVERSTATERESPONSE, - __module__ = 'api_pb2' - # @@protoc_insertion_point(class_scope:CoverStateResponse) - )) +CoverStateResponse = _reflection.GeneratedProtocolMessageType( + "CoverStateResponse", + (_message.Message,), + dict( + DESCRIPTOR=_COVERSTATERESPONSE, + __module__="api_pb2" + # @@protoc_insertion_point(class_scope:CoverStateResponse) + ), +) _sym_db.RegisterMessage(CoverStateResponse) -FanStateResponse = _reflection.GeneratedProtocolMessageType('FanStateResponse', (_message.Message,), dict( - DESCRIPTOR = _FANSTATERESPONSE, - __module__ = 'api_pb2' - # @@protoc_insertion_point(class_scope:FanStateResponse) - )) +FanStateResponse = _reflection.GeneratedProtocolMessageType( + "FanStateResponse", + (_message.Message,), + dict( + DESCRIPTOR=_FANSTATERESPONSE, + __module__="api_pb2" + # @@protoc_insertion_point(class_scope:FanStateResponse) + ), +) _sym_db.RegisterMessage(FanStateResponse) -LightStateResponse = _reflection.GeneratedProtocolMessageType('LightStateResponse', (_message.Message,), dict( - DESCRIPTOR = _LIGHTSTATERESPONSE, - __module__ = 'api_pb2' - # @@protoc_insertion_point(class_scope:LightStateResponse) - )) +LightStateResponse = _reflection.GeneratedProtocolMessageType( + "LightStateResponse", + (_message.Message,), + dict( + DESCRIPTOR=_LIGHTSTATERESPONSE, + __module__="api_pb2" + # @@protoc_insertion_point(class_scope:LightStateResponse) + ), +) _sym_db.RegisterMessage(LightStateResponse) -SensorStateResponse = _reflection.GeneratedProtocolMessageType('SensorStateResponse', (_message.Message,), dict( - DESCRIPTOR = _SENSORSTATERESPONSE, - __module__ = 'api_pb2' - # @@protoc_insertion_point(class_scope:SensorStateResponse) - )) +SensorStateResponse = _reflection.GeneratedProtocolMessageType( + "SensorStateResponse", + (_message.Message,), + dict( + DESCRIPTOR=_SENSORSTATERESPONSE, + __module__="api_pb2" + # @@protoc_insertion_point(class_scope:SensorStateResponse) + ), +) _sym_db.RegisterMessage(SensorStateResponse) -SwitchStateResponse = _reflection.GeneratedProtocolMessageType('SwitchStateResponse', (_message.Message,), dict( - DESCRIPTOR = _SWITCHSTATERESPONSE, - __module__ = 'api_pb2' - # @@protoc_insertion_point(class_scope:SwitchStateResponse) - )) +SwitchStateResponse = _reflection.GeneratedProtocolMessageType( + "SwitchStateResponse", + (_message.Message,), + dict( + DESCRIPTOR=_SWITCHSTATERESPONSE, + __module__="api_pb2" + # @@protoc_insertion_point(class_scope:SwitchStateResponse) + ), +) _sym_db.RegisterMessage(SwitchStateResponse) -TextSensorStateResponse = _reflection.GeneratedProtocolMessageType('TextSensorStateResponse', (_message.Message,), dict( - DESCRIPTOR = _TEXTSENSORSTATERESPONSE, - __module__ = 'api_pb2' - # @@protoc_insertion_point(class_scope:TextSensorStateResponse) - )) +TextSensorStateResponse = _reflection.GeneratedProtocolMessageType( + "TextSensorStateResponse", + (_message.Message,), + dict( + DESCRIPTOR=_TEXTSENSORSTATERESPONSE, + __module__="api_pb2" + # @@protoc_insertion_point(class_scope:TextSensorStateResponse) + ), +) _sym_db.RegisterMessage(TextSensorStateResponse) -CoverCommandRequest = _reflection.GeneratedProtocolMessageType('CoverCommandRequest', (_message.Message,), dict( - DESCRIPTOR = _COVERCOMMANDREQUEST, - __module__ = 'api_pb2' - # @@protoc_insertion_point(class_scope:CoverCommandRequest) - )) +CoverCommandRequest = _reflection.GeneratedProtocolMessageType( + "CoverCommandRequest", + (_message.Message,), + dict( + DESCRIPTOR=_COVERCOMMANDREQUEST, + __module__="api_pb2" + # @@protoc_insertion_point(class_scope:CoverCommandRequest) + ), +) _sym_db.RegisterMessage(CoverCommandRequest) -FanCommandRequest = _reflection.GeneratedProtocolMessageType('FanCommandRequest', (_message.Message,), dict( - DESCRIPTOR = _FANCOMMANDREQUEST, - __module__ = 'api_pb2' - # @@protoc_insertion_point(class_scope:FanCommandRequest) - )) +FanCommandRequest = _reflection.GeneratedProtocolMessageType( + "FanCommandRequest", + (_message.Message,), + dict( + DESCRIPTOR=_FANCOMMANDREQUEST, + __module__="api_pb2" + # @@protoc_insertion_point(class_scope:FanCommandRequest) + ), +) _sym_db.RegisterMessage(FanCommandRequest) -LightCommandRequest = _reflection.GeneratedProtocolMessageType('LightCommandRequest', (_message.Message,), dict( - DESCRIPTOR = _LIGHTCOMMANDREQUEST, - __module__ = 'api_pb2' - # @@protoc_insertion_point(class_scope:LightCommandRequest) - )) +LightCommandRequest = _reflection.GeneratedProtocolMessageType( + "LightCommandRequest", + (_message.Message,), + dict( + DESCRIPTOR=_LIGHTCOMMANDREQUEST, + __module__="api_pb2" + # @@protoc_insertion_point(class_scope:LightCommandRequest) + ), +) _sym_db.RegisterMessage(LightCommandRequest) -SwitchCommandRequest = _reflection.GeneratedProtocolMessageType('SwitchCommandRequest', (_message.Message,), dict( - DESCRIPTOR = _SWITCHCOMMANDREQUEST, - __module__ = 'api_pb2' - # @@protoc_insertion_point(class_scope:SwitchCommandRequest) - )) +SwitchCommandRequest = _reflection.GeneratedProtocolMessageType( + "SwitchCommandRequest", + (_message.Message,), + dict( + DESCRIPTOR=_SWITCHCOMMANDREQUEST, + __module__="api_pb2" + # @@protoc_insertion_point(class_scope:SwitchCommandRequest) + ), +) _sym_db.RegisterMessage(SwitchCommandRequest) -SubscribeLogsRequest = _reflection.GeneratedProtocolMessageType('SubscribeLogsRequest', (_message.Message,), dict( - DESCRIPTOR = _SUBSCRIBELOGSREQUEST, - __module__ = 'api_pb2' - # @@protoc_insertion_point(class_scope:SubscribeLogsRequest) - )) +SubscribeLogsRequest = _reflection.GeneratedProtocolMessageType( + "SubscribeLogsRequest", + (_message.Message,), + dict( + DESCRIPTOR=_SUBSCRIBELOGSREQUEST, + __module__="api_pb2" + # @@protoc_insertion_point(class_scope:SubscribeLogsRequest) + ), +) _sym_db.RegisterMessage(SubscribeLogsRequest) -SubscribeLogsResponse = _reflection.GeneratedProtocolMessageType('SubscribeLogsResponse', (_message.Message,), dict( - DESCRIPTOR = _SUBSCRIBELOGSRESPONSE, - __module__ = 'api_pb2' - # @@protoc_insertion_point(class_scope:SubscribeLogsResponse) - )) +SubscribeLogsResponse = _reflection.GeneratedProtocolMessageType( + "SubscribeLogsResponse", + (_message.Message,), + dict( + DESCRIPTOR=_SUBSCRIBELOGSRESPONSE, + __module__="api_pb2" + # @@protoc_insertion_point(class_scope:SubscribeLogsResponse) + ), +) _sym_db.RegisterMessage(SubscribeLogsResponse) -SubscribeServiceCallsRequest = _reflection.GeneratedProtocolMessageType('SubscribeServiceCallsRequest', (_message.Message,), dict( - DESCRIPTOR = _SUBSCRIBESERVICECALLSREQUEST, - __module__ = 'api_pb2' - # @@protoc_insertion_point(class_scope:SubscribeServiceCallsRequest) - )) +SubscribeServiceCallsRequest = _reflection.GeneratedProtocolMessageType( + "SubscribeServiceCallsRequest", + (_message.Message,), + dict( + DESCRIPTOR=_SUBSCRIBESERVICECALLSREQUEST, + __module__="api_pb2" + # @@protoc_insertion_point(class_scope:SubscribeServiceCallsRequest) + ), +) _sym_db.RegisterMessage(SubscribeServiceCallsRequest) -ServiceCallResponse = _reflection.GeneratedProtocolMessageType('ServiceCallResponse', (_message.Message,), dict( - - DataEntry = _reflection.GeneratedProtocolMessageType('DataEntry', (_message.Message,), dict( - DESCRIPTOR = _SERVICECALLRESPONSE_DATAENTRY, - __module__ = 'api_pb2' - # @@protoc_insertion_point(class_scope:ServiceCallResponse.DataEntry) - )) - , - - DataTemplateEntry = _reflection.GeneratedProtocolMessageType('DataTemplateEntry', (_message.Message,), dict( - DESCRIPTOR = _SERVICECALLRESPONSE_DATATEMPLATEENTRY, - __module__ = 'api_pb2' - # @@protoc_insertion_point(class_scope:ServiceCallResponse.DataTemplateEntry) - )) - , - - VariablesEntry = _reflection.GeneratedProtocolMessageType('VariablesEntry', (_message.Message,), dict( - DESCRIPTOR = _SERVICECALLRESPONSE_VARIABLESENTRY, - __module__ = 'api_pb2' - # @@protoc_insertion_point(class_scope:ServiceCallResponse.VariablesEntry) - )) - , - DESCRIPTOR = _SERVICECALLRESPONSE, - __module__ = 'api_pb2' - # @@protoc_insertion_point(class_scope:ServiceCallResponse) - )) +ServiceCallResponse = _reflection.GeneratedProtocolMessageType( + "ServiceCallResponse", + (_message.Message,), + dict( + DataEntry=_reflection.GeneratedProtocolMessageType( + "DataEntry", + (_message.Message,), + dict( + DESCRIPTOR=_SERVICECALLRESPONSE_DATAENTRY, + __module__="api_pb2" + # @@protoc_insertion_point(class_scope:ServiceCallResponse.DataEntry) + ), + ), + DataTemplateEntry=_reflection.GeneratedProtocolMessageType( + "DataTemplateEntry", + (_message.Message,), + dict( + DESCRIPTOR=_SERVICECALLRESPONSE_DATATEMPLATEENTRY, + __module__="api_pb2" + # @@protoc_insertion_point(class_scope:ServiceCallResponse.DataTemplateEntry) + ), + ), + VariablesEntry=_reflection.GeneratedProtocolMessageType( + "VariablesEntry", + (_message.Message,), + dict( + DESCRIPTOR=_SERVICECALLRESPONSE_VARIABLESENTRY, + __module__="api_pb2" + # @@protoc_insertion_point(class_scope:ServiceCallResponse.VariablesEntry) + ), + ), + DESCRIPTOR=_SERVICECALLRESPONSE, + __module__="api_pb2" + # @@protoc_insertion_point(class_scope:ServiceCallResponse) + ), +) _sym_db.RegisterMessage(ServiceCallResponse) _sym_db.RegisterMessage(ServiceCallResponse.DataEntry) _sym_db.RegisterMessage(ServiceCallResponse.DataTemplateEntry) _sym_db.RegisterMessage(ServiceCallResponse.VariablesEntry) -SubscribeHomeAssistantStatesRequest = _reflection.GeneratedProtocolMessageType('SubscribeHomeAssistantStatesRequest', (_message.Message,), dict( - DESCRIPTOR = _SUBSCRIBEHOMEASSISTANTSTATESREQUEST, - __module__ = 'api_pb2' - # @@protoc_insertion_point(class_scope:SubscribeHomeAssistantStatesRequest) - )) +SubscribeHomeAssistantStatesRequest = _reflection.GeneratedProtocolMessageType( + "SubscribeHomeAssistantStatesRequest", + (_message.Message,), + dict( + DESCRIPTOR=_SUBSCRIBEHOMEASSISTANTSTATESREQUEST, + __module__="api_pb2" + # @@protoc_insertion_point(class_scope:SubscribeHomeAssistantStatesRequest) + ), +) _sym_db.RegisterMessage(SubscribeHomeAssistantStatesRequest) -SubscribeHomeAssistantStateResponse = _reflection.GeneratedProtocolMessageType('SubscribeHomeAssistantStateResponse', (_message.Message,), dict( - DESCRIPTOR = _SUBSCRIBEHOMEASSISTANTSTATERESPONSE, - __module__ = 'api_pb2' - # @@protoc_insertion_point(class_scope:SubscribeHomeAssistantStateResponse) - )) +SubscribeHomeAssistantStateResponse = _reflection.GeneratedProtocolMessageType( + "SubscribeHomeAssistantStateResponse", + (_message.Message,), + dict( + DESCRIPTOR=_SUBSCRIBEHOMEASSISTANTSTATERESPONSE, + __module__="api_pb2" + # @@protoc_insertion_point(class_scope:SubscribeHomeAssistantStateResponse) + ), +) _sym_db.RegisterMessage(SubscribeHomeAssistantStateResponse) -HomeAssistantStateResponse = _reflection.GeneratedProtocolMessageType('HomeAssistantStateResponse', (_message.Message,), dict( - DESCRIPTOR = _HOMEASSISTANTSTATERESPONSE, - __module__ = 'api_pb2' - # @@protoc_insertion_point(class_scope:HomeAssistantStateResponse) - )) +HomeAssistantStateResponse = _reflection.GeneratedProtocolMessageType( + "HomeAssistantStateResponse", + (_message.Message,), + dict( + DESCRIPTOR=_HOMEASSISTANTSTATERESPONSE, + __module__="api_pb2" + # @@protoc_insertion_point(class_scope:HomeAssistantStateResponse) + ), +) _sym_db.RegisterMessage(HomeAssistantStateResponse) -GetTimeRequest = _reflection.GeneratedProtocolMessageType('GetTimeRequest', (_message.Message,), dict( - DESCRIPTOR = _GETTIMEREQUEST, - __module__ = 'api_pb2' - # @@protoc_insertion_point(class_scope:GetTimeRequest) - )) +GetTimeRequest = _reflection.GeneratedProtocolMessageType( + "GetTimeRequest", + (_message.Message,), + dict( + DESCRIPTOR=_GETTIMEREQUEST, + __module__="api_pb2" + # @@protoc_insertion_point(class_scope:GetTimeRequest) + ), +) _sym_db.RegisterMessage(GetTimeRequest) -GetTimeResponse = _reflection.GeneratedProtocolMessageType('GetTimeResponse', (_message.Message,), dict( - DESCRIPTOR = _GETTIMERESPONSE, - __module__ = 'api_pb2' - # @@protoc_insertion_point(class_scope:GetTimeResponse) - )) +GetTimeResponse = _reflection.GeneratedProtocolMessageType( + "GetTimeResponse", + (_message.Message,), + dict( + DESCRIPTOR=_GETTIMERESPONSE, + __module__="api_pb2" + # @@protoc_insertion_point(class_scope:GetTimeResponse) + ), +) _sym_db.RegisterMessage(GetTimeResponse) diff --git a/esphome/api/client.py b/esphome/api/client.py index a32c239819..84c9890fe0 100644 --- a/esphome/api/client.py +++ b/esphome/api/client.py @@ -177,10 +177,14 @@ class APIClient(threading.Thread): try: ip = resolve_ip_address(self._address) except EsphomeError as err: - _LOGGER.warning("Error resolving IP address of %s. Is it connected to WiFi?", - self._address) - _LOGGER.warning("(If this error persists, please set a static IP address: " - "https://esphome.io/components/wifi.html#manual-ips)") + _LOGGER.warning( + "Error resolving IP address of %s. Is it connected to WiFi?", + self._address, + ) + _LOGGER.warning( + "(If this error persists, please set a static IP address: " + "https://esphome.io/components/wifi.html#manual-ips)" + ) raise APIConnectionError(err) from err _LOGGER.info("Connecting to %s:%s (%s)", self._address, self._port, ip) @@ -198,14 +202,19 @@ class APIClient(threading.Thread): self._socket_open_event.set() hello = pb.HelloRequest() - hello.client_info = f'ESPHome v{const.__version__}' + hello.client_info = f"ESPHome v{const.__version__}" try: resp = self._send_message_await_response(hello, pb.HelloResponse) except APIConnectionError as err: self._fatal_error(err) raise err - _LOGGER.debug("Successfully connected to %s ('%s' API=%s.%s)", self._address, - resp.server_info, resp.api_version_major, resp.api_version_minor) + _LOGGER.debug( + "Successfully connected to %s ('%s' API=%s.%s)", + self._address, + resp.server_info, + resp.api_version_major, + resp.api_version_minor, + ) self._connected = True self._refresh_ping() if self.on_connect is not None: @@ -270,7 +279,9 @@ class APIClient(threading.Thread): req += encoded self._write(req) - def _send_message_await_response_complex(self, send_msg, do_append, do_stop, timeout=5): + def _send_message_await_response_complex( + self, send_msg, do_append, do_stop, timeout=5 + ): event = threading.Event() responses = [] @@ -295,12 +306,15 @@ class APIClient(threading.Thread): def is_response(msg): return isinstance(msg, response_type) - return self._send_message_await_response_complex(send_msg, is_response, is_response, - timeout)[0] + return self._send_message_await_response_complex( + send_msg, is_response, is_response, timeout + )[0] def device_info(self): self._check_connected() - return self._send_message_await_response(pb.DeviceInfoRequest(), pb.DeviceInfoResponse) + return self._send_message_await_response( + pb.DeviceInfoRequest(), pb.DeviceInfoResponse + ) def ping(self): self._check_connected() @@ -310,7 +324,9 @@ class APIClient(threading.Thread): self._check_connected() try: - self._send_message_await_response(pb.DisconnectRequest(), pb.DisconnectResponse) + self._send_message_await_response( + pb.DisconnectRequest(), pb.DisconnectResponse + ) except APIConnectionError: pass self._close_socket() @@ -415,7 +431,7 @@ class APIClient(threading.Thread): def run_logs(config, address): - conf = config['api'] + conf = config["api"] port = conf[CONF_PORT] password = conf[CONF_PASSWORD] _LOGGER.info("Starting log output from %s using esphome API", address) @@ -447,24 +463,35 @@ def run_logs(config, address): _LOGGER.info("Successfully connected to %s", address) return - wait_time = int(min(1.5**min(tries, 100), 30)) + wait_time = int(min(1.5 ** min(tries, 100), 30)) if not has_connects: - _LOGGER.warning("Initial connection failed. The ESP might not be connected " - "to WiFi yet (%s). Re-Trying in %s seconds", - error, wait_time) + _LOGGER.warning( + "Initial connection failed. The ESP might not be connected " + "to WiFi yet (%s). Re-Trying in %s seconds", + error, + wait_time, + ) else: - _LOGGER.warning("Couldn't connect to API (%s). Trying to reconnect in %s seconds", - error, wait_time) - timer = threading.Timer(wait_time, functools.partial(try_connect, None, tries + 1)) + _LOGGER.warning( + "Couldn't connect to API (%s). Trying to reconnect in %s seconds", + error, + wait_time, + ) + timer = threading.Timer( + wait_time, functools.partial(try_connect, None, tries + 1) + ) timer.start() retry_timer.append(timer) def on_log(msg): - time_ = datetime.now().time().strftime('[%H:%M:%S]') + time_ = datetime.now().time().strftime("[%H:%M:%S]") text = msg.message if msg.send_failed: - text = color('white', '(Message skipped because it was too big to fit in ' - 'TCP buffer - This is only cosmetic)') + text = color( + "white", + "(Message skipped because it was too big to fit in " + "TCP buffer - This is only cosmetic)", + ) safe_print(time_ + text) def on_login(): diff --git a/esphome/automation.py b/esphome/automation.py index 4b5e39b0f5..63e4ce0372 100644 --- a/esphome/automation.py +++ b/esphome/automation.py @@ -1,7 +1,15 @@ import esphome.codegen as cg import esphome.config_validation as cv -from esphome.const import CONF_AUTOMATION_ID, CONF_CONDITION, CONF_ELSE, CONF_ID, CONF_THEN, \ - CONF_TRIGGER_ID, CONF_TYPE_ID, CONF_TIME +from esphome.const import ( + CONF_AUTOMATION_ID, + CONF_CONDITION, + CONF_ELSE, + CONF_ID, + CONF_THEN, + CONF_TRIGGER_ID, + CONF_TYPE_ID, + CONF_TIME, +) from esphome.core import coroutine from esphome.util import Registry @@ -30,36 +38,34 @@ def register_condition(name, condition_type, schema): return CONDITION_REGISTRY.register(name, condition_type, schema) -Action = cg.esphome_ns.class_('Action') -Trigger = cg.esphome_ns.class_('Trigger') +Action = cg.esphome_ns.class_("Action") +Trigger = cg.esphome_ns.class_("Trigger") ACTION_REGISTRY = Registry() -Condition = cg.esphome_ns.class_('Condition') +Condition = cg.esphome_ns.class_("Condition") CONDITION_REGISTRY = Registry() -validate_action = cv.validate_registry_entry('action', ACTION_REGISTRY) -validate_action_list = cv.validate_registry('action', ACTION_REGISTRY) -validate_condition = cv.validate_registry_entry('condition', CONDITION_REGISTRY) -validate_condition_list = cv.validate_registry('condition', CONDITION_REGISTRY) +validate_action = cv.validate_registry_entry("action", ACTION_REGISTRY) +validate_action_list = cv.validate_registry("action", ACTION_REGISTRY) +validate_condition = cv.validate_registry_entry("condition", CONDITION_REGISTRY) +validate_condition_list = cv.validate_registry("condition", CONDITION_REGISTRY) def validate_potentially_and_condition(value): if isinstance(value, list): - with cv.remove_prepend_path(['and']): - return validate_condition({ - 'and': value - }) + with cv.remove_prepend_path(["and"]): + return validate_condition({"and": value}) return validate_condition(value) -DelayAction = cg.esphome_ns.class_('DelayAction', Action, cg.Component) -LambdaAction = cg.esphome_ns.class_('LambdaAction', Action) -IfAction = cg.esphome_ns.class_('IfAction', Action) -WhileAction = cg.esphome_ns.class_('WhileAction', Action) -WaitUntilAction = cg.esphome_ns.class_('WaitUntilAction', Action, cg.Component) -UpdateComponentAction = cg.esphome_ns.class_('UpdateComponentAction', Action) -Automation = cg.esphome_ns.class_('Automation') +DelayAction = cg.esphome_ns.class_("DelayAction", Action, cg.Component) +LambdaAction = cg.esphome_ns.class_("LambdaAction", Action) +IfAction = cg.esphome_ns.class_("IfAction", Action) +WhileAction = cg.esphome_ns.class_("WhileAction", Action) +WaitUntilAction = cg.esphome_ns.class_("WaitUntilAction", Action, cg.Component) +UpdateComponentAction = cg.esphome_ns.class_("UpdateComponentAction", Action) +Automation = cg.esphome_ns.class_("Automation") -LambdaCondition = cg.esphome_ns.class_('LambdaCondition', Condition) -ForCondition = cg.esphome_ns.class_('ForCondition', Condition, cg.Component) +LambdaCondition = cg.esphome_ns.class_("LambdaCondition", Condition) +ForCondition = cg.esphome_ns.class_("ForCondition", Condition, cg.Component) def validate_automation(extra_schema=None, extra_validators=None, single=False): @@ -83,10 +89,10 @@ def validate_automation(extra_schema=None, extra_validators=None, single=False): try: return cv.Schema([schema])(value) except cv.Invalid as err2: - if 'extra keys not allowed' in str(err2) and len(err2.path) == 2: + if "extra keys not allowed" in str(err2) and len(err2.path) == 2: # pylint: disable=raise-missing-from raise err - if 'Unable to find action' in str(err): + if "Unable to find action" in str(err): raise err2 raise cv.MultipleInvalid([err, err2]) elif isinstance(value, dict): @@ -110,47 +116,59 @@ def validate_automation(extra_schema=None, extra_validators=None, single=False): return validator -AUTOMATION_SCHEMA = cv.Schema({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(Trigger), - cv.GenerateID(CONF_AUTOMATION_ID): cv.declare_id(Automation), - cv.Required(CONF_THEN): validate_action_list, -}) +AUTOMATION_SCHEMA = cv.Schema( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(Trigger), + cv.GenerateID(CONF_AUTOMATION_ID): cv.declare_id(Automation), + cv.Required(CONF_THEN): validate_action_list, + } +) -AndCondition = cg.esphome_ns.class_('AndCondition', Condition) -OrCondition = cg.esphome_ns.class_('OrCondition', Condition) -NotCondition = cg.esphome_ns.class_('NotCondition', Condition) +AndCondition = cg.esphome_ns.class_("AndCondition", Condition) +OrCondition = cg.esphome_ns.class_("OrCondition", Condition) +NotCondition = cg.esphome_ns.class_("NotCondition", Condition) -@register_condition('and', AndCondition, validate_condition_list) +@register_condition("and", AndCondition, validate_condition_list) def and_condition_to_code(config, condition_id, template_arg, args): conditions = yield build_condition_list(config, template_arg, args) yield cg.new_Pvariable(condition_id, template_arg, conditions) -@register_condition('or', OrCondition, validate_condition_list) +@register_condition("or", OrCondition, validate_condition_list) def or_condition_to_code(config, condition_id, template_arg, args): conditions = yield build_condition_list(config, template_arg, args) yield cg.new_Pvariable(condition_id, template_arg, conditions) -@register_condition('not', NotCondition, validate_potentially_and_condition) +@register_condition("not", NotCondition, validate_potentially_and_condition) def not_condition_to_code(config, condition_id, template_arg, args): condition = yield build_condition(config, template_arg, args) yield cg.new_Pvariable(condition_id, template_arg, condition) -@register_condition('lambda', LambdaCondition, cv.lambda_) +@register_condition("lambda", LambdaCondition, cv.lambda_) def lambda_condition_to_code(config, condition_id, template_arg, args): lambda_ = yield cg.process_lambda(config, args, return_type=bool) yield cg.new_Pvariable(condition_id, template_arg, lambda_) -@register_condition('for', ForCondition, cv.Schema({ - cv.Required(CONF_TIME): cv.templatable(cv.positive_time_period_milliseconds), - cv.Required(CONF_CONDITION): validate_potentially_and_condition, -}).extend(cv.COMPONENT_SCHEMA)) +@register_condition( + "for", + ForCondition, + cv.Schema( + { + cv.Required(CONF_TIME): cv.templatable( + cv.positive_time_period_milliseconds + ), + cv.Required(CONF_CONDITION): validate_potentially_and_condition, + } + ).extend(cv.COMPONENT_SCHEMA), +) def for_condition_to_code(config, condition_id, template_arg, args): - condition = yield build_condition(config[CONF_CONDITION], cg.TemplateArguments(), []) + condition = yield build_condition( + config[CONF_CONDITION], cg.TemplateArguments(), [] + ) var = cg.new_Pvariable(condition_id, template_arg, condition) yield cg.register_component(var, config) templ = yield cg.templatable(config[CONF_TIME], args, cg.uint32) @@ -158,7 +176,9 @@ def for_condition_to_code(config, condition_id, template_arg, args): yield var -@register_action('delay', DelayAction, cv.templatable(cv.positive_time_period_milliseconds)) +@register_action( + "delay", DelayAction, cv.templatable(cv.positive_time_period_milliseconds) +) def delay_action_to_code(config, action_id, template_arg, args): var = cg.new_Pvariable(action_id, template_arg) yield cg.register_component(var, {}) @@ -167,11 +187,18 @@ def delay_action_to_code(config, action_id, template_arg, args): yield var -@register_action('if', IfAction, cv.All({ - cv.Required(CONF_CONDITION): validate_potentially_and_condition, - cv.Optional(CONF_THEN): validate_action_list, - cv.Optional(CONF_ELSE): validate_action_list, -}, cv.has_at_least_one_key(CONF_THEN, CONF_ELSE))) +@register_action( + "if", + IfAction, + cv.All( + { + cv.Required(CONF_CONDITION): validate_potentially_and_condition, + cv.Optional(CONF_THEN): validate_action_list, + cv.Optional(CONF_ELSE): validate_action_list, + }, + cv.has_at_least_one_key(CONF_THEN, CONF_ELSE), + ), +) def if_action_to_code(config, action_id, template_arg, args): conditions = yield build_condition(config[CONF_CONDITION], template_arg, args) var = cg.new_Pvariable(action_id, template_arg, conditions) @@ -184,10 +211,16 @@ def if_action_to_code(config, action_id, template_arg, args): yield var -@register_action('while', WhileAction, cv.Schema({ - cv.Required(CONF_CONDITION): validate_potentially_and_condition, - cv.Required(CONF_THEN): validate_action_list, -})) +@register_action( + "while", + WhileAction, + cv.Schema( + { + cv.Required(CONF_CONDITION): validate_potentially_and_condition, + cv.Required(CONF_THEN): validate_action_list, + } + ), +) def while_action_to_code(config, action_id, template_arg, args): conditions = yield build_condition(config[CONF_CONDITION], template_arg, args) var = cg.new_Pvariable(action_id, template_arg, conditions) @@ -197,15 +230,17 @@ def while_action_to_code(config, action_id, template_arg, args): def validate_wait_until(value): - schema = cv.Schema({ - cv.Required(CONF_CONDITION): validate_potentially_and_condition, - }) + schema = cv.Schema( + { + cv.Required(CONF_CONDITION): validate_potentially_and_condition, + } + ) if isinstance(value, dict) and CONF_CONDITION in value: return schema(value) return validate_wait_until({CONF_CONDITION: value}) -@register_action('wait_until', WaitUntilAction, validate_wait_until) +@register_action("wait_until", WaitUntilAction, validate_wait_until) def wait_until_action_to_code(config, action_id, template_arg, args): conditions = yield build_condition(config[CONF_CONDITION], template_arg, args) var = cg.new_Pvariable(action_id, template_arg, conditions) @@ -213,15 +248,21 @@ def wait_until_action_to_code(config, action_id, template_arg, args): yield var -@register_action('lambda', LambdaAction, cv.lambda_) +@register_action("lambda", LambdaAction, cv.lambda_) def lambda_action_to_code(config, action_id, template_arg, args): lambda_ = yield cg.process_lambda(config, args, return_type=cg.void) yield cg.new_Pvariable(action_id, template_arg, lambda_) -@register_action('component.update', UpdateComponentAction, maybe_simple_id({ - cv.Required(CONF_ID): cv.use_id(cg.PollingComponent), -})) +@register_action( + "component.update", + UpdateComponentAction, + maybe_simple_id( + { + cv.Required(CONF_ID): cv.use_id(cg.PollingComponent), + } + ), +) def component_update_action_to_code(config, action_id, template_arg, args): comp = yield cg.get_variable(config[CONF_ID]) yield cg.new_Pvariable(action_id, template_arg, comp) @@ -229,7 +270,9 @@ def component_update_action_to_code(config, action_id, template_arg, args): @coroutine def build_action(full_config, template_arg, args): - registry_entry, config = cg.extract_registry_entry_config(ACTION_REGISTRY, full_config) + registry_entry, config = cg.extract_registry_entry_config( + ACTION_REGISTRY, full_config + ) action_id = full_config[CONF_TYPE_ID] builder = registry_entry.coroutine_fun yield builder(config, action_id, template_arg, args) @@ -246,7 +289,9 @@ def build_action_list(config, templ, arg_type): @coroutine def build_condition(full_config, template_arg, args): - registry_entry, config = cg.extract_registry_entry_config(CONDITION_REGISTRY, full_config) + registry_entry, config = cg.extract_registry_entry_config( + CONDITION_REGISTRY, full_config + ) action_id = full_config[CONF_TYPE_ID] builder = registry_entry.coroutine_fun yield builder(config, action_id, template_arg, args) diff --git a/esphome/codegen.py b/esphome/codegen.py index c30b43f952..57316ab4fa 100644 --- a/esphome/codegen.py +++ b/esphome/codegen.py @@ -9,18 +9,70 @@ # pylint: disable=unused-import from esphome.cpp_generator import ( # noqa - Expression, RawExpression, RawStatement, TemplateArguments, - StructInitializer, ArrayInitializer, safe_exp, Statement, LineComment, - progmem_array, statement, variable, Pvariable, new_Pvariable, - add, add_global, add_library, add_build_flag, add_define, - get_variable, get_variable_with_full_id, process_lambda, is_template, templatable, MockObj, - MockObjClass) + Expression, + RawExpression, + RawStatement, + TemplateArguments, + StructInitializer, + ArrayInitializer, + safe_exp, + Statement, + LineComment, + progmem_array, + statement, + variable, + Pvariable, + new_Pvariable, + add, + add_global, + add_library, + add_build_flag, + add_define, + get_variable, + get_variable_with_full_id, + process_lambda, + is_template, + templatable, + MockObj, + MockObjClass, +) from esphome.cpp_helpers import ( # noqa - gpio_pin_expression, register_component, build_registry_entry, - build_registry_list, extract_registry_entry_config, register_parented) + gpio_pin_expression, + register_component, + build_registry_entry, + build_registry_list, + extract_registry_entry_config, + register_parented, +) from esphome.cpp_types import ( # noqa - global_ns, void, nullptr, float_, double, bool_, int_, std_ns, std_string, - std_vector, uint8, uint16, uint32, int32, const_char_ptr, NAN, - esphome_ns, App, Nameable, Component, ComponentPtr, - PollingComponent, Application, optional, arduino_json_ns, JsonObject, - JsonObjectRef, JsonObjectConstRef, Controller, GPIOPin) + global_ns, + void, + nullptr, + float_, + double, + bool_, + int_, + std_ns, + std_string, + std_vector, + uint8, + uint16, + uint32, + int32, + const_char_ptr, + NAN, + esphome_ns, + App, + Nameable, + Component, + ComponentPtr, + PollingComponent, + Application, + optional, + arduino_json_ns, + JsonObject, + JsonObjectRef, + JsonObjectConstRef, + Controller, + GPIOPin, +) diff --git a/esphome/components/a4988/stepper.py b/esphome/components/a4988/stepper.py index 29696dbd5e..1de3562ff7 100644 --- a/esphome/components/a4988/stepper.py +++ b/esphome/components/a4988/stepper.py @@ -5,15 +5,17 @@ import esphome.codegen as cg from esphome.const import CONF_DIR_PIN, CONF_ID, CONF_SLEEP_PIN, CONF_STEP_PIN -a4988_ns = cg.esphome_ns.namespace('a4988') -A4988 = a4988_ns.class_('A4988', stepper.Stepper, cg.Component) +a4988_ns = cg.esphome_ns.namespace("a4988") +A4988 = a4988_ns.class_("A4988", stepper.Stepper, cg.Component) -CONFIG_SCHEMA = stepper.STEPPER_SCHEMA.extend({ - cv.Required(CONF_ID): cv.declare_id(A4988), - cv.Required(CONF_STEP_PIN): pins.gpio_output_pin_schema, - cv.Required(CONF_DIR_PIN): pins.gpio_output_pin_schema, - cv.Optional(CONF_SLEEP_PIN): pins.gpio_output_pin_schema, -}).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = stepper.STEPPER_SCHEMA.extend( + { + cv.Required(CONF_ID): cv.declare_id(A4988), + cv.Required(CONF_STEP_PIN): pins.gpio_output_pin_schema, + cv.Required(CONF_DIR_PIN): pins.gpio_output_pin_schema, + cv.Optional(CONF_SLEEP_PIN): pins.gpio_output_pin_schema, + } +).extend(cv.COMPONENT_SCHEMA) def to_code(config): diff --git a/esphome/components/ac_dimmer/output.py b/esphome/components/ac_dimmer/output.py index 17dcd8ac26..2f06a0b6fc 100644 --- a/esphome/components/ac_dimmer/output.py +++ b/esphome/components/ac_dimmer/output.py @@ -4,28 +4,32 @@ from esphome import pins from esphome.components import output from esphome.const import CONF_ID, CONF_MIN_POWER, CONF_METHOD -CODEOWNERS = ['@glmnet'] +CODEOWNERS = ["@glmnet"] -ac_dimmer_ns = cg.esphome_ns.namespace('ac_dimmer') -AcDimmer = ac_dimmer_ns.class_('AcDimmer', output.FloatOutput, cg.Component) +ac_dimmer_ns = cg.esphome_ns.namespace("ac_dimmer") +AcDimmer = ac_dimmer_ns.class_("AcDimmer", output.FloatOutput, cg.Component) -DimMethod = ac_dimmer_ns.enum('DimMethod') +DimMethod = ac_dimmer_ns.enum("DimMethod") DIM_METHODS = { - 'LEADING_PULSE': DimMethod.DIM_METHOD_LEADING_PULSE, - 'LEADING': DimMethod.DIM_METHOD_LEADING, - 'TRAILING': DimMethod.DIM_METHOD_TRAILING, + "LEADING_PULSE": DimMethod.DIM_METHOD_LEADING_PULSE, + "LEADING": DimMethod.DIM_METHOD_LEADING, + "TRAILING": DimMethod.DIM_METHOD_TRAILING, } -CONF_GATE_PIN = 'gate_pin' -CONF_ZERO_CROSS_PIN = 'zero_cross_pin' -CONF_INIT_WITH_HALF_CYCLE = 'init_with_half_cycle' -CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend({ - cv.Required(CONF_ID): cv.declare_id(AcDimmer), - cv.Required(CONF_GATE_PIN): pins.internal_gpio_output_pin_schema, - cv.Required(CONF_ZERO_CROSS_PIN): pins.internal_gpio_input_pin_schema, - cv.Optional(CONF_INIT_WITH_HALF_CYCLE, default=True): cv.boolean, - cv.Optional(CONF_METHOD, default='leading pulse'): cv.enum(DIM_METHODS, upper=True, space='_'), -}).extend(cv.COMPONENT_SCHEMA) +CONF_GATE_PIN = "gate_pin" +CONF_ZERO_CROSS_PIN = "zero_cross_pin" +CONF_INIT_WITH_HALF_CYCLE = "init_with_half_cycle" +CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend( + { + cv.Required(CONF_ID): cv.declare_id(AcDimmer), + cv.Required(CONF_GATE_PIN): pins.internal_gpio_output_pin_schema, + cv.Required(CONF_ZERO_CROSS_PIN): pins.internal_gpio_input_pin_schema, + cv.Optional(CONF_INIT_WITH_HALF_CYCLE, default=True): cv.boolean, + cv.Optional(CONF_METHOD, default="leading pulse"): cv.enum( + DIM_METHODS, upper=True, space="_" + ), + } +).extend(cv.COMPONENT_SCHEMA) def to_code(config): diff --git a/esphome/components/adalight/__init__.py b/esphome/components/adalight/__init__.py index 66fae17f1e..169a137165 100644 --- a/esphome/components/adalight/__init__.py +++ b/esphome/components/adalight/__init__.py @@ -5,18 +5,22 @@ from esphome.components.light.types import AddressableLightEffect from esphome.components.light.effects import register_addressable_effect from esphome.const import CONF_NAME, CONF_UART_ID -DEPENDENCIES = ['uart'] +DEPENDENCIES = ["uart"] -adalight_ns = cg.esphome_ns.namespace('adalight') +adalight_ns = cg.esphome_ns.namespace("adalight") AdalightLightEffect = adalight_ns.class_( - 'AdalightLightEffect', uart.UARTDevice, AddressableLightEffect) + "AdalightLightEffect", uart.UARTDevice, AddressableLightEffect +) CONFIG_SCHEMA = cv.Schema({}) -@register_addressable_effect('adalight', AdalightLightEffect, "Adalight", { - cv.GenerateID(CONF_UART_ID): cv.use_id(uart.UARTComponent) -}) +@register_addressable_effect( + "adalight", + AdalightLightEffect, + "Adalight", + {cv.GenerateID(CONF_UART_ID): cv.use_id(uart.UARTComponent)}, +) def adalight_light_effect_to_code(config, effect_id): effect = cg.new_Pvariable(effect_id, config[CONF_NAME]) yield uart.register_uart_device(effect, config) diff --git a/esphome/components/adc/__init__.py b/esphome/components/adc/__init__.py index 63db7aee2e..f70ffa9520 100644 --- a/esphome/components/adc/__init__.py +++ b/esphome/components/adc/__init__.py @@ -1 +1 @@ -CODEOWNERS = ['@esphome/core'] +CODEOWNERS = ["@esphome/core"] diff --git a/esphome/components/adc/sensor.py b/esphome/components/adc/sensor.py index 3c06eb91bf..2e36c6179a 100644 --- a/esphome/components/adc/sensor.py +++ b/esphome/components/adc/sensor.py @@ -2,37 +2,51 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins from esphome.components import sensor, voltage_sampler -from esphome.const import CONF_ATTENUATION, CONF_ID, CONF_PIN, DEVICE_CLASS_VOLTAGE, ICON_EMPTY, \ - UNIT_VOLT +from esphome.const import ( + CONF_ATTENUATION, + CONF_ID, + CONF_PIN, + DEVICE_CLASS_VOLTAGE, + ICON_EMPTY, + UNIT_VOLT, +) -AUTO_LOAD = ['voltage_sampler'] +AUTO_LOAD = ["voltage_sampler"] ATTENUATION_MODES = { - '0db': cg.global_ns.ADC_0db, - '2.5db': cg.global_ns.ADC_2_5db, - '6db': cg.global_ns.ADC_6db, - '11db': cg.global_ns.ADC_11db, + "0db": cg.global_ns.ADC_0db, + "2.5db": cg.global_ns.ADC_2_5db, + "6db": cg.global_ns.ADC_6db, + "11db": cg.global_ns.ADC_11db, } def validate_adc_pin(value): vcc = str(value).upper() - if vcc == 'VCC': + if vcc == "VCC": return cv.only_on_esp8266(vcc) return pins.analog_pin(value) -adc_ns = cg.esphome_ns.namespace('adc') -ADCSensor = adc_ns.class_('ADCSensor', sensor.Sensor, cg.PollingComponent, - voltage_sampler.VoltageSampler) +adc_ns = cg.esphome_ns.namespace("adc") +ADCSensor = adc_ns.class_( + "ADCSensor", sensor.Sensor, cg.PollingComponent, voltage_sampler.VoltageSampler +) -CONFIG_SCHEMA = sensor.sensor_schema(UNIT_VOLT, ICON_EMPTY, 2, DEVICE_CLASS_VOLTAGE).extend({ - cv.GenerateID(): cv.declare_id(ADCSensor), - cv.Required(CONF_PIN): validate_adc_pin, - cv.SplitDefault(CONF_ATTENUATION, esp32='0db'): - cv.All(cv.only_on_esp32, cv.enum(ATTENUATION_MODES, lower=True)), -}).extend(cv.polling_component_schema('60s')) +CONFIG_SCHEMA = ( + sensor.sensor_schema(UNIT_VOLT, ICON_EMPTY, 2, DEVICE_CLASS_VOLTAGE) + .extend( + { + cv.GenerateID(): cv.declare_id(ADCSensor), + cv.Required(CONF_PIN): validate_adc_pin, + cv.SplitDefault(CONF_ATTENUATION, esp32="0db"): cv.All( + cv.only_on_esp32, cv.enum(ATTENUATION_MODES, lower=True) + ), + } + ) + .extend(cv.polling_component_schema("60s")) +) def to_code(config): @@ -40,8 +54,8 @@ def to_code(config): yield cg.register_component(var, config) yield sensor.register_sensor(var, config) - if config[CONF_PIN] == 'VCC': - cg.add_define('USE_ADC_SENSOR_VCC') + if config[CONF_PIN] == "VCC": + cg.add_define("USE_ADC_SENSOR_VCC") else: cg.add(var.set_pin(config[CONF_PIN])) diff --git a/esphome/components/ade7953/sensor.py b/esphome/components/ade7953/sensor.py index 255695c4a5..6bc9917806 100644 --- a/esphome/components/ade7953/sensor.py +++ b/esphome/components/ade7953/sensor.py @@ -2,33 +2,54 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, i2c from esphome import pins -from esphome.const import CONF_ID, CONF_VOLTAGE, DEVICE_CLASS_CURRENT, DEVICE_CLASS_POWER, \ - DEVICE_CLASS_VOLTAGE, ICON_EMPTY, UNIT_VOLT, UNIT_AMPERE, UNIT_WATT +from esphome.const import ( + CONF_ID, + CONF_VOLTAGE, + DEVICE_CLASS_CURRENT, + DEVICE_CLASS_POWER, + DEVICE_CLASS_VOLTAGE, + ICON_EMPTY, + UNIT_VOLT, + UNIT_AMPERE, + UNIT_WATT, +) -DEPENDENCIES = ['i2c'] +DEPENDENCIES = ["i2c"] -ade7953_ns = cg.esphome_ns.namespace('ade7953') -ADE7953 = ade7953_ns.class_('ADE7953', cg.PollingComponent, i2c.I2CDevice) +ade7953_ns = cg.esphome_ns.namespace("ade7953") +ADE7953 = ade7953_ns.class_("ADE7953", cg.PollingComponent, i2c.I2CDevice) -CONF_IRQ_PIN = 'irq_pin' -CONF_CURRENT_A = 'current_a' -CONF_CURRENT_B = 'current_b' -CONF_ACTIVE_POWER_A = 'active_power_a' -CONF_ACTIVE_POWER_B = 'active_power_b' +CONF_IRQ_PIN = "irq_pin" +CONF_CURRENT_A = "current_a" +CONF_CURRENT_B = "current_b" +CONF_ACTIVE_POWER_A = "active_power_a" +CONF_ACTIVE_POWER_B = "active_power_b" -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(ADE7953), - cv.Optional(CONF_IRQ_PIN): pins.input_pin, - cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE), - cv.Optional(CONF_CURRENT_A): sensor.sensor_schema(UNIT_AMPERE, ICON_EMPTY, 2, - DEVICE_CLASS_CURRENT), - cv.Optional(CONF_CURRENT_B): sensor.sensor_schema(UNIT_AMPERE, ICON_EMPTY, 2, - DEVICE_CLASS_CURRENT), - cv.Optional(CONF_ACTIVE_POWER_A): sensor.sensor_schema(UNIT_WATT, ICON_EMPTY, 1, - DEVICE_CLASS_POWER), - cv.Optional(CONF_ACTIVE_POWER_B): sensor.sensor_schema(UNIT_WATT, ICON_EMPTY, 1, - DEVICE_CLASS_POWER), -}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x38)) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(ADE7953), + cv.Optional(CONF_IRQ_PIN): pins.input_pin, + cv.Optional(CONF_VOLTAGE): sensor.sensor_schema( + UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE + ), + cv.Optional(CONF_CURRENT_A): sensor.sensor_schema( + UNIT_AMPERE, ICON_EMPTY, 2, DEVICE_CLASS_CURRENT + ), + cv.Optional(CONF_CURRENT_B): sensor.sensor_schema( + UNIT_AMPERE, ICON_EMPTY, 2, DEVICE_CLASS_CURRENT + ), + cv.Optional(CONF_ACTIVE_POWER_A): sensor.sensor_schema( + UNIT_WATT, ICON_EMPTY, 1, DEVICE_CLASS_POWER + ), + cv.Optional(CONF_ACTIVE_POWER_B): sensor.sensor_schema( + UNIT_WATT, ICON_EMPTY, 1, DEVICE_CLASS_POWER + ), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x38)) +) def to_code(config): @@ -39,10 +60,15 @@ def to_code(config): if CONF_IRQ_PIN in config: cg.add(var.set_irq_pin(config[CONF_IRQ_PIN])) - for key in [CONF_VOLTAGE, CONF_CURRENT_A, CONF_CURRENT_B, CONF_ACTIVE_POWER_A, - CONF_ACTIVE_POWER_B]: + for key in [ + CONF_VOLTAGE, + CONF_CURRENT_A, + CONF_CURRENT_B, + CONF_ACTIVE_POWER_A, + CONF_ACTIVE_POWER_B, + ]: if key not in config: continue conf = config[key] sens = yield sensor.new_sensor(conf) - cg.add(getattr(var, f'set_{key}_sensor')(sens)) + cg.add(getattr(var, f"set_{key}_sensor")(sens)) diff --git a/esphome/components/ads1115/__init__.py b/esphome/components/ads1115/__init__.py index a41a521ba7..32ab17db31 100644 --- a/esphome/components/ads1115/__init__.py +++ b/esphome/components/ads1115/__init__.py @@ -3,18 +3,24 @@ import esphome.config_validation as cv from esphome.components import i2c from esphome.const import CONF_ID -DEPENDENCIES = ['i2c'] -AUTO_LOAD = ['sensor', 'voltage_sampler'] +DEPENDENCIES = ["i2c"] +AUTO_LOAD = ["sensor", "voltage_sampler"] MULTI_CONF = True -ads1115_ns = cg.esphome_ns.namespace('ads1115') -ADS1115Component = ads1115_ns.class_('ADS1115Component', cg.Component, i2c.I2CDevice) +ads1115_ns = cg.esphome_ns.namespace("ads1115") +ADS1115Component = ads1115_ns.class_("ADS1115Component", cg.Component, i2c.I2CDevice) -CONF_CONTINUOUS_MODE = 'continuous_mode' -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(ADS1115Component), - cv.Optional(CONF_CONTINUOUS_MODE, default=False): cv.boolean, -}).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(None)) +CONF_CONTINUOUS_MODE = "continuous_mode" +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(ADS1115Component), + cv.Optional(CONF_CONTINUOUS_MODE, default=False): cv.boolean, + } + ) + .extend(cv.COMPONENT_SCHEMA) + .extend(i2c.i2c_device_schema(None)) +) def to_code(config): diff --git a/esphome/components/ads1115/sensor.py b/esphome/components/ads1115/sensor.py index 48f01337ef..6eb39bdd1a 100644 --- a/esphome/components/ads1115/sensor.py +++ b/esphome/components/ads1115/sensor.py @@ -1,54 +1,67 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, voltage_sampler -from esphome.const import CONF_GAIN, CONF_MULTIPLEXER, DEVICE_CLASS_VOLTAGE, ICON_EMPTY, \ - UNIT_VOLT, CONF_ID +from esphome.const import ( + CONF_GAIN, + CONF_MULTIPLEXER, + DEVICE_CLASS_VOLTAGE, + ICON_EMPTY, + UNIT_VOLT, + CONF_ID, +) from . import ads1115_ns, ADS1115Component -DEPENDENCIES = ['ads1115'] +DEPENDENCIES = ["ads1115"] -ADS1115Multiplexer = ads1115_ns.enum('ADS1115Multiplexer') +ADS1115Multiplexer = ads1115_ns.enum("ADS1115Multiplexer") MUX = { - 'A0_A1': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P0_N1, - 'A0_A3': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P0_N3, - 'A1_A3': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P1_N3, - 'A2_A3': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P2_N3, - 'A0_GND': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P0_NG, - 'A1_GND': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P1_NG, - 'A2_GND': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P2_NG, - 'A3_GND': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P3_NG, + "A0_A1": ADS1115Multiplexer.ADS1115_MULTIPLEXER_P0_N1, + "A0_A3": ADS1115Multiplexer.ADS1115_MULTIPLEXER_P0_N3, + "A1_A3": ADS1115Multiplexer.ADS1115_MULTIPLEXER_P1_N3, + "A2_A3": ADS1115Multiplexer.ADS1115_MULTIPLEXER_P2_N3, + "A0_GND": ADS1115Multiplexer.ADS1115_MULTIPLEXER_P0_NG, + "A1_GND": ADS1115Multiplexer.ADS1115_MULTIPLEXER_P1_NG, + "A2_GND": ADS1115Multiplexer.ADS1115_MULTIPLEXER_P2_NG, + "A3_GND": ADS1115Multiplexer.ADS1115_MULTIPLEXER_P3_NG, } -ADS1115Gain = ads1115_ns.enum('ADS1115Gain') +ADS1115Gain = ads1115_ns.enum("ADS1115Gain") GAIN = { - '6.144': ADS1115Gain.ADS1115_GAIN_6P144, - '4.096': ADS1115Gain.ADS1115_GAIN_4P096, - '2.048': ADS1115Gain.ADS1115_GAIN_2P048, - '1.024': ADS1115Gain.ADS1115_GAIN_1P024, - '0.512': ADS1115Gain.ADS1115_GAIN_0P512, - '0.256': ADS1115Gain.ADS1115_GAIN_0P256, + "6.144": ADS1115Gain.ADS1115_GAIN_6P144, + "4.096": ADS1115Gain.ADS1115_GAIN_4P096, + "2.048": ADS1115Gain.ADS1115_GAIN_2P048, + "1.024": ADS1115Gain.ADS1115_GAIN_1P024, + "0.512": ADS1115Gain.ADS1115_GAIN_0P512, + "0.256": ADS1115Gain.ADS1115_GAIN_0P256, } def validate_gain(value): if isinstance(value, float): - value = f'{value:0.03f}' + value = f"{value:0.03f}" elif not isinstance(value, str): raise cv.Invalid(f'invalid gain "{value}"') return cv.enum(GAIN)(value) -ADS1115Sensor = ads1115_ns.class_('ADS1115Sensor', sensor.Sensor, cg.PollingComponent, - voltage_sampler.VoltageSampler) +ADS1115Sensor = ads1115_ns.class_( + "ADS1115Sensor", sensor.Sensor, cg.PollingComponent, voltage_sampler.VoltageSampler +) -CONF_ADS1115_ID = 'ads1115_id' -CONFIG_SCHEMA = sensor.sensor_schema(UNIT_VOLT, ICON_EMPTY, 3, DEVICE_CLASS_VOLTAGE).extend({ - cv.GenerateID(): cv.declare_id(ADS1115Sensor), - cv.GenerateID(CONF_ADS1115_ID): cv.use_id(ADS1115Component), - cv.Required(CONF_MULTIPLEXER): cv.enum(MUX, upper=True, space='_'), - cv.Required(CONF_GAIN): validate_gain, -}).extend(cv.polling_component_schema('60s')) +CONF_ADS1115_ID = "ads1115_id" +CONFIG_SCHEMA = ( + sensor.sensor_schema(UNIT_VOLT, ICON_EMPTY, 3, DEVICE_CLASS_VOLTAGE) + .extend( + { + cv.GenerateID(): cv.declare_id(ADS1115Sensor), + cv.GenerateID(CONF_ADS1115_ID): cv.use_id(ADS1115Component), + cv.Required(CONF_MULTIPLEXER): cv.enum(MUX, upper=True, space="_"), + cv.Required(CONF_GAIN): validate_gain, + } + ) + .extend(cv.polling_component_schema("60s")) +) def to_code(config): diff --git a/esphome/components/aht10/sensor.py b/esphome/components/aht10/sensor.py index 17cbb8892e..e335934a8e 100644 --- a/esphome/components/aht10/sensor.py +++ b/esphome/components/aht10/sensor.py @@ -1,21 +1,37 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import CONF_HUMIDITY, CONF_ID, CONF_TEMPERATURE, DEVICE_CLASS_HUMIDITY, \ - DEVICE_CLASS_TEMPERATURE, ICON_EMPTY, UNIT_CELSIUS, UNIT_PERCENT +from esphome.const import ( + CONF_HUMIDITY, + CONF_ID, + CONF_TEMPERATURE, + DEVICE_CLASS_HUMIDITY, + DEVICE_CLASS_TEMPERATURE, + ICON_EMPTY, + UNIT_CELSIUS, + UNIT_PERCENT, +) -DEPENDENCIES = ['i2c'] +DEPENDENCIES = ["i2c"] -aht10_ns = cg.esphome_ns.namespace('aht10') -AHT10Component = aht10_ns.class_('AHT10Component', cg.PollingComponent, i2c.I2CDevice) +aht10_ns = cg.esphome_ns.namespace("aht10") +AHT10Component = aht10_ns.class_("AHT10Component", cg.PollingComponent, i2c.I2CDevice) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(AHT10Component), - cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 2, - DEVICE_CLASS_TEMPERATURE), - cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 2, - DEVICE_CLASS_HUMIDITY), -}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x38)) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(AHT10Component), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( + UNIT_CELSIUS, ICON_EMPTY, 2, DEVICE_CLASS_TEMPERATURE + ), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema( + UNIT_PERCENT, ICON_EMPTY, 2, DEVICE_CLASS_HUMIDITY + ), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x38)) +) def to_code(config): diff --git a/esphome/components/am2320/sensor.py b/esphome/components/am2320/sensor.py index 098315290d..6d1cc42581 100644 --- a/esphome/components/am2320/sensor.py +++ b/esphome/components/am2320/sensor.py @@ -1,21 +1,39 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import CONF_HUMIDITY, CONF_ID, CONF_TEMPERATURE, DEVICE_CLASS_HUMIDITY, \ - DEVICE_CLASS_TEMPERATURE, UNIT_CELSIUS, ICON_EMPTY, UNIT_PERCENT +from esphome.const import ( + CONF_HUMIDITY, + CONF_ID, + CONF_TEMPERATURE, + DEVICE_CLASS_HUMIDITY, + DEVICE_CLASS_TEMPERATURE, + UNIT_CELSIUS, + ICON_EMPTY, + UNIT_PERCENT, +) -DEPENDENCIES = ['i2c'] +DEPENDENCIES = ["i2c"] -am2320_ns = cg.esphome_ns.namespace('am2320') -AM2320Component = am2320_ns.class_('AM2320Component', cg.PollingComponent, i2c.I2CDevice) +am2320_ns = cg.esphome_ns.namespace("am2320") +AM2320Component = am2320_ns.class_( + "AM2320Component", cg.PollingComponent, i2c.I2CDevice +) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(AM2320Component), - cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, - DEVICE_CLASS_TEMPERATURE), - cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 1, - DEVICE_CLASS_HUMIDITY), -}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x5C)) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(AM2320Component), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( + UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE + ), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema( + UNIT_PERCENT, ICON_EMPTY, 1, DEVICE_CLASS_HUMIDITY + ), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x5C)) +) def to_code(config): diff --git a/esphome/components/animation/__init__.py b/esphome/components/animation/__init__.py index f19dbd6946..fbcb2b4c1f 100644 --- a/esphome/components/animation/__init__.py +++ b/esphome/components/animation/__init__.py @@ -10,24 +10,28 @@ from esphome.core import CORE, HexInt _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['display'] +DEPENDENCIES = ["display"] MULTI_CONF = True -Animation_ = display.display_ns.class_('Animation') +Animation_ = display.display_ns.class_("Animation") -CONF_RAW_DATA_ID = 'raw_data_id' +CONF_RAW_DATA_ID = "raw_data_id" -ANIMATION_SCHEMA = cv.Schema({ - cv.Required(CONF_ID): cv.declare_id(Animation_), - cv.Required(CONF_FILE): cv.file_, - cv.Optional(CONF_RESIZE): cv.dimensions, - cv.Optional(CONF_TYPE, default='BINARY'): cv.enum(espImage.IMAGE_TYPE, upper=True), - cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_id(cg.uint8), -}) +ANIMATION_SCHEMA = cv.Schema( + { + cv.Required(CONF_ID): cv.declare_id(Animation_), + cv.Required(CONF_FILE): cv.file_, + cv.Optional(CONF_RESIZE): cv.dimensions, + cv.Optional(CONF_TYPE, default="BINARY"): cv.enum( + espImage.IMAGE_TYPE, upper=True + ), + cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_id(cg.uint8), + } +) CONFIG_SCHEMA = cv.All(font.validate_pillow_installed, ANIMATION_SCHEMA) -CODEOWNERS = ['@syndlex'] +CODEOWNERS = ["@syndlex"] def to_code(config): @@ -46,26 +50,28 @@ def to_code(config): width, height = image.size else: if width > 500 or height > 500: - _LOGGER.warning("The image you requested is very big. Please consider using" - " the resize parameter.") + _LOGGER.warning( + "The image you requested is very big. Please consider using" + " the resize parameter." + ) - if config[CONF_TYPE] == 'GRAYSCALE': + if config[CONF_TYPE] == "GRAYSCALE": data = [0 for _ in range(height * width * frames)] pos = 0 for frameIndex in range(frames): image.seek(frameIndex) - frame = image.convert('L', dither=Image.NONE) + frame = image.convert("L", dither=Image.NONE) pixels = list(frame.getdata()) for pix in pixels: data[pos] = pix pos += 1 - elif config[CONF_TYPE] == 'RGB24': + elif config[CONF_TYPE] == "RGB24": data = [0 for _ in range(height * width * 3 * frames)] pos = 0 for frameIndex in range(frames): image.seek(frameIndex) - frame = image.convert('RGB') + frame = image.convert("RGB") pixels = list(frame.getdata()) for pix in pixels: data[pos] = pix[0] @@ -75,12 +81,12 @@ def to_code(config): data[pos] = pix[2] pos += 1 - elif config[CONF_TYPE] == 'BINARY': + elif config[CONF_TYPE] == "BINARY": width8 = ((width + 7) // 8) * 8 data = [0 for _ in range((height * width8 // 8) * frames)] for frameIndex in range(frames): image.seek(frameIndex) - frame = image.convert('1', dither=Image.NONE) + frame = image.convert("1", dither=Image.NONE) for y in range(height): for x in range(width): if frame.getpixel((x, y)): @@ -90,5 +96,11 @@ def to_code(config): rhs = [HexInt(x) for x in data] prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs) - cg.new_Pvariable(config[CONF_ID], prog_arr, width, height, frames, - espImage.IMAGE_TYPE[config[CONF_TYPE]]) + cg.new_Pvariable( + config[CONF_ID], + prog_arr, + width, + height, + frames, + espImage.IMAGE_TYPE[config[CONF_TYPE]], + ) diff --git a/esphome/components/apds9960/__init__.py b/esphome/components/apds9960/__init__.py index 4725c16032..9346b5ece1 100644 --- a/esphome/components/apds9960/__init__.py +++ b/esphome/components/apds9960/__init__.py @@ -3,18 +3,24 @@ import esphome.config_validation as cv from esphome.components import i2c from esphome.const import CONF_ID -DEPENDENCIES = ['i2c'] -AUTO_LOAD = ['sensor', 'binary_sensor'] +DEPENDENCIES = ["i2c"] +AUTO_LOAD = ["sensor", "binary_sensor"] MULTI_CONF = True -CONF_APDS9960_ID = 'apds9960_id' +CONF_APDS9960_ID = "apds9960_id" -apds9960_nds = cg.esphome_ns.namespace('apds9960') -APDS9960 = apds9960_nds.class_('APDS9960', cg.PollingComponent, i2c.I2CDevice) +apds9960_nds = cg.esphome_ns.namespace("apds9960") +APDS9960 = apds9960_nds.class_("APDS9960", cg.PollingComponent, i2c.I2CDevice) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(APDS9960), -}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x39)) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(APDS9960), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x39)) +) def to_code(config): diff --git a/esphome/components/apds9960/binary_sensor.py b/esphome/components/apds9960/binary_sensor.py index 4404510909..0433061385 100644 --- a/esphome/components/apds9960/binary_sensor.py +++ b/esphome/components/apds9960/binary_sensor.py @@ -4,20 +4,24 @@ from esphome.components import binary_sensor from esphome.const import CONF_DIRECTION, CONF_DEVICE_CLASS, DEVICE_CLASS_MOVING from . import APDS9960, CONF_APDS9960_ID -DEPENDENCIES = ['apds9960'] +DEPENDENCIES = ["apds9960"] DIRECTIONS = { - 'UP': 'set_up_direction', - 'DOWN': 'set_down_direction', - 'LEFT': 'set_left_direction', - 'RIGHT': 'set_right_direction', + "UP": "set_up_direction", + "DOWN": "set_down_direction", + "LEFT": "set_left_direction", + "RIGHT": "set_right_direction", } -CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({ - cv.Required(CONF_DIRECTION): cv.one_of(*DIRECTIONS, upper=True), - cv.GenerateID(CONF_APDS9960_ID): cv.use_id(APDS9960), - cv.Optional(CONF_DEVICE_CLASS, default=DEVICE_CLASS_MOVING): binary_sensor.device_class, -}) +CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend( + { + cv.Required(CONF_DIRECTION): cv.one_of(*DIRECTIONS, upper=True), + cv.GenerateID(CONF_APDS9960_ID): cv.use_id(APDS9960), + cv.Optional( + CONF_DEVICE_CLASS, default=DEVICE_CLASS_MOVING + ): binary_sensor.device_class, + } +) def to_code(config): diff --git a/esphome/components/apds9960/sensor.py b/esphome/components/apds9960/sensor.py index eb1008e713..a1ebd9b5c3 100644 --- a/esphome/components/apds9960/sensor.py +++ b/esphome/components/apds9960/sensor.py @@ -4,20 +4,24 @@ from esphome.components import sensor from esphome.const import CONF_TYPE, DEVICE_CLASS_EMPTY, UNIT_PERCENT, ICON_LIGHTBULB from . import APDS9960, CONF_APDS9960_ID -DEPENDENCIES = ['apds9960'] +DEPENDENCIES = ["apds9960"] TYPES = { - 'CLEAR': 'set_clear_channel', - 'RED': 'set_red_channel', - 'GREEN': 'set_green_channel', - 'BLUE': 'set_blue_channel', - 'PROXIMITY': 'set_proximity', + "CLEAR": "set_clear_channel", + "RED": "set_red_channel", + "GREEN": "set_green_channel", + "BLUE": "set_blue_channel", + "PROXIMITY": "set_proximity", } -CONFIG_SCHEMA = sensor.sensor_schema(UNIT_PERCENT, ICON_LIGHTBULB, 1, DEVICE_CLASS_EMPTY).extend({ - cv.Required(CONF_TYPE): cv.one_of(*TYPES, upper=True), - cv.GenerateID(CONF_APDS9960_ID): cv.use_id(APDS9960), -}) +CONFIG_SCHEMA = sensor.sensor_schema( + UNIT_PERCENT, ICON_LIGHTBULB, 1, DEVICE_CLASS_EMPTY +).extend( + { + cv.Required(CONF_TYPE): cv.one_of(*TYPES, upper=True), + cv.GenerateID(CONF_APDS9960_ID): cv.use_id(APDS9960), + } +) def to_code(config): diff --git a/esphome/components/api/__init__.py b/esphome/components/api/__init__.py index 884ae9867a..d12c01e614 100644 --- a/esphome/components/api/__init__.py +++ b/esphome/components/api/__init__.py @@ -2,46 +2,69 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import automation from esphome.automation import Condition -from esphome.const import CONF_DATA, CONF_DATA_TEMPLATE, CONF_ID, CONF_PASSWORD, CONF_PORT, \ - CONF_REBOOT_TIMEOUT, CONF_SERVICE, CONF_VARIABLES, CONF_SERVICES, CONF_TRIGGER_ID, CONF_EVENT, \ - CONF_TAG +from esphome.const import ( + CONF_DATA, + CONF_DATA_TEMPLATE, + CONF_ID, + CONF_PASSWORD, + CONF_PORT, + CONF_REBOOT_TIMEOUT, + CONF_SERVICE, + CONF_VARIABLES, + CONF_SERVICES, + CONF_TRIGGER_ID, + CONF_EVENT, + CONF_TAG, +) from esphome.core import coroutine_with_priority -DEPENDENCIES = ['network'] -AUTO_LOAD = ['async_tcp'] -CODEOWNERS = ['@OttoWinter'] +DEPENDENCIES = ["network"] +AUTO_LOAD = ["async_tcp"] +CODEOWNERS = ["@OttoWinter"] -api_ns = cg.esphome_ns.namespace('api') -APIServer = api_ns.class_('APIServer', cg.Component, cg.Controller) -HomeAssistantServiceCallAction = api_ns.class_('HomeAssistantServiceCallAction', automation.Action) -APIConnectedCondition = api_ns.class_('APIConnectedCondition', Condition) +api_ns = cg.esphome_ns.namespace("api") +APIServer = api_ns.class_("APIServer", cg.Component, cg.Controller) +HomeAssistantServiceCallAction = api_ns.class_( + "HomeAssistantServiceCallAction", automation.Action +) +APIConnectedCondition = api_ns.class_("APIConnectedCondition", Condition) -UserServiceTrigger = api_ns.class_('UserServiceTrigger', automation.Trigger) -ListEntitiesServicesArgument = api_ns.class_('ListEntitiesServicesArgument') +UserServiceTrigger = api_ns.class_("UserServiceTrigger", automation.Trigger) +ListEntitiesServicesArgument = api_ns.class_("ListEntitiesServicesArgument") SERVICE_ARG_NATIVE_TYPES = { - 'bool': bool, - 'int': cg.int32, - 'float': float, - 'string': cg.std_string, - 'bool[]': cg.std_vector.template(bool), - 'int[]': cg.std_vector.template(cg.int32), - 'float[]': cg.std_vector.template(float), - 'string[]': cg.std_vector.template(cg.std_string), + "bool": bool, + "int": cg.int32, + "float": float, + "string": cg.std_string, + "bool[]": cg.std_vector.template(bool), + "int[]": cg.std_vector.template(cg.int32), + "float[]": cg.std_vector.template(float), + "string[]": cg.std_vector.template(cg.std_string), } -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(APIServer), - cv.Optional(CONF_PORT, default=6053): cv.port, - cv.Optional(CONF_PASSWORD, default=''): cv.string_strict, - cv.Optional(CONF_REBOOT_TIMEOUT, default='15min'): cv.positive_time_period_milliseconds, - cv.Optional(CONF_SERVICES): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(UserServiceTrigger), - cv.Required(CONF_SERVICE): cv.valid_name, - cv.Optional(CONF_VARIABLES, default={}): cv.Schema({ - cv.validate_id_name: cv.one_of(*SERVICE_ARG_NATIVE_TYPES, lower=True), - }), - }), -}).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(APIServer), + cv.Optional(CONF_PORT, default=6053): cv.port, + cv.Optional(CONF_PASSWORD, default=""): cv.string_strict, + cv.Optional( + CONF_REBOOT_TIMEOUT, default="15min" + ): cv.positive_time_period_milliseconds, + cv.Optional(CONF_SERVICES): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(UserServiceTrigger), + cv.Required(CONF_SERVICE): cv.valid_name, + cv.Optional(CONF_VARIABLES, default={}): cv.Schema( + { + cv.validate_id_name: cv.one_of( + *SERVICE_ARG_NATIVE_TYPES, lower=True + ), + } + ), + } + ), + } +).extend(cv.COMPONENT_SCHEMA) @coroutine_with_priority(40.0) @@ -63,28 +86,36 @@ def to_code(config): func_args.append((native, name)) service_arg_names.append(name) templ = cg.TemplateArguments(*template_args) - trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], templ, - conf[CONF_SERVICE], service_arg_names) + trigger = cg.new_Pvariable( + conf[CONF_TRIGGER_ID], templ, conf[CONF_SERVICE], service_arg_names + ) cg.add(var.register_user_service(trigger)) yield automation.build_automation(trigger, func_args, conf) - cg.add_define('USE_API') + cg.add_define("USE_API") cg.add_global(api_ns.using) KEY_VALUE_SCHEMA = cv.Schema({cv.string: cv.templatable(cv.string_strict)}) -HOMEASSISTANT_SERVICE_ACTION_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.use_id(APIServer), - cv.Required(CONF_SERVICE): cv.templatable(cv.string), - cv.Optional(CONF_DATA, default={}): KEY_VALUE_SCHEMA, - cv.Optional(CONF_DATA_TEMPLATE, default={}): KEY_VALUE_SCHEMA, - cv.Optional(CONF_VARIABLES, default={}): cv.Schema({cv.string: cv.returning_lambda}), -}) +HOMEASSISTANT_SERVICE_ACTION_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.use_id(APIServer), + cv.Required(CONF_SERVICE): cv.templatable(cv.string), + cv.Optional(CONF_DATA, default={}): KEY_VALUE_SCHEMA, + cv.Optional(CONF_DATA_TEMPLATE, default={}): KEY_VALUE_SCHEMA, + cv.Optional(CONF_VARIABLES, default={}): cv.Schema( + {cv.string: cv.returning_lambda} + ), + } +) -@automation.register_action('homeassistant.service', HomeAssistantServiceCallAction, - HOMEASSISTANT_SERVICE_ACTION_SCHEMA) +@automation.register_action( + "homeassistant.service", + HomeAssistantServiceCallAction, + HOMEASSISTANT_SERVICE_ACTION_SCHEMA, +) def homeassistant_service_to_code(config, action_id, template_arg, args): serv = yield cg.get_variable(config[CONF_ID]) var = cg.new_Pvariable(action_id, template_arg, serv, False) @@ -104,23 +135,30 @@ def homeassistant_service_to_code(config, action_id, template_arg, args): def validate_homeassistant_event(value): value = cv.string(value) - if not value.startswith('esphome.'): - raise cv.Invalid("ESPHome can only generate Home Assistant events that begin with " - "esphome. For example 'esphome.xyz'") + if not value.startswith("esphome."): + raise cv.Invalid( + "ESPHome can only generate Home Assistant events that begin with " + "esphome. For example 'esphome.xyz'" + ) return value -HOMEASSISTANT_EVENT_ACTION_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.use_id(APIServer), - cv.Required(CONF_EVENT): validate_homeassistant_event, - cv.Optional(CONF_DATA, default={}): KEY_VALUE_SCHEMA, - cv.Optional(CONF_DATA_TEMPLATE, default={}): KEY_VALUE_SCHEMA, - cv.Optional(CONF_VARIABLES, default={}): KEY_VALUE_SCHEMA, -}) +HOMEASSISTANT_EVENT_ACTION_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.use_id(APIServer), + cv.Required(CONF_EVENT): validate_homeassistant_event, + cv.Optional(CONF_DATA, default={}): KEY_VALUE_SCHEMA, + cv.Optional(CONF_DATA_TEMPLATE, default={}): KEY_VALUE_SCHEMA, + cv.Optional(CONF_VARIABLES, default={}): KEY_VALUE_SCHEMA, + } +) -@automation.register_action('homeassistant.event', HomeAssistantServiceCallAction, - HOMEASSISTANT_EVENT_ACTION_SCHEMA) +@automation.register_action( + "homeassistant.event", + HomeAssistantServiceCallAction, + HOMEASSISTANT_EVENT_ACTION_SCHEMA, +) def homeassistant_event_to_code(config, action_id, template_arg, args): serv = yield cg.get_variable(config[CONF_ID]) var = cg.new_Pvariable(action_id, template_arg, serv, True) @@ -138,23 +176,29 @@ def homeassistant_event_to_code(config, action_id, template_arg, args): yield var -HOMEASSISTANT_TAG_SCANNED_ACTION_SCHEMA = cv.maybe_simple_value({ - cv.GenerateID(): cv.use_id(APIServer), - cv.Required(CONF_TAG): cv.templatable(cv.string_strict), -}, key=CONF_TAG) +HOMEASSISTANT_TAG_SCANNED_ACTION_SCHEMA = cv.maybe_simple_value( + { + cv.GenerateID(): cv.use_id(APIServer), + cv.Required(CONF_TAG): cv.templatable(cv.string_strict), + }, + key=CONF_TAG, +) -@automation.register_action('homeassistant.tag_scanned', HomeAssistantServiceCallAction, - HOMEASSISTANT_TAG_SCANNED_ACTION_SCHEMA) +@automation.register_action( + "homeassistant.tag_scanned", + HomeAssistantServiceCallAction, + HOMEASSISTANT_TAG_SCANNED_ACTION_SCHEMA, +) def homeassistant_tag_scanned_to_code(config, action_id, template_arg, args): serv = yield cg.get_variable(config[CONF_ID]) var = cg.new_Pvariable(action_id, template_arg, serv, True) - cg.add(var.set_service('esphome.tag_scanned')) + cg.add(var.set_service("esphome.tag_scanned")) templ = yield cg.templatable(config[CONF_TAG], args, cg.std_string) - cg.add(var.add_data('tag_id', templ)) + cg.add(var.add_data("tag_id", templ)) yield var -@automation.register_condition('api.connected', APIConnectedCondition, {}) +@automation.register_condition("api.connected", APIConnectedCondition, {}) def api_connected_to_code(config, condition_id, template_arg, args): yield cg.new_Pvariable(condition_id, template_arg) diff --git a/esphome/components/as3935/__init__.py b/esphome/components/as3935/__init__.py index 51958048ca..337f0a9081 100644 --- a/esphome/components/as3935/__init__.py +++ b/esphome/components/as3935/__init__.py @@ -1,33 +1,43 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins -from esphome.const import CONF_INDOOR, CONF_WATCHDOG_THRESHOLD, \ - CONF_NOISE_LEVEL, CONF_SPIKE_REJECTION, CONF_LIGHTNING_THRESHOLD, \ - CONF_MASK_DISTURBER, CONF_DIV_RATIO, CONF_CAPACITANCE +from esphome.const import ( + CONF_INDOOR, + CONF_WATCHDOG_THRESHOLD, + CONF_NOISE_LEVEL, + CONF_SPIKE_REJECTION, + CONF_LIGHTNING_THRESHOLD, + CONF_MASK_DISTURBER, + CONF_DIV_RATIO, + CONF_CAPACITANCE, +) from esphome.core import coroutine -AUTO_LOAD = ['sensor', 'binary_sensor'] +AUTO_LOAD = ["sensor", "binary_sensor"] MULTI_CONF = True -CONF_AS3935_ID = 'as3935_id' +CONF_AS3935_ID = "as3935_id" -as3935_ns = cg.esphome_ns.namespace('as3935') -AS3935 = as3935_ns.class_('AS3935Component', cg.Component) +as3935_ns = cg.esphome_ns.namespace("as3935") +AS3935 = as3935_ns.class_("AS3935Component", cg.Component) -CONF_IRQ_PIN = 'irq_pin' -AS3935_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(AS3935), - cv.Required(CONF_IRQ_PIN): pins.gpio_input_pin_schema, - - cv.Optional(CONF_INDOOR, default=True): cv.boolean, - cv.Optional(CONF_NOISE_LEVEL, default=2): cv.int_range(min=1, max=7), - cv.Optional(CONF_WATCHDOG_THRESHOLD, default=2): cv.int_range(min=1, max=10), - cv.Optional(CONF_SPIKE_REJECTION, default=2): cv.int_range(min=1, max=11), - cv.Optional(CONF_LIGHTNING_THRESHOLD, default=1): cv.one_of(1, 5, 9, 16, int=True), - cv.Optional(CONF_MASK_DISTURBER, default=False): cv.boolean, - cv.Optional(CONF_DIV_RATIO, default=0): cv.one_of(0, 16, 32, 64, 128, int=True), - cv.Optional(CONF_CAPACITANCE, default=0): cv.int_range(min=0, max=15), -}) +CONF_IRQ_PIN = "irq_pin" +AS3935_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(AS3935), + cv.Required(CONF_IRQ_PIN): pins.gpio_input_pin_schema, + cv.Optional(CONF_INDOOR, default=True): cv.boolean, + cv.Optional(CONF_NOISE_LEVEL, default=2): cv.int_range(min=1, max=7), + cv.Optional(CONF_WATCHDOG_THRESHOLD, default=2): cv.int_range(min=1, max=10), + cv.Optional(CONF_SPIKE_REJECTION, default=2): cv.int_range(min=1, max=11), + cv.Optional(CONF_LIGHTNING_THRESHOLD, default=1): cv.one_of( + 1, 5, 9, 16, int=True + ), + cv.Optional(CONF_MASK_DISTURBER, default=False): cv.boolean, + cv.Optional(CONF_DIV_RATIO, default=0): cv.one_of(0, 16, 32, 64, 128, int=True), + cv.Optional(CONF_CAPACITANCE, default=0): cv.int_range(min=0, max=15), + } +) @coroutine diff --git a/esphome/components/as3935/binary_sensor.py b/esphome/components/as3935/binary_sensor.py index 3748c3484a..85ba052869 100644 --- a/esphome/components/as3935/binary_sensor.py +++ b/esphome/components/as3935/binary_sensor.py @@ -3,11 +3,13 @@ import esphome.config_validation as cv from esphome.components import binary_sensor from . import AS3935, CONF_AS3935_ID -DEPENDENCIES = ['as3935'] +DEPENDENCIES = ["as3935"] -CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({ - cv.GenerateID(CONF_AS3935_ID): cv.use_id(AS3935), -}) +CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend( + { + cv.GenerateID(CONF_AS3935_ID): cv.use_id(AS3935), + } +) def to_code(config): diff --git a/esphome/components/as3935/sensor.py b/esphome/components/as3935/sensor.py index ff6f1d6198..64eeea7b04 100644 --- a/esphome/components/as3935/sensor.py +++ b/esphome/components/as3935/sensor.py @@ -1,19 +1,30 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor -from esphome.const import CONF_DISTANCE, CONF_LIGHTNING_ENERGY, DEVICE_CLASS_EMPTY, \ - UNIT_KILOMETER, UNIT_EMPTY, ICON_SIGNAL_DISTANCE_VARIANT, ICON_FLASH +from esphome.const import ( + CONF_DISTANCE, + CONF_LIGHTNING_ENERGY, + DEVICE_CLASS_EMPTY, + UNIT_KILOMETER, + UNIT_EMPTY, + ICON_SIGNAL_DISTANCE_VARIANT, + ICON_FLASH, +) from . import AS3935, CONF_AS3935_ID -DEPENDENCIES = ['as3935'] +DEPENDENCIES = ["as3935"] -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(CONF_AS3935_ID): cv.use_id(AS3935), - cv.Optional(CONF_DISTANCE): - sensor.sensor_schema(UNIT_KILOMETER, ICON_SIGNAL_DISTANCE_VARIANT, 1, DEVICE_CLASS_EMPTY), - cv.Optional(CONF_LIGHTNING_ENERGY): - sensor.sensor_schema(UNIT_EMPTY, ICON_FLASH, 1, DEVICE_CLASS_EMPTY), -}).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(CONF_AS3935_ID): cv.use_id(AS3935), + cv.Optional(CONF_DISTANCE): sensor.sensor_schema( + UNIT_KILOMETER, ICON_SIGNAL_DISTANCE_VARIANT, 1, DEVICE_CLASS_EMPTY + ), + cv.Optional(CONF_LIGHTNING_ENERGY): sensor.sensor_schema( + UNIT_EMPTY, ICON_FLASH, 1, DEVICE_CLASS_EMPTY + ), + } +).extend(cv.COMPONENT_SCHEMA) def to_code(config): diff --git a/esphome/components/as3935_i2c/__init__.py b/esphome/components/as3935_i2c/__init__.py index e22937ab81..277ccd84d3 100644 --- a/esphome/components/as3935_i2c/__init__.py +++ b/esphome/components/as3935_i2c/__init__.py @@ -3,15 +3,21 @@ import esphome.config_validation as cv from esphome.components import as3935, i2c from esphome.const import CONF_ID -AUTO_LOAD = ['as3935'] -DEPENDENCIES = ['i2c'] +AUTO_LOAD = ["as3935"] +DEPENDENCIES = ["i2c"] -as3935_i2c_ns = cg.esphome_ns.namespace('as3935_i2c') -I2CAS3935 = as3935_i2c_ns.class_('I2CAS3935Component', as3935.AS3935, i2c.I2CDevice) +as3935_i2c_ns = cg.esphome_ns.namespace("as3935_i2c") +I2CAS3935 = as3935_i2c_ns.class_("I2CAS3935Component", as3935.AS3935, i2c.I2CDevice) -CONFIG_SCHEMA = cv.All(as3935.AS3935_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(I2CAS3935), -}).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(0x03))) +CONFIG_SCHEMA = cv.All( + as3935.AS3935_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(I2CAS3935), + } + ) + .extend(cv.COMPONENT_SCHEMA) + .extend(i2c.i2c_device_schema(0x03)) +) def to_code(config): diff --git a/esphome/components/as3935_spi/__init__.py b/esphome/components/as3935_spi/__init__.py index 30c2240c27..b921444b8b 100644 --- a/esphome/components/as3935_spi/__init__.py +++ b/esphome/components/as3935_spi/__init__.py @@ -3,15 +3,21 @@ import esphome.config_validation as cv from esphome.components import as3935, spi from esphome.const import CONF_ID -AUTO_LOAD = ['as3935'] -DEPENDENCIES = ['spi'] +AUTO_LOAD = ["as3935"] +DEPENDENCIES = ["spi"] -as3935_spi_ns = cg.esphome_ns.namespace('as3935_spi') -SPIAS3935 = as3935_spi_ns.class_('SPIAS3935Component', as3935.AS3935, spi.SPIDevice) +as3935_spi_ns = cg.esphome_ns.namespace("as3935_spi") +SPIAS3935 = as3935_spi_ns.class_("SPIAS3935Component", as3935.AS3935, spi.SPIDevice) -CONFIG_SCHEMA = cv.All(as3935.AS3935_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(SPIAS3935), -}).extend(cv.COMPONENT_SCHEMA).extend(spi.spi_device_schema(cs_pin_required=True))) +CONFIG_SCHEMA = cv.All( + as3935.AS3935_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(SPIAS3935), + } + ) + .extend(cv.COMPONENT_SCHEMA) + .extend(spi.spi_device_schema(cs_pin_required=True)) +) def to_code(config): diff --git a/esphome/components/async_tcp/__init__.py b/esphome/components/async_tcp/__init__.py index b2307d5a7b..b07db9ed7c 100644 --- a/esphome/components/async_tcp/__init__.py +++ b/esphome/components/async_tcp/__init__.py @@ -2,14 +2,14 @@ import esphome.codegen as cg from esphome.core import CORE, coroutine_with_priority -CODEOWNERS = ['@OttoWinter'] +CODEOWNERS = ["@OttoWinter"] @coroutine_with_priority(200.0) def to_code(config): if CORE.is_esp32: # https://github.com/OttoWinter/AsyncTCP/blob/master/library.json - cg.add_library('AsyncTCP-esphome', '1.1.1') + cg.add_library("AsyncTCP-esphome", "1.1.1") elif CORE.is_esp8266: # https://github.com/OttoWinter/ESPAsyncTCP - cg.add_library('ESPAsyncTCP-esphome', '1.2.3') + cg.add_library("ESPAsyncTCP-esphome", "1.2.3") diff --git a/esphome/components/atc_mithermometer/sensor.py b/esphome/components/atc_mithermometer/sensor.py index 73115ff4ba..51891b33cb 100644 --- a/esphome/components/atc_mithermometer/sensor.py +++ b/esphome/components/atc_mithermometer/sensor.py @@ -1,32 +1,54 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, esp32_ble_tracker -from esphome.const import CONF_BATTERY_LEVEL, CONF_BATTERY_VOLTAGE, CONF_MAC_ADDRESS, \ - CONF_HUMIDITY, CONF_TEMPERATURE, CONF_ID, DEVICE_CLASS_BATTERY, DEVICE_CLASS_HUMIDITY, \ - DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_VOLTAGE, ICON_EMPTY, UNIT_CELSIUS, UNIT_PERCENT, \ - UNIT_VOLT +from esphome.const import ( + CONF_BATTERY_LEVEL, + CONF_BATTERY_VOLTAGE, + CONF_MAC_ADDRESS, + CONF_HUMIDITY, + CONF_TEMPERATURE, + CONF_ID, + DEVICE_CLASS_BATTERY, + DEVICE_CLASS_HUMIDITY, + DEVICE_CLASS_TEMPERATURE, + DEVICE_CLASS_VOLTAGE, + ICON_EMPTY, + UNIT_CELSIUS, + UNIT_PERCENT, + UNIT_VOLT, +) -CODEOWNERS = ['@ahpohl'] +CODEOWNERS = ["@ahpohl"] -DEPENDENCIES = ['esp32_ble_tracker'] +DEPENDENCIES = ["esp32_ble_tracker"] -atc_mithermometer_ns = cg.esphome_ns.namespace('atc_mithermometer') -ATCMiThermometer = atc_mithermometer_ns.class_('ATCMiThermometer', - esp32_ble_tracker.ESPBTDeviceListener, - cg.Component) +atc_mithermometer_ns = cg.esphome_ns.namespace("atc_mithermometer") +ATCMiThermometer = atc_mithermometer_ns.class_( + "ATCMiThermometer", esp32_ble_tracker.ESPBTDeviceListener, cg.Component +) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(ATCMiThermometer), - cv.Required(CONF_MAC_ADDRESS): cv.mac_address, - cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, - DEVICE_CLASS_TEMPERATURE), - cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 0, - DEVICE_CLASS_HUMIDITY), - cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 0, - DEVICE_CLASS_BATTERY), - cv.Optional(CONF_BATTERY_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_EMPTY, 3, - DEVICE_CLASS_VOLTAGE), -}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(ATCMiThermometer), + cv.Required(CONF_MAC_ADDRESS): cv.mac_address, + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( + UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE + ), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema( + UNIT_PERCENT, ICON_EMPTY, 0, DEVICE_CLASS_HUMIDITY + ), + cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema( + UNIT_PERCENT, ICON_EMPTY, 0, DEVICE_CLASS_BATTERY + ), + cv.Optional(CONF_BATTERY_VOLTAGE): sensor.sensor_schema( + UNIT_VOLT, ICON_EMPTY, 3, DEVICE_CLASS_VOLTAGE + ), + } + ) + .extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA) + .extend(cv.COMPONENT_SCHEMA) +) def to_code(config): diff --git a/esphome/components/atm90e32/sensor.py b/esphome/components/atm90e32/sensor.py index f35232b7e9..d0813cfa52 100644 --- a/esphome/components/atm90e32/sensor.py +++ b/esphome/components/atm90e32/sensor.py @@ -1,66 +1,106 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, spi -from esphome.const import \ - CONF_ID, CONF_VOLTAGE, CONF_CURRENT, CONF_POWER, CONF_POWER_FACTOR, CONF_FREQUENCY, \ - DEVICE_CLASS_CURRENT, DEVICE_CLASS_EMPTY, DEVICE_CLASS_POWER, DEVICE_CLASS_POWER_FACTOR, \ - DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_VOLTAGE, ICON_EMPTY, ICON_LIGHTBULB, ICON_CURRENT_AC, \ - UNIT_HERTZ, UNIT_VOLT, UNIT_AMPERE, UNIT_WATT, UNIT_EMPTY, UNIT_CELSIUS, UNIT_VOLT_AMPS_REACTIVE +from esphome.const import ( + CONF_ID, + CONF_VOLTAGE, + CONF_CURRENT, + CONF_POWER, + CONF_POWER_FACTOR, + CONF_FREQUENCY, + DEVICE_CLASS_CURRENT, + DEVICE_CLASS_EMPTY, + DEVICE_CLASS_POWER, + DEVICE_CLASS_POWER_FACTOR, + DEVICE_CLASS_TEMPERATURE, + DEVICE_CLASS_VOLTAGE, + ICON_EMPTY, + ICON_LIGHTBULB, + ICON_CURRENT_AC, + UNIT_HERTZ, + UNIT_VOLT, + UNIT_AMPERE, + UNIT_WATT, + UNIT_EMPTY, + UNIT_CELSIUS, + UNIT_VOLT_AMPS_REACTIVE, +) -CONF_PHASE_A = 'phase_a' -CONF_PHASE_B = 'phase_b' -CONF_PHASE_C = 'phase_c' +CONF_PHASE_A = "phase_a" +CONF_PHASE_B = "phase_b" +CONF_PHASE_C = "phase_c" -CONF_REACTIVE_POWER = 'reactive_power' -CONF_LINE_FREQUENCY = 'line_frequency' -CONF_CHIP_TEMPERATURE = 'chip_temperature' -CONF_GAIN_PGA = 'gain_pga' -CONF_CURRENT_PHASES = 'current_phases' -CONF_GAIN_VOLTAGE = 'gain_voltage' -CONF_GAIN_CT = 'gain_ct' +CONF_REACTIVE_POWER = "reactive_power" +CONF_LINE_FREQUENCY = "line_frequency" +CONF_CHIP_TEMPERATURE = "chip_temperature" +CONF_GAIN_PGA = "gain_pga" +CONF_CURRENT_PHASES = "current_phases" +CONF_GAIN_VOLTAGE = "gain_voltage" +CONF_GAIN_CT = "gain_ct" LINE_FREQS = { - '50HZ': 50, - '60HZ': 60, + "50HZ": 50, + "60HZ": 60, } CURRENT_PHASES = { - '2': 2, - '3': 3, + "2": 2, + "3": 3, } PGA_GAINS = { - '1X': 0x0, - '2X': 0x15, - '4X': 0x2A, + "1X": 0x0, + "2X": 0x15, + "4X": 0x2A, } -atm90e32_ns = cg.esphome_ns.namespace('atm90e32') -ATM90E32Component = atm90e32_ns.class_('ATM90E32Component', cg.PollingComponent, spi.SPIDevice) +atm90e32_ns = cg.esphome_ns.namespace("atm90e32") +ATM90E32Component = atm90e32_ns.class_( + "ATM90E32Component", cg.PollingComponent, spi.SPIDevice +) -ATM90E32_PHASE_SCHEMA = cv.Schema({ - cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_EMPTY, 2, DEVICE_CLASS_VOLTAGE), - cv.Optional(CONF_CURRENT): sensor.sensor_schema(UNIT_AMPERE, ICON_EMPTY, 2, - DEVICE_CLASS_CURRENT), - cv.Optional(CONF_POWER): sensor.sensor_schema(UNIT_WATT, ICON_EMPTY, 2, DEVICE_CLASS_POWER), - cv.Optional(CONF_REACTIVE_POWER): sensor.sensor_schema(UNIT_VOLT_AMPS_REACTIVE, - ICON_LIGHTBULB, 2, DEVICE_CLASS_EMPTY), - cv.Optional(CONF_POWER_FACTOR): sensor.sensor_schema(UNIT_EMPTY, ICON_EMPTY, 2, - DEVICE_CLASS_POWER_FACTOR), - cv.Optional(CONF_GAIN_VOLTAGE, default=7305): cv.uint16_t, - cv.Optional(CONF_GAIN_CT, default=27961): cv.uint16_t, -}) +ATM90E32_PHASE_SCHEMA = cv.Schema( + { + cv.Optional(CONF_VOLTAGE): sensor.sensor_schema( + UNIT_VOLT, ICON_EMPTY, 2, DEVICE_CLASS_VOLTAGE + ), + cv.Optional(CONF_CURRENT): sensor.sensor_schema( + UNIT_AMPERE, ICON_EMPTY, 2, DEVICE_CLASS_CURRENT + ), + cv.Optional(CONF_POWER): sensor.sensor_schema( + UNIT_WATT, ICON_EMPTY, 2, DEVICE_CLASS_POWER + ), + cv.Optional(CONF_REACTIVE_POWER): sensor.sensor_schema( + UNIT_VOLT_AMPS_REACTIVE, ICON_LIGHTBULB, 2, DEVICE_CLASS_EMPTY + ), + cv.Optional(CONF_POWER_FACTOR): sensor.sensor_schema( + UNIT_EMPTY, ICON_EMPTY, 2, DEVICE_CLASS_POWER_FACTOR + ), + cv.Optional(CONF_GAIN_VOLTAGE, default=7305): cv.uint16_t, + cv.Optional(CONF_GAIN_CT, default=27961): cv.uint16_t, + } +) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(ATM90E32Component), - cv.Optional(CONF_PHASE_A): ATM90E32_PHASE_SCHEMA, - cv.Optional(CONF_PHASE_B): ATM90E32_PHASE_SCHEMA, - cv.Optional(CONF_PHASE_C): ATM90E32_PHASE_SCHEMA, - cv.Optional(CONF_FREQUENCY): sensor.sensor_schema(UNIT_HERTZ, ICON_CURRENT_AC, 1, - DEVICE_CLASS_EMPTY), - cv.Optional(CONF_CHIP_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, - DEVICE_CLASS_TEMPERATURE), - cv.Required(CONF_LINE_FREQUENCY): cv.enum(LINE_FREQS, upper=True), - cv.Optional(CONF_CURRENT_PHASES, default='3'): cv.enum(CURRENT_PHASES, upper=True), - cv.Optional(CONF_GAIN_PGA, default='2X'): cv.enum(PGA_GAINS, upper=True), -}).extend(cv.polling_component_schema('60s')).extend(spi.spi_device_schema()) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(ATM90E32Component), + cv.Optional(CONF_PHASE_A): ATM90E32_PHASE_SCHEMA, + cv.Optional(CONF_PHASE_B): ATM90E32_PHASE_SCHEMA, + cv.Optional(CONF_PHASE_C): ATM90E32_PHASE_SCHEMA, + cv.Optional(CONF_FREQUENCY): sensor.sensor_schema( + UNIT_HERTZ, ICON_CURRENT_AC, 1, DEVICE_CLASS_EMPTY + ), + cv.Optional(CONF_CHIP_TEMPERATURE): sensor.sensor_schema( + UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE + ), + cv.Required(CONF_LINE_FREQUENCY): cv.enum(LINE_FREQS, upper=True), + cv.Optional(CONF_CURRENT_PHASES, default="3"): cv.enum( + CURRENT_PHASES, upper=True + ), + cv.Optional(CONF_GAIN_PGA, default="2X"): cv.enum(PGA_GAINS, upper=True), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(spi.spi_device_schema()) +) def to_code(config): diff --git a/esphome/components/bang_bang/__init__.py b/esphome/components/bang_bang/__init__.py index 6f14e10033..71a87b6ae5 100644 --- a/esphome/components/bang_bang/__init__.py +++ b/esphome/components/bang_bang/__init__.py @@ -1 +1 @@ -CODEOWNERS = ['@OttoWinter'] +CODEOWNERS = ["@OttoWinter"] diff --git a/esphome/components/bang_bang/climate.py b/esphome/components/bang_bang/climate.py index 4ef811c55d..e0d75a6a1e 100644 --- a/esphome/components/bang_bang/climate.py +++ b/esphome/components/bang_bang/climate.py @@ -2,27 +2,41 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import automation from esphome.components import climate, sensor -from esphome.const import CONF_AWAY_CONFIG, CONF_COOL_ACTION, \ - CONF_DEFAULT_TARGET_TEMPERATURE_HIGH, CONF_DEFAULT_TARGET_TEMPERATURE_LOW, CONF_HEAT_ACTION, \ - CONF_ID, CONF_IDLE_ACTION, CONF_SENSOR +from esphome.const import ( + CONF_AWAY_CONFIG, + CONF_COOL_ACTION, + CONF_DEFAULT_TARGET_TEMPERATURE_HIGH, + CONF_DEFAULT_TARGET_TEMPERATURE_LOW, + CONF_HEAT_ACTION, + CONF_ID, + CONF_IDLE_ACTION, + CONF_SENSOR, +) -bang_bang_ns = cg.esphome_ns.namespace('bang_bang') -BangBangClimate = bang_bang_ns.class_('BangBangClimate', climate.Climate, cg.Component) -BangBangClimateTargetTempConfig = bang_bang_ns.struct('BangBangClimateTargetTempConfig') +bang_bang_ns = cg.esphome_ns.namespace("bang_bang") +BangBangClimate = bang_bang_ns.class_("BangBangClimate", climate.Climate, cg.Component) +BangBangClimateTargetTempConfig = bang_bang_ns.struct("BangBangClimateTargetTempConfig") -CONFIG_SCHEMA = cv.All(climate.CLIMATE_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(BangBangClimate), - cv.Required(CONF_SENSOR): cv.use_id(sensor.Sensor), - cv.Required(CONF_DEFAULT_TARGET_TEMPERATURE_LOW): cv.temperature, - cv.Required(CONF_DEFAULT_TARGET_TEMPERATURE_HIGH): cv.temperature, - cv.Required(CONF_IDLE_ACTION): automation.validate_automation(single=True), - cv.Optional(CONF_COOL_ACTION): automation.validate_automation(single=True), - cv.Optional(CONF_HEAT_ACTION): automation.validate_automation(single=True), - cv.Optional(CONF_AWAY_CONFIG): cv.Schema({ - cv.Required(CONF_DEFAULT_TARGET_TEMPERATURE_LOW): cv.temperature, - cv.Required(CONF_DEFAULT_TARGET_TEMPERATURE_HIGH): cv.temperature, - }), -}).extend(cv.COMPONENT_SCHEMA), cv.has_at_least_one_key(CONF_COOL_ACTION, CONF_HEAT_ACTION)) +CONFIG_SCHEMA = cv.All( + climate.CLIMATE_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(BangBangClimate), + cv.Required(CONF_SENSOR): cv.use_id(sensor.Sensor), + cv.Required(CONF_DEFAULT_TARGET_TEMPERATURE_LOW): cv.temperature, + cv.Required(CONF_DEFAULT_TARGET_TEMPERATURE_HIGH): cv.temperature, + cv.Required(CONF_IDLE_ACTION): automation.validate_automation(single=True), + cv.Optional(CONF_COOL_ACTION): automation.validate_automation(single=True), + cv.Optional(CONF_HEAT_ACTION): automation.validate_automation(single=True), + cv.Optional(CONF_AWAY_CONFIG): cv.Schema( + { + cv.Required(CONF_DEFAULT_TARGET_TEMPERATURE_LOW): cv.temperature, + cv.Required(CONF_DEFAULT_TARGET_TEMPERATURE_HIGH): cv.temperature, + } + ), + } + ).extend(cv.COMPONENT_SCHEMA), + cv.has_at_least_one_key(CONF_COOL_ACTION, CONF_HEAT_ACTION), +) def to_code(config): @@ -35,23 +49,29 @@ def to_code(config): normal_config = BangBangClimateTargetTempConfig( config[CONF_DEFAULT_TARGET_TEMPERATURE_LOW], - config[CONF_DEFAULT_TARGET_TEMPERATURE_HIGH] + config[CONF_DEFAULT_TARGET_TEMPERATURE_HIGH], ) cg.add(var.set_normal_config(normal_config)) - yield automation.build_automation(var.get_idle_trigger(), [], config[CONF_IDLE_ACTION]) + yield automation.build_automation( + var.get_idle_trigger(), [], config[CONF_IDLE_ACTION] + ) if CONF_COOL_ACTION in config: - yield automation.build_automation(var.get_cool_trigger(), [], config[CONF_COOL_ACTION]) + yield automation.build_automation( + var.get_cool_trigger(), [], config[CONF_COOL_ACTION] + ) cg.add(var.set_supports_cool(True)) if CONF_HEAT_ACTION in config: - yield automation.build_automation(var.get_heat_trigger(), [], config[CONF_HEAT_ACTION]) + yield automation.build_automation( + var.get_heat_trigger(), [], config[CONF_HEAT_ACTION] + ) cg.add(var.set_supports_heat(True)) if CONF_AWAY_CONFIG in config: away = config[CONF_AWAY_CONFIG] away_config = BangBangClimateTargetTempConfig( away[CONF_DEFAULT_TARGET_TEMPERATURE_LOW], - away[CONF_DEFAULT_TARGET_TEMPERATURE_HIGH] + away[CONF_DEFAULT_TARGET_TEMPERATURE_HIGH], ) cg.add(var.set_away_config(away_config)) diff --git a/esphome/components/bh1750/sensor.py b/esphome/components/bh1750/sensor.py index 98777ab9c1..9ad1b7eadb 100644 --- a/esphome/components/bh1750/sensor.py +++ b/esphome/components/bh1750/sensor.py @@ -1,26 +1,45 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import CONF_ID, CONF_RESOLUTION, DEVICE_CLASS_ILLUMINANCE, ICON_EMPTY, UNIT_LUX +from esphome.const import ( + CONF_ID, + CONF_RESOLUTION, + DEVICE_CLASS_ILLUMINANCE, + ICON_EMPTY, + UNIT_LUX, +) -DEPENDENCIES = ['i2c'] +DEPENDENCIES = ["i2c"] -bh1750_ns = cg.esphome_ns.namespace('bh1750') -BH1750Resolution = bh1750_ns.enum('BH1750Resolution') +bh1750_ns = cg.esphome_ns.namespace("bh1750") +BH1750Resolution = bh1750_ns.enum("BH1750Resolution") BH1750_RESOLUTIONS = { 4.0: BH1750Resolution.BH1750_RESOLUTION_4P0_LX, 1.0: BH1750Resolution.BH1750_RESOLUTION_1P0_LX, 0.5: BH1750Resolution.BH1750_RESOLUTION_0P5_LX, } -BH1750Sensor = bh1750_ns.class_('BH1750Sensor', sensor.Sensor, cg.PollingComponent, i2c.I2CDevice) +BH1750Sensor = bh1750_ns.class_( + "BH1750Sensor", sensor.Sensor, cg.PollingComponent, i2c.I2CDevice +) -CONF_MEASUREMENT_TIME = 'measurement_time' -CONFIG_SCHEMA = sensor.sensor_schema(UNIT_LUX, ICON_EMPTY, 1, DEVICE_CLASS_ILLUMINANCE).extend({ - cv.GenerateID(): cv.declare_id(BH1750Sensor), - cv.Optional(CONF_RESOLUTION, default=0.5): cv.enum(BH1750_RESOLUTIONS, float=True), - cv.Optional(CONF_MEASUREMENT_TIME, default=69): cv.int_range(min=31, max=254), -}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x23)) +CONF_MEASUREMENT_TIME = "measurement_time" +CONFIG_SCHEMA = ( + sensor.sensor_schema(UNIT_LUX, ICON_EMPTY, 1, DEVICE_CLASS_ILLUMINANCE) + .extend( + { + cv.GenerateID(): cv.declare_id(BH1750Sensor), + cv.Optional(CONF_RESOLUTION, default=0.5): cv.enum( + BH1750_RESOLUTIONS, float=True + ), + cv.Optional(CONF_MEASUREMENT_TIME, default=69): cv.int_range( + min=31, max=254 + ), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x23)) +) def to_code(config): diff --git a/esphome/components/binary/__init__.py b/esphome/components/binary/__init__.py index 7a8031df54..3f6a45d28a 100644 --- a/esphome/components/binary/__init__.py +++ b/esphome/components/binary/__init__.py @@ -1,3 +1,3 @@ import esphome.codegen as cg -binary_ns = cg.esphome_ns.namespace('binary') +binary_ns = cg.esphome_ns.namespace("binary") diff --git a/esphome/components/binary/fan/__init__.py b/esphome/components/binary/fan/__init__.py index 6969c1dbbf..88dc506235 100644 --- a/esphome/components/binary/fan/__init__.py +++ b/esphome/components/binary/fan/__init__.py @@ -1,18 +1,24 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import fan, output -from esphome.const import CONF_DIRECTION_OUTPUT, CONF_OSCILLATION_OUTPUT, \ - CONF_OUTPUT, CONF_OUTPUT_ID +from esphome.const import ( + CONF_DIRECTION_OUTPUT, + CONF_OSCILLATION_OUTPUT, + CONF_OUTPUT, + CONF_OUTPUT_ID, +) from .. import binary_ns -BinaryFan = binary_ns.class_('BinaryFan', cg.Component) +BinaryFan = binary_ns.class_("BinaryFan", cg.Component) -CONFIG_SCHEMA = fan.FAN_SCHEMA.extend({ - cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(BinaryFan), - cv.Required(CONF_OUTPUT): cv.use_id(output.BinaryOutput), - cv.Optional(CONF_DIRECTION_OUTPUT): cv.use_id(output.BinaryOutput), - cv.Optional(CONF_OSCILLATION_OUTPUT): cv.use_id(output.BinaryOutput), -}).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = fan.FAN_SCHEMA.extend( + { + cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(BinaryFan), + cv.Required(CONF_OUTPUT): cv.use_id(output.BinaryOutput), + cv.Optional(CONF_DIRECTION_OUTPUT): cv.use_id(output.BinaryOutput), + cv.Optional(CONF_OSCILLATION_OUTPUT): cv.use_id(output.BinaryOutput), + } +).extend(cv.COMPONENT_SCHEMA) def to_code(config): diff --git a/esphome/components/binary/light/__init__.py b/esphome/components/binary/light/__init__.py index 6167ae239f..7e62f4705f 100644 --- a/esphome/components/binary/light/__init__.py +++ b/esphome/components/binary/light/__init__.py @@ -4,12 +4,14 @@ from esphome.components import light, output from esphome.const import CONF_OUTPUT_ID, CONF_OUTPUT from .. import binary_ns -BinaryLightOutput = binary_ns.class_('BinaryLightOutput', light.LightOutput) +BinaryLightOutput = binary_ns.class_("BinaryLightOutput", light.LightOutput) -CONFIG_SCHEMA = light.BINARY_LIGHT_SCHEMA.extend({ - cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(BinaryLightOutput), - cv.Required(CONF_OUTPUT): cv.use_id(output.BinaryOutput), -}) +CONFIG_SCHEMA = light.BINARY_LIGHT_SCHEMA.extend( + { + cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(BinaryLightOutput), + cv.Required(CONF_OUTPUT): cv.use_id(output.BinaryOutput), + } +) def to_code(config): diff --git a/esphome/components/binary_sensor/__init__.py b/esphome/components/binary_sensor/__init__.py index da1dc697a2..56e6b7dc97 100644 --- a/esphome/components/binary_sensor/__init__.py +++ b/esphome/components/binary_sensor/__init__.py @@ -3,141 +3,214 @@ import esphome.config_validation as cv from esphome import automation, core from esphome.automation import Condition, maybe_simple_id from esphome.components import mqtt -from esphome.const import CONF_DEVICE_CLASS, CONF_FILTERS, \ - CONF_ID, CONF_INTERNAL, CONF_INVALID_COOLDOWN, CONF_INVERTED, \ - CONF_MAX_LENGTH, CONF_MIN_LENGTH, CONF_ON_CLICK, \ - CONF_ON_DOUBLE_CLICK, CONF_ON_MULTI_CLICK, CONF_ON_PRESS, CONF_ON_RELEASE, CONF_ON_STATE, \ - CONF_STATE, CONF_TIMING, CONF_TRIGGER_ID, CONF_FOR, CONF_NAME, CONF_MQTT_ID, \ - DEVICE_CLASS_EMPTY, DEVICE_CLASS_BATTERY, DEVICE_CLASS_BATTERY_CHARGING, DEVICE_CLASS_COLD, \ - DEVICE_CLASS_CONNECTIVITY, DEVICE_CLASS_DOOR, DEVICE_CLASS_GARAGE_DOOR, DEVICE_CLASS_GAS, \ - DEVICE_CLASS_HEAT, DEVICE_CLASS_LIGHT, DEVICE_CLASS_LOCK, DEVICE_CLASS_MOISTURE, \ - DEVICE_CLASS_MOTION, DEVICE_CLASS_MOVING, DEVICE_CLASS_OCCUPANCY, DEVICE_CLASS_OPENING, \ - DEVICE_CLASS_PLUG, DEVICE_CLASS_POWER, DEVICE_CLASS_PRESENCE, DEVICE_CLASS_PROBLEM, \ - DEVICE_CLASS_SAFETY, DEVICE_CLASS_SMOKE, DEVICE_CLASS_SOUND, DEVICE_CLASS_VIBRATION, \ - DEVICE_CLASS_WINDOW +from esphome.const import ( + CONF_DEVICE_CLASS, + CONF_FILTERS, + CONF_ID, + CONF_INTERNAL, + CONF_INVALID_COOLDOWN, + CONF_INVERTED, + CONF_MAX_LENGTH, + CONF_MIN_LENGTH, + CONF_ON_CLICK, + CONF_ON_DOUBLE_CLICK, + CONF_ON_MULTI_CLICK, + CONF_ON_PRESS, + CONF_ON_RELEASE, + CONF_ON_STATE, + CONF_STATE, + CONF_TIMING, + CONF_TRIGGER_ID, + CONF_FOR, + CONF_NAME, + CONF_MQTT_ID, + DEVICE_CLASS_EMPTY, + DEVICE_CLASS_BATTERY, + DEVICE_CLASS_BATTERY_CHARGING, + DEVICE_CLASS_COLD, + DEVICE_CLASS_CONNECTIVITY, + DEVICE_CLASS_DOOR, + DEVICE_CLASS_GARAGE_DOOR, + DEVICE_CLASS_GAS, + DEVICE_CLASS_HEAT, + DEVICE_CLASS_LIGHT, + DEVICE_CLASS_LOCK, + DEVICE_CLASS_MOISTURE, + DEVICE_CLASS_MOTION, + DEVICE_CLASS_MOVING, + DEVICE_CLASS_OCCUPANCY, + DEVICE_CLASS_OPENING, + DEVICE_CLASS_PLUG, + DEVICE_CLASS_POWER, + DEVICE_CLASS_PRESENCE, + DEVICE_CLASS_PROBLEM, + DEVICE_CLASS_SAFETY, + DEVICE_CLASS_SMOKE, + DEVICE_CLASS_SOUND, + DEVICE_CLASS_VIBRATION, + DEVICE_CLASS_WINDOW, +) from esphome.core import CORE, coroutine, coroutine_with_priority from esphome.util import Registry -CODEOWNERS = ['@esphome/core'] +CODEOWNERS = ["@esphome/core"] DEVICE_CLASSES = [ - DEVICE_CLASS_EMPTY, DEVICE_CLASS_BATTERY, DEVICE_CLASS_BATTERY_CHARGING, DEVICE_CLASS_COLD, - DEVICE_CLASS_CONNECTIVITY, DEVICE_CLASS_DOOR, DEVICE_CLASS_GARAGE_DOOR, DEVICE_CLASS_GAS, - DEVICE_CLASS_HEAT, DEVICE_CLASS_LIGHT, DEVICE_CLASS_LOCK, DEVICE_CLASS_MOISTURE, - DEVICE_CLASS_MOTION, DEVICE_CLASS_MOVING, DEVICE_CLASS_OCCUPANCY, DEVICE_CLASS_OPENING, - DEVICE_CLASS_PLUG, DEVICE_CLASS_POWER, DEVICE_CLASS_PRESENCE, DEVICE_CLASS_PROBLEM, - DEVICE_CLASS_SAFETY, DEVICE_CLASS_SMOKE, DEVICE_CLASS_SOUND, DEVICE_CLASS_VIBRATION, - DEVICE_CLASS_WINDOW + DEVICE_CLASS_EMPTY, + DEVICE_CLASS_BATTERY, + DEVICE_CLASS_BATTERY_CHARGING, + DEVICE_CLASS_COLD, + DEVICE_CLASS_CONNECTIVITY, + DEVICE_CLASS_DOOR, + DEVICE_CLASS_GARAGE_DOOR, + DEVICE_CLASS_GAS, + DEVICE_CLASS_HEAT, + DEVICE_CLASS_LIGHT, + DEVICE_CLASS_LOCK, + DEVICE_CLASS_MOISTURE, + DEVICE_CLASS_MOTION, + DEVICE_CLASS_MOVING, + DEVICE_CLASS_OCCUPANCY, + DEVICE_CLASS_OPENING, + DEVICE_CLASS_PLUG, + DEVICE_CLASS_POWER, + DEVICE_CLASS_PRESENCE, + DEVICE_CLASS_PROBLEM, + DEVICE_CLASS_SAFETY, + DEVICE_CLASS_SMOKE, + DEVICE_CLASS_SOUND, + DEVICE_CLASS_VIBRATION, + DEVICE_CLASS_WINDOW, ] IS_PLATFORM_COMPONENT = True -binary_sensor_ns = cg.esphome_ns.namespace('binary_sensor') -BinarySensor = binary_sensor_ns.class_('BinarySensor', cg.Nameable) -BinarySensorInitiallyOff = binary_sensor_ns.class_('BinarySensorInitiallyOff', BinarySensor) -BinarySensorPtr = BinarySensor.operator('ptr') +binary_sensor_ns = cg.esphome_ns.namespace("binary_sensor") +BinarySensor = binary_sensor_ns.class_("BinarySensor", cg.Nameable) +BinarySensorInitiallyOff = binary_sensor_ns.class_( + "BinarySensorInitiallyOff", BinarySensor +) +BinarySensorPtr = BinarySensor.operator("ptr") # Triggers -PressTrigger = binary_sensor_ns.class_('PressTrigger', automation.Trigger.template()) -ReleaseTrigger = binary_sensor_ns.class_('ReleaseTrigger', automation.Trigger.template()) -ClickTrigger = binary_sensor_ns.class_('ClickTrigger', automation.Trigger.template()) -DoubleClickTrigger = binary_sensor_ns.class_('DoubleClickTrigger', automation.Trigger.template()) -MultiClickTrigger = binary_sensor_ns.class_('MultiClickTrigger', automation.Trigger.template(), - cg.Component) -MultiClickTriggerEvent = binary_sensor_ns.struct('MultiClickTriggerEvent') -StateTrigger = binary_sensor_ns.class_('StateTrigger', automation.Trigger.template(bool)) -BinarySensorPublishAction = binary_sensor_ns.class_('BinarySensorPublishAction', automation.Action) +PressTrigger = binary_sensor_ns.class_("PressTrigger", automation.Trigger.template()) +ReleaseTrigger = binary_sensor_ns.class_( + "ReleaseTrigger", automation.Trigger.template() +) +ClickTrigger = binary_sensor_ns.class_("ClickTrigger", automation.Trigger.template()) +DoubleClickTrigger = binary_sensor_ns.class_( + "DoubleClickTrigger", automation.Trigger.template() +) +MultiClickTrigger = binary_sensor_ns.class_( + "MultiClickTrigger", automation.Trigger.template(), cg.Component +) +MultiClickTriggerEvent = binary_sensor_ns.struct("MultiClickTriggerEvent") +StateTrigger = binary_sensor_ns.class_( + "StateTrigger", automation.Trigger.template(bool) +) +BinarySensorPublishAction = binary_sensor_ns.class_( + "BinarySensorPublishAction", automation.Action +) # Condition -BinarySensorCondition = binary_sensor_ns.class_('BinarySensorCondition', Condition) +BinarySensorCondition = binary_sensor_ns.class_("BinarySensorCondition", Condition) # Filters -Filter = binary_sensor_ns.class_('Filter') -DelayedOnOffFilter = binary_sensor_ns.class_('DelayedOnOffFilter', Filter, cg.Component) -DelayedOnFilter = binary_sensor_ns.class_('DelayedOnFilter', Filter, cg.Component) -DelayedOffFilter = binary_sensor_ns.class_('DelayedOffFilter', Filter, cg.Component) -InvertFilter = binary_sensor_ns.class_('InvertFilter', Filter) -LambdaFilter = binary_sensor_ns.class_('LambdaFilter', Filter) +Filter = binary_sensor_ns.class_("Filter") +DelayedOnOffFilter = binary_sensor_ns.class_("DelayedOnOffFilter", Filter, cg.Component) +DelayedOnFilter = binary_sensor_ns.class_("DelayedOnFilter", Filter, cg.Component) +DelayedOffFilter = binary_sensor_ns.class_("DelayedOffFilter", Filter, cg.Component) +InvertFilter = binary_sensor_ns.class_("InvertFilter", Filter) +LambdaFilter = binary_sensor_ns.class_("LambdaFilter", Filter) FILTER_REGISTRY = Registry() -validate_filters = cv.validate_registry('filter', FILTER_REGISTRY) +validate_filters = cv.validate_registry("filter", FILTER_REGISTRY) -@FILTER_REGISTRY.register('invert', InvertFilter, {}) +@FILTER_REGISTRY.register("invert", InvertFilter, {}) def invert_filter_to_code(config, filter_id): yield cg.new_Pvariable(filter_id) -@FILTER_REGISTRY.register('delayed_on_off', DelayedOnOffFilter, - cv.positive_time_period_milliseconds) +@FILTER_REGISTRY.register( + "delayed_on_off", DelayedOnOffFilter, cv.positive_time_period_milliseconds +) def delayed_on_off_filter_to_code(config, filter_id): var = cg.new_Pvariable(filter_id, config) yield cg.register_component(var, {}) yield var -@FILTER_REGISTRY.register('delayed_on', DelayedOnFilter, - cv.positive_time_period_milliseconds) +@FILTER_REGISTRY.register( + "delayed_on", DelayedOnFilter, cv.positive_time_period_milliseconds +) def delayed_on_filter_to_code(config, filter_id): var = cg.new_Pvariable(filter_id, config) yield cg.register_component(var, {}) yield var -@FILTER_REGISTRY.register('delayed_off', DelayedOffFilter, cv.positive_time_period_milliseconds) +@FILTER_REGISTRY.register( + "delayed_off", DelayedOffFilter, cv.positive_time_period_milliseconds +) def delayed_off_filter_to_code(config, filter_id): var = cg.new_Pvariable(filter_id, config) yield cg.register_component(var, {}) yield var -@FILTER_REGISTRY.register('lambda', LambdaFilter, cv.returning_lambda) +@FILTER_REGISTRY.register("lambda", LambdaFilter, cv.returning_lambda) def lambda_filter_to_code(config, filter_id): - lambda_ = yield cg.process_lambda(config, [(bool, 'x')], return_type=cg.optional.template(bool)) + lambda_ = yield cg.process_lambda( + config, [(bool, "x")], return_type=cg.optional.template(bool) + ) yield cg.new_Pvariable(filter_id, lambda_) -MULTI_CLICK_TIMING_SCHEMA = cv.Schema({ - cv.Optional(CONF_STATE): cv.boolean, - cv.Optional(CONF_MIN_LENGTH): cv.positive_time_period_milliseconds, - cv.Optional(CONF_MAX_LENGTH): cv.positive_time_period_milliseconds, -}) +MULTI_CLICK_TIMING_SCHEMA = cv.Schema( + { + cv.Optional(CONF_STATE): cv.boolean, + cv.Optional(CONF_MIN_LENGTH): cv.positive_time_period_milliseconds, + cv.Optional(CONF_MAX_LENGTH): cv.positive_time_period_milliseconds, + } +) def parse_multi_click_timing_str(value): if not isinstance(value, str): return value - parts = value.lower().split(' ') + parts = value.lower().split(" ") if len(parts) != 5: - raise cv.Invalid("Multi click timing grammar consists of exactly 5 words, not {}" - "".format(len(parts))) + raise cv.Invalid( + "Multi click timing grammar consists of exactly 5 words, not {}" + "".format(len(parts)) + ) try: state = cv.boolean(parts[0]) except cv.Invalid: # pylint: disable=raise-missing-from raise cv.Invalid("First word must either be ON or OFF, not {}".format(parts[0])) - if parts[1] != 'for': + if parts[1] != "for": raise cv.Invalid("Second word must be 'for', got {}".format(parts[1])) - if parts[2] == 'at': - if parts[3] == 'least': + if parts[2] == "at": + if parts[3] == "least": key = CONF_MIN_LENGTH - elif parts[3] == 'most': + elif parts[3] == "most": key = CONF_MAX_LENGTH else: - raise cv.Invalid("Third word after at must either be 'least' or 'most', got {}" - "".format(parts[3])) + raise cv.Invalid( + "Third word after at must either be 'least' or 'most', got {}" + "".format(parts[3]) + ) try: length = cv.positive_time_period_milliseconds(parts[4]) except cv.Invalid as err: raise cv.Invalid(f"Multi Click Grammar Parsing length failed: {err}") - return { - CONF_STATE: state, - key: str(length) - } + return {CONF_STATE: state, key: str(length)} - if parts[3] != 'to': + if parts[3] != "to": raise cv.Invalid("Multi click grammar: 4th word must be 'to'") try: @@ -153,7 +226,7 @@ def parse_multi_click_timing_str(value): return { CONF_STATE: state, CONF_MIN_LENGTH: str(min_length), - CONF_MAX_LENGTH: str(max_length) + CONF_MAX_LENGTH: str(max_length), } @@ -173,11 +246,15 @@ def validate_multi_click_timing(value): new_state = v_.get(CONF_STATE, not state) if new_state == state: - raise cv.Invalid("Timings must have alternating state. Indices {} and {} have " - "the same state {}".format(i, i + 1, state)) + raise cv.Invalid( + "Timings must have alternating state. Indices {} and {} have " + "the same state {}".format(i, i + 1, state) + ) if max_length is not None and max_length < min_length: - raise cv.Invalid("Max length ({}) must be larger than min length ({})." - "".format(max_length, min_length)) + raise cv.Invalid( + "Max length ({}) must be larger than min length ({})." + "".format(max_length, min_length) + ) state = new_state tim = { @@ -190,46 +267,71 @@ def validate_multi_click_timing(value): return timings -device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space='_') +device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_") -BINARY_SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(BinarySensor), - cv.OnlyWith(CONF_MQTT_ID, 'mqtt'): cv.declare_id(mqtt.MQTTBinarySensorComponent), - - cv.Optional(CONF_DEVICE_CLASS): device_class, - cv.Optional(CONF_FILTERS): validate_filters, - cv.Optional(CONF_ON_PRESS): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PressTrigger), - }), - cv.Optional(CONF_ON_RELEASE): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ReleaseTrigger), - }), - cv.Optional(CONF_ON_CLICK): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ClickTrigger), - cv.Optional(CONF_MIN_LENGTH, default='50ms'): cv.positive_time_period_milliseconds, - cv.Optional(CONF_MAX_LENGTH, default='350ms'): cv.positive_time_period_milliseconds, - }), - cv.Optional(CONF_ON_DOUBLE_CLICK): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(DoubleClickTrigger), - cv.Optional(CONF_MIN_LENGTH, default='50ms'): cv.positive_time_period_milliseconds, - cv.Optional(CONF_MAX_LENGTH, default='350ms'): cv.positive_time_period_milliseconds, - }), - cv.Optional(CONF_ON_MULTI_CLICK): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(MultiClickTrigger), - cv.Required(CONF_TIMING): cv.All([parse_multi_click_timing_str], - validate_multi_click_timing), - cv.Optional(CONF_INVALID_COOLDOWN, default='1s'): cv.positive_time_period_milliseconds, - }), - cv.Optional(CONF_ON_STATE): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StateTrigger), - }), - - cv.Optional(CONF_INVERTED): cv.invalid( - "The inverted binary_sensor property has been replaced by the " - "new 'invert' binary sensor filter. Please see " - "https://esphome.io/components/binary_sensor/index.html." - ), -}) +BINARY_SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(BinarySensor), + cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id( + mqtt.MQTTBinarySensorComponent + ), + cv.Optional(CONF_DEVICE_CLASS): device_class, + cv.Optional(CONF_FILTERS): validate_filters, + cv.Optional(CONF_ON_PRESS): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PressTrigger), + } + ), + cv.Optional(CONF_ON_RELEASE): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ReleaseTrigger), + } + ), + cv.Optional(CONF_ON_CLICK): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ClickTrigger), + cv.Optional( + CONF_MIN_LENGTH, default="50ms" + ): cv.positive_time_period_milliseconds, + cv.Optional( + CONF_MAX_LENGTH, default="350ms" + ): cv.positive_time_period_milliseconds, + } + ), + cv.Optional(CONF_ON_DOUBLE_CLICK): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(DoubleClickTrigger), + cv.Optional( + CONF_MIN_LENGTH, default="50ms" + ): cv.positive_time_period_milliseconds, + cv.Optional( + CONF_MAX_LENGTH, default="350ms" + ): cv.positive_time_period_milliseconds, + } + ), + cv.Optional(CONF_ON_MULTI_CLICK): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(MultiClickTrigger), + cv.Required(CONF_TIMING): cv.All( + [parse_multi_click_timing_str], validate_multi_click_timing + ), + cv.Optional( + CONF_INVALID_COOLDOWN, default="1s" + ): cv.positive_time_period_milliseconds, + } + ), + cv.Optional(CONF_ON_STATE): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StateTrigger), + } + ), + cv.Optional(CONF_INVERTED): cv.invalid( + "The inverted binary_sensor property has been replaced by the " + "new 'invert' binary sensor filter. Please see " + "https://esphome.io/components/binary_sensor/index.html." + ), + } +) @coroutine @@ -254,24 +356,28 @@ def setup_binary_sensor_core_(var, config): yield automation.build_automation(trigger, [], conf) for conf in config.get(CONF_ON_CLICK, []): - trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var, - conf[CONF_MIN_LENGTH], conf[CONF_MAX_LENGTH]) + trigger = cg.new_Pvariable( + conf[CONF_TRIGGER_ID], var, conf[CONF_MIN_LENGTH], conf[CONF_MAX_LENGTH] + ) yield automation.build_automation(trigger, [], conf) for conf in config.get(CONF_ON_DOUBLE_CLICK, []): - trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var, - conf[CONF_MIN_LENGTH], conf[CONF_MAX_LENGTH]) + trigger = cg.new_Pvariable( + conf[CONF_TRIGGER_ID], var, conf[CONF_MIN_LENGTH], conf[CONF_MAX_LENGTH] + ) yield automation.build_automation(trigger, [], conf) for conf in config.get(CONF_ON_MULTI_CLICK, []): timings = [] for tim in conf[CONF_TIMING]: - timings.append(cg.StructInitializer( - MultiClickTriggerEvent, - ('state', tim[CONF_STATE]), - ('min_length', tim[CONF_MIN_LENGTH]), - ('max_length', tim.get(CONF_MAX_LENGTH, 4294967294)), - )) + timings.append( + cg.StructInitializer( + MultiClickTriggerEvent, + ("state", tim[CONF_STATE]), + ("min_length", tim[CONF_MIN_LENGTH]), + ("max_length", tim.get(CONF_MAX_LENGTH, 4294967294)), + ) + ) trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var, timings) if CONF_INVALID_COOLDOWN in conf: cg.add(trigger.set_invalid_cooldown(conf[CONF_INVALID_COOLDOWN])) @@ -280,7 +386,7 @@ def setup_binary_sensor_core_(var, config): for conf in config.get(CONF_ON_STATE, []): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) - yield automation.build_automation(trigger, [(bool, 'x')], conf) + yield automation.build_automation(trigger, [(bool, "x")], conf) if CONF_MQTT_ID in config: mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var) @@ -302,22 +408,28 @@ def new_binary_sensor(config): yield var -BINARY_SENSOR_CONDITION_SCHEMA = maybe_simple_id({ - cv.Required(CONF_ID): cv.use_id(BinarySensor), - cv.Optional(CONF_FOR): cv.invalid("This option has been removed in 1.13, please use the " - "'for' condition instead."), -}) +BINARY_SENSOR_CONDITION_SCHEMA = maybe_simple_id( + { + cv.Required(CONF_ID): cv.use_id(BinarySensor), + cv.Optional(CONF_FOR): cv.invalid( + "This option has been removed in 1.13, please use the " + "'for' condition instead." + ), + } +) -@automation.register_condition('binary_sensor.is_on', BinarySensorCondition, - BINARY_SENSOR_CONDITION_SCHEMA) +@automation.register_condition( + "binary_sensor.is_on", BinarySensorCondition, BINARY_SENSOR_CONDITION_SCHEMA +) def binary_sensor_is_on_to_code(config, condition_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) yield cg.new_Pvariable(condition_id, template_arg, paren, True) -@automation.register_condition('binary_sensor.is_off', BinarySensorCondition, - BINARY_SENSOR_CONDITION_SCHEMA) +@automation.register_condition( + "binary_sensor.is_off", BinarySensorCondition, BINARY_SENSOR_CONDITION_SCHEMA +) def binary_sensor_is_off_to_code(config, condition_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) yield cg.new_Pvariable(condition_id, template_arg, paren, False) @@ -325,5 +437,5 @@ def binary_sensor_is_off_to_code(config, condition_id, template_arg, args): @coroutine_with_priority(100.0) def to_code(config): - cg.add_define('USE_BINARY_SENSOR') + cg.add_define("USE_BINARY_SENSOR") cg.add_global(binary_sensor_ns.using) diff --git a/esphome/components/binary_sensor_map/sensor.py b/esphome/components/binary_sensor_map/sensor.py index ffd945bb5a..81bc85e570 100644 --- a/esphome/components/binary_sensor_map/sensor.py +++ b/esphome/components/binary_sensor_map/sensor.py @@ -2,14 +2,25 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, binary_sensor -from esphome.const import CONF_ID, CONF_CHANNELS, CONF_VALUE, CONF_TYPE, DEVICE_CLASS_EMPTY, \ - UNIT_EMPTY, ICON_CHECK_CIRCLE_OUTLINE, CONF_BINARY_SENSOR, CONF_GROUP +from esphome.const import ( + CONF_ID, + CONF_CHANNELS, + CONF_VALUE, + CONF_TYPE, + DEVICE_CLASS_EMPTY, + UNIT_EMPTY, + ICON_CHECK_CIRCLE_OUTLINE, + CONF_BINARY_SENSOR, + CONF_GROUP, +) -DEPENDENCIES = ['binary_sensor'] +DEPENDENCIES = ["binary_sensor"] -binary_sensor_map_ns = cg.esphome_ns.namespace('binary_sensor_map') -BinarySensorMap = binary_sensor_map_ns.class_('BinarySensorMap', cg.Component, sensor.Sensor) -SensorMapType = binary_sensor_map_ns.enum('SensorMapType') +binary_sensor_map_ns = cg.esphome_ns.namespace("binary_sensor_map") +BinarySensorMap = binary_sensor_map_ns.class_( + "BinarySensorMap", cg.Component, sensor.Sensor +) +SensorMapType = binary_sensor_map_ns.enum("SensorMapType") SENSOR_MAP_TYPES = { CONF_GROUP: SensorMapType.BINARY_SENSOR_MAP_TYPE_GROUP, @@ -20,14 +31,21 @@ entry = { cv.Required(CONF_VALUE): cv.float_, } -CONFIG_SCHEMA = cv.typed_schema({ - CONF_GROUP: sensor.sensor_schema( - UNIT_EMPTY, ICON_CHECK_CIRCLE_OUTLINE, 0, DEVICE_CLASS_EMPTY - ).extend({ - cv.GenerateID(): cv.declare_id(BinarySensorMap), - cv.Required(CONF_CHANNELS): cv.All(cv.ensure_list(entry), cv.Length(min=1)), - }), -}, lower=True) +CONFIG_SCHEMA = cv.typed_schema( + { + CONF_GROUP: sensor.sensor_schema( + UNIT_EMPTY, ICON_CHECK_CIRCLE_OUTLINE, 0, DEVICE_CLASS_EMPTY + ).extend( + { + cv.GenerateID(): cv.declare_id(BinarySensorMap), + cv.Required(CONF_CHANNELS): cv.All( + cv.ensure_list(entry), cv.Length(min=1) + ), + } + ), + }, + lower=True, +) def to_code(config): diff --git a/esphome/components/ble_presence/binary_sensor.py b/esphome/components/ble_presence/binary_sensor.py index 1cf8009384..4c6e7ee567 100644 --- a/esphome/components/ble_presence/binary_sensor.py +++ b/esphome/components/ble_presence/binary_sensor.py @@ -3,18 +3,28 @@ import esphome.config_validation as cv from esphome.components import binary_sensor, esp32_ble_tracker from esphome.const import CONF_MAC_ADDRESS, CONF_SERVICE_UUID, CONF_ID -DEPENDENCIES = ['esp32_ble_tracker'] +DEPENDENCIES = ["esp32_ble_tracker"] -ble_presence_ns = cg.esphome_ns.namespace('ble_presence') -BLEPresenceDevice = ble_presence_ns.class_('BLEPresenceDevice', binary_sensor.BinarySensor, - cg.Component, esp32_ble_tracker.ESPBTDeviceListener) +ble_presence_ns = cg.esphome_ns.namespace("ble_presence") +BLEPresenceDevice = ble_presence_ns.class_( + "BLEPresenceDevice", + binary_sensor.BinarySensor, + cg.Component, + esp32_ble_tracker.ESPBTDeviceListener, +) -CONFIG_SCHEMA = cv.All(binary_sensor.BINARY_SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(BLEPresenceDevice), - cv.Optional(CONF_MAC_ADDRESS): cv.mac_address, - cv.Optional(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid, -}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend( - cv.COMPONENT_SCHEMA), cv.has_exactly_one_key(CONF_MAC_ADDRESS, CONF_SERVICE_UUID)) +CONFIG_SCHEMA = cv.All( + binary_sensor.BINARY_SENSOR_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(BLEPresenceDevice), + cv.Optional(CONF_MAC_ADDRESS): cv.mac_address, + cv.Optional(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid, + } + ) + .extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA) + .extend(cv.COMPONENT_SCHEMA), + cv.has_exactly_one_key(CONF_MAC_ADDRESS, CONF_SERVICE_UUID), +) def to_code(config): @@ -28,9 +38,17 @@ def to_code(config): if CONF_SERVICE_UUID in config: if len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid16_format): - cg.add(var.set_service_uuid16(esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID]))) + cg.add( + var.set_service_uuid16( + esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID]) + ) + ) elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid32_format): - cg.add(var.set_service_uuid32(esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID]))) + cg.add( + var.set_service_uuid32( + esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID]) + ) + ) elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid128_format): uuid128 = esp32_ble_tracker.as_hex_array(config[CONF_SERVICE_UUID]) cg.add(var.set_service_uuid128(uuid128)) diff --git a/esphome/components/ble_rssi/sensor.py b/esphome/components/ble_rssi/sensor.py index 16903dd9d0..f6ee209ae1 100644 --- a/esphome/components/ble_rssi/sensor.py +++ b/esphome/components/ble_rssi/sensor.py @@ -1,23 +1,35 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, esp32_ble_tracker -from esphome.const import CONF_SERVICE_UUID, CONF_MAC_ADDRESS, CONF_ID, \ - DEVICE_CLASS_SIGNAL_STRENGTH, UNIT_DECIBEL, ICON_EMPTY +from esphome.const import ( + CONF_SERVICE_UUID, + CONF_MAC_ADDRESS, + CONF_ID, + DEVICE_CLASS_SIGNAL_STRENGTH, + UNIT_DECIBEL, + ICON_EMPTY, +) -DEPENDENCIES = ['esp32_ble_tracker'] +DEPENDENCIES = ["esp32_ble_tracker"] -ble_rssi_ns = cg.esphome_ns.namespace('ble_rssi') -BLERSSISensor = ble_rssi_ns.class_('BLERSSISensor', sensor.Sensor, cg.Component, - esp32_ble_tracker.ESPBTDeviceListener) +ble_rssi_ns = cg.esphome_ns.namespace("ble_rssi") +BLERSSISensor = ble_rssi_ns.class_( + "BLERSSISensor", sensor.Sensor, cg.Component, esp32_ble_tracker.ESPBTDeviceListener +) CONFIG_SCHEMA = cv.All( - sensor.sensor_schema(UNIT_DECIBEL, ICON_EMPTY, 0, DEVICE_CLASS_SIGNAL_STRENGTH).extend({ - cv.GenerateID(): cv.declare_id(BLERSSISensor), - cv.Optional(CONF_MAC_ADDRESS): cv.mac_address, - cv.Optional(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid, - }).extend( - esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA - ).extend(cv.COMPONENT_SCHEMA), cv.has_exactly_one_key(CONF_MAC_ADDRESS, CONF_SERVICE_UUID)) + sensor.sensor_schema(UNIT_DECIBEL, ICON_EMPTY, 0, DEVICE_CLASS_SIGNAL_STRENGTH) + .extend( + { + cv.GenerateID(): cv.declare_id(BLERSSISensor), + cv.Optional(CONF_MAC_ADDRESS): cv.mac_address, + cv.Optional(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid, + } + ) + .extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA) + .extend(cv.COMPONENT_SCHEMA), + cv.has_exactly_one_key(CONF_MAC_ADDRESS, CONF_SERVICE_UUID), +) def to_code(config): @@ -31,9 +43,17 @@ def to_code(config): if CONF_SERVICE_UUID in config: if len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid16_format): - cg.add(var.set_service_uuid16(esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID]))) + cg.add( + var.set_service_uuid16( + esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID]) + ) + ) elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid32_format): - cg.add(var.set_service_uuid32(esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID]))) + cg.add( + var.set_service_uuid32( + esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID]) + ) + ) elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid128_format): uuid128 = esp32_ble_tracker.as_hex_array(config[CONF_SERVICE_UUID]) cg.add(var.set_service_uuid128(uuid128)) diff --git a/esphome/components/ble_scanner/text_sensor.py b/esphome/components/ble_scanner/text_sensor.py index 1a43ffb68a..96c7b4d887 100644 --- a/esphome/components/ble_scanner/text_sensor.py +++ b/esphome/components/ble_scanner/text_sensor.py @@ -3,16 +3,25 @@ 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'] +DEPENDENCIES = ["esp32_ble_tracker"] -ble_scanner_ns = cg.esphome_ns.namespace('ble_scanner') -BLEScanner = ble_scanner_ns.class_('BLEScanner', text_sensor.TextSensor, cg.Component, - esp32_ble_tracker.ESPBTDeviceListener) +ble_scanner_ns = cg.esphome_ns.namespace("ble_scanner") +BLEScanner = ble_scanner_ns.class_( + "BLEScanner", + text_sensor.TextSensor, + cg.Component, + esp32_ble_tracker.ESPBTDeviceListener, +) -CONFIG_SCHEMA = cv.All(text_sensor.TEXT_SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(BLEScanner), -}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend( - cv.COMPONENT_SCHEMA)) +CONFIG_SCHEMA = cv.All( + text_sensor.TEXT_SENSOR_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(BLEScanner), + } + ) + .extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA) + .extend(cv.COMPONENT_SCHEMA) +) def to_code(config): diff --git a/esphome/components/bme280/sensor.py b/esphome/components/bme280/sensor.py index 26e1df40ee..5b0e418a66 100644 --- a/esphome/components/bme280/sensor.py +++ b/esphome/components/bme280/sensor.py @@ -1,53 +1,87 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import CONF_HUMIDITY, CONF_ID, CONF_IIR_FILTER, CONF_OVERSAMPLING, \ - CONF_PRESSURE, CONF_TEMPERATURE, DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_PRESSURE, \ - DEVICE_CLASS_TEMPERATURE, ICON_EMPTY, UNIT_CELSIUS, UNIT_HECTOPASCAL, UNIT_PERCENT +from esphome.const import ( + CONF_HUMIDITY, + CONF_ID, + CONF_IIR_FILTER, + CONF_OVERSAMPLING, + CONF_PRESSURE, + CONF_TEMPERATURE, + DEVICE_CLASS_HUMIDITY, + DEVICE_CLASS_PRESSURE, + DEVICE_CLASS_TEMPERATURE, + ICON_EMPTY, + UNIT_CELSIUS, + UNIT_HECTOPASCAL, + UNIT_PERCENT, +) -DEPENDENCIES = ['i2c'] +DEPENDENCIES = ["i2c"] -bme280_ns = cg.esphome_ns.namespace('bme280') -BME280Oversampling = bme280_ns.enum('BME280Oversampling') +bme280_ns = cg.esphome_ns.namespace("bme280") +BME280Oversampling = bme280_ns.enum("BME280Oversampling") OVERSAMPLING_OPTIONS = { - 'NONE': BME280Oversampling.BME280_OVERSAMPLING_NONE, - '1X': BME280Oversampling.BME280_OVERSAMPLING_1X, - '2X': BME280Oversampling.BME280_OVERSAMPLING_2X, - '4X': BME280Oversampling.BME280_OVERSAMPLING_4X, - '8X': BME280Oversampling.BME280_OVERSAMPLING_8X, - '16X': BME280Oversampling.BME280_OVERSAMPLING_16X, + "NONE": BME280Oversampling.BME280_OVERSAMPLING_NONE, + "1X": BME280Oversampling.BME280_OVERSAMPLING_1X, + "2X": BME280Oversampling.BME280_OVERSAMPLING_2X, + "4X": BME280Oversampling.BME280_OVERSAMPLING_4X, + "8X": BME280Oversampling.BME280_OVERSAMPLING_8X, + "16X": BME280Oversampling.BME280_OVERSAMPLING_16X, } -BME280IIRFilter = bme280_ns.enum('BME280IIRFilter') +BME280IIRFilter = bme280_ns.enum("BME280IIRFilter") IIR_FILTER_OPTIONS = { - 'OFF': BME280IIRFilter.BME280_IIR_FILTER_OFF, - '2X': BME280IIRFilter.BME280_IIR_FILTER_2X, - '4X': BME280IIRFilter.BME280_IIR_FILTER_4X, - '8X': BME280IIRFilter.BME280_IIR_FILTER_8X, - '16X': BME280IIRFilter.BME280_IIR_FILTER_16X, + "OFF": BME280IIRFilter.BME280_IIR_FILTER_OFF, + "2X": BME280IIRFilter.BME280_IIR_FILTER_2X, + "4X": BME280IIRFilter.BME280_IIR_FILTER_4X, + "8X": BME280IIRFilter.BME280_IIR_FILTER_8X, + "16X": BME280IIRFilter.BME280_IIR_FILTER_16X, } -BME280Component = bme280_ns.class_('BME280Component', cg.PollingComponent, i2c.I2CDevice) +BME280Component = bme280_ns.class_( + "BME280Component", cg.PollingComponent, i2c.I2CDevice +) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(BME280Component), - cv.Optional(CONF_TEMPERATURE): - sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE).extend({ - cv.Optional(CONF_OVERSAMPLING, default='16X'): - cv.enum(OVERSAMPLING_OPTIONS, upper=True), - }), - cv.Optional(CONF_PRESSURE): - sensor.sensor_schema(UNIT_HECTOPASCAL, ICON_EMPTY, 1, DEVICE_CLASS_PRESSURE).extend({ - cv.Optional(CONF_OVERSAMPLING, default='16X'): - cv.enum(OVERSAMPLING_OPTIONS, upper=True), - }), - cv.Optional(CONF_HUMIDITY): - sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 1, DEVICE_CLASS_HUMIDITY).extend({ - cv.Optional(CONF_OVERSAMPLING, default='16X'): - cv.enum(OVERSAMPLING_OPTIONS, upper=True), - }), - cv.Optional(CONF_IIR_FILTER, default='OFF'): cv.enum(IIR_FILTER_OPTIONS, upper=True), -}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x77)) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(BME280Component), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( + UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE + ).extend( + { + cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum( + OVERSAMPLING_OPTIONS, upper=True + ), + } + ), + cv.Optional(CONF_PRESSURE): sensor.sensor_schema( + UNIT_HECTOPASCAL, ICON_EMPTY, 1, DEVICE_CLASS_PRESSURE + ).extend( + { + cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum( + OVERSAMPLING_OPTIONS, upper=True + ), + } + ), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema( + UNIT_PERCENT, ICON_EMPTY, 1, DEVICE_CLASS_HUMIDITY + ).extend( + { + cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum( + OVERSAMPLING_OPTIONS, upper=True + ), + } + ), + cv.Optional(CONF_IIR_FILTER, default="OFF"): cv.enum( + IIR_FILTER_OPTIONS, upper=True + ), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x77)) +) def to_code(config): diff --git a/esphome/components/bme680/sensor.py b/esphome/components/bme680/sensor.py index 705ddc41f7..a82ecefe99 100644 --- a/esphome/components/bme680/sensor.py +++ b/esphome/components/bme680/sensor.py @@ -2,65 +2,116 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import core from esphome.components import i2c, sensor -from esphome.const import CONF_DURATION, CONF_GAS_RESISTANCE, CONF_HEATER, \ - CONF_HUMIDITY, CONF_ID, CONF_IIR_FILTER, CONF_OVERSAMPLING, CONF_PRESSURE, \ - CONF_TEMPERATURE, DEVICE_CLASS_EMPTY, DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_PRESSURE, \ - DEVICE_CLASS_TEMPERATURE, UNIT_OHM, ICON_GAS_CYLINDER, UNIT_CELSIUS, ICON_EMPTY, \ - UNIT_HECTOPASCAL, UNIT_PERCENT +from esphome.const import ( + CONF_DURATION, + CONF_GAS_RESISTANCE, + CONF_HEATER, + CONF_HUMIDITY, + CONF_ID, + CONF_IIR_FILTER, + CONF_OVERSAMPLING, + CONF_PRESSURE, + CONF_TEMPERATURE, + DEVICE_CLASS_EMPTY, + DEVICE_CLASS_HUMIDITY, + DEVICE_CLASS_PRESSURE, + DEVICE_CLASS_TEMPERATURE, + UNIT_OHM, + ICON_GAS_CYLINDER, + UNIT_CELSIUS, + ICON_EMPTY, + UNIT_HECTOPASCAL, + UNIT_PERCENT, +) -DEPENDENCIES = ['i2c'] +DEPENDENCIES = ["i2c"] -bme680_ns = cg.esphome_ns.namespace('bme680') -BME680Oversampling = bme680_ns.enum('BME680Oversampling') +bme680_ns = cg.esphome_ns.namespace("bme680") +BME680Oversampling = bme680_ns.enum("BME680Oversampling") OVERSAMPLING_OPTIONS = { - 'NONE': BME680Oversampling.BME680_OVERSAMPLING_NONE, - '1X': BME680Oversampling.BME680_OVERSAMPLING_1X, - '2X': BME680Oversampling.BME680_OVERSAMPLING_2X, - '4X': BME680Oversampling.BME680_OVERSAMPLING_4X, - '8X': BME680Oversampling.BME680_OVERSAMPLING_8X, - '16X': BME680Oversampling.BME680_OVERSAMPLING_16X, + "NONE": BME680Oversampling.BME680_OVERSAMPLING_NONE, + "1X": BME680Oversampling.BME680_OVERSAMPLING_1X, + "2X": BME680Oversampling.BME680_OVERSAMPLING_2X, + "4X": BME680Oversampling.BME680_OVERSAMPLING_4X, + "8X": BME680Oversampling.BME680_OVERSAMPLING_8X, + "16X": BME680Oversampling.BME680_OVERSAMPLING_16X, } -BME680IIRFilter = bme680_ns.enum('BME680IIRFilter') +BME680IIRFilter = bme680_ns.enum("BME680IIRFilter") IIR_FILTER_OPTIONS = { - 'OFF': BME680IIRFilter.BME680_IIR_FILTER_OFF, - '1X': BME680IIRFilter.BME680_IIR_FILTER_1X, - '3X': BME680IIRFilter.BME680_IIR_FILTER_3X, - '7X': BME680IIRFilter.BME680_IIR_FILTER_7X, - '15X': BME680IIRFilter.BME680_IIR_FILTER_15X, - '31X': BME680IIRFilter.BME680_IIR_FILTER_31X, - '63X': BME680IIRFilter.BME680_IIR_FILTER_63X, - '127X': BME680IIRFilter.BME680_IIR_FILTER_127X, + "OFF": BME680IIRFilter.BME680_IIR_FILTER_OFF, + "1X": BME680IIRFilter.BME680_IIR_FILTER_1X, + "3X": BME680IIRFilter.BME680_IIR_FILTER_3X, + "7X": BME680IIRFilter.BME680_IIR_FILTER_7X, + "15X": BME680IIRFilter.BME680_IIR_FILTER_15X, + "31X": BME680IIRFilter.BME680_IIR_FILTER_31X, + "63X": BME680IIRFilter.BME680_IIR_FILTER_63X, + "127X": BME680IIRFilter.BME680_IIR_FILTER_127X, } -BME680Component = bme680_ns.class_('BME680Component', cg.PollingComponent, i2c.I2CDevice) +BME680Component = bme680_ns.class_( + "BME680Component", cg.PollingComponent, i2c.I2CDevice +) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(BME680Component), - cv.Optional(CONF_TEMPERATURE): - sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE).extend({ - cv.Optional(CONF_OVERSAMPLING, default='16X'): - cv.enum(OVERSAMPLING_OPTIONS, upper=True), - }), - cv.Optional(CONF_PRESSURE): - sensor.sensor_schema(UNIT_HECTOPASCAL, ICON_EMPTY, 1, DEVICE_CLASS_PRESSURE).extend({ - cv.Optional(CONF_OVERSAMPLING, default='16X'): - cv.enum(OVERSAMPLING_OPTIONS, upper=True), - }), - cv.Optional(CONF_HUMIDITY): - sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 1, DEVICE_CLASS_HUMIDITY).extend({ - cv.Optional(CONF_OVERSAMPLING, default='16X'): - cv.enum(OVERSAMPLING_OPTIONS, upper=True), - }), - cv.Optional(CONF_GAS_RESISTANCE): - sensor.sensor_schema(UNIT_OHM, ICON_GAS_CYLINDER, 1, DEVICE_CLASS_EMPTY), - cv.Optional(CONF_IIR_FILTER, default='OFF'): cv.enum(IIR_FILTER_OPTIONS, upper=True), - cv.Optional(CONF_HEATER): cv.Any(None, cv.All(cv.Schema({ - cv.Optional(CONF_TEMPERATURE, default=320): cv.int_range(min=200, max=400), - cv.Optional(CONF_DURATION, default='150ms'): cv.All( - cv.positive_time_period_milliseconds, cv.Range(max=core.TimePeriod(milliseconds=4032))) - }), cv.has_at_least_one_key(CONF_TEMPERATURE, CONF_DURATION))), -}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x76)) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(BME680Component), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( + UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE + ).extend( + { + cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum( + OVERSAMPLING_OPTIONS, upper=True + ), + } + ), + cv.Optional(CONF_PRESSURE): sensor.sensor_schema( + UNIT_HECTOPASCAL, ICON_EMPTY, 1, DEVICE_CLASS_PRESSURE + ).extend( + { + cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum( + OVERSAMPLING_OPTIONS, upper=True + ), + } + ), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema( + UNIT_PERCENT, ICON_EMPTY, 1, DEVICE_CLASS_HUMIDITY + ).extend( + { + cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum( + OVERSAMPLING_OPTIONS, upper=True + ), + } + ), + cv.Optional(CONF_GAS_RESISTANCE): sensor.sensor_schema( + UNIT_OHM, ICON_GAS_CYLINDER, 1, DEVICE_CLASS_EMPTY + ), + cv.Optional(CONF_IIR_FILTER, default="OFF"): cv.enum( + IIR_FILTER_OPTIONS, upper=True + ), + cv.Optional(CONF_HEATER): cv.Any( + None, + cv.All( + cv.Schema( + { + cv.Optional(CONF_TEMPERATURE, default=320): cv.int_range( + min=200, max=400 + ), + cv.Optional(CONF_DURATION, default="150ms"): cv.All( + cv.positive_time_period_milliseconds, + cv.Range(max=core.TimePeriod(milliseconds=4032)), + ), + } + ), + cv.has_at_least_one_key(CONF_TEMPERATURE, CONF_DURATION), + ), + ), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x76)) +) def to_code(config): diff --git a/esphome/components/bmp085/sensor.py b/esphome/components/bmp085/sensor.py index e15fe1335c..a070e4aa69 100644 --- a/esphome/components/bmp085/sensor.py +++ b/esphome/components/bmp085/sensor.py @@ -1,21 +1,39 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import CONF_ID, CONF_PRESSURE, CONF_TEMPERATURE, DEVICE_CLASS_PRESSURE, \ - DEVICE_CLASS_TEMPERATURE, UNIT_CELSIUS, ICON_EMPTY, UNIT_HECTOPASCAL +from esphome.const import ( + CONF_ID, + CONF_PRESSURE, + CONF_TEMPERATURE, + DEVICE_CLASS_PRESSURE, + DEVICE_CLASS_TEMPERATURE, + UNIT_CELSIUS, + ICON_EMPTY, + UNIT_HECTOPASCAL, +) -DEPENDENCIES = ['i2c'] +DEPENDENCIES = ["i2c"] -bmp085_ns = cg.esphome_ns.namespace('bmp085') -BMP085Component = bmp085_ns.class_('BMP085Component', cg.PollingComponent, i2c.I2CDevice) +bmp085_ns = cg.esphome_ns.namespace("bmp085") +BMP085Component = bmp085_ns.class_( + "BMP085Component", cg.PollingComponent, i2c.I2CDevice +) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(BMP085Component), - cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, - DEVICE_CLASS_TEMPERATURE), - cv.Optional(CONF_PRESSURE): sensor.sensor_schema(UNIT_HECTOPASCAL, ICON_EMPTY, 1, - DEVICE_CLASS_PRESSURE), -}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x77)) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(BMP085Component), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( + UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE + ), + cv.Optional(CONF_PRESSURE): sensor.sensor_schema( + UNIT_HECTOPASCAL, ICON_EMPTY, 1, DEVICE_CLASS_PRESSURE + ), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x77)) +) def to_code(config): diff --git a/esphome/components/bmp280/sensor.py b/esphome/components/bmp280/sensor.py index c485cdb710..b12d7bff7f 100644 --- a/esphome/components/bmp280/sensor.py +++ b/esphome/components/bmp280/sensor.py @@ -1,48 +1,75 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import CONF_ID, CONF_PRESSURE, CONF_TEMPERATURE, DEVICE_CLASS_PRESSURE, \ - DEVICE_CLASS_TEMPERATURE, UNIT_CELSIUS, ICON_EMPTY, UNIT_HECTOPASCAL, CONF_IIR_FILTER, \ - CONF_OVERSAMPLING +from esphome.const import ( + CONF_ID, + CONF_PRESSURE, + CONF_TEMPERATURE, + DEVICE_CLASS_PRESSURE, + DEVICE_CLASS_TEMPERATURE, + UNIT_CELSIUS, + ICON_EMPTY, + UNIT_HECTOPASCAL, + CONF_IIR_FILTER, + CONF_OVERSAMPLING, +) -DEPENDENCIES = ['i2c'] +DEPENDENCIES = ["i2c"] -bmp280_ns = cg.esphome_ns.namespace('bmp280') -BMP280Oversampling = bmp280_ns.enum('BMP280Oversampling') +bmp280_ns = cg.esphome_ns.namespace("bmp280") +BMP280Oversampling = bmp280_ns.enum("BMP280Oversampling") OVERSAMPLING_OPTIONS = { - 'NONE': BMP280Oversampling.BMP280_OVERSAMPLING_NONE, - '1X': BMP280Oversampling.BMP280_OVERSAMPLING_1X, - '2X': BMP280Oversampling.BMP280_OVERSAMPLING_2X, - '4X': BMP280Oversampling.BMP280_OVERSAMPLING_4X, - '8X': BMP280Oversampling.BMP280_OVERSAMPLING_8X, - '16X': BMP280Oversampling.BMP280_OVERSAMPLING_16X, + "NONE": BMP280Oversampling.BMP280_OVERSAMPLING_NONE, + "1X": BMP280Oversampling.BMP280_OVERSAMPLING_1X, + "2X": BMP280Oversampling.BMP280_OVERSAMPLING_2X, + "4X": BMP280Oversampling.BMP280_OVERSAMPLING_4X, + "8X": BMP280Oversampling.BMP280_OVERSAMPLING_8X, + "16X": BMP280Oversampling.BMP280_OVERSAMPLING_16X, } -BMP280IIRFilter = bmp280_ns.enum('BMP280IIRFilter') +BMP280IIRFilter = bmp280_ns.enum("BMP280IIRFilter") IIR_FILTER_OPTIONS = { - 'OFF': BMP280IIRFilter.BMP280_IIR_FILTER_OFF, - '2X': BMP280IIRFilter.BMP280_IIR_FILTER_2X, - '4X': BMP280IIRFilter.BMP280_IIR_FILTER_4X, - '8X': BMP280IIRFilter.BMP280_IIR_FILTER_8X, - '16X': BMP280IIRFilter.BMP280_IIR_FILTER_16X, + "OFF": BMP280IIRFilter.BMP280_IIR_FILTER_OFF, + "2X": BMP280IIRFilter.BMP280_IIR_FILTER_2X, + "4X": BMP280IIRFilter.BMP280_IIR_FILTER_4X, + "8X": BMP280IIRFilter.BMP280_IIR_FILTER_8X, + "16X": BMP280IIRFilter.BMP280_IIR_FILTER_16X, } -BMP280Component = bmp280_ns.class_('BMP280Component', cg.PollingComponent, i2c.I2CDevice) +BMP280Component = bmp280_ns.class_( + "BMP280Component", cg.PollingComponent, i2c.I2CDevice +) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(BMP280Component), - cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( - UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE - ).extend({ - cv.Optional(CONF_OVERSAMPLING, default='16X'): cv.enum(OVERSAMPLING_OPTIONS, upper=True), - }), - cv.Optional(CONF_PRESSURE): sensor.sensor_schema( - UNIT_HECTOPASCAL, ICON_EMPTY, 1, DEVICE_CLASS_PRESSURE - ).extend({ - cv.Optional(CONF_OVERSAMPLING, default='16X'): cv.enum(OVERSAMPLING_OPTIONS, upper=True), - }), - cv.Optional(CONF_IIR_FILTER, default='OFF'): cv.enum(IIR_FILTER_OPTIONS, upper=True), -}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x77)) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(BMP280Component), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( + UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE + ).extend( + { + cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum( + OVERSAMPLING_OPTIONS, upper=True + ), + } + ), + cv.Optional(CONF_PRESSURE): sensor.sensor_schema( + UNIT_HECTOPASCAL, ICON_EMPTY, 1, DEVICE_CLASS_PRESSURE + ).extend( + { + cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum( + OVERSAMPLING_OPTIONS, upper=True + ), + } + ), + cv.Optional(CONF_IIR_FILTER, default="OFF"): cv.enum( + IIR_FILTER_OPTIONS, upper=True + ), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x77)) +) def to_code(config): diff --git a/esphome/components/canbus/__init__.py b/esphome/components/canbus/__init__.py index 24decc14b0..28501c7a85 100644 --- a/esphome/components/canbus/__init__.py +++ b/esphome/components/canbus/__init__.py @@ -4,68 +4,76 @@ from esphome import automation from esphome.core import CORE, coroutine from esphome.const import CONF_ID, CONF_TRIGGER_ID, CONF_DATA -CODEOWNERS = ['@mvturnho', '@danielschramm'] +CODEOWNERS = ["@mvturnho", "@danielschramm"] IS_PLATFORM_COMPONENT = True -CONF_CAN_ID = 'can_id' -CONF_USE_EXTENDED_ID = 'use_extended_id' -CONF_CANBUS_ID = 'canbus_id' -CONF_BIT_RATE = 'bit_rate' -CONF_ON_FRAME = 'on_frame' -CONF_CANBUS_SEND = 'canbus.send' +CONF_CAN_ID = "can_id" +CONF_USE_EXTENDED_ID = "use_extended_id" +CONF_CANBUS_ID = "canbus_id" +CONF_BIT_RATE = "bit_rate" +CONF_ON_FRAME = "on_frame" +CONF_CANBUS_SEND = "canbus.send" def validate_id(id_value, id_ext): if not id_ext: - if id_value > 0x7ff: + if id_value > 0x7FF: raise cv.Invalid("Standard IDs must be 11 Bit (0x000-0x7ff / 0-2047)") def validate_raw_data(value): if isinstance(value, str): - return value.encode('utf-8') + return value.encode("utf-8") if isinstance(value, list): return cv.Schema([cv.hex_uint8_t])(value) - raise cv.Invalid("data must either be a string wrapped in quotes or a list of bytes") + raise cv.Invalid( + "data must either be a string wrapped in quotes or a list of bytes" + ) -canbus_ns = cg.esphome_ns.namespace('canbus') -CanbusComponent = canbus_ns.class_('CanbusComponent', cg.Component) -CanbusTrigger = canbus_ns.class_('CanbusTrigger', - automation.Trigger.template(cg.std_vector.template(cg.uint8)), - cg.Component) -CanSpeed = canbus_ns.enum('CAN_SPEED') +canbus_ns = cg.esphome_ns.namespace("canbus") +CanbusComponent = canbus_ns.class_("CanbusComponent", cg.Component) +CanbusTrigger = canbus_ns.class_( + "CanbusTrigger", + automation.Trigger.template(cg.std_vector.template(cg.uint8)), + cg.Component, +) +CanSpeed = canbus_ns.enum("CAN_SPEED") CAN_SPEEDS = { - '5KBPS': CanSpeed.CAN_5KBPS, - '10KBPS': CanSpeed.CAN_10KBPS, - '20KBPS': CanSpeed.CAN_20KBPS, - '31K25BPS': CanSpeed.CAN_31K25BPS, - '33KBPS': CanSpeed.CAN_33KBPS, - '40KBPS': CanSpeed.CAN_40KBPS, - '50KBPS': CanSpeed.CAN_50KBPS, - '80KBPS': CanSpeed.CAN_80KBPS, - '83K3BPS': CanSpeed.CAN_83K3BPS, - '95KBPS': CanSpeed.CAN_95KBPS, - '100KBPS': CanSpeed.CAN_100KBPS, - '125KBPS': CanSpeed.CAN_125KBPS, - '200KBPS': CanSpeed.CAN_200KBPS, - '250KBPS': CanSpeed.CAN_250KBPS, - '500KBPS': CanSpeed.CAN_500KBPS, - '1000KBPS': CanSpeed.CAN_1000KBPS, + "5KBPS": CanSpeed.CAN_5KBPS, + "10KBPS": CanSpeed.CAN_10KBPS, + "20KBPS": CanSpeed.CAN_20KBPS, + "31K25BPS": CanSpeed.CAN_31K25BPS, + "33KBPS": CanSpeed.CAN_33KBPS, + "40KBPS": CanSpeed.CAN_40KBPS, + "50KBPS": CanSpeed.CAN_50KBPS, + "80KBPS": CanSpeed.CAN_80KBPS, + "83K3BPS": CanSpeed.CAN_83K3BPS, + "95KBPS": CanSpeed.CAN_95KBPS, + "100KBPS": CanSpeed.CAN_100KBPS, + "125KBPS": CanSpeed.CAN_125KBPS, + "200KBPS": CanSpeed.CAN_200KBPS, + "250KBPS": CanSpeed.CAN_250KBPS, + "500KBPS": CanSpeed.CAN_500KBPS, + "1000KBPS": CanSpeed.CAN_1000KBPS, } -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(CanbusComponent), - cv.Required(CONF_CAN_ID): cv.int_range(min=0, max=0x1fffffff), - cv.Optional(CONF_BIT_RATE, default='125KBPS'): cv.enum(CAN_SPEEDS, upper=True), - 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), +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(CanbusComponent), + cv.Required(CONF_CAN_ID): cv.int_range(min=0, max=0x1FFFFFFF), + cv.Optional(CONF_BIT_RATE, default="125KBPS"): cv.enum(CAN_SPEEDS, upper=True), cv.Optional(CONF_USE_EXTENDED_ID, default=False): cv.boolean, - }), -}).extend(cv.COMPONENT_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.Optional(CONF_USE_EXTENDED_ID, default=False): cv.boolean, + } + ), + } +).extend(cv.COMPONENT_SCHEMA) @coroutine @@ -82,7 +90,9 @@ def setup_canbus_core_(var, config): validate_id(can_id, ext_id) trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var, can_id, ext_id) yield cg.register_component(trigger, conf) - yield automation.build_automation(trigger, [(cg.std_vector.template(cg.uint8), 'x')], conf) + yield automation.build_automation( + trigger, [(cg.std_vector.template(cg.uint8), "x")], conf + ) @coroutine @@ -93,14 +103,19 @@ def register_canbus(var, config): # Actions -@automation.register_action(CONF_CANBUS_SEND, - canbus_ns.class_('CanbusSendAction', automation.Action), - cv.maybe_simple_value({ - cv.GenerateID(CONF_CANBUS_ID): cv.use_id(CanbusComponent), - cv.Optional(CONF_CAN_ID): cv.int_range(min=0, max=0x1fffffff), - cv.Optional(CONF_USE_EXTENDED_ID, default=False): cv.boolean, - cv.Required(CONF_DATA): cv.templatable(validate_raw_data), - }, key=CONF_DATA)) +@automation.register_action( + CONF_CANBUS_SEND, + canbus_ns.class_("CanbusSendAction", automation.Action), + cv.maybe_simple_value( + { + cv.GenerateID(CONF_CANBUS_ID): cv.use_id(CanbusComponent), + cv.Optional(CONF_CAN_ID): cv.int_range(min=0, max=0x1FFFFFFF), + cv.Optional(CONF_USE_EXTENDED_ID, default=False): cv.boolean, + cv.Required(CONF_DATA): cv.templatable(validate_raw_data), + }, + key=CONF_DATA, + ), +) 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) @@ -110,7 +125,9 @@ def canbus_action_to_code(config, action_id, template_arg, args): can_id = yield cg.templatable(config[CONF_CAN_ID], args, cg.uint32) cg.add(var.set_can_id(can_id)) - use_extended_id = yield cg.templatable(config[CONF_USE_EXTENDED_ID], args, cg.uint32) + use_extended_id = yield cg.templatable( + config[CONF_USE_EXTENDED_ID], args, cg.uint32 + ) cg.add(var.set_use_extended_id(use_extended_id)) data = config[CONF_DATA] diff --git a/esphome/components/captive_portal/__init__.py b/esphome/components/captive_portal/__init__.py index bb8b2198a1..e158db6746 100644 --- a/esphome/components/captive_portal/__init__.py +++ b/esphome/components/captive_portal/__init__.py @@ -5,17 +5,21 @@ from esphome.components.web_server_base import CONF_WEB_SERVER_BASE_ID from esphome.const import CONF_ID from esphome.core import coroutine_with_priority -AUTO_LOAD = ['web_server_base'] -DEPENDENCIES = ['wifi'] -CODEOWNERS = ['@OttoWinter'] +AUTO_LOAD = ["web_server_base"] +DEPENDENCIES = ["wifi"] +CODEOWNERS = ["@OttoWinter"] -captive_portal_ns = cg.esphome_ns.namespace('captive_portal') -CaptivePortal = captive_portal_ns.class_('CaptivePortal', cg.Component) +captive_portal_ns = cg.esphome_ns.namespace("captive_portal") +CaptivePortal = captive_portal_ns.class_("CaptivePortal", cg.Component) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(CaptivePortal), - cv.GenerateID(CONF_WEB_SERVER_BASE_ID): cv.use_id(web_server_base.WebServerBase), -}).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(CaptivePortal), + cv.GenerateID(CONF_WEB_SERVER_BASE_ID): cv.use_id( + web_server_base.WebServerBase + ), + } +).extend(cv.COMPONENT_SCHEMA) @coroutine_with_priority(64.0) @@ -24,4 +28,4 @@ def to_code(config): var = cg.new_Pvariable(config[CONF_ID], paren) yield cg.register_component(var, config) - cg.add_define('USE_CAPTIVE_PORTAL') + cg.add_define("USE_CAPTIVE_PORTAL") diff --git a/esphome/components/ccs811/sensor.py b/esphome/components/ccs811/sensor.py index 869a49dcdc..95b108a225 100644 --- a/esphome/components/ccs811/sensor.py +++ b/esphome/components/ccs811/sensor.py @@ -1,28 +1,46 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import CONF_ID, DEVICE_CLASS_EMPTY, ICON_RADIATOR, UNIT_PARTS_PER_MILLION, \ - UNIT_PARTS_PER_BILLION, CONF_TEMPERATURE, CONF_TVOC, CONF_HUMIDITY, ICON_MOLECULE_CO2 +from esphome.const import ( + CONF_ID, + DEVICE_CLASS_EMPTY, + ICON_RADIATOR, + UNIT_PARTS_PER_MILLION, + UNIT_PARTS_PER_BILLION, + CONF_TEMPERATURE, + CONF_TVOC, + CONF_HUMIDITY, + ICON_MOLECULE_CO2, +) -DEPENDENCIES = ['i2c'] +DEPENDENCIES = ["i2c"] -ccs811_ns = cg.esphome_ns.namespace('ccs811') -CCS811Component = ccs811_ns.class_('CCS811Component', cg.PollingComponent, i2c.I2CDevice) +ccs811_ns = cg.esphome_ns.namespace("ccs811") +CCS811Component = ccs811_ns.class_( + "CCS811Component", cg.PollingComponent, i2c.I2CDevice +) -CONF_ECO2 = 'eco2' -CONF_BASELINE = 'baseline' +CONF_ECO2 = "eco2" +CONF_BASELINE = "baseline" -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(CCS811Component), - cv.Required(CONF_ECO2): sensor.sensor_schema(UNIT_PARTS_PER_MILLION, ICON_MOLECULE_CO2, - 0, DEVICE_CLASS_EMPTY), - cv.Required(CONF_TVOC): sensor.sensor_schema(UNIT_PARTS_PER_BILLION, ICON_RADIATOR, - 0, DEVICE_CLASS_EMPTY), - - cv.Optional(CONF_BASELINE): cv.hex_uint16_t, - cv.Optional(CONF_TEMPERATURE): cv.use_id(sensor.Sensor), - cv.Optional(CONF_HUMIDITY): cv.use_id(sensor.Sensor), -}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x5A)) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(CCS811Component), + cv.Required(CONF_ECO2): sensor.sensor_schema( + UNIT_PARTS_PER_MILLION, ICON_MOLECULE_CO2, 0, DEVICE_CLASS_EMPTY + ), + cv.Required(CONF_TVOC): sensor.sensor_schema( + UNIT_PARTS_PER_BILLION, ICON_RADIATOR, 0, DEVICE_CLASS_EMPTY + ), + cv.Optional(CONF_BASELINE): cv.hex_uint16_t, + cv.Optional(CONF_TEMPERATURE): cv.use_id(sensor.Sensor), + cv.Optional(CONF_HUMIDITY): cv.use_id(sensor.Sensor), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x5A)) +) def to_code(config): diff --git a/esphome/components/climate/__init__.py b/esphome/components/climate/__init__.py index 38e254bb9d..7f74b62c61 100644 --- a/esphome/components/climate/__init__.py +++ b/esphome/components/climate/__init__.py @@ -2,70 +2,87 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import automation from esphome.components import mqtt -from esphome.const import CONF_AWAY, CONF_ID, CONF_INTERNAL, CONF_MAX_TEMPERATURE, \ - CONF_MIN_TEMPERATURE, CONF_MODE, CONF_TARGET_TEMPERATURE, \ - CONF_TARGET_TEMPERATURE_HIGH, CONF_TARGET_TEMPERATURE_LOW, CONF_TEMPERATURE_STEP, CONF_VISUAL, \ - CONF_MQTT_ID, CONF_NAME, CONF_FAN_MODE, CONF_SWING_MODE +from esphome.const import ( + CONF_AWAY, + CONF_ID, + CONF_INTERNAL, + CONF_MAX_TEMPERATURE, + CONF_MIN_TEMPERATURE, + CONF_MODE, + CONF_TARGET_TEMPERATURE, + CONF_TARGET_TEMPERATURE_HIGH, + CONF_TARGET_TEMPERATURE_LOW, + CONF_TEMPERATURE_STEP, + CONF_VISUAL, + CONF_MQTT_ID, + CONF_NAME, + CONF_FAN_MODE, + CONF_SWING_MODE, +) from esphome.core import CORE, coroutine, coroutine_with_priority IS_PLATFORM_COMPONENT = True -CODEOWNERS = ['@esphome/core'] -climate_ns = cg.esphome_ns.namespace('climate') +CODEOWNERS = ["@esphome/core"] +climate_ns = cg.esphome_ns.namespace("climate") -Climate = climate_ns.class_('Climate', cg.Nameable) -ClimateCall = climate_ns.class_('ClimateCall') -ClimateTraits = climate_ns.class_('ClimateTraits') +Climate = climate_ns.class_("Climate", cg.Nameable) +ClimateCall = climate_ns.class_("ClimateCall") +ClimateTraits = climate_ns.class_("ClimateTraits") -ClimateMode = climate_ns.enum('ClimateMode') +ClimateMode = climate_ns.enum("ClimateMode") CLIMATE_MODES = { - 'OFF': ClimateMode.CLIMATE_MODE_OFF, - 'AUTO': ClimateMode.CLIMATE_MODE_AUTO, - 'COOL': ClimateMode.CLIMATE_MODE_COOL, - 'HEAT': ClimateMode.CLIMATE_MODE_HEAT, - 'DRY': ClimateMode.CLIMATE_MODE_DRY, - 'FAN_ONLY': ClimateMode.CLIMATE_MODE_FAN_ONLY, + "OFF": ClimateMode.CLIMATE_MODE_OFF, + "AUTO": ClimateMode.CLIMATE_MODE_AUTO, + "COOL": ClimateMode.CLIMATE_MODE_COOL, + "HEAT": ClimateMode.CLIMATE_MODE_HEAT, + "DRY": ClimateMode.CLIMATE_MODE_DRY, + "FAN_ONLY": ClimateMode.CLIMATE_MODE_FAN_ONLY, } validate_climate_mode = cv.enum(CLIMATE_MODES, upper=True) -ClimateFanMode = climate_ns.enum('ClimateFanMode') +ClimateFanMode = climate_ns.enum("ClimateFanMode") CLIMATE_FAN_MODES = { - 'ON': ClimateFanMode.CLIMATE_FAN_ON, - 'OFF': ClimateFanMode.CLIMATE_FAN_OFF, - 'AUTO': ClimateFanMode.CLIMATE_FAN_AUTO, - 'LOW': ClimateFanMode.CLIMATE_FAN_LOW, - 'MEDIUM': ClimateFanMode.CLIMATE_FAN_MEDIUM, - 'HIGH': ClimateFanMode.CLIMATE_FAN_HIGH, - 'MIDDLE': ClimateFanMode.CLIMATE_FAN_MIDDLE, - 'FOCUS': ClimateFanMode.CLIMATE_FAN_FOCUS, - 'DIFFUSE': ClimateFanMode.CLIMATE_FAN_DIFFUSE, + "ON": ClimateFanMode.CLIMATE_FAN_ON, + "OFF": ClimateFanMode.CLIMATE_FAN_OFF, + "AUTO": ClimateFanMode.CLIMATE_FAN_AUTO, + "LOW": ClimateFanMode.CLIMATE_FAN_LOW, + "MEDIUM": ClimateFanMode.CLIMATE_FAN_MEDIUM, + "HIGH": ClimateFanMode.CLIMATE_FAN_HIGH, + "MIDDLE": ClimateFanMode.CLIMATE_FAN_MIDDLE, + "FOCUS": ClimateFanMode.CLIMATE_FAN_FOCUS, + "DIFFUSE": ClimateFanMode.CLIMATE_FAN_DIFFUSE, } validate_climate_fan_mode = cv.enum(CLIMATE_FAN_MODES, upper=True) -ClimateSwingMode = climate_ns.enum('ClimateSwingMode') +ClimateSwingMode = climate_ns.enum("ClimateSwingMode") CLIMATE_SWING_MODES = { - 'OFF': ClimateSwingMode.CLIMATE_SWING_OFF, - 'BOTH': ClimateSwingMode.CLIMATE_SWING_BOTH, - 'VERTICAL': ClimateSwingMode.CLIMATE_SWING_VERTICAL, - 'HORIZONTAL': ClimateSwingMode.CLIMATE_SWING_HORIZONTAL, + "OFF": ClimateSwingMode.CLIMATE_SWING_OFF, + "BOTH": ClimateSwingMode.CLIMATE_SWING_BOTH, + "VERTICAL": ClimateSwingMode.CLIMATE_SWING_VERTICAL, + "HORIZONTAL": ClimateSwingMode.CLIMATE_SWING_HORIZONTAL, } validate_climate_swing_mode = cv.enum(CLIMATE_SWING_MODES, upper=True) # Actions -ControlAction = climate_ns.class_('ControlAction', automation.Action) +ControlAction = climate_ns.class_("ControlAction", automation.Action) -CLIMATE_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(Climate), - cv.OnlyWith(CONF_MQTT_ID, 'mqtt'): cv.declare_id(mqtt.MQTTClimateComponent), - cv.Optional(CONF_VISUAL, default={}): cv.Schema({ - cv.Optional(CONF_MIN_TEMPERATURE): cv.temperature, - cv.Optional(CONF_MAX_TEMPERATURE): cv.temperature, - cv.Optional(CONF_TEMPERATURE_STEP): cv.temperature, - }), - # TODO: MQTT topic options -}) +CLIMATE_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(Climate), + cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTClimateComponent), + cv.Optional(CONF_VISUAL, default={}): cv.Schema( + { + cv.Optional(CONF_MIN_TEMPERATURE): cv.temperature, + cv.Optional(CONF_MAX_TEMPERATURE): cv.temperature, + cv.Optional(CONF_TEMPERATURE_STEP): cv.temperature, + } + ), + # TODO: MQTT topic options + } +) @coroutine @@ -94,19 +111,23 @@ def register_climate(var, config): yield setup_climate_core_(var, config) -CLIMATE_CONTROL_ACTION_SCHEMA = cv.Schema({ - cv.Required(CONF_ID): cv.use_id(Climate), - cv.Optional(CONF_MODE): cv.templatable(validate_climate_mode), - cv.Optional(CONF_TARGET_TEMPERATURE): cv.templatable(cv.temperature), - cv.Optional(CONF_TARGET_TEMPERATURE_LOW): cv.templatable(cv.temperature), - cv.Optional(CONF_TARGET_TEMPERATURE_HIGH): cv.templatable(cv.temperature), - cv.Optional(CONF_AWAY): cv.templatable(cv.boolean), - cv.Optional(CONF_FAN_MODE): cv.templatable(validate_climate_fan_mode), - cv.Optional(CONF_SWING_MODE): cv.templatable(validate_climate_swing_mode), -}) +CLIMATE_CONTROL_ACTION_SCHEMA = cv.Schema( + { + cv.Required(CONF_ID): cv.use_id(Climate), + cv.Optional(CONF_MODE): cv.templatable(validate_climate_mode), + cv.Optional(CONF_TARGET_TEMPERATURE): cv.templatable(cv.temperature), + cv.Optional(CONF_TARGET_TEMPERATURE_LOW): cv.templatable(cv.temperature), + cv.Optional(CONF_TARGET_TEMPERATURE_HIGH): cv.templatable(cv.temperature), + cv.Optional(CONF_AWAY): cv.templatable(cv.boolean), + cv.Optional(CONF_FAN_MODE): cv.templatable(validate_climate_fan_mode), + cv.Optional(CONF_SWING_MODE): cv.templatable(validate_climate_swing_mode), + } +) -@automation.register_action('climate.control', ControlAction, CLIMATE_CONTROL_ACTION_SCHEMA) +@automation.register_action( + "climate.control", ControlAction, CLIMATE_CONTROL_ACTION_SCHEMA +) def climate_control_to_code(config, action_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) var = cg.new_Pvariable(action_id, template_arg, paren) @@ -117,10 +138,14 @@ def climate_control_to_code(config, action_id, template_arg, args): template_ = yield cg.templatable(config[CONF_TARGET_TEMPERATURE], args, float) cg.add(var.set_target_temperature(template_)) if CONF_TARGET_TEMPERATURE_LOW in config: - template_ = yield cg.templatable(config[CONF_TARGET_TEMPERATURE_LOW], args, float) + template_ = yield cg.templatable( + config[CONF_TARGET_TEMPERATURE_LOW], args, float + ) cg.add(var.set_target_temperature_low(template_)) if CONF_TARGET_TEMPERATURE_HIGH in config: - template_ = yield cg.templatable(config[CONF_TARGET_TEMPERATURE_HIGH], args, float) + template_ = yield cg.templatable( + config[CONF_TARGET_TEMPERATURE_HIGH], args, float + ) cg.add(var.set_target_temperature_high(template_)) if CONF_AWAY in config: template_ = yield cg.templatable(config[CONF_AWAY], args, bool) @@ -129,12 +154,14 @@ def climate_control_to_code(config, action_id, template_arg, args): template_ = yield cg.templatable(config[CONF_FAN_MODE], args, ClimateFanMode) cg.add(var.set_fan_mode(template_)) if CONF_SWING_MODE in config: - template_ = yield cg.templatable(config[CONF_SWING_MODE], args, ClimateSwingMode) + template_ = yield cg.templatable( + config[CONF_SWING_MODE], args, ClimateSwingMode + ) cg.add(var.set_swing_mode(template_)) yield var @coroutine_with_priority(100.0) def to_code(config): - cg.add_define('USE_CLIMATE') + cg.add_define("USE_CLIMATE") cg.add_global(climate_ns.using) diff --git a/esphome/components/climate_ir/__init__.py b/esphome/components/climate_ir/__init__.py index 40ab3f22e8..8dcd75c31f 100644 --- a/esphome/components/climate_ir/__init__.py +++ b/esphome/components/climate_ir/__init__.py @@ -1,27 +1,42 @@ import esphome.codegen as cg import esphome.config_validation as cv -from esphome.components import climate, remote_transmitter, remote_receiver, sensor, remote_base +from esphome.components import ( + climate, + remote_transmitter, + remote_receiver, + sensor, + remote_base, +) from esphome.components.remote_base import CONF_RECEIVER_ID, CONF_TRANSMITTER_ID from esphome.const import CONF_SUPPORTS_COOL, CONF_SUPPORTS_HEAT, CONF_SENSOR from esphome.core import coroutine -AUTO_LOAD = ['sensor', 'remote_base'] -CODEOWNERS = ['@glmnet'] +AUTO_LOAD = ["sensor", "remote_base"] +CODEOWNERS = ["@glmnet"] -climate_ir_ns = cg.esphome_ns.namespace('climate_ir') -ClimateIR = climate_ir_ns.class_('ClimateIR', climate.Climate, cg.Component, - remote_base.RemoteReceiverListener) +climate_ir_ns = cg.esphome_ns.namespace("climate_ir") +ClimateIR = climate_ir_ns.class_( + "ClimateIR", climate.Climate, cg.Component, remote_base.RemoteReceiverListener +) -CLIMATE_IR_SCHEMA = climate.CLIMATE_SCHEMA.extend({ - cv.GenerateID(CONF_TRANSMITTER_ID): cv.use_id(remote_transmitter.RemoteTransmitterComponent), - cv.Optional(CONF_SUPPORTS_COOL, default=True): cv.boolean, - cv.Optional(CONF_SUPPORTS_HEAT, default=True): cv.boolean, - cv.Optional(CONF_SENSOR): cv.use_id(sensor.Sensor), -}).extend(cv.COMPONENT_SCHEMA) +CLIMATE_IR_SCHEMA = climate.CLIMATE_SCHEMA.extend( + { + cv.GenerateID(CONF_TRANSMITTER_ID): cv.use_id( + remote_transmitter.RemoteTransmitterComponent + ), + cv.Optional(CONF_SUPPORTS_COOL, default=True): cv.boolean, + cv.Optional(CONF_SUPPORTS_HEAT, default=True): cv.boolean, + cv.Optional(CONF_SENSOR): cv.use_id(sensor.Sensor), + } +).extend(cv.COMPONENT_SCHEMA) -CLIMATE_IR_WITH_RECEIVER_SCHEMA = CLIMATE_IR_SCHEMA.extend({ - cv.Optional(CONF_RECEIVER_ID): cv.use_id(remote_receiver.RemoteReceiverComponent), -}) +CLIMATE_IR_WITH_RECEIVER_SCHEMA = CLIMATE_IR_SCHEMA.extend( + { + cv.Optional(CONF_RECEIVER_ID): cv.use_id( + remote_receiver.RemoteReceiverComponent + ), + } +) @coroutine diff --git a/esphome/components/climate_ir_lg/climate.py b/esphome/components/climate_ir_lg/climate.py index a3f5730680..06e538d9c7 100644 --- a/esphome/components/climate_ir_lg/climate.py +++ b/esphome/components/climate_ir_lg/climate.py @@ -3,25 +3,37 @@ import esphome.config_validation as cv from esphome.components import climate_ir from esphome.const import CONF_ID -AUTO_LOAD = ['climate_ir'] +AUTO_LOAD = ["climate_ir"] -climate_ir_lg_ns = cg.esphome_ns.namespace('climate_ir_lg') -LgIrClimate = climate_ir_lg_ns.class_('LgIrClimate', climate_ir.ClimateIR) +climate_ir_lg_ns = cg.esphome_ns.namespace("climate_ir_lg") +LgIrClimate = climate_ir_lg_ns.class_("LgIrClimate", climate_ir.ClimateIR) -CONF_HEADER_HIGH = 'header_high' -CONF_HEADER_LOW = 'header_low' -CONF_BIT_HIGH = 'bit_high' -CONF_BIT_ONE_LOW = 'bit_one_low' -CONF_BIT_ZERO_LOW = 'bit_zero_low' +CONF_HEADER_HIGH = "header_high" +CONF_HEADER_LOW = "header_low" +CONF_BIT_HIGH = "bit_high" +CONF_BIT_ONE_LOW = "bit_one_low" +CONF_BIT_ZERO_LOW = "bit_zero_low" -CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(LgIrClimate), - cv.Optional(CONF_HEADER_HIGH, default='8000us'): cv.positive_time_period_microseconds, - cv.Optional(CONF_HEADER_LOW, default='4000us'): cv.positive_time_period_microseconds, - cv.Optional(CONF_BIT_HIGH, default='600us'): cv.positive_time_period_microseconds, - cv.Optional(CONF_BIT_ONE_LOW, default='1600us'): cv.positive_time_period_microseconds, - cv.Optional(CONF_BIT_ZERO_LOW, default='550us'): cv.positive_time_period_microseconds, -}) +CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(LgIrClimate), + cv.Optional( + CONF_HEADER_HIGH, default="8000us" + ): cv.positive_time_period_microseconds, + cv.Optional( + CONF_HEADER_LOW, default="4000us" + ): cv.positive_time_period_microseconds, + cv.Optional( + CONF_BIT_HIGH, default="600us" + ): cv.positive_time_period_microseconds, + cv.Optional( + CONF_BIT_ONE_LOW, default="1600us" + ): cv.positive_time_period_microseconds, + cv.Optional( + CONF_BIT_ZERO_LOW, default="550us" + ): cv.positive_time_period_microseconds, + } +) def to_code(config): diff --git a/esphome/components/color/__init__.py b/esphome/components/color/__init__.py index db2fc6c093..7c044040ee 100644 --- a/esphome/components/color/__init__.py +++ b/esphome/components/color/__init__.py @@ -14,14 +14,14 @@ CONF_WHITE_INT = "white_int" CONFIG_SCHEMA = cv.Schema( { cv.Required(CONF_ID): cv.declare_id(ColorStruct), - cv.Exclusive(CONF_RED, 'red'): cv.percentage, - cv.Exclusive(CONF_RED_INT, 'red'): cv.uint8_t, - cv.Exclusive(CONF_GREEN, 'green'): cv.percentage, - cv.Exclusive(CONF_GREEN_INT, 'green'): cv.uint8_t, - cv.Exclusive(CONF_BLUE, 'blue'): cv.percentage, - cv.Exclusive(CONF_BLUE_INT, 'blue'): cv.uint8_t, - cv.Exclusive(CONF_WHITE, 'white'): cv.percentage, - cv.Exclusive(CONF_WHITE_INT, 'white'): cv.uint8_t, + cv.Exclusive(CONF_RED, "red"): cv.percentage, + cv.Exclusive(CONF_RED_INT, "red"): cv.uint8_t, + cv.Exclusive(CONF_GREEN, "green"): cv.percentage, + cv.Exclusive(CONF_GREEN_INT, "green"): cv.uint8_t, + cv.Exclusive(CONF_BLUE, "blue"): cv.percentage, + cv.Exclusive(CONF_BLUE_INT, "blue"): cv.uint8_t, + cv.Exclusive(CONF_WHITE, "white"): cv.percentage, + cv.Exclusive(CONF_WHITE_INT, "white"): cv.uint8_t, } ).extend(cv.COMPONENT_SCHEMA) @@ -29,31 +29,29 @@ CONFIG_SCHEMA = cv.Schema( def to_code(config): r = 0 if CONF_RED in config: - r = int(config[CONF_RED]*255) + r = int(config[CONF_RED] * 255) elif CONF_RED_INT in config: r = config[CONF_RED_INT] g = 0 if CONF_GREEN in config: - g = int(config[CONF_GREEN]*255) + g = int(config[CONF_GREEN] * 255) elif CONF_GREEN_INT in config: g = config[CONF_GREEN_INT] b = 0 if CONF_BLUE in config: - b = int(config[CONF_BLUE]*255) + b = int(config[CONF_BLUE] * 255) elif CONF_BLUE_INT in config: b = config[CONF_BLUE_INT] w = 0 if CONF_WHITE in config: - w = int(config[CONF_WHITE]*255) + w = int(config[CONF_WHITE] * 255) elif CONF_WHITE_INT in config: w = config[CONF_WHITE_INT] - cg.variable(config[CONF_ID], cg.StructInitializer( - ColorStruct, - ('r', r), - ('g', g), - ('b', b), - ('w', w))) + cg.variable( + config[CONF_ID], + cg.StructInitializer(ColorStruct, ("r", r), ("g", g), ("b", b), ("w", w)), + ) diff --git a/esphome/components/coolix/climate.py b/esphome/components/coolix/climate.py index 075fad5a4b..b7a584643b 100644 --- a/esphome/components/coolix/climate.py +++ b/esphome/components/coolix/climate.py @@ -3,15 +3,17 @@ import esphome.config_validation as cv from esphome.components import climate_ir from esphome.const import CONF_ID -AUTO_LOAD = ['climate_ir'] -CODEOWNERS = ['@glmnet'] +AUTO_LOAD = ["climate_ir"] +CODEOWNERS = ["@glmnet"] -coolix_ns = cg.esphome_ns.namespace('coolix') -CoolixClimate = coolix_ns.class_('CoolixClimate', climate_ir.ClimateIR) +coolix_ns = cg.esphome_ns.namespace("coolix") +CoolixClimate = coolix_ns.class_("CoolixClimate", climate_ir.ClimateIR) -CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(CoolixClimate), -}) +CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(CoolixClimate), + } +) def to_code(config): diff --git a/esphome/components/cover/__init__.py b/esphome/components/cover/__init__.py index f39e7ba540..e731c18333 100644 --- a/esphome/components/cover/__init__.py +++ b/esphome/components/cover/__init__.py @@ -3,54 +3,74 @@ import esphome.config_validation as cv from esphome import automation from esphome.automation import maybe_simple_id, Condition from esphome.components import mqtt -from esphome.const import CONF_ID, CONF_INTERNAL, CONF_DEVICE_CLASS, CONF_STATE, \ - CONF_POSITION, CONF_TILT, CONF_STOP, CONF_MQTT_ID, CONF_NAME +from esphome.const import ( + CONF_ID, + CONF_INTERNAL, + CONF_DEVICE_CLASS, + CONF_STATE, + CONF_POSITION, + CONF_TILT, + CONF_STOP, + CONF_MQTT_ID, + CONF_NAME, +) from esphome.core import CORE, coroutine, coroutine_with_priority IS_PLATFORM_COMPONENT = True -CODEOWNERS = ['@esphome/core'] +CODEOWNERS = ["@esphome/core"] DEVICE_CLASSES = [ - '', 'awning', 'blind', 'curtain', 'damper', 'door', 'garage', - 'gate', 'shade', 'shutter', 'window' + "", + "awning", + "blind", + "curtain", + "damper", + "door", + "garage", + "gate", + "shade", + "shutter", + "window", ] -cover_ns = cg.esphome_ns.namespace('cover') +cover_ns = cg.esphome_ns.namespace("cover") -Cover = cover_ns.class_('Cover', cg.Nameable) +Cover = cover_ns.class_("Cover", cg.Nameable) COVER_OPEN = cover_ns.COVER_OPEN COVER_CLOSED = cover_ns.COVER_CLOSED COVER_STATES = { - 'OPEN': COVER_OPEN, - 'CLOSED': COVER_CLOSED, + "OPEN": COVER_OPEN, + "CLOSED": COVER_CLOSED, } validate_cover_state = cv.enum(COVER_STATES, upper=True) -CoverOperation = cover_ns.enum('CoverOperation') +CoverOperation = cover_ns.enum("CoverOperation") COVER_OPERATIONS = { - 'IDLE': CoverOperation.COVER_OPERATION_IDLE, - 'OPENING': CoverOperation.COVER_OPERATION_OPENING, - 'CLOSING': CoverOperation.COVER_OPERATION_CLOSING, + "IDLE": CoverOperation.COVER_OPERATION_IDLE, + "OPENING": CoverOperation.COVER_OPERATION_OPENING, + "CLOSING": CoverOperation.COVER_OPERATION_CLOSING, } validate_cover_operation = cv.enum(COVER_OPERATIONS, upper=True) # Actions -OpenAction = cover_ns.class_('OpenAction', automation.Action) -CloseAction = cover_ns.class_('CloseAction', automation.Action) -StopAction = cover_ns.class_('StopAction', automation.Action) -ControlAction = cover_ns.class_('ControlAction', automation.Action) -CoverPublishAction = cover_ns.class_('CoverPublishAction', automation.Action) -CoverIsOpenCondition = cover_ns.class_('CoverIsOpenCondition', Condition) -CoverIsClosedCondition = cover_ns.class_('CoverIsClosedCondition', Condition) +OpenAction = cover_ns.class_("OpenAction", automation.Action) +CloseAction = cover_ns.class_("CloseAction", automation.Action) +StopAction = cover_ns.class_("StopAction", automation.Action) +ControlAction = cover_ns.class_("ControlAction", automation.Action) +CoverPublishAction = cover_ns.class_("CoverPublishAction", automation.Action) +CoverIsOpenCondition = cover_ns.class_("CoverIsOpenCondition", Condition) +CoverIsClosedCondition = cover_ns.class_("CoverIsClosedCondition", Condition) -COVER_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(Cover), - cv.OnlyWith(CONF_MQTT_ID, 'mqtt'): cv.declare_id(mqtt.MQTTCoverComponent), - cv.Optional(CONF_DEVICE_CLASS): cv.one_of(*DEVICE_CLASSES, lower=True), - # TODO: MQTT topic options -}) +COVER_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(Cover), + cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTCoverComponent), + cv.Optional(CONF_DEVICE_CLASS): cv.one_of(*DEVICE_CLASSES, lower=True), + # TODO: MQTT topic options + } +) @coroutine @@ -74,39 +94,43 @@ def register_cover(var, config): yield setup_cover_core_(var, config) -COVER_ACTION_SCHEMA = maybe_simple_id({ - cv.Required(CONF_ID): cv.use_id(Cover), -}) +COVER_ACTION_SCHEMA = maybe_simple_id( + { + cv.Required(CONF_ID): cv.use_id(Cover), + } +) -@automation.register_action('cover.open', OpenAction, COVER_ACTION_SCHEMA) +@automation.register_action("cover.open", OpenAction, COVER_ACTION_SCHEMA) def cover_open_to_code(config, action_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) yield cg.new_Pvariable(action_id, template_arg, paren) -@automation.register_action('cover.close', CloseAction, COVER_ACTION_SCHEMA) +@automation.register_action("cover.close", CloseAction, COVER_ACTION_SCHEMA) def cover_close_to_code(config, action_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) yield cg.new_Pvariable(action_id, template_arg, paren) -@automation.register_action('cover.stop', StopAction, COVER_ACTION_SCHEMA) +@automation.register_action("cover.stop", StopAction, COVER_ACTION_SCHEMA) def cover_stop_to_code(config, action_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) yield cg.new_Pvariable(action_id, template_arg, paren) -COVER_CONTROL_ACTION_SCHEMA = cv.Schema({ - cv.Required(CONF_ID): cv.use_id(Cover), - cv.Optional(CONF_STOP): cv.templatable(cv.boolean), - cv.Exclusive(CONF_STATE, 'pos'): cv.templatable(validate_cover_state), - cv.Exclusive(CONF_POSITION, 'pos'): cv.templatable(cv.percentage), - cv.Optional(CONF_TILT): cv.templatable(cv.percentage), -}) +COVER_CONTROL_ACTION_SCHEMA = cv.Schema( + { + cv.Required(CONF_ID): cv.use_id(Cover), + cv.Optional(CONF_STOP): cv.templatable(cv.boolean), + cv.Exclusive(CONF_STATE, "pos"): cv.templatable(validate_cover_state), + cv.Exclusive(CONF_POSITION, "pos"): cv.templatable(cv.percentage), + cv.Optional(CONF_TILT): cv.templatable(cv.percentage), + } +) -@automation.register_action('cover.control', ControlAction, COVER_CONTROL_ACTION_SCHEMA) +@automation.register_action("cover.control", ControlAction, COVER_CONTROL_ACTION_SCHEMA) def cover_control_to_code(config, action_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) var = cg.new_Pvariable(action_id, template_arg, paren) @@ -127,5 +151,5 @@ def cover_control_to_code(config, action_id, template_arg, args): @coroutine_with_priority(100.0) def to_code(config): - cg.add_define('USE_COVER') + cg.add_define("USE_COVER") cg.add_global(cover_ns.using) diff --git a/esphome/components/cse7766/sensor.py b/esphome/components/cse7766/sensor.py index 1c5fcd8368..71e9458618 100644 --- a/esphome/components/cse7766/sensor.py +++ b/esphome/components/cse7766/sensor.py @@ -1,22 +1,45 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, uart -from esphome.const import CONF_CURRENT, CONF_ID, CONF_POWER, CONF_VOLTAGE, DEVICE_CLASS_CURRENT, \ - DEVICE_CLASS_POWER, DEVICE_CLASS_VOLTAGE, ICON_EMPTY, UNIT_VOLT, UNIT_AMPERE, UNIT_WATT +from esphome.const import ( + CONF_CURRENT, + CONF_ID, + CONF_POWER, + CONF_VOLTAGE, + DEVICE_CLASS_CURRENT, + DEVICE_CLASS_POWER, + DEVICE_CLASS_VOLTAGE, + ICON_EMPTY, + UNIT_VOLT, + UNIT_AMPERE, + UNIT_WATT, +) -DEPENDENCIES = ['uart'] +DEPENDENCIES = ["uart"] -cse7766_ns = cg.esphome_ns.namespace('cse7766') -CSE7766Component = cse7766_ns.class_('CSE7766Component', cg.PollingComponent, uart.UARTDevice) +cse7766_ns = cg.esphome_ns.namespace("cse7766") +CSE7766Component = cse7766_ns.class_( + "CSE7766Component", cg.PollingComponent, uart.UARTDevice +) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(CSE7766Component), - - cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE), - cv.Optional(CONF_CURRENT): sensor.sensor_schema(UNIT_AMPERE, ICON_EMPTY, 2, - DEVICE_CLASS_CURRENT), - cv.Optional(CONF_POWER): sensor.sensor_schema(UNIT_WATT, ICON_EMPTY, 1, DEVICE_CLASS_POWER), -}).extend(cv.polling_component_schema('60s')).extend(uart.UART_DEVICE_SCHEMA) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(CSE7766Component), + cv.Optional(CONF_VOLTAGE): sensor.sensor_schema( + UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE + ), + cv.Optional(CONF_CURRENT): sensor.sensor_schema( + UNIT_AMPERE, ICON_EMPTY, 2, DEVICE_CLASS_CURRENT + ), + cv.Optional(CONF_POWER): sensor.sensor_schema( + UNIT_WATT, ICON_EMPTY, 1, DEVICE_CLASS_POWER + ), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(uart.UART_DEVICE_SCHEMA) +) def to_code(config): diff --git a/esphome/components/ct_clamp/sensor.py b/esphome/components/ct_clamp/sensor.py index 276fd8f1a3..e4dbd92387 100644 --- a/esphome/components/ct_clamp/sensor.py +++ b/esphome/components/ct_clamp/sensor.py @@ -1,21 +1,35 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, voltage_sampler -from esphome.const import CONF_SENSOR, CONF_ID, DEVICE_CLASS_CURRENT, ICON_EMPTY, UNIT_AMPERE +from esphome.const import ( + CONF_SENSOR, + CONF_ID, + DEVICE_CLASS_CURRENT, + ICON_EMPTY, + UNIT_AMPERE, +) -AUTO_LOAD = ['voltage_sampler'] -CODEOWNERS = ['@jesserockz'] +AUTO_LOAD = ["voltage_sampler"] +CODEOWNERS = ["@jesserockz"] -CONF_SAMPLE_DURATION = 'sample_duration' +CONF_SAMPLE_DURATION = "sample_duration" -ct_clamp_ns = cg.esphome_ns.namespace('ct_clamp') -CTClampSensor = ct_clamp_ns.class_('CTClampSensor', sensor.Sensor, cg.PollingComponent) +ct_clamp_ns = cg.esphome_ns.namespace("ct_clamp") +CTClampSensor = ct_clamp_ns.class_("CTClampSensor", sensor.Sensor, cg.PollingComponent) -CONFIG_SCHEMA = sensor.sensor_schema(UNIT_AMPERE, ICON_EMPTY, 2, DEVICE_CLASS_CURRENT).extend({ - cv.GenerateID(): cv.declare_id(CTClampSensor), - cv.Required(CONF_SENSOR): cv.use_id(voltage_sampler.VoltageSampler), - cv.Optional(CONF_SAMPLE_DURATION, default='200ms'): cv.positive_time_period_milliseconds, -}).extend(cv.polling_component_schema('60s')) +CONFIG_SCHEMA = ( + sensor.sensor_schema(UNIT_AMPERE, ICON_EMPTY, 2, DEVICE_CLASS_CURRENT) + .extend( + { + cv.GenerateID(): cv.declare_id(CTClampSensor), + cv.Required(CONF_SENSOR): cv.use_id(voltage_sampler.VoltageSampler), + cv.Optional( + CONF_SAMPLE_DURATION, default="200ms" + ): cv.positive_time_period_milliseconds, + } + ) + .extend(cv.polling_component_schema("60s")) +) def to_code(config): diff --git a/esphome/components/custom/__init__.py b/esphome/components/custom/__init__.py index 0ef87cc6e1..74450300f3 100644 --- a/esphome/components/custom/__init__.py +++ b/esphome/components/custom/__init__.py @@ -1,3 +1,3 @@ import esphome.codegen as cg -custom_ns = cg.esphome_ns.namespace('custom') +custom_ns = cg.esphome_ns.namespace("custom") diff --git a/esphome/components/custom/binary_sensor/__init__.py b/esphome/components/custom/binary_sensor/__init__.py index 83b4a3dad8..402540c254 100644 --- a/esphome/components/custom/binary_sensor/__init__.py +++ b/esphome/components/custom/binary_sensor/__init__.py @@ -4,18 +4,25 @@ from esphome.components import binary_sensor from esphome.const import CONF_BINARY_SENSORS, CONF_ID, CONF_LAMBDA from .. import custom_ns -CustomBinarySensorConstructor = custom_ns.class_('CustomBinarySensorConstructor') +CustomBinarySensorConstructor = custom_ns.class_("CustomBinarySensorConstructor") -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(CustomBinarySensorConstructor), - cv.Required(CONF_LAMBDA): cv.returning_lambda, - cv.Required(CONF_BINARY_SENSORS): cv.ensure_list(binary_sensor.BINARY_SENSOR_SCHEMA), -}) +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(CustomBinarySensorConstructor), + cv.Required(CONF_LAMBDA): cv.returning_lambda, + cv.Required(CONF_BINARY_SENSORS): cv.ensure_list( + binary_sensor.BINARY_SENSOR_SCHEMA + ), + } +) def to_code(config): template_ = yield cg.process_lambda( - config[CONF_LAMBDA], [], return_type=cg.std_vector.template(binary_sensor.BinarySensorPtr)) + config[CONF_LAMBDA], + [], + return_type=cg.std_vector.template(binary_sensor.BinarySensorPtr), + ) rhs = CustomBinarySensorConstructor(template_) custom = cg.variable(config[CONF_ID], rhs) diff --git a/esphome/components/custom/climate/__init__.py b/esphome/components/custom/climate/__init__.py index ed19452dee..75f9c3247e 100644 --- a/esphome/components/custom/climate/__init__.py +++ b/esphome/components/custom/climate/__init__.py @@ -4,20 +4,24 @@ from esphome.components import climate from esphome.const import CONF_ID, CONF_LAMBDA from .. import custom_ns -CustomClimateConstructor = custom_ns.class_('CustomClimateConstructor') -CONF_CLIMATES = 'climates' +CustomClimateConstructor = custom_ns.class_("CustomClimateConstructor") +CONF_CLIMATES = "climates" -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(CustomClimateConstructor), - cv.Required(CONF_LAMBDA): cv.returning_lambda, - cv.Required(CONF_CLIMATES): cv.ensure_list(climate.CLIMATE_SCHEMA), -}) +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(CustomClimateConstructor), + cv.Required(CONF_LAMBDA): cv.returning_lambda, + cv.Required(CONF_CLIMATES): cv.ensure_list(climate.CLIMATE_SCHEMA), + } +) def to_code(config): template_ = yield cg.process_lambda( - config[CONF_LAMBDA], [], - return_type=cg.std_vector.template(climate.Climate.operator('ptr'))) + config[CONF_LAMBDA], + [], + return_type=cg.std_vector.template(climate.Climate.operator("ptr")), + ) rhs = CustomClimateConstructor(template_) custom = cg.variable(config[CONF_ID], rhs) diff --git a/esphome/components/custom/cover/__init__.py b/esphome/components/custom/cover/__init__.py index 3ab3ec1d04..35f25b827d 100644 --- a/esphome/components/custom/cover/__init__.py +++ b/esphome/components/custom/cover/__init__.py @@ -4,20 +4,24 @@ from esphome.components import cover from esphome.const import CONF_ID, CONF_LAMBDA from .. import custom_ns -CustomCoverConstructor = custom_ns.class_('CustomCoverConstructor') -CONF_COVERS = 'covers' +CustomCoverConstructor = custom_ns.class_("CustomCoverConstructor") +CONF_COVERS = "covers" -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(CustomCoverConstructor), - cv.Required(CONF_LAMBDA): cv.returning_lambda, - cv.Required(CONF_COVERS): cv.ensure_list(cover.COVER_SCHEMA), -}) +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(CustomCoverConstructor), + cv.Required(CONF_LAMBDA): cv.returning_lambda, + cv.Required(CONF_COVERS): cv.ensure_list(cover.COVER_SCHEMA), + } +) def to_code(config): template_ = yield cg.process_lambda( - config[CONF_LAMBDA], [], - return_type=cg.std_vector.template(cover.Cover.operator('ptr'))) + config[CONF_LAMBDA], + [], + return_type=cg.std_vector.template(cover.Cover.operator("ptr")), + ) rhs = CustomCoverConstructor(template_) custom = cg.variable(config[CONF_ID], rhs) diff --git a/esphome/components/custom/light/__init__.py b/esphome/components/custom/light/__init__.py index 61dd74e661..f4bd8331f1 100644 --- a/esphome/components/custom/light/__init__.py +++ b/esphome/components/custom/light/__init__.py @@ -4,20 +4,24 @@ from esphome.components import light from esphome.const import CONF_ID, CONF_LAMBDA from .. import custom_ns -CustomLightOutputConstructor = custom_ns.class_('CustomLightOutputConstructor') -CONF_LIGHTS = 'lights' +CustomLightOutputConstructor = custom_ns.class_("CustomLightOutputConstructor") +CONF_LIGHTS = "lights" -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(CustomLightOutputConstructor), - cv.Required(CONF_LAMBDA): cv.returning_lambda, - cv.Required(CONF_LIGHTS): cv.ensure_list(light.ADDRESSABLE_LIGHT_SCHEMA), -}) +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(CustomLightOutputConstructor), + cv.Required(CONF_LAMBDA): cv.returning_lambda, + cv.Required(CONF_LIGHTS): cv.ensure_list(light.ADDRESSABLE_LIGHT_SCHEMA), + } +) def to_code(config): template_ = yield cg.process_lambda( - config[CONF_LAMBDA], [], - return_type=cg.std_vector.template(light.LightOutput.operator('ptr'))) + config[CONF_LAMBDA], + [], + return_type=cg.std_vector.template(light.LightOutput.operator("ptr")), + ) rhs = CustomLightOutputConstructor(template_) custom = cg.variable(config[CONF_ID], rhs) diff --git a/esphome/components/custom/output/__init__.py b/esphome/components/custom/output/__init__.py index efe6f19dab..c803d89c32 100644 --- a/esphome/components/custom/output/__init__.py +++ b/esphome/components/custom/output/__init__.py @@ -4,41 +4,55 @@ from esphome.components import output from esphome.const import CONF_ID, CONF_LAMBDA, CONF_OUTPUTS, CONF_TYPE, CONF_BINARY from .. import custom_ns -CustomBinaryOutputConstructor = custom_ns.class_('CustomBinaryOutputConstructor') -CustomFloatOutputConstructor = custom_ns.class_('CustomFloatOutputConstructor') +CustomBinaryOutputConstructor = custom_ns.class_("CustomBinaryOutputConstructor") +CustomFloatOutputConstructor = custom_ns.class_("CustomFloatOutputConstructor") -CONF_FLOAT = 'float' +CONF_FLOAT = "float" -CONFIG_SCHEMA = cv.typed_schema({ - CONF_BINARY: cv.Schema({ - cv.GenerateID(): cv.declare_id(CustomBinaryOutputConstructor), - cv.Required(CONF_LAMBDA): cv.returning_lambda, - cv.Required(CONF_OUTPUTS): - cv.ensure_list(output.BINARY_OUTPUT_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(output.BinaryOutput), - })), - }), - CONF_FLOAT: cv.Schema({ - cv.GenerateID(): cv.declare_id(CustomFloatOutputConstructor), - cv.Required(CONF_LAMBDA): cv.returning_lambda, - cv.Required(CONF_OUTPUTS): - cv.ensure_list(output.FLOAT_OUTPUT_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(output.FloatOutput), - })), - }) -}, lower=True) +CONFIG_SCHEMA = cv.typed_schema( + { + CONF_BINARY: cv.Schema( + { + cv.GenerateID(): cv.declare_id(CustomBinaryOutputConstructor), + cv.Required(CONF_LAMBDA): cv.returning_lambda, + cv.Required(CONF_OUTPUTS): cv.ensure_list( + output.BINARY_OUTPUT_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(output.BinaryOutput), + } + ) + ), + } + ), + CONF_FLOAT: cv.Schema( + { + cv.GenerateID(): cv.declare_id(CustomFloatOutputConstructor), + cv.Required(CONF_LAMBDA): cv.returning_lambda, + cv.Required(CONF_OUTPUTS): cv.ensure_list( + output.FLOAT_OUTPUT_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(output.FloatOutput), + } + ) + ), + } + ), + }, + lower=True, +) def to_code(config): type = config[CONF_TYPE] - if type == 'binary': + if type == "binary": ret_type = output.BinaryOutputPtr klass = CustomBinaryOutputConstructor else: ret_type = output.FloatOutputPtr klass = CustomFloatOutputConstructor - template_ = yield cg.process_lambda(config[CONF_LAMBDA], [], - return_type=cg.std_vector.template(ret_type)) + template_ = yield cg.process_lambda( + config[CONF_LAMBDA], [], return_type=cg.std_vector.template(ret_type) + ) rhs = klass(template_) custom = cg.variable(config[CONF_ID], rhs) diff --git a/esphome/components/custom/sensor/__init__.py b/esphome/components/custom/sensor/__init__.py index e6da4a733c..2be14afc0d 100644 --- a/esphome/components/custom/sensor/__init__.py +++ b/esphome/components/custom/sensor/__init__.py @@ -4,18 +4,21 @@ from esphome.components import sensor from esphome.const import CONF_ID, CONF_LAMBDA, CONF_SENSORS from .. import custom_ns -CustomSensorConstructor = custom_ns.class_('CustomSensorConstructor') +CustomSensorConstructor = custom_ns.class_("CustomSensorConstructor") -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(CustomSensorConstructor), - cv.Required(CONF_LAMBDA): cv.returning_lambda, - cv.Required(CONF_SENSORS): cv.ensure_list(sensor.SENSOR_SCHEMA), -}) +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(CustomSensorConstructor), + cv.Required(CONF_LAMBDA): cv.returning_lambda, + cv.Required(CONF_SENSORS): cv.ensure_list(sensor.SENSOR_SCHEMA), + } +) def to_code(config): template_ = yield cg.process_lambda( - config[CONF_LAMBDA], [], return_type=cg.std_vector.template(sensor.SensorPtr)) + config[CONF_LAMBDA], [], return_type=cg.std_vector.template(sensor.SensorPtr) + ) rhs = CustomSensorConstructor(template_) var = cg.variable(config[CONF_ID], rhs) diff --git a/esphome/components/custom/switch/__init__.py b/esphome/components/custom/switch/__init__.py index fd619e6769..a470c3e440 100644 --- a/esphome/components/custom/switch/__init__.py +++ b/esphome/components/custom/switch/__init__.py @@ -4,21 +4,27 @@ from esphome.components import switch from esphome.const import CONF_ID, CONF_LAMBDA, CONF_SWITCHES from .. import custom_ns -CustomSwitchConstructor = custom_ns.class_('CustomSwitchConstructor') +CustomSwitchConstructor = custom_ns.class_("CustomSwitchConstructor") -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(CustomSwitchConstructor), - cv.Required(CONF_LAMBDA): cv.returning_lambda, - cv.Required(CONF_SWITCHES): - cv.ensure_list(switch.SWITCH_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(switch.Switch), - })), -}) +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(CustomSwitchConstructor), + cv.Required(CONF_LAMBDA): cv.returning_lambda, + cv.Required(CONF_SWITCHES): cv.ensure_list( + switch.SWITCH_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(switch.Switch), + } + ) + ), + } +) def to_code(config): template_ = yield cg.process_lambda( - config[CONF_LAMBDA], [], return_type=cg.std_vector.template(switch.SwitchPtr)) + config[CONF_LAMBDA], [], return_type=cg.std_vector.template(switch.SwitchPtr) + ) rhs = CustomSwitchConstructor(template_) var = cg.variable(config[CONF_ID], rhs) diff --git a/esphome/components/custom/text_sensor/__init__.py b/esphome/components/custom/text_sensor/__init__.py index 58db2a3f9e..f09d72d228 100644 --- a/esphome/components/custom/text_sensor/__init__.py +++ b/esphome/components/custom/text_sensor/__init__.py @@ -4,21 +4,29 @@ from esphome.components import text_sensor from esphome.const import CONF_ID, CONF_LAMBDA, CONF_TEXT_SENSORS from .. import custom_ns -CustomTextSensorConstructor = custom_ns.class_('CustomTextSensorConstructor') +CustomTextSensorConstructor = custom_ns.class_("CustomTextSensorConstructor") -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), - })), -}) +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), + } + ) + ), + } +) def to_code(config): template_ = yield cg.process_lambda( - config[CONF_LAMBDA], [], return_type=cg.std_vector.template(text_sensor.TextSensorPtr)) + config[CONF_LAMBDA], + [], + return_type=cg.std_vector.template(text_sensor.TextSensorPtr), + ) rhs = CustomTextSensorConstructor(template_) var = cg.variable(config[CONF_ID], rhs) diff --git a/esphome/components/custom_component/__init__.py b/esphome/components/custom_component/__init__.py index 198aa6a9ec..fc154a82c3 100644 --- a/esphome/components/custom_component/__init__.py +++ b/esphome/components/custom_component/__init__.py @@ -2,22 +2,27 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.const import CONF_COMPONENTS, CONF_ID, CONF_LAMBDA -custom_component_ns = cg.esphome_ns.namespace('custom_component') -CustomComponentConstructor = custom_component_ns.class_('CustomComponentConstructor') +custom_component_ns = cg.esphome_ns.namespace("custom_component") +CustomComponentConstructor = custom_component_ns.class_("CustomComponentConstructor") MULTI_CONF = True -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(CustomComponentConstructor), - cv.Required(CONF_LAMBDA): cv.returning_lambda, - cv.Optional(CONF_COMPONENTS): cv.ensure_list(cv.Schema({ - cv.GenerateID(): cv.declare_id(cg.Component) - }).extend(cv.COMPONENT_SCHEMA)), -}) +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(CustomComponentConstructor), + cv.Required(CONF_LAMBDA): cv.returning_lambda, + cv.Optional(CONF_COMPONENTS): cv.ensure_list( + cv.Schema({cv.GenerateID(): cv.declare_id(cg.Component)}).extend( + cv.COMPONENT_SCHEMA + ) + ), + } +) def to_code(config): template_ = yield cg.process_lambda( - config[CONF_LAMBDA], [], return_type=cg.std_vector.template(cg.ComponentPtr)) + config[CONF_LAMBDA], [], return_type=cg.std_vector.template(cg.ComponentPtr) + ) rhs = CustomComponentConstructor(template_) var = cg.variable(config[CONF_ID], rhs) diff --git a/esphome/components/cwww/light.py b/esphome/components/cwww/light.py index 5cc4262105..b099de435d 100644 --- a/esphome/components/cwww/light.py +++ b/esphome/components/cwww/light.py @@ -1,22 +1,29 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import light, output -from esphome.const import CONF_OUTPUT_ID, CONF_COLD_WHITE, CONF_WARM_WHITE, \ - CONF_COLD_WHITE_COLOR_TEMPERATURE, CONF_WARM_WHITE_COLOR_TEMPERATURE +from esphome.const import ( + CONF_OUTPUT_ID, + CONF_COLD_WHITE, + CONF_WARM_WHITE, + CONF_COLD_WHITE_COLOR_TEMPERATURE, + CONF_WARM_WHITE_COLOR_TEMPERATURE, +) -cwww_ns = cg.esphome_ns.namespace('cwww') -CWWWLightOutput = cwww_ns.class_('CWWWLightOutput', light.LightOutput) +cwww_ns = cg.esphome_ns.namespace("cwww") +CWWWLightOutput = cwww_ns.class_("CWWWLightOutput", light.LightOutput) -CONF_CONSTANT_BRIGHTNESS = 'constant_brightness' +CONF_CONSTANT_BRIGHTNESS = "constant_brightness" -CONFIG_SCHEMA = light.RGB_LIGHT_SCHEMA.extend({ - cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(CWWWLightOutput), - cv.Required(CONF_COLD_WHITE): cv.use_id(output.FloatOutput), - cv.Required(CONF_WARM_WHITE): cv.use_id(output.FloatOutput), - cv.Required(CONF_COLD_WHITE_COLOR_TEMPERATURE): cv.color_temperature, - cv.Required(CONF_WARM_WHITE_COLOR_TEMPERATURE): cv.color_temperature, - cv.Optional(CONF_CONSTANT_BRIGHTNESS, default=False): cv.boolean, -}) +CONFIG_SCHEMA = light.RGB_LIGHT_SCHEMA.extend( + { + cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(CWWWLightOutput), + cv.Required(CONF_COLD_WHITE): cv.use_id(output.FloatOutput), + cv.Required(CONF_WARM_WHITE): cv.use_id(output.FloatOutput), + cv.Required(CONF_COLD_WHITE_COLOR_TEMPERATURE): cv.color_temperature, + cv.Required(CONF_WARM_WHITE_COLOR_TEMPERATURE): cv.color_temperature, + cv.Optional(CONF_CONSTANT_BRIGHTNESS, default=False): cv.boolean, + } +) def to_code(config): diff --git a/esphome/components/daikin/climate.py b/esphome/components/daikin/climate.py index ff3f506fb2..07bc079a9a 100644 --- a/esphome/components/daikin/climate.py +++ b/esphome/components/daikin/climate.py @@ -3,14 +3,16 @@ import esphome.config_validation as cv from esphome.components import climate_ir from esphome.const import CONF_ID -AUTO_LOAD = ['climate_ir'] +AUTO_LOAD = ["climate_ir"] -daikin_ns = cg.esphome_ns.namespace('daikin') -DaikinClimate = daikin_ns.class_('DaikinClimate', climate_ir.ClimateIR) +daikin_ns = cg.esphome_ns.namespace("daikin") +DaikinClimate = daikin_ns.class_("DaikinClimate", climate_ir.ClimateIR) -CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(DaikinClimate), -}) +CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(DaikinClimate), + } +) def to_code(config): diff --git a/esphome/components/dallas/__init__.py b/esphome/components/dallas/__init__.py index 85ab4300ee..87049b8c64 100644 --- a/esphome/components/dallas/__init__.py +++ b/esphome/components/dallas/__init__.py @@ -4,18 +4,20 @@ from esphome import pins from esphome.const import CONF_ID, CONF_PIN MULTI_CONF = True -AUTO_LOAD = ['sensor'] +AUTO_LOAD = ["sensor"] -CONF_ONE_WIRE_ID = 'one_wire_id' -dallas_ns = cg.esphome_ns.namespace('dallas') -DallasComponent = dallas_ns.class_('DallasComponent', cg.PollingComponent) -ESPOneWire = dallas_ns.class_('ESPOneWire') +CONF_ONE_WIRE_ID = "one_wire_id" +dallas_ns = cg.esphome_ns.namespace("dallas") +DallasComponent = dallas_ns.class_("DallasComponent", cg.PollingComponent) +ESPOneWire = dallas_ns.class_("ESPOneWire") -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(DallasComponent), - cv.GenerateID(CONF_ONE_WIRE_ID): cv.declare_id(ESPOneWire), - cv.Required(CONF_PIN): pins.gpio_input_pin_schema, -}).extend(cv.polling_component_schema('60s')) +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(DallasComponent), + cv.GenerateID(CONF_ONE_WIRE_ID): cv.declare_id(ESPOneWire), + cv.Required(CONF_PIN): pins.gpio_input_pin_schema, + } +).extend(cv.polling_component_schema("60s")) def to_code(config): diff --git a/esphome/components/dallas/sensor.py b/esphome/components/dallas/sensor.py index 457a21100f..bb0a463a62 100644 --- a/esphome/components/dallas/sensor.py +++ b/esphome/components/dallas/sensor.py @@ -1,22 +1,32 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor -from esphome.const import CONF_ADDRESS, CONF_DALLAS_ID, CONF_INDEX, CONF_RESOLUTION, \ - DEVICE_CLASS_TEMPERATURE, ICON_EMPTY, UNIT_CELSIUS, CONF_ID +from esphome.const import ( + CONF_ADDRESS, + CONF_DALLAS_ID, + CONF_INDEX, + CONF_RESOLUTION, + DEVICE_CLASS_TEMPERATURE, + ICON_EMPTY, + UNIT_CELSIUS, + CONF_ID, +) from . import DallasComponent, dallas_ns -DallasTemperatureSensor = dallas_ns.class_('DallasTemperatureSensor', sensor.Sensor) +DallasTemperatureSensor = dallas_ns.class_("DallasTemperatureSensor", sensor.Sensor) -CONFIG_SCHEMA = cv.All(sensor.sensor_schema( - UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE -).extend({ - cv.GenerateID(): cv.declare_id(DallasTemperatureSensor), - cv.GenerateID(CONF_DALLAS_ID): cv.use_id(DallasComponent), - - cv.Optional(CONF_ADDRESS): cv.hex_int, - cv.Optional(CONF_INDEX): cv.positive_int, - cv.Optional(CONF_RESOLUTION, default=12): cv.int_range(min=9, max=12), -}), cv.has_exactly_one_key(CONF_ADDRESS, CONF_INDEX)) +CONFIG_SCHEMA = cv.All( + sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE).extend( + { + cv.GenerateID(): cv.declare_id(DallasTemperatureSensor), + cv.GenerateID(CONF_DALLAS_ID): cv.use_id(DallasComponent), + cv.Optional(CONF_ADDRESS): cv.hex_int, + cv.Optional(CONF_INDEX): cv.positive_int, + cv.Optional(CONF_RESOLUTION, default=12): cv.int_range(min=9, max=12), + } + ), + cv.has_exactly_one_key(CONF_ADDRESS, CONF_INDEX), +) def to_code(config): diff --git a/esphome/components/debug/__init__.py b/esphome/components/debug/__init__.py index d43b0de06a..e43eb8bfc1 100644 --- a/esphome/components/debug/__init__.py +++ b/esphome/components/debug/__init__.py @@ -2,14 +2,16 @@ import esphome.config_validation as cv import esphome.codegen as cg from esphome.const import CONF_ID -CODEOWNERS = ['@OttoWinter'] -DEPENDENCIES = ['logger'] +CODEOWNERS = ["@OttoWinter"] +DEPENDENCIES = ["logger"] -debug_ns = cg.esphome_ns.namespace('debug') -DebugComponent = debug_ns.class_('DebugComponent', cg.Component) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(DebugComponent), -}).extend(cv.COMPONENT_SCHEMA) +debug_ns = cg.esphome_ns.namespace("debug") +DebugComponent = debug_ns.class_("DebugComponent", cg.Component) +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(DebugComponent), + } +).extend(cv.COMPONENT_SCHEMA) def to_code(config): diff --git a/esphome/components/deep_sleep/__init__.py b/esphome/components/deep_sleep/__init__.py index 9eafcc8609..793d6b2ebb 100644 --- a/esphome/components/deep_sleep/__init__.py +++ b/esphome/components/deep_sleep/__init__.py @@ -1,58 +1,81 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins, automation -from esphome.const import CONF_ID, CONF_MODE, CONF_NUMBER, CONF_PINS, CONF_RUN_CYCLES, \ - CONF_RUN_DURATION, CONF_SLEEP_DURATION, CONF_WAKEUP_PIN +from esphome.const import ( + CONF_ID, + CONF_MODE, + CONF_NUMBER, + CONF_PINS, + CONF_RUN_CYCLES, + CONF_RUN_DURATION, + CONF_SLEEP_DURATION, + CONF_WAKEUP_PIN, +) def validate_pin_number(value): valid_pins = [0, 2, 4, 12, 13, 14, 15, 25, 26, 27, 32, 33, 34, 35, 36, 37, 38, 39] if value[CONF_NUMBER] not in valid_pins: - raise cv.Invalid("Only pins {} support wakeup" - "".format(', '.join(str(x) for x in valid_pins))) + raise cv.Invalid( + "Only pins {} support wakeup" + "".format(", ".join(str(x) for x in valid_pins)) + ) return value -deep_sleep_ns = cg.esphome_ns.namespace('deep_sleep') -DeepSleepComponent = deep_sleep_ns.class_('DeepSleepComponent', cg.Component) -EnterDeepSleepAction = deep_sleep_ns.class_('EnterDeepSleepAction', automation.Action) -PreventDeepSleepAction = deep_sleep_ns.class_('PreventDeepSleepAction', automation.Action) +deep_sleep_ns = cg.esphome_ns.namespace("deep_sleep") +DeepSleepComponent = deep_sleep_ns.class_("DeepSleepComponent", cg.Component) +EnterDeepSleepAction = deep_sleep_ns.class_("EnterDeepSleepAction", automation.Action) +PreventDeepSleepAction = deep_sleep_ns.class_( + "PreventDeepSleepAction", automation.Action +) -WakeupPinMode = deep_sleep_ns.enum('WakeupPinMode') +WakeupPinMode = deep_sleep_ns.enum("WakeupPinMode") WAKEUP_PIN_MODES = { - 'IGNORE': WakeupPinMode.WAKEUP_PIN_MODE_IGNORE, - 'KEEP_AWAKE': WakeupPinMode.WAKEUP_PIN_MODE_KEEP_AWAKE, - 'INVERT_WAKEUP': WakeupPinMode.WAKEUP_PIN_MODE_INVERT_WAKEUP, + "IGNORE": WakeupPinMode.WAKEUP_PIN_MODE_IGNORE, + "KEEP_AWAKE": WakeupPinMode.WAKEUP_PIN_MODE_KEEP_AWAKE, + "INVERT_WAKEUP": WakeupPinMode.WAKEUP_PIN_MODE_INVERT_WAKEUP, } -esp_sleep_ext1_wakeup_mode_t = cg.global_ns.enum('esp_sleep_ext1_wakeup_mode_t') -Ext1Wakeup = deep_sleep_ns.struct('Ext1Wakeup') +esp_sleep_ext1_wakeup_mode_t = cg.global_ns.enum("esp_sleep_ext1_wakeup_mode_t") +Ext1Wakeup = deep_sleep_ns.struct("Ext1Wakeup") EXT1_WAKEUP_MODES = { - 'ALL_LOW': esp_sleep_ext1_wakeup_mode_t.ESP_EXT1_WAKEUP_ALL_LOW, - 'ANY_HIGH': esp_sleep_ext1_wakeup_mode_t.ESP_EXT1_WAKEUP_ANY_HIGH, + "ALL_LOW": esp_sleep_ext1_wakeup_mode_t.ESP_EXT1_WAKEUP_ALL_LOW, + "ANY_HIGH": esp_sleep_ext1_wakeup_mode_t.ESP_EXT1_WAKEUP_ANY_HIGH, } -CONF_WAKEUP_PIN_MODE = 'wakeup_pin_mode' -CONF_ESP32_EXT1_WAKEUP = 'esp32_ext1_wakeup' +CONF_WAKEUP_PIN_MODE = "wakeup_pin_mode" +CONF_ESP32_EXT1_WAKEUP = "esp32_ext1_wakeup" -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(DeepSleepComponent), - cv.Optional(CONF_RUN_DURATION): cv.positive_time_period_milliseconds, - - cv.Optional(CONF_SLEEP_DURATION): cv.positive_time_period_milliseconds, - cv.Optional(CONF_WAKEUP_PIN): cv.All(cv.only_on_esp32, pins.internal_gpio_input_pin_schema, - validate_pin_number), - cv.Optional(CONF_WAKEUP_PIN_MODE): cv.All(cv.only_on_esp32, - cv.enum(WAKEUP_PIN_MODES), upper=True), - cv.Optional(CONF_ESP32_EXT1_WAKEUP): cv.All(cv.only_on_esp32, cv.Schema({ - cv.Required(CONF_PINS): cv.ensure_list(pins.shorthand_input_pin, validate_pin_number), - cv.Required(CONF_MODE): cv.enum(EXT1_WAKEUP_MODES, upper=True), - })), - - cv.Optional(CONF_RUN_CYCLES): cv.invalid("The run_cycles option has been removed in 1.11.0 as " - "it was essentially the same as a run_duration of 0s." - "Please use run_duration now.") -}).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(DeepSleepComponent), + cv.Optional(CONF_RUN_DURATION): cv.positive_time_period_milliseconds, + cv.Optional(CONF_SLEEP_DURATION): cv.positive_time_period_milliseconds, + cv.Optional(CONF_WAKEUP_PIN): cv.All( + cv.only_on_esp32, pins.internal_gpio_input_pin_schema, validate_pin_number + ), + cv.Optional(CONF_WAKEUP_PIN_MODE): cv.All( + cv.only_on_esp32, cv.enum(WAKEUP_PIN_MODES), upper=True + ), + cv.Optional(CONF_ESP32_EXT1_WAKEUP): cv.All( + cv.only_on_esp32, + cv.Schema( + { + cv.Required(CONF_PINS): cv.ensure_list( + pins.shorthand_input_pin, validate_pin_number + ), + cv.Required(CONF_MODE): cv.enum(EXT1_WAKEUP_MODES, upper=True), + } + ), + ), + cv.Optional(CONF_RUN_CYCLES): cv.invalid( + "The run_cycles option has been removed in 1.11.0 as " + "it was essentially the same as a run_duration of 0s." + "Please use run_duration now." + ), + } +).extend(cv.COMPONENT_SCHEMA) def to_code(config): @@ -75,27 +98,31 @@ def to_code(config): for pin in conf[CONF_PINS]: mask |= 1 << pin[CONF_NUMBER] struct = cg.StructInitializer( - Ext1Wakeup, - ('mask', mask), - ('wakeup_mode', conf[CONF_MODE]) + Ext1Wakeup, ("mask", mask), ("wakeup_mode", conf[CONF_MODE]) ) cg.add(var.set_ext1_wakeup(struct)) - cg.add_define('USE_DEEP_SLEEP') + cg.add_define("USE_DEEP_SLEEP") -DEEP_SLEEP_ENTER_SCHEMA = automation.maybe_simple_id({ - cv.GenerateID(): cv.use_id(DeepSleepComponent), - cv.Optional(CONF_SLEEP_DURATION): cv.positive_time_period_milliseconds, -}) +DEEP_SLEEP_ENTER_SCHEMA = automation.maybe_simple_id( + { + cv.GenerateID(): cv.use_id(DeepSleepComponent), + cv.Optional(CONF_SLEEP_DURATION): cv.positive_time_period_milliseconds, + } +) -DEEP_SLEEP_PREVENT_SCHEMA = automation.maybe_simple_id({ - cv.GenerateID(): cv.use_id(DeepSleepComponent), -}) +DEEP_SLEEP_PREVENT_SCHEMA = automation.maybe_simple_id( + { + cv.GenerateID(): cv.use_id(DeepSleepComponent), + } +) -@automation.register_action('deep_sleep.enter', EnterDeepSleepAction, DEEP_SLEEP_ENTER_SCHEMA) +@automation.register_action( + "deep_sleep.enter", EnterDeepSleepAction, DEEP_SLEEP_ENTER_SCHEMA +) def deep_sleep_enter_to_code(config, action_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) var = cg.new_Pvariable(action_id, template_arg, paren) @@ -105,7 +132,9 @@ def deep_sleep_enter_to_code(config, action_id, template_arg, args): yield var -@automation.register_action('deep_sleep.prevent', PreventDeepSleepAction, DEEP_SLEEP_PREVENT_SCHEMA) +@automation.register_action( + "deep_sleep.prevent", PreventDeepSleepAction, DEEP_SLEEP_PREVENT_SCHEMA +) def deep_sleep_prevent_to_code(config, action_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) yield cg.new_Pvariable(action_id, template_arg, paren) diff --git a/esphome/components/dfplayer/__init__.py b/esphome/components/dfplayer/__init__.py index 680a6b89ec..33e74a220b 100644 --- a/esphome/components/dfplayer/__init__.py +++ b/esphome/components/dfplayer/__init__.py @@ -4,57 +4,68 @@ from esphome import automation from esphome.const import CONF_ID, CONF_TRIGGER_ID, CONF_FILE, CONF_DEVICE from esphome.components import uart -DEPENDENCIES = ['uart'] -CODEOWNERS = ['@glmnet'] +DEPENDENCIES = ["uart"] +CODEOWNERS = ["@glmnet"] -dfplayer_ns = cg.esphome_ns.namespace('dfplayer') -DFPlayer = dfplayer_ns.class_('DFPlayer', cg.Component) -DFPlayerFinishedPlaybackTrigger = dfplayer_ns.class_('DFPlayerFinishedPlaybackTrigger', - automation.Trigger.template()) -DFPlayerIsPlayingCondition = dfplayer_ns.class_('DFPlayerIsPlayingCondition', automation.Condition) +dfplayer_ns = cg.esphome_ns.namespace("dfplayer") +DFPlayer = dfplayer_ns.class_("DFPlayer", cg.Component) +DFPlayerFinishedPlaybackTrigger = dfplayer_ns.class_( + "DFPlayerFinishedPlaybackTrigger", automation.Trigger.template() +) +DFPlayerIsPlayingCondition = dfplayer_ns.class_( + "DFPlayerIsPlayingCondition", automation.Condition +) MULTI_CONF = True -CONF_FOLDER = 'folder' -CONF_LOOP = 'loop' -CONF_VOLUME = 'volume' -CONF_EQ_PRESET = 'eq_preset' -CONF_ON_FINISHED_PLAYBACK = 'on_finished_playback' +CONF_FOLDER = "folder" +CONF_LOOP = "loop" +CONF_VOLUME = "volume" +CONF_EQ_PRESET = "eq_preset" +CONF_ON_FINISHED_PLAYBACK = "on_finished_playback" EqPreset = dfplayer_ns.enum("EqPreset") EQ_PRESET = { - 'NORMAL': EqPreset.NORMAL, - 'POP': EqPreset.POP, - 'ROCK': EqPreset.ROCK, - 'JAZZ': EqPreset.JAZZ, - 'CLASSIC': EqPreset.CLASSIC, - 'BASS': EqPreset.BASS, + "NORMAL": EqPreset.NORMAL, + "POP": EqPreset.POP, + "ROCK": EqPreset.ROCK, + "JAZZ": EqPreset.JAZZ, + "CLASSIC": EqPreset.CLASSIC, + "BASS": EqPreset.BASS, } Device = dfplayer_ns.enum("Device") DEVICE = { - 'USB': Device.USB, - 'TF_CARD': Device.TF_CARD, + "USB": Device.USB, + "TF_CARD": Device.TF_CARD, } -NextAction = dfplayer_ns.class_('NextAction', automation.Action) -PreviousAction = dfplayer_ns.class_('PreviousAction', automation.Action) -PlayFileAction = dfplayer_ns.class_('PlayFileAction', automation.Action) -PlayFolderAction = dfplayer_ns.class_('PlayFolderAction', automation.Action) -SetVolumeAction = dfplayer_ns.class_('SetVolumeAction', automation.Action) -SetEqAction = dfplayer_ns.class_('SetEqAction', automation.Action) -SleepAction = dfplayer_ns.class_('SleepAction', automation.Action) -ResetAction = dfplayer_ns.class_('ResetAction', automation.Action) -StartAction = dfplayer_ns.class_('StartAction', automation.Action) -PauseAction = dfplayer_ns.class_('PauseAction', automation.Action) -StopAction = dfplayer_ns.class_('StopAction', automation.Action) -RandomAction = dfplayer_ns.class_('RandomAction', automation.Action) -SetDeviceAction = dfplayer_ns.class_('SetDeviceAction', automation.Action) +NextAction = dfplayer_ns.class_("NextAction", automation.Action) +PreviousAction = dfplayer_ns.class_("PreviousAction", automation.Action) +PlayFileAction = dfplayer_ns.class_("PlayFileAction", automation.Action) +PlayFolderAction = dfplayer_ns.class_("PlayFolderAction", automation.Action) +SetVolumeAction = dfplayer_ns.class_("SetVolumeAction", automation.Action) +SetEqAction = dfplayer_ns.class_("SetEqAction", automation.Action) +SleepAction = dfplayer_ns.class_("SleepAction", automation.Action) +ResetAction = dfplayer_ns.class_("ResetAction", automation.Action) +StartAction = dfplayer_ns.class_("StartAction", automation.Action) +PauseAction = dfplayer_ns.class_("PauseAction", automation.Action) +StopAction = dfplayer_ns.class_("StopAction", automation.Action) +RandomAction = dfplayer_ns.class_("RandomAction", automation.Action) +SetDeviceAction = dfplayer_ns.class_("SetDeviceAction", automation.Action) -CONFIG_SCHEMA = cv.All(cv.Schema({ - cv.GenerateID(): cv.declare_id(DFPlayer), - cv.Optional(CONF_ON_FINISHED_PLAYBACK): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(DFPlayerFinishedPlaybackTrigger), - }), -}).extend(uart.UART_DEVICE_SCHEMA)) +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(DFPlayer), + cv.Optional(CONF_ON_FINISHED_PLAYBACK): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( + DFPlayerFinishedPlaybackTrigger + ), + } + ), + } + ).extend(uart.UART_DEVICE_SCHEMA) +) def to_code(config): @@ -67,29 +78,48 @@ def to_code(config): yield automation.build_automation(trigger, [], conf) -@automation.register_action('dfplayer.play_next', NextAction, cv.Schema({ - cv.GenerateID(): cv.use_id(DFPlayer), -})) +@automation.register_action( + "dfplayer.play_next", + NextAction, + cv.Schema( + { + cv.GenerateID(): cv.use_id(DFPlayer), + } + ), +) def dfplayer_next_to_code(config, action_id, template_arg, args): var = cg.new_Pvariable(action_id, template_arg) yield cg.register_parented(var, config[CONF_ID]) yield var -@automation.register_action('dfplayer.play_previous', PreviousAction, cv.Schema({ - cv.GenerateID(): cv.use_id(DFPlayer), -})) +@automation.register_action( + "dfplayer.play_previous", + PreviousAction, + cv.Schema( + { + cv.GenerateID(): cv.use_id(DFPlayer), + } + ), +) def dfplayer_previous_to_code(config, action_id, template_arg, args): var = cg.new_Pvariable(action_id, template_arg) yield cg.register_parented(var, config[CONF_ID]) yield var -@automation.register_action('dfplayer.play', PlayFileAction, cv.maybe_simple_value({ - cv.GenerateID(): cv.use_id(DFPlayer), - cv.Required(CONF_FILE): cv.templatable(cv.int_), - cv.Optional(CONF_LOOP): cv.templatable(cv.boolean), -}, key=CONF_FILE)) +@automation.register_action( + "dfplayer.play", + PlayFileAction, + cv.maybe_simple_value( + { + cv.GenerateID(): cv.use_id(DFPlayer), + cv.Required(CONF_FILE): cv.templatable(cv.int_), + cv.Optional(CONF_LOOP): cv.templatable(cv.boolean), + }, + key=CONF_FILE, + ), +) def dfplayer_play_to_code(config, action_id, template_arg, args): var = cg.new_Pvariable(action_id, template_arg) yield cg.register_parented(var, config[CONF_ID]) @@ -101,12 +131,18 @@ def dfplayer_play_to_code(config, action_id, template_arg, args): yield var -@automation.register_action('dfplayer.play_folder', PlayFolderAction, cv.Schema({ - cv.GenerateID(): cv.use_id(DFPlayer), - cv.Required(CONF_FOLDER): cv.templatable(cv.int_), - cv.Optional(CONF_FILE): cv.templatable(cv.int_), - cv.Optional(CONF_LOOP): cv.templatable(cv.boolean), -})) +@automation.register_action( + "dfplayer.play_folder", + PlayFolderAction, + cv.Schema( + { + cv.GenerateID(): cv.use_id(DFPlayer), + cv.Required(CONF_FOLDER): cv.templatable(cv.int_), + cv.Optional(CONF_FILE): cv.templatable(cv.int_), + cv.Optional(CONF_LOOP): cv.templatable(cv.boolean), + } + ), +) def dfplayer_play_folder_to_code(config, action_id, template_arg, args): var = cg.new_Pvariable(action_id, template_arg) yield cg.register_parented(var, config[CONF_ID]) @@ -121,10 +157,17 @@ def dfplayer_play_folder_to_code(config, action_id, template_arg, args): yield var -@automation.register_action('dfplayer.set_device', SetDeviceAction, cv.maybe_simple_value({ - cv.GenerateID(): cv.use_id(DFPlayer), - cv.Required(CONF_DEVICE): cv.enum(DEVICE, upper=True), -}, key=CONF_DEVICE)) +@automation.register_action( + "dfplayer.set_device", + SetDeviceAction, + cv.maybe_simple_value( + { + cv.GenerateID(): cv.use_id(DFPlayer), + cv.Required(CONF_DEVICE): cv.enum(DEVICE, upper=True), + }, + key=CONF_DEVICE, + ), +) def dfplayer_set_device_to_code(config, action_id, template_arg, args): var = cg.new_Pvariable(action_id, template_arg) yield cg.register_parented(var, config[CONF_ID]) @@ -133,10 +176,17 @@ def dfplayer_set_device_to_code(config, action_id, template_arg, args): yield var -@automation.register_action('dfplayer.set_volume', SetVolumeAction, cv.maybe_simple_value({ - cv.GenerateID(): cv.use_id(DFPlayer), - cv.Required(CONF_VOLUME): cv.templatable(cv.int_), -}, key=CONF_VOLUME)) +@automation.register_action( + "dfplayer.set_volume", + SetVolumeAction, + cv.maybe_simple_value( + { + cv.GenerateID(): cv.use_id(DFPlayer), + cv.Required(CONF_VOLUME): cv.templatable(cv.int_), + }, + key=CONF_VOLUME, + ), +) def dfplayer_set_volume_to_code(config, action_id, template_arg, args): var = cg.new_Pvariable(action_id, template_arg) yield cg.register_parented(var, config[CONF_ID]) @@ -145,10 +195,17 @@ def dfplayer_set_volume_to_code(config, action_id, template_arg, args): yield var -@automation.register_action('dfplayer.set_eq', SetEqAction, cv.maybe_simple_value({ - cv.GenerateID(): cv.use_id(DFPlayer), - cv.Required(CONF_EQ_PRESET): cv.templatable(cv.enum(EQ_PRESET, upper=True)), -}, key=CONF_EQ_PRESET)) +@automation.register_action( + "dfplayer.set_eq", + SetEqAction, + cv.maybe_simple_value( + { + cv.GenerateID(): cv.use_id(DFPlayer), + cv.Required(CONF_EQ_PRESET): cv.templatable(cv.enum(EQ_PRESET, upper=True)), + }, + key=CONF_EQ_PRESET, + ), +) def dfplayer_set_eq_to_code(config, action_id, template_arg, args): var = cg.new_Pvariable(action_id, template_arg) yield cg.register_parented(var, config[CONF_ID]) @@ -157,63 +214,105 @@ def dfplayer_set_eq_to_code(config, action_id, template_arg, args): yield var -@automation.register_action('dfplayer.sleep', SleepAction, cv.Schema({ - cv.GenerateID(): cv.use_id(DFPlayer), -})) +@automation.register_action( + "dfplayer.sleep", + SleepAction, + cv.Schema( + { + cv.GenerateID(): cv.use_id(DFPlayer), + } + ), +) def dfplayer_sleep_to_code(config, action_id, template_arg, args): var = cg.new_Pvariable(action_id, template_arg) yield cg.register_parented(var, config[CONF_ID]) yield var -@automation.register_action('dfplayer.reset', ResetAction, cv.Schema({ - cv.GenerateID(): cv.use_id(DFPlayer), -})) +@automation.register_action( + "dfplayer.reset", + ResetAction, + cv.Schema( + { + cv.GenerateID(): cv.use_id(DFPlayer), + } + ), +) def dfplayer_reset_to_code(config, action_id, template_arg, args): var = cg.new_Pvariable(action_id, template_arg) yield cg.register_parented(var, config[CONF_ID]) yield var -@automation.register_action('dfplayer.start', StartAction, cv.Schema({ - cv.GenerateID(): cv.use_id(DFPlayer), -})) +@automation.register_action( + "dfplayer.start", + StartAction, + cv.Schema( + { + cv.GenerateID(): cv.use_id(DFPlayer), + } + ), +) def dfplayer_start_to_code(config, action_id, template_arg, args): var = cg.new_Pvariable(action_id, template_arg) yield cg.register_parented(var, config[CONF_ID]) yield var -@automation.register_action('dfplayer.pause', PauseAction, cv.Schema({ - cv.GenerateID(): cv.use_id(DFPlayer), -})) +@automation.register_action( + "dfplayer.pause", + PauseAction, + cv.Schema( + { + cv.GenerateID(): cv.use_id(DFPlayer), + } + ), +) def dfplayer_pause_to_code(config, action_id, template_arg, args): var = cg.new_Pvariable(action_id, template_arg) yield cg.register_parented(var, config[CONF_ID]) yield var -@automation.register_action('dfplayer.stop', StopAction, cv.Schema({ - cv.GenerateID(): cv.use_id(DFPlayer), -})) +@automation.register_action( + "dfplayer.stop", + StopAction, + cv.Schema( + { + cv.GenerateID(): cv.use_id(DFPlayer), + } + ), +) def dfplayer_stop_to_code(config, action_id, template_arg, args): var = cg.new_Pvariable(action_id, template_arg) yield cg.register_parented(var, config[CONF_ID]) yield var -@automation.register_action('dfplayer.random', RandomAction, cv.Schema({ - cv.GenerateID(): cv.use_id(DFPlayer), -})) +@automation.register_action( + "dfplayer.random", + RandomAction, + cv.Schema( + { + cv.GenerateID(): cv.use_id(DFPlayer), + } + ), +) def dfplayer_random_to_code(config, action_id, template_arg, args): var = cg.new_Pvariable(action_id, template_arg) yield cg.register_parented(var, config[CONF_ID]) yield var -@automation.register_condition('dfplayer.is_playing', DFPlayerIsPlayingCondition, cv.Schema({ - cv.GenerateID(): cv.use_id(DFPlayer), -})) +@automation.register_condition( + "dfplayer.is_playing", + DFPlayerIsPlayingCondition, + cv.Schema( + { + cv.GenerateID(): cv.use_id(DFPlayer), + } + ), +) def dfplyaer_is_playing_to_code(config, condition_id, template_arg, args): var = cg.new_Pvariable(condition_id, template_arg) yield cg.register_parented(var, config[CONF_ID]) diff --git a/esphome/components/dht/__init__.py b/esphome/components/dht/__init__.py index 6f14e10033..71a87b6ae5 100644 --- a/esphome/components/dht/__init__.py +++ b/esphome/components/dht/__init__.py @@ -1 +1 @@ -CODEOWNERS = ['@OttoWinter'] +CODEOWNERS = ["@OttoWinter"] diff --git a/esphome/components/dht/sensor.py b/esphome/components/dht/sensor.py index a8a101ddf4..4b2f6ee8d2 100644 --- a/esphome/components/dht/sensor.py +++ b/esphome/components/dht/sensor.py @@ -2,33 +2,49 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins from esphome.components import sensor -from esphome.const import CONF_HUMIDITY, CONF_ID, CONF_MODEL, CONF_PIN, CONF_TEMPERATURE, \ - ICON_EMPTY, UNIT_CELSIUS, UNIT_PERCENT, DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_HUMIDITY +from esphome.const import ( + CONF_HUMIDITY, + CONF_ID, + CONF_MODEL, + CONF_PIN, + CONF_TEMPERATURE, + ICON_EMPTY, + UNIT_CELSIUS, + UNIT_PERCENT, + DEVICE_CLASS_TEMPERATURE, + DEVICE_CLASS_HUMIDITY, +) from esphome.cpp_helpers import gpio_pin_expression -dht_ns = cg.esphome_ns.namespace('dht') -DHTModel = dht_ns.enum('DHTModel') +dht_ns = cg.esphome_ns.namespace("dht") +DHTModel = dht_ns.enum("DHTModel") DHT_MODELS = { - 'AUTO_DETECT': DHTModel.DHT_MODEL_AUTO_DETECT, - 'DHT11': DHTModel.DHT_MODEL_DHT11, - 'DHT22': DHTModel.DHT_MODEL_DHT22, - 'AM2302': DHTModel.DHT_MODEL_AM2302, - 'RHT03': DHTModel.DHT_MODEL_RHT03, - 'SI7021': DHTModel.DHT_MODEL_SI7021, - 'DHT22_TYPE2': DHTModel.DHT_MODEL_DHT22_TYPE2, + "AUTO_DETECT": DHTModel.DHT_MODEL_AUTO_DETECT, + "DHT11": DHTModel.DHT_MODEL_DHT11, + "DHT22": DHTModel.DHT_MODEL_DHT22, + "AM2302": DHTModel.DHT_MODEL_AM2302, + "RHT03": DHTModel.DHT_MODEL_RHT03, + "SI7021": DHTModel.DHT_MODEL_SI7021, + "DHT22_TYPE2": DHTModel.DHT_MODEL_DHT22_TYPE2, } -DHT = dht_ns.class_('DHT', cg.PollingComponent) +DHT = dht_ns.class_("DHT", cg.PollingComponent) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(DHT), - cv.Required(CONF_PIN): pins.gpio_input_pin_schema, - cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, - DEVICE_CLASS_TEMPERATURE), - cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 0, - DEVICE_CLASS_HUMIDITY), - cv.Optional(CONF_MODEL, default='auto detect'): cv.enum(DHT_MODELS, upper=True, space='_'), -}).extend(cv.polling_component_schema('60s')) +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(DHT), + cv.Required(CONF_PIN): pins.gpio_input_pin_schema, + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( + UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE + ), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema( + UNIT_PERCENT, ICON_EMPTY, 0, DEVICE_CLASS_HUMIDITY + ), + cv.Optional(CONF_MODEL, default="auto detect"): cv.enum( + DHT_MODELS, upper=True, space="_" + ), + } +).extend(cv.polling_component_schema("60s")) def to_code(config): diff --git a/esphome/components/dht12/sensor.py b/esphome/components/dht12/sensor.py index 4e418dbc9e..f63768142c 100644 --- a/esphome/components/dht12/sensor.py +++ b/esphome/components/dht12/sensor.py @@ -1,21 +1,37 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import CONF_HUMIDITY, CONF_ID, CONF_TEMPERATURE, \ - UNIT_CELSIUS, ICON_EMPTY, UNIT_PERCENT, DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_HUMIDITY +from esphome.const import ( + CONF_HUMIDITY, + CONF_ID, + CONF_TEMPERATURE, + UNIT_CELSIUS, + ICON_EMPTY, + UNIT_PERCENT, + DEVICE_CLASS_TEMPERATURE, + DEVICE_CLASS_HUMIDITY, +) -DEPENDENCIES = ['i2c'] +DEPENDENCIES = ["i2c"] -dht12_ns = cg.esphome_ns.namespace('dht12') -DHT12Component = dht12_ns.class_('DHT12Component', cg.PollingComponent, i2c.I2CDevice) +dht12_ns = cg.esphome_ns.namespace("dht12") +DHT12Component = dht12_ns.class_("DHT12Component", cg.PollingComponent, i2c.I2CDevice) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(DHT12Component), - cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, - DEVICE_CLASS_TEMPERATURE), - cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 1, - DEVICE_CLASS_HUMIDITY), -}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x5C)) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(DHT12Component), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( + UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE + ), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema( + UNIT_PERCENT, ICON_EMPTY, 1, DEVICE_CLASS_HUMIDITY + ), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x5C)) +) def to_code(config): diff --git a/esphome/components/display/__init__.py b/esphome/components/display/__init__.py index 951d561caa..f72ec88fae 100644 --- a/esphome/components/display/__init__.py +++ b/esphome/components/display/__init__.py @@ -7,14 +7,18 @@ from esphome.core import coroutine, coroutine_with_priority IS_PLATFORM_COMPONENT = True -display_ns = cg.esphome_ns.namespace('display') -DisplayBuffer = display_ns.class_('DisplayBuffer') -DisplayPage = display_ns.class_('DisplayPage') -DisplayPagePtr = DisplayPage.operator('ptr') -DisplayBufferRef = DisplayBuffer.operator('ref') -DisplayPageShowAction = display_ns.class_('DisplayPageShowAction', automation.Action) -DisplayPageShowNextAction = display_ns.class_('DisplayPageShowNextAction', automation.Action) -DisplayPageShowPrevAction = display_ns.class_('DisplayPageShowPrevAction', automation.Action) +display_ns = cg.esphome_ns.namespace("display") +DisplayBuffer = display_ns.class_("DisplayBuffer") +DisplayPage = display_ns.class_("DisplayPage") +DisplayPagePtr = DisplayPage.operator("ptr") +DisplayBufferRef = DisplayBuffer.operator("ref") +DisplayPageShowAction = display_ns.class_("DisplayPageShowAction", automation.Action) +DisplayPageShowNextAction = display_ns.class_( + "DisplayPageShowNextAction", automation.Action +) +DisplayPageShowPrevAction = display_ns.class_( + "DisplayPageShowPrevAction", automation.Action +) DISPLAY_ROTATIONS = { 0: display_ns.DISPLAY_ROTATION_0_DEGREES, @@ -31,17 +35,26 @@ def validate_rotation(value): return cv.enum(DISPLAY_ROTATIONS, int=True)(value) -BASIC_DISPLAY_SCHEMA = cv.Schema({ - cv.Optional(CONF_LAMBDA): cv.lambda_, -}) +BASIC_DISPLAY_SCHEMA = cv.Schema( + { + cv.Optional(CONF_LAMBDA): cv.lambda_, + } +) -FULL_DISPLAY_SCHEMA = BASIC_DISPLAY_SCHEMA.extend({ - cv.Optional(CONF_ROTATION): validate_rotation, - cv.Optional(CONF_PAGES): cv.All(cv.ensure_list({ - cv.GenerateID(): cv.declare_id(DisplayPage), - cv.Required(CONF_LAMBDA): cv.lambda_, - }), cv.Length(min=1)), -}) +FULL_DISPLAY_SCHEMA = BASIC_DISPLAY_SCHEMA.extend( + { + cv.Optional(CONF_ROTATION): validate_rotation, + cv.Optional(CONF_PAGES): cv.All( + cv.ensure_list( + { + cv.GenerateID(): cv.declare_id(DisplayPage), + cv.Required(CONF_LAMBDA): cv.lambda_, + } + ), + cv.Length(min=1), + ), + } +) @coroutine @@ -51,8 +64,9 @@ def setup_display_core_(var, config): if CONF_PAGES in config: pages = [] for conf in config[CONF_PAGES]: - lambda_ = yield cg.process_lambda(conf[CONF_LAMBDA], [(DisplayBufferRef, 'it')], - return_type=cg.void) + lambda_ = yield cg.process_lambda( + conf[CONF_LAMBDA], [(DisplayBufferRef, "it")], return_type=cg.void + ) page = cg.new_Pvariable(conf[CONF_ID], lambda_) pages.append(page) cg.add(var.set_pages(pages)) @@ -63,9 +77,15 @@ def register_display(var, config): yield setup_display_core_(var, config) -@automation.register_action('display.page.show', DisplayPageShowAction, maybe_simple_id({ - cv.Required(CONF_ID): cv.templatable(cv.use_id(DisplayPage)), -})) +@automation.register_action( + "display.page.show", + DisplayPageShowAction, + maybe_simple_id( + { + cv.Required(CONF_ID): cv.templatable(cv.use_id(DisplayPage)), + } + ), +) def display_page_show_to_code(config, action_id, template_arg, args): var = cg.new_Pvariable(action_id, template_arg) if isinstance(config[CONF_ID], core.Lambda): @@ -77,18 +97,29 @@ def display_page_show_to_code(config, action_id, template_arg, args): yield var -@automation.register_action('display.page.show_next', DisplayPageShowNextAction, maybe_simple_id({ - cv.Required(CONF_ID): cv.templatable(cv.use_id(DisplayBuffer)), -})) +@automation.register_action( + "display.page.show_next", + DisplayPageShowNextAction, + maybe_simple_id( + { + cv.Required(CONF_ID): cv.templatable(cv.use_id(DisplayBuffer)), + } + ), +) def display_page_show_next_to_code(config, action_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) yield cg.new_Pvariable(action_id, template_arg, paren) -@automation.register_action('display.page.show_previous', DisplayPageShowPrevAction, - maybe_simple_id({ - cv.Required(CONF_ID): cv.templatable(cv.use_id(DisplayBuffer)), - })) +@automation.register_action( + "display.page.show_previous", + DisplayPageShowPrevAction, + maybe_simple_id( + { + cv.Required(CONF_ID): cv.templatable(cv.use_id(DisplayBuffer)), + } + ), +) def display_page_show_previous_to_code(config, action_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) yield cg.new_Pvariable(action_id, template_arg, paren) diff --git a/esphome/components/ds1307/time.py b/esphome/components/ds1307/time.py index 371dc85be8..9c1d75c029 100644 --- a/esphome/components/ds1307/time.py +++ b/esphome/components/ds1307/time.py @@ -5,31 +5,45 @@ from esphome.components import i2c, time from esphome.const import CONF_ID -CODEOWNERS = ['@badbadc0ffee'] -DEPENDENCIES = ['i2c'] -ds1307_ns = cg.esphome_ns.namespace('ds1307') -DS1307Component = ds1307_ns.class_('DS1307Component', time.RealTimeClock, i2c.I2CDevice) -WriteAction = ds1307_ns.class_('WriteAction', automation.Action) -ReadAction = ds1307_ns.class_('ReadAction', automation.Action) +CODEOWNERS = ["@badbadc0ffee"] +DEPENDENCIES = ["i2c"] +ds1307_ns = cg.esphome_ns.namespace("ds1307") +DS1307Component = ds1307_ns.class_("DS1307Component", time.RealTimeClock, i2c.I2CDevice) +WriteAction = ds1307_ns.class_("WriteAction", automation.Action) +ReadAction = ds1307_ns.class_("ReadAction", automation.Action) -CONFIG_SCHEMA = time.TIME_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(DS1307Component), -}).extend(i2c.i2c_device_schema(0x68)) +CONFIG_SCHEMA = time.TIME_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(DS1307Component), + } +).extend(i2c.i2c_device_schema(0x68)) -@automation.register_action('ds1307.write_time', WriteAction, cv.Schema({ - cv.GenerateID(): cv.use_id(DS1307Component), -})) +@automation.register_action( + "ds1307.write_time", + WriteAction, + cv.Schema( + { + cv.GenerateID(): cv.use_id(DS1307Component), + } + ), +) def ds1307_write_time_to_code(config, action_id, template_arg, args): var = cg.new_Pvariable(action_id, template_arg) yield cg.register_parented(var, config[CONF_ID]) yield var -@automation.register_action('ds1307.read_time', ReadAction, automation.maybe_simple_id({ - cv.GenerateID(): cv.use_id(DS1307Component), -})) +@automation.register_action( + "ds1307.read_time", + ReadAction, + automation.maybe_simple_id( + { + cv.GenerateID(): cv.use_id(DS1307Component), + } + ), +) def ds1307_read_time_to_code(config, action_id, template_arg, args): var = cg.new_Pvariable(action_id, template_arg) yield cg.register_parented(var, config[CONF_ID]) diff --git a/esphome/components/duty_cycle/sensor.py b/esphome/components/duty_cycle/sensor.py index 930745cfc4..0dd91bade3 100644 --- a/esphome/components/duty_cycle/sensor.py +++ b/esphome/components/duty_cycle/sensor.py @@ -2,16 +2,31 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins from esphome.components import sensor -from esphome.const import CONF_ID, CONF_PIN, DEVICE_CLASS_EMPTY, UNIT_PERCENT, ICON_PERCENT +from esphome.const import ( + CONF_ID, + CONF_PIN, + DEVICE_CLASS_EMPTY, + UNIT_PERCENT, + ICON_PERCENT, +) -duty_cycle_ns = cg.esphome_ns.namespace('duty_cycle') -DutyCycleSensor = duty_cycle_ns.class_('DutyCycleSensor', sensor.Sensor, cg.PollingComponent) +duty_cycle_ns = cg.esphome_ns.namespace("duty_cycle") +DutyCycleSensor = duty_cycle_ns.class_( + "DutyCycleSensor", sensor.Sensor, cg.PollingComponent +) -CONFIG_SCHEMA = sensor.sensor_schema(UNIT_PERCENT, ICON_PERCENT, 1, DEVICE_CLASS_EMPTY).extend({ - cv.GenerateID(): cv.declare_id(DutyCycleSensor), - cv.Required(CONF_PIN): cv.All(pins.internal_gpio_input_pin_schema, - pins.validate_has_interrupt), -}).extend(cv.polling_component_schema('60s')) +CONFIG_SCHEMA = ( + sensor.sensor_schema(UNIT_PERCENT, ICON_PERCENT, 1, DEVICE_CLASS_EMPTY) + .extend( + { + cv.GenerateID(): cv.declare_id(DutyCycleSensor), + cv.Required(CONF_PIN): cv.All( + pins.internal_gpio_input_pin_schema, pins.validate_has_interrupt + ), + } + ) + .extend(cv.polling_component_schema("60s")) +) def to_code(config): diff --git a/esphome/components/e131/__init__.py b/esphome/components/e131/__init__.py index 0ba16bc928..b33aef8cf3 100644 --- a/esphome/components/e131/__init__.py +++ b/esphome/components/e131/__init__.py @@ -4,28 +4,29 @@ from esphome.components.light.types import AddressableLightEffect from esphome.components.light.effects import register_addressable_effect from esphome.const import CONF_ID, CONF_NAME, CONF_METHOD, CONF_CHANNELS -e131_ns = cg.esphome_ns.namespace('e131') -E131AddressableLightEffect = e131_ns.class_('E131AddressableLightEffect', AddressableLightEffect) -E131Component = e131_ns.class_('E131Component', cg.Component) +e131_ns = cg.esphome_ns.namespace("e131") +E131AddressableLightEffect = e131_ns.class_( + "E131AddressableLightEffect", AddressableLightEffect +) +E131Component = e131_ns.class_("E131Component", cg.Component) -METHODS = { - 'UNICAST': e131_ns.E131_UNICAST, - 'MULTICAST': e131_ns.E131_MULTICAST -} +METHODS = {"UNICAST": e131_ns.E131_UNICAST, "MULTICAST": e131_ns.E131_MULTICAST} CHANNELS = { - 'MONO': e131_ns.E131_MONO, - 'RGB': e131_ns.E131_RGB, - 'RGBW': e131_ns.E131_RGBW + "MONO": e131_ns.E131_MONO, + "RGB": e131_ns.E131_RGB, + "RGBW": e131_ns.E131_RGBW, } -CONF_UNIVERSE = 'universe' -CONF_E131_ID = 'e131_id' +CONF_UNIVERSE = "universe" +CONF_E131_ID = "e131_id" -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(E131Component), - cv.Optional(CONF_METHOD, default='MULTICAST'): cv.one_of(*METHODS, upper=True), -}) +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(E131Component), + cv.Optional(CONF_METHOD, default="MULTICAST"): cv.one_of(*METHODS, upper=True), + } +) def to_code(config): @@ -34,11 +35,16 @@ def to_code(config): cg.add(var.set_method(METHODS[config[CONF_METHOD]])) -@register_addressable_effect('e131', E131AddressableLightEffect, "E1.31", { - cv.GenerateID(CONF_E131_ID): cv.use_id(E131Component), - cv.Required(CONF_UNIVERSE): cv.int_range(min=1, max=512), - cv.Optional(CONF_CHANNELS, default='RGB'): cv.one_of(*CHANNELS, upper=True) -}) +@register_addressable_effect( + "e131", + E131AddressableLightEffect, + "E1.31", + { + cv.GenerateID(CONF_E131_ID): cv.use_id(E131Component), + cv.Required(CONF_UNIVERSE): cv.int_range(min=1, max=512), + cv.Optional(CONF_CHANNELS, default="RGB"): cv.one_of(*CHANNELS, upper=True), + }, +) def e131_light_effect_to_code(config, effect_id): parent = yield cg.get_variable(config[CONF_E131_ID]) diff --git a/esphome/components/endstop/cover.py b/esphome/components/endstop/cover.py index 0d65cc1078..b463d5d960 100644 --- a/esphome/components/endstop/cover.py +++ b/esphome/components/endstop/cover.py @@ -2,27 +2,34 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import automation from esphome.components import binary_sensor, cover -from esphome.const import CONF_CLOSE_ACTION, CONF_CLOSE_DURATION, \ - CONF_CLOSE_ENDSTOP, CONF_ID, CONF_OPEN_ACTION, CONF_OPEN_DURATION, \ - CONF_OPEN_ENDSTOP, CONF_STOP_ACTION, CONF_MAX_DURATION +from esphome.const import ( + CONF_CLOSE_ACTION, + CONF_CLOSE_DURATION, + CONF_CLOSE_ENDSTOP, + CONF_ID, + CONF_OPEN_ACTION, + CONF_OPEN_DURATION, + CONF_OPEN_ENDSTOP, + CONF_STOP_ACTION, + CONF_MAX_DURATION, +) -endstop_ns = cg.esphome_ns.namespace('endstop') -EndstopCover = endstop_ns.class_('EndstopCover', cover.Cover, cg.Component) +endstop_ns = cg.esphome_ns.namespace("endstop") +EndstopCover = endstop_ns.class_("EndstopCover", cover.Cover, cg.Component) -CONFIG_SCHEMA = cover.COVER_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(EndstopCover), - cv.Required(CONF_STOP_ACTION): automation.validate_automation(single=True), - - cv.Required(CONF_OPEN_ENDSTOP): cv.use_id(binary_sensor.BinarySensor), - cv.Required(CONF_OPEN_ACTION): automation.validate_automation(single=True), - cv.Required(CONF_OPEN_DURATION): cv.positive_time_period_milliseconds, - - cv.Required(CONF_CLOSE_ACTION): automation.validate_automation(single=True), - cv.Required(CONF_CLOSE_ENDSTOP): cv.use_id(binary_sensor.BinarySensor), - cv.Required(CONF_CLOSE_DURATION): cv.positive_time_period_milliseconds, - - cv.Optional(CONF_MAX_DURATION): cv.positive_time_period_milliseconds, -}).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = cover.COVER_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(EndstopCover), + cv.Required(CONF_STOP_ACTION): automation.validate_automation(single=True), + cv.Required(CONF_OPEN_ENDSTOP): cv.use_id(binary_sensor.BinarySensor), + cv.Required(CONF_OPEN_ACTION): automation.validate_automation(single=True), + cv.Required(CONF_OPEN_DURATION): cv.positive_time_period_milliseconds, + cv.Required(CONF_CLOSE_ACTION): automation.validate_automation(single=True), + cv.Required(CONF_CLOSE_ENDSTOP): cv.use_id(binary_sensor.BinarySensor), + cv.Required(CONF_CLOSE_DURATION): cv.positive_time_period_milliseconds, + cv.Optional(CONF_MAX_DURATION): cv.positive_time_period_milliseconds, + } +).extend(cv.COMPONENT_SCHEMA) def to_code(config): @@ -30,17 +37,23 @@ def to_code(config): yield cg.register_component(var, config) yield cover.register_cover(var, config) - yield automation.build_automation(var.get_stop_trigger(), [], config[CONF_STOP_ACTION]) + yield automation.build_automation( + var.get_stop_trigger(), [], config[CONF_STOP_ACTION] + ) bin = yield cg.get_variable(config[CONF_OPEN_ENDSTOP]) cg.add(var.set_open_endstop(bin)) cg.add(var.set_open_duration(config[CONF_OPEN_DURATION])) - yield automation.build_automation(var.get_open_trigger(), [], config[CONF_OPEN_ACTION]) + yield automation.build_automation( + var.get_open_trigger(), [], config[CONF_OPEN_ACTION] + ) bin = yield cg.get_variable(config[CONF_CLOSE_ENDSTOP]) cg.add(var.set_close_endstop(bin)) cg.add(var.set_close_duration(config[CONF_CLOSE_DURATION])) - yield automation.build_automation(var.get_close_trigger(), [], config[CONF_CLOSE_ACTION]) + yield automation.build_automation( + var.get_close_trigger(), [], config[CONF_CLOSE_ACTION] + ) if CONF_MAX_DURATION in config: cg.add(var.set_max_duration(config[CONF_MAX_DURATION])) diff --git a/esphome/components/esp32_ble_beacon/__init__.py b/esphome/components/esp32_ble_beacon/__init__.py index 2f02e71fef..46f5678856 100644 --- a/esphome/components/esp32_ble_beacon/__init__.py +++ b/esphome/components/esp32_ble_beacon/__init__.py @@ -3,26 +3,30 @@ import esphome.config_validation as cv from esphome.const import CONF_ID, CONF_TYPE, CONF_UUID, ESP_PLATFORM_ESP32 ESP_PLATFORMS = [ESP_PLATFORM_ESP32] -CONFLICTS_WITH = ['esp32_ble_tracker'] +CONFLICTS_WITH = ["esp32_ble_tracker"] -esp32_ble_beacon_ns = cg.esphome_ns.namespace('esp32_ble_beacon') -ESP32BLEBeacon = esp32_ble_beacon_ns.class_('ESP32BLEBeacon', cg.Component) +esp32_ble_beacon_ns = cg.esphome_ns.namespace("esp32_ble_beacon") +ESP32BLEBeacon = esp32_ble_beacon_ns.class_("ESP32BLEBeacon", cg.Component) -CONF_MAJOR = 'major' -CONF_MINOR = 'minor' +CONF_MAJOR = "major" +CONF_MINOR = "minor" -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(ESP32BLEBeacon), - cv.Required(CONF_TYPE): cv.one_of('IBEACON', upper=True), - cv.Required(CONF_UUID): cv.uuid, - cv.Optional(CONF_MAJOR, default=10167): cv.uint16_t, - cv.Optional(CONF_MINOR, default=61958): cv.uint16_t, -}).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(ESP32BLEBeacon), + cv.Required(CONF_TYPE): cv.one_of("IBEACON", upper=True), + cv.Required(CONF_UUID): cv.uuid, + cv.Optional(CONF_MAJOR, default=10167): cv.uint16_t, + cv.Optional(CONF_MINOR, default=61958): cv.uint16_t, + } +).extend(cv.COMPONENT_SCHEMA) def to_code(config): uuid = config[CONF_UUID].hex - uuid_arr = [cg.RawExpression('0x{}'.format(uuid[i:i + 2])) for i in range(0, len(uuid), 2)] + uuid_arr = [ + cg.RawExpression("0x{}".format(uuid[i : i + 2])) for i in range(0, len(uuid), 2) + ] var = cg.new_Pvariable(config[CONF_ID], uuid_arr) yield cg.register_component(var, config) cg.add(var.set_major(config[CONF_MAJOR])) diff --git a/esphome/components/esp32_ble_tracker/__init__.py b/esphome/components/esp32_ble_tracker/__init__.py index c4cc7260fd..2e567b49bc 100644 --- a/esphome/components/esp32_ble_tracker/__init__.py +++ b/esphome/components/esp32_ble_tracker/__init__.py @@ -2,33 +2,46 @@ import re import esphome.codegen as cg import esphome.config_validation as cv from esphome import automation -from esphome.const import CONF_ID, ESP_PLATFORM_ESP32, CONF_INTERVAL, \ - CONF_DURATION, CONF_TRIGGER_ID, CONF_MAC_ADDRESS, CONF_SERVICE_UUID, CONF_MANUFACTURER_ID, \ - CONF_ON_BLE_ADVERTISE, CONF_ON_BLE_SERVICE_DATA_ADVERTISE, \ - CONF_ON_BLE_MANUFACTURER_DATA_ADVERTISE +from esphome.const import ( + CONF_ID, + ESP_PLATFORM_ESP32, + CONF_INTERVAL, + CONF_DURATION, + CONF_TRIGGER_ID, + CONF_MAC_ADDRESS, + CONF_SERVICE_UUID, + CONF_MANUFACTURER_ID, + CONF_ON_BLE_ADVERTISE, + CONF_ON_BLE_SERVICE_DATA_ADVERTISE, + CONF_ON_BLE_MANUFACTURER_DATA_ADVERTISE, +) from esphome.core import coroutine ESP_PLATFORMS = [ESP_PLATFORM_ESP32] -AUTO_LOAD = ['xiaomi_ble', 'ruuvi_ble'] +AUTO_LOAD = ["xiaomi_ble", "ruuvi_ble"] -CONF_ESP32_BLE_ID = 'esp32_ble_id' -CONF_SCAN_PARAMETERS = 'scan_parameters' -CONF_WINDOW = 'window' -CONF_ACTIVE = 'active' -esp32_ble_tracker_ns = cg.esphome_ns.namespace('esp32_ble_tracker') -ESP32BLETracker = esp32_ble_tracker_ns.class_('ESP32BLETracker', cg.Component) -ESPBTDeviceListener = esp32_ble_tracker_ns.class_('ESPBTDeviceListener') -ESPBTDevice = esp32_ble_tracker_ns.class_('ESPBTDevice') -ESPBTDeviceConstRef = ESPBTDevice.operator('ref').operator('const') +CONF_ESP32_BLE_ID = "esp32_ble_id" +CONF_SCAN_PARAMETERS = "scan_parameters" +CONF_WINDOW = "window" +CONF_ACTIVE = "active" +esp32_ble_tracker_ns = cg.esphome_ns.namespace("esp32_ble_tracker") +ESP32BLETracker = esp32_ble_tracker_ns.class_("ESP32BLETracker", cg.Component) +ESPBTDeviceListener = esp32_ble_tracker_ns.class_("ESPBTDeviceListener") +ESPBTDevice = esp32_ble_tracker_ns.class_("ESPBTDevice") +ESPBTDeviceConstRef = ESPBTDevice.operator("ref").operator("const") adv_data_t = cg.std_vector.template(cg.uint8) -adv_data_t_const_ref = adv_data_t.operator('ref').operator('const') +adv_data_t_const_ref = adv_data_t.operator("ref").operator("const") # Triggers ESPBTAdvertiseTrigger = esp32_ble_tracker_ns.class_( - 'ESPBTAdvertiseTrigger', automation.Trigger.template(ESPBTDeviceConstRef)) + "ESPBTAdvertiseTrigger", automation.Trigger.template(ESPBTDeviceConstRef) +) BLEServiceDataAdvertiseTrigger = esp32_ble_tracker_ns.class_( - 'BLEServiceDataAdvertiseTrigger', automation.Trigger.template(adv_data_t_const_ref)) + "BLEServiceDataAdvertiseTrigger", automation.Trigger.template(adv_data_t_const_ref) +) BLEManufacturerDataAdvertiseTrigger = esp32_ble_tracker_ns.class_( - 'BLEManufacturerDataAdvertiseTrigger', automation.Trigger.template(adv_data_t_const_ref)) + "BLEManufacturerDataAdvertiseTrigger", + automation.Trigger.template(adv_data_t_const_ref), +) def validate_scan_parameters(config): @@ -37,19 +50,23 @@ def validate_scan_parameters(config): window = config[CONF_WINDOW] if window > interval: - raise cv.Invalid("Scan window ({}) needs to be smaller than scan interval ({})" - "".format(window, interval)) + raise cv.Invalid( + "Scan window ({}) needs to be smaller than scan interval ({})" + "".format(window, interval) + ) if interval.total_milliseconds * 3 > duration.total_milliseconds: - raise cv.Invalid("Scan duration needs to be at least three times the scan interval to" - "cover all BLE channels.") + raise cv.Invalid( + "Scan duration needs to be at least three times the scan interval to" + "cover all BLE channels." + ) return config -bt_uuid16_format = 'XXXX' -bt_uuid32_format = 'XXXXXXXX' -bt_uuid128_format = 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' +bt_uuid16_format = "XXXX" +bt_uuid32_format = "XXXXXXXX" +bt_uuid128_format = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" def bt_uuid(value): @@ -60,67 +77,103 @@ def bt_uuid(value): pattern = re.compile("^[A-F|0-9]{4,}$") if not pattern.match(value): raise cv.Invalid( - f"Invalid hexadecimal value for 16 bit UUID format: '{in_value}'") + f"Invalid hexadecimal value for 16 bit UUID format: '{in_value}'" + ) return value if len(value) == len(bt_uuid32_format): pattern = re.compile("^[A-F|0-9]{8,}$") if not pattern.match(value): raise cv.Invalid( - f"Invalid hexadecimal value for 32 bit UUID format: '{in_value}'") + f"Invalid hexadecimal value for 32 bit UUID format: '{in_value}'" + ) return value if len(value) == len(bt_uuid128_format): pattern = re.compile( - "^[A-F|0-9]{8,}-[A-F|0-9]{4,}-[A-F|0-9]{4,}-[A-F|0-9]{4,}-[A-F|0-9]{12,}$") + "^[A-F|0-9]{8,}-[A-F|0-9]{4,}-[A-F|0-9]{4,}-[A-F|0-9]{4,}-[A-F|0-9]{12,}$" + ) if not pattern.match(value): raise cv.Invalid( - f"Invalid hexadecimal value for 128 UUID format: '{in_value}'") + f"Invalid hexadecimal value for 128 UUID format: '{in_value}'" + ) return value raise cv.Invalid( "Service UUID must be in 16 bit '{}', 32 bit '{}', or 128 bit '{}' format".format( - bt_uuid16_format, bt_uuid32_format, bt_uuid128_format)) + bt_uuid16_format, bt_uuid32_format, bt_uuid128_format + ) + ) def as_hex(value): - return cg.RawExpression(f'0x{value}ULL') + return cg.RawExpression(f"0x{value}ULL") def as_hex_array(value): value = value.replace("-", "") - cpp_array = [f'0x{part}' for part in [value[i:i+2] for i in range(0, len(value), 2)]] + cpp_array = [ + f"0x{part}" for part in [value[i : i + 2] for i in range(0, len(value), 2)] + ] return cg.RawExpression( - '(uint8_t*)(const uint8_t[16]){{{}}}'.format(','.join(reversed(cpp_array)))) + "(uint8_t*)(const uint8_t[16]){{{}}}".format(",".join(reversed(cpp_array))) + ) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(ESP32BLETracker), - cv.Optional(CONF_SCAN_PARAMETERS, default={}): cv.All(cv.Schema({ - cv.Optional(CONF_DURATION, default='5min'): cv.positive_time_period_seconds, - cv.Optional(CONF_INTERVAL, default='320ms'): cv.positive_time_period_milliseconds, - cv.Optional(CONF_WINDOW, default='30ms'): cv.positive_time_period_milliseconds, - cv.Optional(CONF_ACTIVE, default=True): cv.boolean, - }), validate_scan_parameters), - cv.Optional(CONF_ON_BLE_ADVERTISE): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ESPBTAdvertiseTrigger), - cv.Optional(CONF_MAC_ADDRESS): cv.mac_address, - }), - cv.Optional(CONF_ON_BLE_SERVICE_DATA_ADVERTISE): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(BLEServiceDataAdvertiseTrigger), - cv.Optional(CONF_MAC_ADDRESS): cv.mac_address, - cv.Required(CONF_SERVICE_UUID): bt_uuid, - }), - cv.Optional(CONF_ON_BLE_MANUFACTURER_DATA_ADVERTISE): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(BLEManufacturerDataAdvertiseTrigger), - cv.Optional(CONF_MAC_ADDRESS): cv.mac_address, - cv.Required(CONF_MANUFACTURER_ID): bt_uuid, - }), +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(ESP32BLETracker), + cv.Optional(CONF_SCAN_PARAMETERS, default={}): cv.All( + cv.Schema( + { + cv.Optional( + CONF_DURATION, default="5min" + ): cv.positive_time_period_seconds, + cv.Optional( + CONF_INTERVAL, default="320ms" + ): cv.positive_time_period_milliseconds, + cv.Optional( + CONF_WINDOW, default="30ms" + ): cv.positive_time_period_milliseconds, + cv.Optional(CONF_ACTIVE, default=True): cv.boolean, + } + ), + validate_scan_parameters, + ), + cv.Optional(CONF_ON_BLE_ADVERTISE): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ESPBTAdvertiseTrigger), + cv.Optional(CONF_MAC_ADDRESS): cv.mac_address, + } + ), + cv.Optional(CONF_ON_BLE_SERVICE_DATA_ADVERTISE): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( + BLEServiceDataAdvertiseTrigger + ), + cv.Optional(CONF_MAC_ADDRESS): cv.mac_address, + cv.Required(CONF_SERVICE_UUID): bt_uuid, + } + ), + cv.Optional( + CONF_ON_BLE_MANUFACTURER_DATA_ADVERTISE + ): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( + BLEManufacturerDataAdvertiseTrigger + ), + cv.Optional(CONF_MAC_ADDRESS): cv.mac_address, + cv.Required(CONF_MANUFACTURER_ID): bt_uuid, + } + ), + cv.Optional("scan_interval"): cv.invalid( + "This option has been removed in 1.14 (Reason: " "it never had an effect)" + ), + } +).extend(cv.COMPONENT_SCHEMA) - cv.Optional('scan_interval'): cv.invalid("This option has been removed in 1.14 (Reason: " - "it never had an effect)"), -}).extend(cv.COMPONENT_SCHEMA) - -ESP_BLE_DEVICE_SCHEMA = cv.Schema({ - cv.GenerateID(CONF_ESP32_BLE_ID): cv.use_id(ESP32BLETracker), -}) +ESP_BLE_DEVICE_SCHEMA = cv.Schema( + { + cv.GenerateID(CONF_ESP32_BLE_ID): cv.use_id(ESP32BLETracker), + } +) def to_code(config): @@ -135,7 +188,7 @@ def to_code(config): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) if CONF_MAC_ADDRESS in conf: cg.add(trigger.set_address(conf[CONF_MAC_ADDRESS].as_hex)) - yield automation.build_automation(trigger, [(ESPBTDeviceConstRef, 'x')], conf) + yield automation.build_automation(trigger, [(ESPBTDeviceConstRef, "x")], conf) for conf in config.get(CONF_ON_BLE_SERVICE_DATA_ADVERTISE, []): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) if len(conf[CONF_SERVICE_UUID]) == len(bt_uuid16_format): @@ -147,7 +200,7 @@ def to_code(config): cg.add(trigger.set_service_uuid128(uuid128)) if CONF_MAC_ADDRESS in conf: cg.add(trigger.set_address(conf[CONF_MAC_ADDRESS].as_hex)) - yield automation.build_automation(trigger, [(adv_data_t_const_ref, 'x')], conf) + yield automation.build_automation(trigger, [(adv_data_t_const_ref, "x")], conf) for conf in config.get(CONF_ON_BLE_MANUFACTURER_DATA_ADVERTISE, []): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) if len(conf[CONF_MANUFACTURER_ID]) == len(bt_uuid16_format): @@ -159,7 +212,7 @@ def to_code(config): cg.add(trigger.set_manufacturer_uuid128(uuid128)) if CONF_MAC_ADDRESS in conf: cg.add(trigger.set_address(conf[CONF_MAC_ADDRESS].as_hex)) - yield automation.build_automation(trigger, [(adv_data_t_const_ref, 'x')], conf) + yield automation.build_automation(trigger, [(adv_data_t_const_ref, "x")], conf) @coroutine diff --git a/esphome/components/esp32_camera/__init__.py b/esphome/components/esp32_camera/__init__.py index 57a6394c8a..f7169257a4 100644 --- a/esphome/components/esp32_camera/__init__.py +++ b/esphome/components/esp32_camera/__init__.py @@ -1,105 +1,126 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins -from esphome.const import CONF_FREQUENCY, CONF_ID, CONF_NAME, CONF_PIN, CONF_SCL, CONF_SDA, \ - ESP_PLATFORM_ESP32, CONF_DATA_PINS, CONF_RESET_PIN, CONF_RESOLUTION, CONF_BRIGHTNESS, \ - CONF_CONTRAST +from esphome.const import ( + CONF_FREQUENCY, + CONF_ID, + CONF_NAME, + CONF_PIN, + CONF_SCL, + CONF_SDA, + ESP_PLATFORM_ESP32, + CONF_DATA_PINS, + CONF_RESET_PIN, + CONF_RESOLUTION, + CONF_BRIGHTNESS, + CONF_CONTRAST, +) ESP_PLATFORMS = [ESP_PLATFORM_ESP32] -DEPENDENCIES = ['api'] +DEPENDENCIES = ["api"] -esp32_camera_ns = cg.esphome_ns.namespace('esp32_camera') -ESP32Camera = esp32_camera_ns.class_('ESP32Camera', cg.PollingComponent, cg.Nameable) -ESP32CameraFrameSize = esp32_camera_ns.enum('ESP32CameraFrameSize') +esp32_camera_ns = cg.esphome_ns.namespace("esp32_camera") +ESP32Camera = esp32_camera_ns.class_("ESP32Camera", cg.PollingComponent, cg.Nameable) +ESP32CameraFrameSize = esp32_camera_ns.enum("ESP32CameraFrameSize") FRAME_SIZES = { - '160X120': ESP32CameraFrameSize.ESP32_CAMERA_SIZE_160X120, - 'QQVGA': ESP32CameraFrameSize.ESP32_CAMERA_SIZE_160X120, - '128X160': ESP32CameraFrameSize.ESP32_CAMERA_SIZE_128X160, - 'QQVGA2': ESP32CameraFrameSize.ESP32_CAMERA_SIZE_128X160, - '176X144': ESP32CameraFrameSize.ESP32_CAMERA_SIZE_176X144, - 'QCIF': ESP32CameraFrameSize.ESP32_CAMERA_SIZE_176X144, - '240X176': ESP32CameraFrameSize.ESP32_CAMERA_SIZE_240X176, - 'HQVGA': ESP32CameraFrameSize.ESP32_CAMERA_SIZE_240X176, - '320X240': ESP32CameraFrameSize.ESP32_CAMERA_SIZE_320X240, - 'QVGA': ESP32CameraFrameSize.ESP32_CAMERA_SIZE_320X240, - '400X296': ESP32CameraFrameSize.ESP32_CAMERA_SIZE_400X296, - 'CIF': ESP32CameraFrameSize.ESP32_CAMERA_SIZE_400X296, - '640X480': ESP32CameraFrameSize.ESP32_CAMERA_SIZE_640X480, - 'VGA': ESP32CameraFrameSize.ESP32_CAMERA_SIZE_640X480, - '800X600': ESP32CameraFrameSize.ESP32_CAMERA_SIZE_800X600, - 'SVGA': ESP32CameraFrameSize.ESP32_CAMERA_SIZE_800X600, - '1024X768': ESP32CameraFrameSize.ESP32_CAMERA_SIZE_1024X768, - 'XGA': ESP32CameraFrameSize.ESP32_CAMERA_SIZE_1024X768, - '1280X1024': ESP32CameraFrameSize.ESP32_CAMERA_SIZE_1280X1024, - 'SXGA': ESP32CameraFrameSize.ESP32_CAMERA_SIZE_1280X1024, - '1600X1200': ESP32CameraFrameSize.ESP32_CAMERA_SIZE_1600X1200, - 'UXGA': ESP32CameraFrameSize.ESP32_CAMERA_SIZE_1600X1200, + "160X120": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_160X120, + "QQVGA": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_160X120, + "128X160": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_128X160, + "QQVGA2": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_128X160, + "176X144": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_176X144, + "QCIF": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_176X144, + "240X176": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_240X176, + "HQVGA": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_240X176, + "320X240": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_320X240, + "QVGA": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_320X240, + "400X296": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_400X296, + "CIF": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_400X296, + "640X480": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_640X480, + "VGA": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_640X480, + "800X600": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_800X600, + "SVGA": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_800X600, + "1024X768": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_1024X768, + "XGA": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_1024X768, + "1280X1024": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_1280X1024, + "SXGA": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_1280X1024, + "1600X1200": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_1600X1200, + "UXGA": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_1600X1200, } -CONF_VSYNC_PIN = 'vsync_pin' -CONF_HREF_PIN = 'href_pin' -CONF_PIXEL_CLOCK_PIN = 'pixel_clock_pin' -CONF_EXTERNAL_CLOCK = 'external_clock' -CONF_I2C_PINS = 'i2c_pins' -CONF_POWER_DOWN_PIN = 'power_down_pin' +CONF_VSYNC_PIN = "vsync_pin" +CONF_HREF_PIN = "href_pin" +CONF_PIXEL_CLOCK_PIN = "pixel_clock_pin" +CONF_EXTERNAL_CLOCK = "external_clock" +CONF_I2C_PINS = "i2c_pins" +CONF_POWER_DOWN_PIN = "power_down_pin" -CONF_MAX_FRAMERATE = 'max_framerate' -CONF_IDLE_FRAMERATE = 'idle_framerate' -CONF_JPEG_QUALITY = 'jpeg_quality' -CONF_VERTICAL_FLIP = 'vertical_flip' -CONF_HORIZONTAL_MIRROR = 'horizontal_mirror' -CONF_SATURATION = 'saturation' -CONF_TEST_PATTERN = 'test_pattern' +CONF_MAX_FRAMERATE = "max_framerate" +CONF_IDLE_FRAMERATE = "idle_framerate" +CONF_JPEG_QUALITY = "jpeg_quality" +CONF_VERTICAL_FLIP = "vertical_flip" +CONF_HORIZONTAL_MIRROR = "horizontal_mirror" +CONF_SATURATION = "saturation" +CONF_TEST_PATTERN = "test_pattern" camera_range_param = cv.int_range(min=-2, max=2) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(ESP32Camera), - cv.Required(CONF_NAME): cv.string, - cv.Required(CONF_DATA_PINS): cv.All([pins.input_pin], cv.Length(min=8, max=8)), - cv.Required(CONF_VSYNC_PIN): pins.input_pin, - cv.Required(CONF_HREF_PIN): pins.input_pin, - cv.Required(CONF_PIXEL_CLOCK_PIN): pins.input_pin, - cv.Required(CONF_EXTERNAL_CLOCK): cv.Schema({ - cv.Required(CONF_PIN): pins.output_pin, - cv.Optional(CONF_FREQUENCY, default='20MHz'): cv.All(cv.frequency, cv.one_of(20e6, 10e6)), - }), - cv.Required(CONF_I2C_PINS): cv.Schema({ - cv.Required(CONF_SDA): pins.output_pin, - cv.Required(CONF_SCL): pins.output_pin, - }), - cv.Optional(CONF_RESET_PIN): pins.output_pin, - cv.Optional(CONF_POWER_DOWN_PIN): pins.output_pin, - - cv.Optional(CONF_MAX_FRAMERATE, default='10 fps'): cv.All(cv.framerate, - cv.Range(min=0, min_included=False, - max=60)), - cv.Optional(CONF_IDLE_FRAMERATE, default='0.1 fps'): cv.All(cv.framerate, - cv.Range(min=0, max=1)), - cv.Optional(CONF_RESOLUTION, default='640X480'): cv.enum(FRAME_SIZES, upper=True), - cv.Optional(CONF_JPEG_QUALITY, default=10): cv.int_range(min=10, max=63), - cv.Optional(CONF_CONTRAST, default=0): camera_range_param, - cv.Optional(CONF_BRIGHTNESS, default=0): camera_range_param, - cv.Optional(CONF_SATURATION, default=0): camera_range_param, - cv.Optional(CONF_VERTICAL_FLIP, default=True): cv.boolean, - cv.Optional(CONF_HORIZONTAL_MIRROR, default=True): cv.boolean, - cv.Optional(CONF_TEST_PATTERN, default=False): cv.boolean, -}).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(ESP32Camera), + cv.Required(CONF_NAME): cv.string, + cv.Required(CONF_DATA_PINS): cv.All([pins.input_pin], cv.Length(min=8, max=8)), + cv.Required(CONF_VSYNC_PIN): pins.input_pin, + cv.Required(CONF_HREF_PIN): pins.input_pin, + cv.Required(CONF_PIXEL_CLOCK_PIN): pins.input_pin, + cv.Required(CONF_EXTERNAL_CLOCK): cv.Schema( + { + cv.Required(CONF_PIN): pins.output_pin, + cv.Optional(CONF_FREQUENCY, default="20MHz"): cv.All( + cv.frequency, cv.one_of(20e6, 10e6) + ), + } + ), + cv.Required(CONF_I2C_PINS): cv.Schema( + { + cv.Required(CONF_SDA): pins.output_pin, + cv.Required(CONF_SCL): pins.output_pin, + } + ), + cv.Optional(CONF_RESET_PIN): pins.output_pin, + cv.Optional(CONF_POWER_DOWN_PIN): pins.output_pin, + cv.Optional(CONF_MAX_FRAMERATE, default="10 fps"): cv.All( + cv.framerate, cv.Range(min=0, min_included=False, max=60) + ), + cv.Optional(CONF_IDLE_FRAMERATE, default="0.1 fps"): cv.All( + cv.framerate, cv.Range(min=0, max=1) + ), + cv.Optional(CONF_RESOLUTION, default="640X480"): cv.enum( + FRAME_SIZES, upper=True + ), + cv.Optional(CONF_JPEG_QUALITY, default=10): cv.int_range(min=10, max=63), + cv.Optional(CONF_CONTRAST, default=0): camera_range_param, + cv.Optional(CONF_BRIGHTNESS, default=0): camera_range_param, + cv.Optional(CONF_SATURATION, default=0): camera_range_param, + cv.Optional(CONF_VERTICAL_FLIP, default=True): cv.boolean, + cv.Optional(CONF_HORIZONTAL_MIRROR, default=True): cv.boolean, + cv.Optional(CONF_TEST_PATTERN, default=False): cv.boolean, + } +).extend(cv.COMPONENT_SCHEMA) SETTERS = { - CONF_DATA_PINS: 'set_data_pins', - CONF_VSYNC_PIN: 'set_vsync_pin', - CONF_HREF_PIN: 'set_href_pin', - CONF_PIXEL_CLOCK_PIN: 'set_pixel_clock_pin', - CONF_RESET_PIN: 'set_reset_pin', - CONF_POWER_DOWN_PIN: 'set_power_down_pin', - CONF_JPEG_QUALITY: 'set_jpeg_quality', - CONF_VERTICAL_FLIP: 'set_vertical_flip', - CONF_HORIZONTAL_MIRROR: 'set_horizontal_mirror', - CONF_CONTRAST: 'set_contrast', - CONF_BRIGHTNESS: 'set_brightness', - CONF_SATURATION: 'set_saturation', - CONF_TEST_PATTERN: 'set_test_pattern', + CONF_DATA_PINS: "set_data_pins", + CONF_VSYNC_PIN: "set_vsync_pin", + CONF_HREF_PIN: "set_href_pin", + CONF_PIXEL_CLOCK_PIN: "set_pixel_clock_pin", + CONF_RESET_PIN: "set_reset_pin", + CONF_POWER_DOWN_PIN: "set_power_down_pin", + CONF_JPEG_QUALITY: "set_jpeg_quality", + CONF_VERTICAL_FLIP: "set_vertical_flip", + CONF_HORIZONTAL_MIRROR: "set_horizontal_mirror", + CONF_CONTRAST: "set_contrast", + CONF_BRIGHTNESS: "set_brightness", + CONF_SATURATION: "set_saturation", + CONF_TEST_PATTERN: "set_test_pattern", } @@ -122,5 +143,5 @@ def to_code(config): cg.add(var.set_idle_update_interval(1000 / config[CONF_IDLE_FRAMERATE])) cg.add(var.set_frame_size(config[CONF_RESOLUTION])) - cg.add_define('USE_ESP32_CAMERA') - cg.add_build_flag('-DBOARD_HAS_PSRAM') + cg.add_define("USE_ESP32_CAMERA") + cg.add_build_flag("-DBOARD_HAS_PSRAM") diff --git a/esphome/components/esp32_dac/output.py b/esphome/components/esp32_dac/output.py index bb0a8b60e9..8cfc7570e9 100644 --- a/esphome/components/esp32_dac/output.py +++ b/esphome/components/esp32_dac/output.py @@ -13,13 +13,17 @@ def valid_dac_pin(value): return value -esp32_dac_ns = cg.esphome_ns.namespace('esp32_dac') -ESP32DAC = esp32_dac_ns.class_('ESP32DAC', output.FloatOutput, cg.Component) +esp32_dac_ns = cg.esphome_ns.namespace("esp32_dac") +ESP32DAC = esp32_dac_ns.class_("ESP32DAC", output.FloatOutput, cg.Component) -CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend({ - cv.Required(CONF_ID): cv.declare_id(ESP32DAC), - cv.Required(CONF_PIN): cv.All(pins.internal_gpio_output_pin_schema, valid_dac_pin), -}).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend( + { + cv.Required(CONF_ID): cv.declare_id(ESP32DAC), + cv.Required(CONF_PIN): cv.All( + pins.internal_gpio_output_pin_schema, valid_dac_pin + ), + } +).extend(cv.COMPONENT_SCHEMA) def to_code(config): diff --git a/esphome/components/esp32_hall/sensor.py b/esphome/components/esp32_hall/sensor.py index 02a4b4900e..363b3f7548 100644 --- a/esphome/components/esp32_hall/sensor.py +++ b/esphome/components/esp32_hall/sensor.py @@ -1,17 +1,30 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor -from esphome.const import CONF_ID, DEVICE_CLASS_EMPTY, ESP_PLATFORM_ESP32, UNIT_MICROTESLA, \ - ICON_MAGNET +from esphome.const import ( + CONF_ID, + DEVICE_CLASS_EMPTY, + ESP_PLATFORM_ESP32, + UNIT_MICROTESLA, + ICON_MAGNET, +) ESP_PLATFORMS = [ESP_PLATFORM_ESP32] -esp32_hall_ns = cg.esphome_ns.namespace('esp32_hall') -ESP32HallSensor = esp32_hall_ns.class_('ESP32HallSensor', sensor.Sensor, cg.PollingComponent) +esp32_hall_ns = cg.esphome_ns.namespace("esp32_hall") +ESP32HallSensor = esp32_hall_ns.class_( + "ESP32HallSensor", sensor.Sensor, cg.PollingComponent +) -CONFIG_SCHEMA = sensor.sensor_schema(UNIT_MICROTESLA, ICON_MAGNET, 1, DEVICE_CLASS_EMPTY).extend({ - cv.GenerateID(): cv.declare_id(ESP32HallSensor), -}).extend(cv.polling_component_schema('60s')) +CONFIG_SCHEMA = ( + sensor.sensor_schema(UNIT_MICROTESLA, ICON_MAGNET, 1, DEVICE_CLASS_EMPTY) + .extend( + { + cv.GenerateID(): cv.declare_id(ESP32HallSensor), + } + ) + .extend(cv.polling_component_schema("60s")) +) def to_code(config): diff --git a/esphome/components/esp32_touch/__init__.py b/esphome/components/esp32_touch/__init__.py index 3bdc988fcc..5c3b47af77 100644 --- a/esphome/components/esp32_touch/__init__.py +++ b/esphome/components/esp32_touch/__init__.py @@ -1,15 +1,23 @@ import esphome.codegen as cg import esphome.config_validation as cv -from esphome.const import CONF_HIGH_VOLTAGE_REFERENCE, CONF_ID, CONF_IIR_FILTER, \ - CONF_LOW_VOLTAGE_REFERENCE, CONF_MEASUREMENT_DURATION, CONF_SETUP_MODE, CONF_SLEEP_DURATION, \ - CONF_VOLTAGE_ATTENUATION, ESP_PLATFORM_ESP32 +from esphome.const import ( + CONF_HIGH_VOLTAGE_REFERENCE, + CONF_ID, + CONF_IIR_FILTER, + CONF_LOW_VOLTAGE_REFERENCE, + CONF_MEASUREMENT_DURATION, + CONF_SETUP_MODE, + CONF_SLEEP_DURATION, + CONF_VOLTAGE_ATTENUATION, + ESP_PLATFORM_ESP32, +) from esphome.core import TimePeriod -AUTO_LOAD = ['binary_sensor'] +AUTO_LOAD = ["binary_sensor"] ESP_PLATFORMS = [ESP_PLATFORM_ESP32] -esp32_touch_ns = cg.esphome_ns.namespace('esp32_touch') -ESP32TouchComponent = esp32_touch_ns.class_('ESP32TouchComponent', cg.Component) +esp32_touch_ns = cg.esphome_ns.namespace("esp32_touch") +ESP32TouchComponent = esp32_touch_ns.class_("ESP32TouchComponent", cg.Component) def validate_voltage(values): @@ -17,46 +25,56 @@ def validate_voltage(values): if isinstance(value, float) and value.is_integer(): value = int(value) value = cv.string(value) - if not value.endswith('V'): - value += 'V' + if not value.endswith("V"): + value += "V" return cv.one_of(*values)(value) return validator LOW_VOLTAGE_REFERENCE = { - '0.5V': cg.global_ns.TOUCH_LVOLT_0V5, - '0.6V': cg.global_ns.TOUCH_LVOLT_0V6, - '0.7V': cg.global_ns.TOUCH_LVOLT_0V7, - '0.8V': cg.global_ns.TOUCH_LVOLT_0V8, + "0.5V": cg.global_ns.TOUCH_LVOLT_0V5, + "0.6V": cg.global_ns.TOUCH_LVOLT_0V6, + "0.7V": cg.global_ns.TOUCH_LVOLT_0V7, + "0.8V": cg.global_ns.TOUCH_LVOLT_0V8, } HIGH_VOLTAGE_REFERENCE = { - '2.4V': cg.global_ns.TOUCH_HVOLT_2V4, - '2.5V': cg.global_ns.TOUCH_HVOLT_2V5, - '2.6V': cg.global_ns.TOUCH_HVOLT_2V6, - '2.7V': cg.global_ns.TOUCH_HVOLT_2V7, + "2.4V": cg.global_ns.TOUCH_HVOLT_2V4, + "2.5V": cg.global_ns.TOUCH_HVOLT_2V5, + "2.6V": cg.global_ns.TOUCH_HVOLT_2V6, + "2.7V": cg.global_ns.TOUCH_HVOLT_2V7, } VOLTAGE_ATTENUATION = { - '1.5V': cg.global_ns.TOUCH_HVOLT_ATTEN_1V5, - '1V': cg.global_ns.TOUCH_HVOLT_ATTEN_1V, - '0.5V': cg.global_ns.TOUCH_HVOLT_ATTEN_0V5, - '0V': cg.global_ns.TOUCH_HVOLT_ATTEN_0V, + "1.5V": cg.global_ns.TOUCH_HVOLT_ATTEN_1V5, + "1V": cg.global_ns.TOUCH_HVOLT_ATTEN_1V, + "0.5V": cg.global_ns.TOUCH_HVOLT_ATTEN_0V5, + "0V": cg.global_ns.TOUCH_HVOLT_ATTEN_0V, } -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(ESP32TouchComponent), - cv.Optional(CONF_SETUP_MODE, default=False): cv.boolean, - cv.Optional(CONF_IIR_FILTER, default='0ms'): cv.positive_time_period_milliseconds, - cv.Optional(CONF_SLEEP_DURATION, default='27306us'): - cv.All(cv.positive_time_period, cv.Range(max=TimePeriod(microseconds=436906))), - cv.Optional(CONF_MEASUREMENT_DURATION, default='8192us'): - cv.All(cv.positive_time_period, cv.Range(max=TimePeriod(microseconds=8192))), - cv.Optional(CONF_LOW_VOLTAGE_REFERENCE, default='0.5V'): - validate_voltage(LOW_VOLTAGE_REFERENCE), - cv.Optional(CONF_HIGH_VOLTAGE_REFERENCE, default='2.7V'): - validate_voltage(HIGH_VOLTAGE_REFERENCE), - cv.Optional(CONF_VOLTAGE_ATTENUATION, default='0V'): validate_voltage(VOLTAGE_ATTENUATION), -}).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(ESP32TouchComponent), + cv.Optional(CONF_SETUP_MODE, default=False): cv.boolean, + cv.Optional( + CONF_IIR_FILTER, default="0ms" + ): cv.positive_time_period_milliseconds, + cv.Optional(CONF_SLEEP_DURATION, default="27306us"): cv.All( + cv.positive_time_period, cv.Range(max=TimePeriod(microseconds=436906)) + ), + cv.Optional(CONF_MEASUREMENT_DURATION, default="8192us"): cv.All( + cv.positive_time_period, cv.Range(max=TimePeriod(microseconds=8192)) + ), + cv.Optional(CONF_LOW_VOLTAGE_REFERENCE, default="0.5V"): validate_voltage( + LOW_VOLTAGE_REFERENCE + ), + cv.Optional(CONF_HIGH_VOLTAGE_REFERENCE, default="2.7V"): validate_voltage( + HIGH_VOLTAGE_REFERENCE + ), + cv.Optional(CONF_VOLTAGE_ATTENUATION, default="0V"): validate_voltage( + VOLTAGE_ATTENUATION + ), + } +).extend(cv.COMPONENT_SCHEMA) def to_code(config): @@ -69,12 +87,23 @@ def to_code(config): sleep_duration = int(round(config[CONF_SLEEP_DURATION].total_microseconds * 0.15)) cg.add(touch.set_sleep_duration(sleep_duration)) - measurement_duration = int(round(config[CONF_MEASUREMENT_DURATION].total_microseconds * - 7.99987793)) + measurement_duration = int( + round(config[CONF_MEASUREMENT_DURATION].total_microseconds * 7.99987793) + ) cg.add(touch.set_measurement_duration(measurement_duration)) - cg.add(touch.set_low_voltage_reference( - LOW_VOLTAGE_REFERENCE[config[CONF_LOW_VOLTAGE_REFERENCE]])) - cg.add(touch.set_high_voltage_reference( - HIGH_VOLTAGE_REFERENCE[config[CONF_HIGH_VOLTAGE_REFERENCE]])) - cg.add(touch.set_voltage_attenuation(VOLTAGE_ATTENUATION[config[CONF_VOLTAGE_ATTENUATION]])) + cg.add( + touch.set_low_voltage_reference( + LOW_VOLTAGE_REFERENCE[config[CONF_LOW_VOLTAGE_REFERENCE]] + ) + ) + cg.add( + touch.set_high_voltage_reference( + HIGH_VOLTAGE_REFERENCE[config[CONF_HIGH_VOLTAGE_REFERENCE]] + ) + ) + cg.add( + touch.set_voltage_attenuation( + VOLTAGE_ATTENUATION[config[CONF_VOLTAGE_ATTENUATION]] + ) + ) diff --git a/esphome/components/esp32_touch/binary_sensor.py b/esphome/components/esp32_touch/binary_sensor.py index 5142879a04..f8bc348132 100644 --- a/esphome/components/esp32_touch/binary_sensor.py +++ b/esphome/components/esp32_touch/binary_sensor.py @@ -1,14 +1,20 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import binary_sensor -from esphome.const import CONF_NAME, CONF_PIN, CONF_THRESHOLD, ESP_PLATFORM_ESP32, CONF_ID +from esphome.const import ( + CONF_NAME, + CONF_PIN, + CONF_THRESHOLD, + ESP_PLATFORM_ESP32, + CONF_ID, +) from esphome.pins import validate_gpio_pin from . import esp32_touch_ns, ESP32TouchComponent ESP_PLATFORMS = [ESP_PLATFORM_ESP32] -DEPENDENCIES = ['esp32_touch'] +DEPENDENCIES = ["esp32_touch"] -CONF_ESP32_TOUCH_ID = 'esp32_touch_id' +CONF_ESP32_TOUCH_ID = "esp32_touch_id" TOUCH_PADS = { 4: cg.global_ns.TOUCH_PAD_NUM0, @@ -31,19 +37,27 @@ def validate_touch_pad(value): return value -ESP32TouchBinarySensor = esp32_touch_ns.class_('ESP32TouchBinarySensor', binary_sensor.BinarySensor) +ESP32TouchBinarySensor = esp32_touch_ns.class_( + "ESP32TouchBinarySensor", binary_sensor.BinarySensor +) -CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(ESP32TouchBinarySensor), - cv.GenerateID(CONF_ESP32_TOUCH_ID): cv.use_id(ESP32TouchComponent), - cv.Required(CONF_PIN): validate_touch_pad, - cv.Required(CONF_THRESHOLD): cv.uint16_t, -}) +CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(ESP32TouchBinarySensor), + cv.GenerateID(CONF_ESP32_TOUCH_ID): cv.use_id(ESP32TouchComponent), + cv.Required(CONF_PIN): validate_touch_pad, + cv.Required(CONF_THRESHOLD): cv.uint16_t, + } +) def to_code(config): hub = yield cg.get_variable(config[CONF_ESP32_TOUCH_ID]) - var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME], TOUCH_PADS[config[CONF_PIN]], - config[CONF_THRESHOLD]) + var = cg.new_Pvariable( + config[CONF_ID], + config[CONF_NAME], + TOUCH_PADS[config[CONF_PIN]], + config[CONF_THRESHOLD], + ) yield binary_sensor.register_binary_sensor(var, config) cg.add(hub.register_touch_pad(var)) diff --git a/esphome/components/esp8266_pwm/output.py b/esphome/components/esp8266_pwm/output.py index e973490525..ad7da4e001 100644 --- a/esphome/components/esp8266_pwm/output.py +++ b/esphome/components/esp8266_pwm/output.py @@ -2,7 +2,13 @@ from esphome import pins, automation from esphome.components import output import esphome.config_validation as cv import esphome.codegen as cg -from esphome.const import CONF_FREQUENCY, CONF_ID, CONF_NUMBER, CONF_PIN, ESP_PLATFORM_ESP8266 +from esphome.const import ( + CONF_FREQUENCY, + CONF_ID, + CONF_NUMBER, + CONF_PIN, + ESP_PLATFORM_ESP8266, +) ESP_PLATFORMS = [ESP_PLATFORM_ESP8266] @@ -13,16 +19,20 @@ def valid_pwm_pin(value): return value -esp8266_pwm_ns = cg.esphome_ns.namespace('esp8266_pwm') -ESP8266PWM = esp8266_pwm_ns.class_('ESP8266PWM', output.FloatOutput, cg.Component) -SetFrequencyAction = esp8266_pwm_ns.class_('SetFrequencyAction', automation.Action) +esp8266_pwm_ns = cg.esphome_ns.namespace("esp8266_pwm") +ESP8266PWM = esp8266_pwm_ns.class_("ESP8266PWM", output.FloatOutput, cg.Component) +SetFrequencyAction = esp8266_pwm_ns.class_("SetFrequencyAction", automation.Action) validate_frequency = cv.All(cv.frequency, cv.Range(min=1.0e-6)) -CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend({ - cv.Required(CONF_ID): cv.declare_id(ESP8266PWM), - cv.Required(CONF_PIN): cv.All(pins.internal_gpio_output_pin_schema, valid_pwm_pin), - cv.Optional(CONF_FREQUENCY, default='1kHz'): validate_frequency, -}).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend( + { + cv.Required(CONF_ID): cv.declare_id(ESP8266PWM), + cv.Required(CONF_PIN): cv.All( + pins.internal_gpio_output_pin_schema, valid_pwm_pin + ), + cv.Optional(CONF_FREQUENCY, default="1kHz"): validate_frequency, + } +).extend(cv.COMPONENT_SCHEMA) def to_code(config): @@ -36,10 +46,16 @@ def to_code(config): cg.add(var.set_frequency(config[CONF_FREQUENCY])) -@automation.register_action('output.esp8266_pwm.set_frequency', SetFrequencyAction, cv.Schema({ - cv.Required(CONF_ID): cv.use_id(ESP8266PWM), - cv.Required(CONF_FREQUENCY): cv.templatable(validate_frequency), -})) +@automation.register_action( + "output.esp8266_pwm.set_frequency", + SetFrequencyAction, + cv.Schema( + { + cv.Required(CONF_ID): cv.use_id(ESP8266PWM), + cv.Required(CONF_FREQUENCY): cv.templatable(validate_frequency), + } + ), +) def esp8266_set_frequency_to_code(config, action_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) var = cg.new_Pvariable(action_id, template_arg, paren) diff --git a/esphome/components/ethernet/__init__.py b/esphome/components/ethernet/__init__.py index ce7422d05b..ca00f33359 100644 --- a/esphome/components/ethernet/__init__.py +++ b/esphome/components/ethernet/__init__.py @@ -1,47 +1,60 @@ from esphome import pins import esphome.config_validation as cv import esphome.codegen as cg -from esphome.const import CONF_DOMAIN, CONF_ID, CONF_MANUAL_IP, CONF_STATIC_IP, CONF_TYPE, \ - CONF_USE_ADDRESS, ESP_PLATFORM_ESP32, CONF_GATEWAY, CONF_SUBNET, CONF_DNS1, CONF_DNS2 +from esphome.const import ( + CONF_DOMAIN, + CONF_ID, + CONF_MANUAL_IP, + CONF_STATIC_IP, + CONF_TYPE, + CONF_USE_ADDRESS, + ESP_PLATFORM_ESP32, + CONF_GATEWAY, + CONF_SUBNET, + CONF_DNS1, + CONF_DNS2, +) from esphome.core import CORE, coroutine_with_priority -CONFLICTS_WITH = ['wifi'] +CONFLICTS_WITH = ["wifi"] ESP_PLATFORMS = [ESP_PLATFORM_ESP32] -AUTO_LOAD = ['network'] +AUTO_LOAD = ["network"] -ethernet_ns = cg.esphome_ns.namespace('ethernet') -CONF_PHY_ADDR = 'phy_addr' -CONF_MDC_PIN = 'mdc_pin' -CONF_MDIO_PIN = 'mdio_pin' -CONF_CLK_MODE = 'clk_mode' -CONF_POWER_PIN = 'power_pin' +ethernet_ns = cg.esphome_ns.namespace("ethernet") +CONF_PHY_ADDR = "phy_addr" +CONF_MDC_PIN = "mdc_pin" +CONF_MDIO_PIN = "mdio_pin" +CONF_CLK_MODE = "clk_mode" +CONF_POWER_PIN = "power_pin" -EthernetType = ethernet_ns.enum('EthernetType') +EthernetType = ethernet_ns.enum("EthernetType") ETHERNET_TYPES = { - 'LAN8720': EthernetType.ETHERNET_TYPE_LAN8720, - 'TLK110': EthernetType.ETHERNET_TYPE_TLK110, + "LAN8720": EthernetType.ETHERNET_TYPE_LAN8720, + "TLK110": EthernetType.ETHERNET_TYPE_TLK110, } -eth_clock_mode_t = cg.global_ns.enum('eth_clock_mode_t') +eth_clock_mode_t = cg.global_ns.enum("eth_clock_mode_t") CLK_MODES = { - 'GPIO0_IN': eth_clock_mode_t.ETH_CLOCK_GPIO0_IN, - 'GPIO0_OUT': eth_clock_mode_t.ETH_CLOCK_GPIO0_OUT, - 'GPIO16_OUT': eth_clock_mode_t.ETH_CLOCK_GPIO16_OUT, - 'GPIO17_OUT': eth_clock_mode_t.ETH_CLOCK_GPIO17_OUT, + "GPIO0_IN": eth_clock_mode_t.ETH_CLOCK_GPIO0_IN, + "GPIO0_OUT": eth_clock_mode_t.ETH_CLOCK_GPIO0_OUT, + "GPIO16_OUT": eth_clock_mode_t.ETH_CLOCK_GPIO16_OUT, + "GPIO17_OUT": eth_clock_mode_t.ETH_CLOCK_GPIO17_OUT, } -MANUAL_IP_SCHEMA = cv.Schema({ - cv.Required(CONF_STATIC_IP): cv.ipv4, - cv.Required(CONF_GATEWAY): cv.ipv4, - cv.Required(CONF_SUBNET): cv.ipv4, - cv.Optional(CONF_DNS1, default="0.0.0.0"): cv.ipv4, - cv.Optional(CONF_DNS2, default="0.0.0.0"): cv.ipv4, -}) +MANUAL_IP_SCHEMA = cv.Schema( + { + cv.Required(CONF_STATIC_IP): cv.ipv4, + cv.Required(CONF_GATEWAY): cv.ipv4, + cv.Required(CONF_SUBNET): cv.ipv4, + cv.Optional(CONF_DNS1, default="0.0.0.0"): cv.ipv4, + cv.Optional(CONF_DNS2, default="0.0.0.0"): cv.ipv4, + } +) -EthernetComponent = ethernet_ns.class_('EthernetComponent', cg.Component) -IPAddress = cg.global_ns.class_('IPAddress') -ManualIP = ethernet_ns.struct('ManualIP') +EthernetComponent = ethernet_ns.class_("EthernetComponent", cg.Component) +IPAddress = cg.global_ns.class_("IPAddress") +ManualIP = ethernet_ns.struct("ManualIP") def validate(config): @@ -54,30 +67,38 @@ def validate(config): return config -CONFIG_SCHEMA = cv.All(cv.Schema({ - cv.GenerateID(): cv.declare_id(EthernetComponent), - cv.Required(CONF_TYPE): cv.enum(ETHERNET_TYPES, upper=True), - cv.Required(CONF_MDC_PIN): pins.output_pin, - cv.Required(CONF_MDIO_PIN): pins.input_output_pin, - cv.Optional(CONF_CLK_MODE, default='GPIO0_IN'): cv.enum(CLK_MODES, upper=True, space='_'), - cv.Optional(CONF_PHY_ADDR, default=0): cv.int_range(min=0, max=31), - cv.Optional(CONF_POWER_PIN): pins.gpio_output_pin_schema, - cv.Optional(CONF_MANUAL_IP): MANUAL_IP_SCHEMA, - cv.Optional(CONF_DOMAIN, default='.local'): cv.domain_name, - cv.Optional(CONF_USE_ADDRESS): cv.string_strict, - - cv.Optional('hostname'): cv.invalid("The hostname option has been removed in 1.11.0"), -}).extend(cv.COMPONENT_SCHEMA), validate) +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(EthernetComponent), + cv.Required(CONF_TYPE): cv.enum(ETHERNET_TYPES, upper=True), + cv.Required(CONF_MDC_PIN): pins.output_pin, + cv.Required(CONF_MDIO_PIN): pins.input_output_pin, + cv.Optional(CONF_CLK_MODE, default="GPIO0_IN"): cv.enum( + CLK_MODES, upper=True, space="_" + ), + cv.Optional(CONF_PHY_ADDR, default=0): cv.int_range(min=0, max=31), + cv.Optional(CONF_POWER_PIN): pins.gpio_output_pin_schema, + cv.Optional(CONF_MANUAL_IP): MANUAL_IP_SCHEMA, + cv.Optional(CONF_DOMAIN, default=".local"): cv.domain_name, + cv.Optional(CONF_USE_ADDRESS): cv.string_strict, + cv.Optional("hostname"): cv.invalid( + "The hostname option has been removed in 1.11.0" + ), + } + ).extend(cv.COMPONENT_SCHEMA), + validate, +) def manual_ip(config): return cg.StructInitializer( ManualIP, - ('static_ip', IPAddress(*config[CONF_STATIC_IP].args)), - ('gateway', IPAddress(*config[CONF_GATEWAY].args)), - ('subnet', IPAddress(*config[CONF_SUBNET].args)), - ('dns1', IPAddress(*config[CONF_DNS1].args)), - ('dns2', IPAddress(*config[CONF_DNS2].args)), + ("static_ip", IPAddress(*config[CONF_STATIC_IP].args)), + ("gateway", IPAddress(*config[CONF_GATEWAY].args)), + ("subnet", IPAddress(*config[CONF_SUBNET].args)), + ("dns1", IPAddress(*config[CONF_DNS1].args)), + ("dns2", IPAddress(*config[CONF_DNS2].args)), ) @@ -100,4 +121,4 @@ def to_code(config): if CONF_MANUAL_IP in config: cg.add(var.set_manual_ip(manual_ip(config[CONF_MANUAL_IP]))) - cg.add_define('USE_ETHERNET') + cg.add_define("USE_ETHERNET") diff --git a/esphome/components/exposure_notifications/__init__.py b/esphome/components/exposure_notifications/__init__.py index 8175a2d3aa..1bd8007785 100644 --- a/esphome/components/exposure_notifications/__init__.py +++ b/esphome/components/exposure_notifications/__init__.py @@ -4,26 +4,36 @@ import esphome.config_validation as cv from esphome.components import esp32_ble_tracker from esphome.const import CONF_TRIGGER_ID -CODEOWNERS = ['@OttoWinter'] -DEPENDENCIES = ['esp32_ble_tracker'] +CODEOWNERS = ["@OttoWinter"] +DEPENDENCIES = ["esp32_ble_tracker"] -exposure_notifications_ns = cg.esphome_ns.namespace('exposure_notifications') -ExposureNotification = exposure_notifications_ns.struct('ExposureNotification') +exposure_notifications_ns = cg.esphome_ns.namespace("exposure_notifications") +ExposureNotification = exposure_notifications_ns.struct("ExposureNotification") ExposureNotificationTrigger = exposure_notifications_ns.class_( - 'ExposureNotificationTrigger', esp32_ble_tracker.ESPBTDeviceListener, - automation.Trigger.template(ExposureNotification)) + "ExposureNotificationTrigger", + esp32_ble_tracker.ESPBTDeviceListener, + automation.Trigger.template(ExposureNotification), +) -CONF_ON_EXPOSURE_NOTIFICATION = 'on_exposure_notification' +CONF_ON_EXPOSURE_NOTIFICATION = "on_exposure_notification" -CONFIG_SCHEMA = cv.Schema({ - cv.Required(CONF_ON_EXPOSURE_NOTIFICATION): automation.validate_automation(cv.Schema({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ExposureNotificationTrigger), - }).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)), -}) +CONFIG_SCHEMA = cv.Schema( + { + cv.Required(CONF_ON_EXPOSURE_NOTIFICATION): automation.validate_automation( + cv.Schema( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( + ExposureNotificationTrigger + ), + } + ).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA) + ), + } +) def to_code(config): for conf in config.get(CONF_ON_EXPOSURE_NOTIFICATION, []): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID]) - yield automation.build_automation(trigger, [(ExposureNotification, 'x')], conf) + yield automation.build_automation(trigger, [(ExposureNotification, "x")], conf) yield esp32_ble_tracker.register_ble_device(trigger, conf) diff --git a/esphome/components/ezo/sensor.py b/esphome/components/ezo/sensor.py index ee896eea15..12640f4038 100644 --- a/esphome/components/ezo/sensor.py +++ b/esphome/components/ezo/sensor.py @@ -3,17 +3,25 @@ import esphome.config_validation as cv from esphome.components import i2c, sensor from esphome.const import CONF_ID -CODEOWNERS = ['@ssieb'] +CODEOWNERS = ["@ssieb"] -DEPENDENCIES = ['i2c'] +DEPENDENCIES = ["i2c"] -ezo_ns = cg.esphome_ns.namespace('ezo') +ezo_ns = cg.esphome_ns.namespace("ezo") -EZOSensor = ezo_ns.class_('EZOSensor', sensor.Sensor, cg.PollingComponent, i2c.I2CDevice) +EZOSensor = ezo_ns.class_( + "EZOSensor", sensor.Sensor, cg.PollingComponent, i2c.I2CDevice +) -CONFIG_SCHEMA = sensor.SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(EZOSensor), -}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(None)) +CONFIG_SCHEMA = ( + sensor.SENSOR_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(EZOSensor), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(None)) +) def to_code(config): diff --git a/esphome/components/fan/__init__.py b/esphome/components/fan/__init__.py index 7b0a79a0d3..79ed72f496 100644 --- a/esphome/components/fan/__init__.py +++ b/esphome/components/fan/__init__.py @@ -3,42 +3,57 @@ import esphome.config_validation as cv from esphome import automation from esphome.automation import maybe_simple_id from esphome.components import mqtt -from esphome.const import CONF_ID, CONF_INTERNAL, CONF_MQTT_ID, CONF_OSCILLATING, \ - CONF_OSCILLATION_COMMAND_TOPIC, CONF_OSCILLATION_STATE_TOPIC, CONF_SPEED, \ - CONF_SPEED_COMMAND_TOPIC, CONF_SPEED_STATE_TOPIC, CONF_NAME +from esphome.const import ( + CONF_ID, + CONF_INTERNAL, + CONF_MQTT_ID, + CONF_OSCILLATING, + CONF_OSCILLATION_COMMAND_TOPIC, + CONF_OSCILLATION_STATE_TOPIC, + CONF_SPEED, + CONF_SPEED_COMMAND_TOPIC, + CONF_SPEED_STATE_TOPIC, + CONF_NAME, +) from esphome.core import CORE, coroutine, coroutine_with_priority IS_PLATFORM_COMPONENT = True -fan_ns = cg.esphome_ns.namespace('fan') -FanState = fan_ns.class_('FanState', cg.Nameable, cg.Component) -MakeFan = cg.Application.struct('MakeFan') +fan_ns = cg.esphome_ns.namespace("fan") +FanState = fan_ns.class_("FanState", cg.Nameable, cg.Component) +MakeFan = cg.Application.struct("MakeFan") # Actions -TurnOnAction = fan_ns.class_('TurnOnAction', automation.Action) -TurnOffAction = fan_ns.class_('TurnOffAction', automation.Action) -ToggleAction = fan_ns.class_('ToggleAction', automation.Action) +TurnOnAction = fan_ns.class_("TurnOnAction", automation.Action) +TurnOffAction = fan_ns.class_("TurnOffAction", automation.Action) +ToggleAction = fan_ns.class_("ToggleAction", automation.Action) -FanSpeed = fan_ns.enum('FanSpeed') +FanSpeed = fan_ns.enum("FanSpeed") FAN_SPEEDS = { - 'OFF': FanSpeed.FAN_SPEED_OFF, - 'LOW': FanSpeed.FAN_SPEED_LOW, - 'MEDIUM': FanSpeed.FAN_SPEED_MEDIUM, - 'HIGH': FanSpeed.FAN_SPEED_HIGH, + "OFF": FanSpeed.FAN_SPEED_OFF, + "LOW": FanSpeed.FAN_SPEED_LOW, + "MEDIUM": FanSpeed.FAN_SPEED_MEDIUM, + "HIGH": FanSpeed.FAN_SPEED_HIGH, } -FAN_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(FanState), - cv.OnlyWith(CONF_MQTT_ID, 'mqtt'): cv.declare_id(mqtt.MQTTFanComponent), - cv.Optional(CONF_OSCILLATION_STATE_TOPIC): cv.All(cv.requires_component('mqtt'), - cv.publish_topic), - cv.Optional(CONF_OSCILLATION_COMMAND_TOPIC): cv.All(cv.requires_component('mqtt'), - cv.subscribe_topic), - cv.Optional(CONF_SPEED_STATE_TOPIC): cv.All(cv.requires_component('mqtt'), - cv.publish_topic), - cv.Optional(CONF_SPEED_COMMAND_TOPIC): cv.All(cv.requires_component('mqtt'), - cv.subscribe_topic), -}) +FAN_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(FanState), + cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTFanComponent), + cv.Optional(CONF_OSCILLATION_STATE_TOPIC): cv.All( + cv.requires_component("mqtt"), cv.publish_topic + ), + cv.Optional(CONF_OSCILLATION_COMMAND_TOPIC): cv.All( + cv.requires_component("mqtt"), cv.subscribe_topic + ), + cv.Optional(CONF_SPEED_STATE_TOPIC): cv.All( + cv.requires_component("mqtt"), cv.publish_topic + ), + cv.Optional(CONF_SPEED_COMMAND_TOPIC): cv.All( + cv.requires_component("mqtt"), cv.subscribe_topic + ), + } +) @coroutine @@ -52,14 +67,23 @@ def setup_fan_core_(var, config): yield mqtt.register_mqtt_component(mqtt_, config) if CONF_OSCILLATION_STATE_TOPIC in config: - cg.add(mqtt_.set_custom_oscillation_state_topic(config[CONF_OSCILLATION_STATE_TOPIC])) + cg.add( + mqtt_.set_custom_oscillation_state_topic( + config[CONF_OSCILLATION_STATE_TOPIC] + ) + ) if CONF_OSCILLATION_COMMAND_TOPIC in config: - cg.add(mqtt_.set_custom_oscillation_command_topic( - config[CONF_OSCILLATION_COMMAND_TOPIC])) + cg.add( + mqtt_.set_custom_oscillation_command_topic( + config[CONF_OSCILLATION_COMMAND_TOPIC] + ) + ) if CONF_SPEED_STATE_TOPIC in config: cg.add(mqtt_.set_custom_speed_state_topic(config[CONF_SPEED_STATE_TOPIC])) if CONF_SPEED_COMMAND_TOPIC in config: - cg.add(mqtt_.set_custom_speed_command_topic(config[CONF_SPEED_COMMAND_TOPIC])) + cg.add( + mqtt_.set_custom_speed_command_topic(config[CONF_SPEED_COMMAND_TOPIC]) + ) @coroutine @@ -78,28 +102,36 @@ def create_fan_state(config): yield var -FAN_ACTION_SCHEMA = maybe_simple_id({ - cv.Required(CONF_ID): cv.use_id(FanState), -}) +FAN_ACTION_SCHEMA = maybe_simple_id( + { + cv.Required(CONF_ID): cv.use_id(FanState), + } +) -@automation.register_action('fan.toggle', ToggleAction, FAN_ACTION_SCHEMA) +@automation.register_action("fan.toggle", ToggleAction, FAN_ACTION_SCHEMA) def fan_toggle_to_code(config, action_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) yield cg.new_Pvariable(action_id, template_arg, paren) -@automation.register_action('fan.turn_off', TurnOffAction, FAN_ACTION_SCHEMA) +@automation.register_action("fan.turn_off", TurnOffAction, FAN_ACTION_SCHEMA) def fan_turn_off_to_code(config, action_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) yield cg.new_Pvariable(action_id, template_arg, paren) -@automation.register_action('fan.turn_on', TurnOnAction, maybe_simple_id({ - cv.Required(CONF_ID): cv.use_id(FanState), - cv.Optional(CONF_OSCILLATING): cv.templatable(cv.boolean), - cv.Optional(CONF_SPEED): cv.templatable(cv.enum(FAN_SPEEDS, upper=True)), -})) +@automation.register_action( + "fan.turn_on", + TurnOnAction, + maybe_simple_id( + { + cv.Required(CONF_ID): cv.use_id(FanState), + cv.Optional(CONF_OSCILLATING): cv.templatable(cv.boolean), + cv.Optional(CONF_SPEED): cv.templatable(cv.enum(FAN_SPEEDS, upper=True)), + } + ), +) def fan_turn_on_to_code(config, action_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) var = cg.new_Pvariable(action_id, template_arg, paren) @@ -114,5 +146,5 @@ def fan_turn_on_to_code(config, action_id, template_arg, args): @coroutine_with_priority(100.0) def to_code(config): - cg.add_define('USE_FAN') + cg.add_define("USE_FAN") cg.add_global(fan_ns.using) diff --git a/esphome/components/fastled_base/__init__.py b/esphome/components/fastled_base/__init__.py index ab78f7537f..4c720191b9 100644 --- a/esphome/components/fastled_base/__init__.py +++ b/esphome/components/fastled_base/__init__.py @@ -1,29 +1,37 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import light -from esphome.const import CONF_OUTPUT_ID, CONF_NUM_LEDS, CONF_RGB_ORDER, CONF_MAX_REFRESH_RATE +from esphome.const import ( + CONF_OUTPUT_ID, + CONF_NUM_LEDS, + CONF_RGB_ORDER, + CONF_MAX_REFRESH_RATE, +) from esphome.core import coroutine -CODEOWNERS = ['@OttoWinter'] -fastled_base_ns = cg.esphome_ns.namespace('fastled_base') -FastLEDLightOutput = fastled_base_ns.class_('FastLEDLightOutput', light.AddressableLight) +CODEOWNERS = ["@OttoWinter"] +fastled_base_ns = cg.esphome_ns.namespace("fastled_base") +FastLEDLightOutput = fastled_base_ns.class_( + "FastLEDLightOutput", light.AddressableLight +) RGB_ORDERS = [ - 'RGB', - 'RBG', - 'GRB', - 'GBR', - 'BRG', - 'BGR', + "RGB", + "RBG", + "GRB", + "GBR", + "BRG", + "BGR", ] -BASE_SCHEMA = light.ADDRESSABLE_LIGHT_SCHEMA.extend({ - cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(FastLEDLightOutput), - - cv.Required(CONF_NUM_LEDS): cv.positive_not_null_int, - cv.Optional(CONF_RGB_ORDER): cv.one_of(*RGB_ORDERS, upper=True), - cv.Optional(CONF_MAX_REFRESH_RATE): cv.positive_time_period_microseconds, -}).extend(cv.COMPONENT_SCHEMA) +BASE_SCHEMA = light.ADDRESSABLE_LIGHT_SCHEMA.extend( + { + cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(FastLEDLightOutput), + cv.Required(CONF_NUM_LEDS): cv.positive_not_null_int, + cv.Optional(CONF_RGB_ORDER): cv.one_of(*RGB_ORDERS, upper=True), + cv.Optional(CONF_MAX_REFRESH_RATE): cv.positive_time_period_microseconds, + } +).extend(cv.COMPONENT_SCHEMA) @coroutine @@ -38,5 +46,5 @@ def new_fastled_light(config): # https://github.com/FastLED/FastLED/blob/master/library.json # 3.3.3 has an issue on ESP32 with RMT and fastled_clockless: # https://github.com/esphome/issues/issues/1375 - cg.add_library('FastLED', '3.3.2') + cg.add_library("FastLED", "3.3.2") yield var diff --git a/esphome/components/fastled_clockless/light.py b/esphome/components/fastled_clockless/light.py index 30fa910e59..b8a36ff390 100644 --- a/esphome/components/fastled_clockless/light.py +++ b/esphome/components/fastled_clockless/light.py @@ -4,46 +4,51 @@ from esphome import pins from esphome.components import fastled_base from esphome.const import CONF_CHIPSET, CONF_NUM_LEDS, CONF_PIN, CONF_RGB_ORDER -AUTO_LOAD = ['fastled_base'] +AUTO_LOAD = ["fastled_base"] CHIPSETS = [ - 'NEOPIXEL', - 'TM1829', - 'TM1809', - 'TM1804', - 'TM1803', - 'UCS1903', - 'UCS1903B', - 'UCS1904', - 'UCS2903', - 'WS2812', - 'WS2852', - 'WS2812B', - 'SK6812', - 'SK6822', - 'APA106', - 'PL9823', - 'WS2811', - 'WS2813', - 'APA104', - 'WS2811_400', - 'GW6205', - 'GW6205_400', - 'LPD1886', - 'LPD1886_8BIT', + "NEOPIXEL", + "TM1829", + "TM1809", + "TM1804", + "TM1803", + "UCS1903", + "UCS1903B", + "UCS1904", + "UCS2903", + "WS2812", + "WS2852", + "WS2812B", + "SK6812", + "SK6822", + "APA106", + "PL9823", + "WS2811", + "WS2813", + "APA104", + "WS2811_400", + "GW6205", + "GW6205_400", + "LPD1886", + "LPD1886_8BIT", ] def validate(value): - if value[CONF_CHIPSET] == 'NEOPIXEL' and CONF_RGB_ORDER in value: + if value[CONF_CHIPSET] == "NEOPIXEL" and CONF_RGB_ORDER in value: raise cv.Invalid("NEOPIXEL doesn't support RGB order") return value -CONFIG_SCHEMA = cv.All(fastled_base.BASE_SCHEMA.extend({ - cv.Required(CONF_CHIPSET): cv.one_of(*CHIPSETS, upper=True), - cv.Required(CONF_PIN): pins.output_pin, -}), validate) +CONFIG_SCHEMA = cv.All( + fastled_base.BASE_SCHEMA.extend( + { + cv.Required(CONF_CHIPSET): cv.one_of(*CHIPSETS, upper=True), + cv.Required(CONF_PIN): pins.output_pin, + } + ), + validate, +) def to_code(config): @@ -52,6 +57,7 @@ def to_code(config): rgb_order = None if CONF_RGB_ORDER in config: rgb_order = cg.RawExpression(config[CONF_RGB_ORDER]) - template_args = cg.TemplateArguments(cg.RawExpression(config[CONF_CHIPSET]), - config[CONF_PIN], rgb_order) + template_args = cg.TemplateArguments( + cg.RawExpression(config[CONF_CHIPSET]), config[CONF_PIN], rgb_order + ) cg.add(var.add_leds(template_args, config[CONF_NUM_LEDS])) diff --git a/esphome/components/fastled_spi/light.py b/esphome/components/fastled_spi/light.py index ef14c05738..11e0a8159c 100644 --- a/esphome/components/fastled_spi/light.py +++ b/esphome/components/fastled_spi/light.py @@ -2,34 +2,44 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins from esphome.components import fastled_base -from esphome.const import CONF_CHIPSET, CONF_CLOCK_PIN, CONF_DATA_PIN, CONF_DATA_RATE, \ - CONF_NUM_LEDS, CONF_RGB_ORDER +from esphome.const import ( + CONF_CHIPSET, + CONF_CLOCK_PIN, + CONF_DATA_PIN, + CONF_DATA_RATE, + CONF_NUM_LEDS, + CONF_RGB_ORDER, +) -AUTO_LOAD = ['fastled_base'] +AUTO_LOAD = ["fastled_base"] CHIPSETS = [ - 'LPD8806', - 'WS2801', - 'WS2803', - 'SM16716', - 'P9813', - 'APA102', - 'SK9822', - 'DOTSTAR', + "LPD8806", + "WS2801", + "WS2803", + "SM16716", + "P9813", + "APA102", + "SK9822", + "DOTSTAR", ] -CONFIG_SCHEMA = fastled_base.BASE_SCHEMA.extend({ - cv.Required(CONF_CHIPSET): cv.one_of(*CHIPSETS, upper=True), - cv.Required(CONF_DATA_PIN): pins.output_pin, - cv.Required(CONF_CLOCK_PIN): pins.output_pin, - cv.Optional(CONF_DATA_RATE): cv.frequency, -}) +CONFIG_SCHEMA = fastled_base.BASE_SCHEMA.extend( + { + cv.Required(CONF_CHIPSET): cv.one_of(*CHIPSETS, upper=True), + cv.Required(CONF_DATA_PIN): pins.output_pin, + cv.Required(CONF_CLOCK_PIN): pins.output_pin, + cv.Optional(CONF_DATA_RATE): cv.frequency, + } +) def to_code(config): var = yield fastled_base.new_fastled_light(config) - rgb_order = cg.RawExpression(config[CONF_RGB_ORDER] if CONF_RGB_ORDER in config else "RGB") + rgb_order = cg.RawExpression( + config[CONF_RGB_ORDER] if CONF_RGB_ORDER in config else "RGB" + ) data_rate = None if CONF_DATA_RATE in config: @@ -39,7 +49,11 @@ def to_code(config): else: data_rate_mhz = int(data_rate_khz / 1000) data_rate = cg.RawExpression(f"DATA_RATE_MHZ({data_rate_mhz})") - template_args = cg.TemplateArguments(cg.RawExpression(config[CONF_CHIPSET]), - config[CONF_DATA_PIN], config[CONF_CLOCK_PIN], rgb_order, - data_rate) + template_args = cg.TemplateArguments( + cg.RawExpression(config[CONF_CHIPSET]), + config[CONF_DATA_PIN], + config[CONF_CLOCK_PIN], + rgb_order, + data_rate, + ) cg.add(var.add_leds(template_args, config[CONF_NUM_LEDS])) diff --git a/esphome/components/font/__init__.py b/esphome/components/font/__init__.py index ee50b10830..e79d311dab 100644 --- a/esphome/components/font/__init__.py +++ b/esphome/components/font/__init__.py @@ -7,11 +7,11 @@ import esphome.codegen as cg from esphome.const import CONF_FILE, CONF_GLYPHS, CONF_ID, CONF_SIZE from esphome.core import CORE, HexInt -DEPENDENCIES = ['display'] +DEPENDENCIES = ["display"] MULTI_CONF = True -Font = display.display_ns.class_('Font') -Glyph = display.display_ns.class_('Glyph') +Font = display.display_ns.class_("Font") +Glyph = display.display_ns.class_("Glyph") def validate_glyphs(value): @@ -20,8 +20,8 @@ def validate_glyphs(value): value = cv.Schema([cv.string])(list(value)) def comparator(x, y): - x_ = x.encode('utf-8') - y_ = y.encode('utf-8') + x_ = x.encode("utf-8") + y_ = y.encode("utf-8") for c in range(min(len(x_), len(y_))): if x_[c] < y_[c]: @@ -43,36 +43,48 @@ def validate_pillow_installed(value): try: import PIL except ImportError as err: - raise cv.Invalid("Please install the pillow python package to use this feature. " - "(pip install pillow)") from err + raise cv.Invalid( + "Please install the pillow python package to use this feature. " + "(pip install pillow)" + ) from err - if PIL.__version__[0] < '4': - raise cv.Invalid("Please update your pillow installation to at least 4.0.x. " - "(pip install -U pillow)") + if PIL.__version__[0] < "4": + raise cv.Invalid( + "Please update your pillow installation to at least 4.0.x. " + "(pip install -U pillow)" + ) return value def validate_truetype_file(value): - if value.endswith('.zip'): # for Google Fonts downloads - raise cv.Invalid("Please unzip the font archive '{}' first and then use the .ttf files " - "inside.".format(value)) - if not value.endswith('.ttf'): - raise cv.Invalid("Only truetype (.ttf) files are supported. Please make sure you're " - "using the correct format or rename the extension to .ttf") + if value.endswith(".zip"): # for Google Fonts downloads + raise cv.Invalid( + "Please unzip the font archive '{}' first and then use the .ttf files " + "inside.".format(value) + ) + if not value.endswith(".ttf"): + raise cv.Invalid( + "Only truetype (.ttf) files are supported. Please make sure you're " + "using the correct format or rename the extension to .ttf" + ) return cv.file_(value) -DEFAULT_GLYPHS = ' !"%()+,-.:0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz°' -CONF_RAW_DATA_ID = 'raw_data_id' +DEFAULT_GLYPHS = ( + ' !"%()+,-.:0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz°' +) +CONF_RAW_DATA_ID = "raw_data_id" -FONT_SCHEMA = cv.Schema({ - cv.Required(CONF_ID): cv.declare_id(Font), - cv.Required(CONF_FILE): validate_truetype_file, - cv.Optional(CONF_GLYPHS, default=DEFAULT_GLYPHS): validate_glyphs, - cv.Optional(CONF_SIZE, default=20): cv.int_range(min=1), - cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_id(cg.uint8), -}) +FONT_SCHEMA = cv.Schema( + { + cv.Required(CONF_ID): cv.declare_id(Font), + cv.Required(CONF_FILE): validate_truetype_file, + cv.Optional(CONF_GLYPHS, default=DEFAULT_GLYPHS): validate_glyphs, + cv.Optional(CONF_SIZE, default=20): cv.int_range(min=1), + cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_id(cg.uint8), + } +) CONFIG_SCHEMA = cv.All(validate_pillow_installed, FONT_SCHEMA) @@ -91,7 +103,7 @@ def to_code(config): glyph_args = {} data = [] for glyph in config[CONF_GLYPHS]: - mask = font.getmask(glyph, mode='1') + mask = font.getmask(glyph, mode="1") _, (offset_x, offset_y) = font.font.getsize(glyph) width, height = mask.size width8 = ((width + 7) // 8) * 8 diff --git a/esphome/components/fujitsu_general/climate.py b/esphome/components/fujitsu_general/climate.py index a6774c397a..64319eff20 100644 --- a/esphome/components/fujitsu_general/climate.py +++ b/esphome/components/fujitsu_general/climate.py @@ -3,14 +3,18 @@ import esphome.config_validation as cv from esphome.components import climate_ir from esphome.const import CONF_ID -AUTO_LOAD = ['climate_ir'] +AUTO_LOAD = ["climate_ir"] -fujitsu_general_ns = cg.esphome_ns.namespace('fujitsu_general') -FujitsuGeneralClimate = fujitsu_general_ns.class_('FujitsuGeneralClimate', climate_ir.ClimateIR) +fujitsu_general_ns = cg.esphome_ns.namespace("fujitsu_general") +FujitsuGeneralClimate = fujitsu_general_ns.class_( + "FujitsuGeneralClimate", climate_ir.ClimateIR +) -CONFIG_SCHEMA = climate_ir.CLIMATE_IR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(FujitsuGeneralClimate), -}) +CONFIG_SCHEMA = climate_ir.CLIMATE_IR_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(FujitsuGeneralClimate), + } +) def to_code(config): diff --git a/esphome/components/globals/__init__.py b/esphome/components/globals/__init__.py index f1c4f4faf2..9ec3bc17ce 100644 --- a/esphome/components/globals/__init__.py +++ b/esphome/components/globals/__init__.py @@ -2,21 +2,29 @@ import hashlib from esphome import config_validation as cv, automation from esphome import codegen as cg -from esphome.const import CONF_ID, CONF_INITIAL_VALUE, CONF_RESTORE_VALUE, CONF_TYPE, CONF_VALUE +from esphome.const import ( + CONF_ID, + CONF_INITIAL_VALUE, + CONF_RESTORE_VALUE, + CONF_TYPE, + CONF_VALUE, +) from esphome.core import coroutine_with_priority -CODEOWNERS = ['@esphome/core'] -globals_ns = cg.esphome_ns.namespace('globals') -GlobalsComponent = globals_ns.class_('GlobalsComponent', cg.Component) -GlobalVarSetAction = globals_ns.class_('GlobalVarSetAction', automation.Action) +CODEOWNERS = ["@esphome/core"] +globals_ns = cg.esphome_ns.namespace("globals") +GlobalsComponent = globals_ns.class_("GlobalsComponent", cg.Component) +GlobalVarSetAction = globals_ns.class_("GlobalVarSetAction", automation.Action) MULTI_CONF = True -CONFIG_SCHEMA = cv.Schema({ - cv.Required(CONF_ID): cv.declare_id(GlobalsComponent), - cv.Required(CONF_TYPE): cv.string_strict, - cv.Optional(CONF_INITIAL_VALUE): cv.string_strict, - cv.Optional(CONF_RESTORE_VALUE, default=False): cv.boolean, -}).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = cv.Schema( + { + cv.Required(CONF_ID): cv.declare_id(GlobalsComponent), + cv.Required(CONF_TYPE): cv.string_strict, + cv.Optional(CONF_INITIAL_VALUE): cv.string_strict, + cv.Optional(CONF_RESTORE_VALUE, default=False): cv.boolean, + } +).extend(cv.COMPONENT_SCHEMA) # Run with low priority so that namespaces are registered first @@ -42,15 +50,22 @@ def to_code(config): cg.add(glob.set_restore_value(hash_)) -@automation.register_action('globals.set', GlobalVarSetAction, cv.Schema({ - cv.Required(CONF_ID): cv.use_id(GlobalsComponent), - cv.Required(CONF_VALUE): cv.templatable(cv.string_strict), -})) +@automation.register_action( + "globals.set", + GlobalVarSetAction, + cv.Schema( + { + cv.Required(CONF_ID): cv.use_id(GlobalsComponent), + cv.Required(CONF_VALUE): cv.templatable(cv.string_strict), + } + ), +) def globals_set_to_code(config, action_id, template_arg, args): full_id, paren = yield cg.get_variable_with_full_id(config[CONF_ID]) template_arg = cg.TemplateArguments(full_id.type, *template_arg) var = cg.new_Pvariable(action_id, template_arg, paren) - templ = yield cg.templatable(config[CONF_VALUE], args, None, - to_exp=cg.RawExpression) + templ = yield cg.templatable( + config[CONF_VALUE], args, None, to_exp=cg.RawExpression + ) cg.add(var.set_value(templ)) yield var diff --git a/esphome/components/gpio/__init__.py b/esphome/components/gpio/__init__.py index c36ba8f433..07ebb64cd2 100644 --- a/esphome/components/gpio/__init__.py +++ b/esphome/components/gpio/__init__.py @@ -1,4 +1,4 @@ import esphome.codegen as cg -CODEOWNERS = ['@esphome/core'] -gpio_ns = cg.esphome_ns.namespace('gpio') +CODEOWNERS = ["@esphome/core"] +gpio_ns = cg.esphome_ns.namespace("gpio") diff --git a/esphome/components/gpio/binary_sensor/__init__.py b/esphome/components/gpio/binary_sensor/__init__.py index e269de5a71..4a24efcdb0 100644 --- a/esphome/components/gpio/binary_sensor/__init__.py +++ b/esphome/components/gpio/binary_sensor/__init__.py @@ -5,12 +5,16 @@ from esphome.components import binary_sensor from esphome.const import CONF_ID, CONF_PIN from .. import gpio_ns -GPIOBinarySensor = gpio_ns.class_('GPIOBinarySensor', binary_sensor.BinarySensor, cg.Component) +GPIOBinarySensor = gpio_ns.class_( + "GPIOBinarySensor", binary_sensor.BinarySensor, cg.Component +) -CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(GPIOBinarySensor), - cv.Required(CONF_PIN): pins.gpio_input_pin_schema -}).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(GPIOBinarySensor), + cv.Required(CONF_PIN): pins.gpio_input_pin_schema, + } +).extend(cv.COMPONENT_SCHEMA) def to_code(config): diff --git a/esphome/components/gpio/output/__init__.py b/esphome/components/gpio/output/__init__.py index bab23c824b..d98a490566 100644 --- a/esphome/components/gpio/output/__init__.py +++ b/esphome/components/gpio/output/__init__.py @@ -5,13 +5,14 @@ from esphome.components import output from esphome.const import CONF_ID, CONF_PIN from .. import gpio_ns -GPIOBinaryOutput = gpio_ns.class_('GPIOBinaryOutput', output.BinaryOutput, - cg.Component) +GPIOBinaryOutput = gpio_ns.class_("GPIOBinaryOutput", output.BinaryOutput, cg.Component) -CONFIG_SCHEMA = output.BINARY_OUTPUT_SCHEMA.extend({ - cv.Required(CONF_ID): cv.declare_id(GPIOBinaryOutput), - cv.Required(CONF_PIN): pins.gpio_output_pin_schema, -}).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = output.BINARY_OUTPUT_SCHEMA.extend( + { + cv.Required(CONF_ID): cv.declare_id(GPIOBinaryOutput), + cv.Required(CONF_PIN): pins.gpio_output_pin_schema, + } +).extend(cv.COMPONENT_SCHEMA) def to_code(config): diff --git a/esphome/components/gpio/switch/__init__.py b/esphome/components/gpio/switch/__init__.py index f75bc71009..ebd12d10ca 100644 --- a/esphome/components/gpio/switch/__init__.py +++ b/esphome/components/gpio/switch/__init__.py @@ -5,25 +5,30 @@ from esphome.components import switch from esphome.const import CONF_ID, CONF_INTERLOCK, CONF_PIN, CONF_RESTORE_MODE from .. import gpio_ns -GPIOSwitch = gpio_ns.class_('GPIOSwitch', switch.Switch, cg.Component) -GPIOSwitchRestoreMode = gpio_ns.enum('GPIOSwitchRestoreMode') +GPIOSwitch = gpio_ns.class_("GPIOSwitch", switch.Switch, cg.Component) +GPIOSwitchRestoreMode = gpio_ns.enum("GPIOSwitchRestoreMode") RESTORE_MODES = { - 'RESTORE_DEFAULT_OFF': GPIOSwitchRestoreMode.GPIO_SWITCH_RESTORE_DEFAULT_OFF, - 'RESTORE_DEFAULT_ON': GPIOSwitchRestoreMode.GPIO_SWITCH_RESTORE_DEFAULT_ON, - 'ALWAYS_OFF': GPIOSwitchRestoreMode.GPIO_SWITCH_ALWAYS_OFF, - 'ALWAYS_ON': GPIOSwitchRestoreMode.GPIO_SWITCH_ALWAYS_ON, + "RESTORE_DEFAULT_OFF": GPIOSwitchRestoreMode.GPIO_SWITCH_RESTORE_DEFAULT_OFF, + "RESTORE_DEFAULT_ON": GPIOSwitchRestoreMode.GPIO_SWITCH_RESTORE_DEFAULT_ON, + "ALWAYS_OFF": GPIOSwitchRestoreMode.GPIO_SWITCH_ALWAYS_OFF, + "ALWAYS_ON": GPIOSwitchRestoreMode.GPIO_SWITCH_ALWAYS_ON, } -CONF_INTERLOCK_WAIT_TIME = 'interlock_wait_time' -CONFIG_SCHEMA = switch.SWITCH_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(GPIOSwitch), - cv.Required(CONF_PIN): pins.gpio_output_pin_schema, - cv.Optional(CONF_RESTORE_MODE, default='RESTORE_DEFAULT_OFF'): - cv.enum(RESTORE_MODES, upper=True, space='_'), - cv.Optional(CONF_INTERLOCK): cv.ensure_list(cv.use_id(switch.Switch)), - cv.Optional(CONF_INTERLOCK_WAIT_TIME, default='0ms'): cv.positive_time_period_milliseconds, -}).extend(cv.COMPONENT_SCHEMA) +CONF_INTERLOCK_WAIT_TIME = "interlock_wait_time" +CONFIG_SCHEMA = switch.SWITCH_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(GPIOSwitch), + cv.Required(CONF_PIN): pins.gpio_output_pin_schema, + cv.Optional(CONF_RESTORE_MODE, default="RESTORE_DEFAULT_OFF"): cv.enum( + RESTORE_MODES, upper=True, space="_" + ), + cv.Optional(CONF_INTERLOCK): cv.ensure_list(cv.use_id(switch.Switch)), + cv.Optional( + CONF_INTERLOCK_WAIT_TIME, default="0ms" + ): cv.positive_time_period_milliseconds, + } +).extend(cv.COMPONENT_SCHEMA) def to_code(config): diff --git a/esphome/components/gps/__init__.py b/esphome/components/gps/__init__.py index ddbd29d5f8..60dcc2002c 100644 --- a/esphome/components/gps/__init__.py +++ b/esphome/components/gps/__init__.py @@ -3,17 +3,23 @@ import esphome.config_validation as cv from esphome.components import uart from esphome.const import CONF_ID -DEPENDENCIES = ['uart'] +DEPENDENCIES = ["uart"] -gps_ns = cg.esphome_ns.namespace('gps') -GPS = gps_ns.class_('GPS', cg.Component, uart.UARTDevice) -GPSListener = gps_ns.class_('GPSListener') +gps_ns = cg.esphome_ns.namespace("gps") +GPS = gps_ns.class_("GPS", cg.Component, uart.UARTDevice) +GPSListener = gps_ns.class_("GPSListener") -CONF_GPS_ID = 'gps_id' +CONF_GPS_ID = "gps_id" MULTI_CONF = True -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(GPS), -}).extend(cv.COMPONENT_SCHEMA).extend(uart.UART_DEVICE_SCHEMA) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(GPS), + } + ) + .extend(cv.COMPONENT_SCHEMA) + .extend(uart.UART_DEVICE_SCHEMA) +) def to_code(config): @@ -22,4 +28,4 @@ def to_code(config): yield uart.register_uart_device(var, config) # https://platformio.org/lib/show/1655/TinyGPSPlus - cg.add_library('1655', '1.0.2') # TinyGPSPlus, has name conflict + cg.add_library("1655", "1.0.2") # TinyGPSPlus, has name conflict diff --git a/esphome/components/gps/time/__init__.py b/esphome/components/gps/time/__init__.py index 421d2e6717..24ea263f6c 100644 --- a/esphome/components/gps/time/__init__.py +++ b/esphome/components/gps/time/__init__.py @@ -4,14 +4,18 @@ import esphome.codegen as cg from esphome.const import CONF_ID from .. import gps_ns, GPSListener, CONF_GPS_ID, GPS -DEPENDENCIES = ['gps'] +DEPENDENCIES = ["gps"] -GPSTime = gps_ns.class_('GPSTime', cg.PollingComponent, time_.RealTimeClock, GPSListener) +GPSTime = gps_ns.class_( + "GPSTime", cg.PollingComponent, time_.RealTimeClock, GPSListener +) -CONFIG_SCHEMA = time_.TIME_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(GPSTime), - cv.GenerateID(CONF_GPS_ID): cv.use_id(GPS), -}).extend(cv.polling_component_schema('5min')) +CONFIG_SCHEMA = time_.TIME_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(GPSTime), + cv.GenerateID(CONF_GPS_ID): cv.use_id(GPS), + } +).extend(cv.polling_component_schema("5min")) def to_code(config): diff --git a/esphome/components/hbridge/light.py b/esphome/components/hbridge/light.py index abaf7152d7..6c695fbd4a 100644 --- a/esphome/components/hbridge/light.py +++ b/esphome/components/hbridge/light.py @@ -3,14 +3,18 @@ import esphome.config_validation as cv from esphome.components import light, output from esphome.const import CONF_OUTPUT_ID, CONF_PIN_A, CONF_PIN_B -hbridge_ns = cg.esphome_ns.namespace('hbridge') -HBridgeLightOutput = hbridge_ns.class_('HBridgeLightOutput', cg.PollingComponent, light.LightOutput) +hbridge_ns = cg.esphome_ns.namespace("hbridge") +HBridgeLightOutput = hbridge_ns.class_( + "HBridgeLightOutput", cg.PollingComponent, light.LightOutput +) -CONFIG_SCHEMA = light.RGB_LIGHT_SCHEMA.extend({ - cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(HBridgeLightOutput), - cv.Required(CONF_PIN_A): cv.use_id(output.FloatOutput), - cv.Required(CONF_PIN_B): cv.use_id(output.FloatOutput), -}) +CONFIG_SCHEMA = light.RGB_LIGHT_SCHEMA.extend( + { + cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(HBridgeLightOutput), + cv.Required(CONF_PIN_A): cv.use_id(output.FloatOutput), + cv.Required(CONF_PIN_B): cv.use_id(output.FloatOutput), + } +) def to_code(config): diff --git a/esphome/components/hdc1080/sensor.py b/esphome/components/hdc1080/sensor.py index fe80374beb..15a92cccf2 100644 --- a/esphome/components/hdc1080/sensor.py +++ b/esphome/components/hdc1080/sensor.py @@ -1,21 +1,39 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import CONF_HUMIDITY, CONF_ID, CONF_TEMPERATURE, DEVICE_CLASS_HUMIDITY, \ - DEVICE_CLASS_TEMPERATURE, ICON_EMPTY, UNIT_CELSIUS, UNIT_PERCENT +from esphome.const import ( + CONF_HUMIDITY, + CONF_ID, + CONF_TEMPERATURE, + DEVICE_CLASS_HUMIDITY, + DEVICE_CLASS_TEMPERATURE, + ICON_EMPTY, + UNIT_CELSIUS, + UNIT_PERCENT, +) -DEPENDENCIES = ['i2c'] +DEPENDENCIES = ["i2c"] -hdc1080_ns = cg.esphome_ns.namespace('hdc1080') -HDC1080Component = hdc1080_ns.class_('HDC1080Component', cg.PollingComponent, i2c.I2CDevice) +hdc1080_ns = cg.esphome_ns.namespace("hdc1080") +HDC1080Component = hdc1080_ns.class_( + "HDC1080Component", cg.PollingComponent, i2c.I2CDevice +) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(HDC1080Component), - cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, - DEVICE_CLASS_TEMPERATURE), - cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 0, - DEVICE_CLASS_HUMIDITY), -}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x40)) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(HDC1080Component), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( + UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE + ), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema( + UNIT_PERCENT, ICON_EMPTY, 0, DEVICE_CLASS_HUMIDITY + ), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x40)) +) def to_code(config): diff --git a/esphome/components/hitachi_ac344/climate.py b/esphome/components/hitachi_ac344/climate.py index fb1c21b200..9ae0cd39de 100644 --- a/esphome/components/hitachi_ac344/climate.py +++ b/esphome/components/hitachi_ac344/climate.py @@ -3,14 +3,16 @@ import esphome.config_validation as cv from esphome.components import climate_ir from esphome.const import CONF_ID -AUTO_LOAD = ['climate_ir'] +AUTO_LOAD = ["climate_ir"] -hitachi_ac344_ns = cg.esphome_ns.namespace('hitachi_ac344') -HitachiClimate = hitachi_ac344_ns.class_('HitachiClimate', climate_ir.ClimateIR) +hitachi_ac344_ns = cg.esphome_ns.namespace("hitachi_ac344") +HitachiClimate = hitachi_ac344_ns.class_("HitachiClimate", climate_ir.ClimateIR) -CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(HitachiClimate), -}) +CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(HitachiClimate), + } +) def to_code(config): diff --git a/esphome/components/hlw8012/sensor.py b/esphome/components/hlw8012/sensor.py index 74230c53c1..28aa24d7cd 100644 --- a/esphome/components/hlw8012/sensor.py +++ b/esphome/components/hlw8012/sensor.py @@ -2,43 +2,72 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins from esphome.components import sensor -from esphome.const import CONF_CHANGE_MODE_EVERY, CONF_INITIAL_MODE, CONF_CURRENT, \ - CONF_CURRENT_RESISTOR, CONF_ID, CONF_POWER, CONF_ENERGY, CONF_SEL_PIN, CONF_VOLTAGE, \ - CONF_VOLTAGE_DIVIDER, DEVICE_CLASS_CURRENT, DEVICE_CLASS_ENERGY, DEVICE_CLASS_POWER, \ - DEVICE_CLASS_VOLTAGE, ICON_EMPTY, UNIT_VOLT, UNIT_AMPERE, UNIT_WATT, UNIT_WATT_HOURS +from esphome.const import ( + CONF_CHANGE_MODE_EVERY, + CONF_INITIAL_MODE, + CONF_CURRENT, + CONF_CURRENT_RESISTOR, + CONF_ID, + CONF_POWER, + CONF_ENERGY, + CONF_SEL_PIN, + CONF_VOLTAGE, + CONF_VOLTAGE_DIVIDER, + DEVICE_CLASS_CURRENT, + DEVICE_CLASS_ENERGY, + DEVICE_CLASS_POWER, + DEVICE_CLASS_VOLTAGE, + ICON_EMPTY, + UNIT_VOLT, + UNIT_AMPERE, + UNIT_WATT, + UNIT_WATT_HOURS, +) -AUTO_LOAD = ['pulse_counter'] +AUTO_LOAD = ["pulse_counter"] -hlw8012_ns = cg.esphome_ns.namespace('hlw8012') -HLW8012Component = hlw8012_ns.class_('HLW8012Component', cg.PollingComponent) -HLW8012InitialMode = hlw8012_ns.enum('HLW8012InitialMode') +hlw8012_ns = cg.esphome_ns.namespace("hlw8012") +HLW8012Component = hlw8012_ns.class_("HLW8012Component", cg.PollingComponent) +HLW8012InitialMode = hlw8012_ns.enum("HLW8012InitialMode") INITIAL_MODES = { CONF_CURRENT: HLW8012InitialMode.HLW8012_INITIAL_MODE_CURRENT, CONF_VOLTAGE: HLW8012InitialMode.HLW8012_INITIAL_MODE_VOLTAGE, } -CONF_CF1_PIN = 'cf1_pin' -CONF_CF_PIN = 'cf_pin' -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(HLW8012Component), - cv.Required(CONF_SEL_PIN): pins.gpio_output_pin_schema, - cv.Required(CONF_CF_PIN): cv.All(pins.internal_gpio_input_pullup_pin_schema, - pins.validate_has_interrupt), - cv.Required(CONF_CF1_PIN): cv.All(pins.internal_gpio_input_pullup_pin_schema, - pins.validate_has_interrupt), - - cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE), - cv.Optional(CONF_CURRENT): sensor.sensor_schema(UNIT_AMPERE, ICON_EMPTY, 2, - DEVICE_CLASS_CURRENT), - cv.Optional(CONF_POWER): sensor.sensor_schema(UNIT_WATT, ICON_EMPTY, 1, DEVICE_CLASS_POWER), - cv.Optional(CONF_ENERGY): sensor.sensor_schema(UNIT_WATT_HOURS, ICON_EMPTY, 1, - DEVICE_CLASS_ENERGY), - - cv.Optional(CONF_CURRENT_RESISTOR, default=0.001): cv.resistance, - cv.Optional(CONF_VOLTAGE_DIVIDER, default=2351): cv.positive_float, - cv.Optional(CONF_CHANGE_MODE_EVERY, default=8): cv.All(cv.uint32_t, cv.Range(min=1)), - cv.Optional(CONF_INITIAL_MODE, default=CONF_VOLTAGE): cv.one_of(*INITIAL_MODES, lower=True), -}).extend(cv.polling_component_schema('60s')) +CONF_CF1_PIN = "cf1_pin" +CONF_CF_PIN = "cf_pin" +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(HLW8012Component), + cv.Required(CONF_SEL_PIN): pins.gpio_output_pin_schema, + cv.Required(CONF_CF_PIN): cv.All( + pins.internal_gpio_input_pullup_pin_schema, pins.validate_has_interrupt + ), + cv.Required(CONF_CF1_PIN): cv.All( + pins.internal_gpio_input_pullup_pin_schema, pins.validate_has_interrupt + ), + cv.Optional(CONF_VOLTAGE): sensor.sensor_schema( + UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE + ), + cv.Optional(CONF_CURRENT): sensor.sensor_schema( + UNIT_AMPERE, ICON_EMPTY, 2, DEVICE_CLASS_CURRENT + ), + cv.Optional(CONF_POWER): sensor.sensor_schema( + UNIT_WATT, ICON_EMPTY, 1, DEVICE_CLASS_POWER + ), + cv.Optional(CONF_ENERGY): sensor.sensor_schema( + UNIT_WATT_HOURS, ICON_EMPTY, 1, DEVICE_CLASS_ENERGY + ), + cv.Optional(CONF_CURRENT_RESISTOR, default=0.001): cv.resistance, + cv.Optional(CONF_VOLTAGE_DIVIDER, default=2351): cv.positive_float, + cv.Optional(CONF_CHANGE_MODE_EVERY, default=8): cv.All( + cv.uint32_t, cv.Range(min=1) + ), + cv.Optional(CONF_INITIAL_MODE, default=CONF_VOLTAGE): cv.one_of( + *INITIAL_MODES, lower=True + ), + } +).extend(cv.polling_component_schema("60s")) def to_code(config): diff --git a/esphome/components/hm3301/sensor.py b/esphome/components/hm3301/sensor.py index 67bfd3541a..14ca2e77ba 100644 --- a/esphome/components/hm3301/sensor.py +++ b/esphome/components/hm3301/sensor.py @@ -1,22 +1,31 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import CONF_ID, CONF_PM_2_5, CONF_PM_10_0, CONF_PM_1_0, DEVICE_CLASS_EMPTY, \ - UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_CHEMICAL_WEAPON +from esphome.const import ( + CONF_ID, + CONF_PM_2_5, + CONF_PM_10_0, + CONF_PM_1_0, + DEVICE_CLASS_EMPTY, + UNIT_MICROGRAMS_PER_CUBIC_METER, + ICON_CHEMICAL_WEAPON, +) -DEPENDENCIES = ['i2c'] +DEPENDENCIES = ["i2c"] -hm3301_ns = cg.esphome_ns.namespace('hm3301') -HM3301Component = hm3301_ns.class_('HM3301Component', cg.PollingComponent, i2c.I2CDevice) -AQICalculatorType = hm3301_ns.enum('AQICalculatorType') +hm3301_ns = cg.esphome_ns.namespace("hm3301") +HM3301Component = hm3301_ns.class_( + "HM3301Component", cg.PollingComponent, i2c.I2CDevice +) +AQICalculatorType = hm3301_ns.enum("AQICalculatorType") -CONF_AQI = 'aqi' -CONF_CALCULATION_TYPE = 'calculation_type' -UNIT_INDEX = 'index' +CONF_AQI = "aqi" +CONF_CALCULATION_TYPE = "calculation_type" +UNIT_INDEX = "index" AQI_CALCULATION_TYPE = { - 'CAQI': AQICalculatorType.CAQI_TYPE, - 'AQI': AQICalculatorType.AQI_TYPE + "CAQI": AQICalculatorType.CAQI_TYPE, + "AQI": AQICalculatorType.AQI_TYPE, } @@ -28,23 +37,43 @@ def validate(config): return config -CONFIG_SCHEMA = cv.All(cv.Schema({ - cv.GenerateID(): cv.declare_id(HM3301Component), - - cv.Optional(CONF_PM_1_0): - sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_CHEMICAL_WEAPON, 0, - DEVICE_CLASS_EMPTY), - cv.Optional(CONF_PM_2_5): - sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_CHEMICAL_WEAPON, 0, - DEVICE_CLASS_EMPTY), - cv.Optional(CONF_PM_10_0): - sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_CHEMICAL_WEAPON, 0, - DEVICE_CLASS_EMPTY), - cv.Optional(CONF_AQI): - sensor.sensor_schema(UNIT_INDEX, ICON_CHEMICAL_WEAPON, 0, DEVICE_CLASS_EMPTY).extend({ - cv.Required(CONF_CALCULATION_TYPE): cv.enum(AQI_CALCULATION_TYPE, upper=True), - }) -}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x40)), validate) +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(HM3301Component), + cv.Optional(CONF_PM_1_0): sensor.sensor_schema( + UNIT_MICROGRAMS_PER_CUBIC_METER, + ICON_CHEMICAL_WEAPON, + 0, + DEVICE_CLASS_EMPTY, + ), + cv.Optional(CONF_PM_2_5): sensor.sensor_schema( + UNIT_MICROGRAMS_PER_CUBIC_METER, + ICON_CHEMICAL_WEAPON, + 0, + DEVICE_CLASS_EMPTY, + ), + cv.Optional(CONF_PM_10_0): sensor.sensor_schema( + UNIT_MICROGRAMS_PER_CUBIC_METER, + ICON_CHEMICAL_WEAPON, + 0, + DEVICE_CLASS_EMPTY, + ), + cv.Optional(CONF_AQI): sensor.sensor_schema( + UNIT_INDEX, ICON_CHEMICAL_WEAPON, 0, DEVICE_CLASS_EMPTY + ).extend( + { + cv.Required(CONF_CALCULATION_TYPE): cv.enum( + AQI_CALCULATION_TYPE, upper=True + ), + } + ), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x40)), + validate, +) def to_code(config): @@ -70,4 +99,4 @@ def to_code(config): cg.add(var.set_aqi_calculation_type(config[CONF_AQI][CONF_CALCULATION_TYPE])) # https://platformio.org/lib/show/6306/Grove%20-%20Laser%20PM2.5%20Sensor%20HM3301 - cg.add_library('6306', '1.0.3') + cg.add_library("6306", "1.0.3") diff --git a/esphome/components/hmc5883l/sensor.py b/esphome/components/hmc5883l/sensor.py index d985694934..d057caf030 100644 --- a/esphome/components/hmc5883l/sensor.py +++ b/esphome/components/hmc5883l/sensor.py @@ -1,22 +1,33 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import CONF_ADDRESS, CONF_ID, CONF_OVERSAMPLING, CONF_RANGE, \ - DEVICE_CLASS_EMPTY, ICON_MAGNET, UNIT_MICROTESLA, UNIT_DEGREES, ICON_SCREEN_ROTATION, \ - CONF_UPDATE_INTERVAL +from esphome.const import ( + CONF_ADDRESS, + CONF_ID, + CONF_OVERSAMPLING, + CONF_RANGE, + DEVICE_CLASS_EMPTY, + ICON_MAGNET, + UNIT_MICROTESLA, + UNIT_DEGREES, + ICON_SCREEN_ROTATION, + CONF_UPDATE_INTERVAL, +) -DEPENDENCIES = ['i2c'] +DEPENDENCIES = ["i2c"] -hmc5883l_ns = cg.esphome_ns.namespace('hmc5883l') +hmc5883l_ns = cg.esphome_ns.namespace("hmc5883l") -CONF_FIELD_STRENGTH_X = 'field_strength_x' -CONF_FIELD_STRENGTH_Y = 'field_strength_y' -CONF_FIELD_STRENGTH_Z = 'field_strength_z' -CONF_HEADING = 'heading' +CONF_FIELD_STRENGTH_X = "field_strength_x" +CONF_FIELD_STRENGTH_Y = "field_strength_y" +CONF_FIELD_STRENGTH_Z = "field_strength_z" +CONF_HEADING = "heading" -HMC5883LComponent = hmc5883l_ns.class_('HMC5883LComponent', cg.PollingComponent, i2c.I2CDevice) +HMC5883LComponent = hmc5883l_ns.class_( + "HMC5883LComponent", cg.PollingComponent, i2c.I2CDevice +) -HMC5883LOversampling = hmc5883l_ns.enum('HMC5883LOversampling') +HMC5883LOversampling = hmc5883l_ns.enum("HMC5883LOversampling") HMC5883LOversamplings = { 1: HMC5883LOversampling.HMC5883L_OVERSAMPLING_1, 2: HMC5883LOversampling.HMC5883L_OVERSAMPLING_2, @@ -24,7 +35,7 @@ HMC5883LOversamplings = { 8: HMC5883LOversampling.HMC5883L_OVERSAMPLING_8, } -HMC5883LDatarate = hmc5883l_ns.enum('HMC5883LDatarate') +HMC5883LDatarate = hmc5883l_ns.enum("HMC5883LDatarate") HMC5883LDatarates = { 0.75: HMC5883LDatarate.HMC5883L_DATARATE_0_75_HZ, 1.5: HMC5883LDatarate.HMC5883L_DATARATE_1_5_HZ, @@ -35,7 +46,7 @@ HMC5883LDatarates = { 75: HMC5883LDatarate.HMC5883L_DATARATE_75_0_HZ, } -HMC5883LRange = hmc5883l_ns.enum('HMC5883LRange') +HMC5883LRange = hmc5883l_ns.enum("HMC5883LRange") HMC5883L_RANGES = { 88: HMC5883LRange.HMC5883L_RANGE_88_UT, 130: HMC5883LRange.HMC5883L_RANGE_130_UT, @@ -59,30 +70,45 @@ def validate_enum(enum_values, units=None, int=True): value = cv.string(value) for unit in _units: if value.endswith(unit): - value = value[:-len(unit)] + value = value[: -len(unit)] break return enum_bound(value) + return validate_enum_bound -field_strength_schema = sensor.sensor_schema(UNIT_MICROTESLA, ICON_MAGNET, 1, DEVICE_CLASS_EMPTY) -heading_schema = sensor.sensor_schema(UNIT_DEGREES, ICON_SCREEN_ROTATION, 1, DEVICE_CLASS_EMPTY) +field_strength_schema = sensor.sensor_schema( + UNIT_MICROTESLA, ICON_MAGNET, 1, DEVICE_CLASS_EMPTY +) +heading_schema = sensor.sensor_schema( + UNIT_DEGREES, ICON_SCREEN_ROTATION, 1, DEVICE_CLASS_EMPTY +) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(HMC5883LComponent), - cv.Optional(CONF_ADDRESS): cv.i2c_address, - cv.Optional(CONF_OVERSAMPLING, default='1x'): validate_enum(HMC5883LOversamplings, units="x"), - cv.Optional(CONF_RANGE, default='130µT'): validate_enum(HMC5883L_RANGES, units=["uT", "µT"]), - cv.Optional(CONF_FIELD_STRENGTH_X): field_strength_schema, - cv.Optional(CONF_FIELD_STRENGTH_Y): field_strength_schema, - cv.Optional(CONF_FIELD_STRENGTH_Z): field_strength_schema, - cv.Optional(CONF_HEADING): heading_schema, -}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x1E)) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(HMC5883LComponent), + cv.Optional(CONF_ADDRESS): cv.i2c_address, + cv.Optional(CONF_OVERSAMPLING, default="1x"): validate_enum( + HMC5883LOversamplings, units="x" + ), + cv.Optional(CONF_RANGE, default="130µT"): validate_enum( + HMC5883L_RANGES, units=["uT", "µT"] + ), + cv.Optional(CONF_FIELD_STRENGTH_X): field_strength_schema, + cv.Optional(CONF_FIELD_STRENGTH_Y): field_strength_schema, + cv.Optional(CONF_FIELD_STRENGTH_Z): field_strength_schema, + cv.Optional(CONF_HEADING): heading_schema, + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x1E)) +) def auto_data_rate(config): interval_sec = config[CONF_UPDATE_INTERVAL].seconds - interval_hz = 1.0/interval_sec + interval_hz = 1.0 / interval_sec for datarate in sorted(HMC5883LDatarates.keys()): if float(datarate) >= interval_hz: return HMC5883LDatarates[datarate] diff --git a/esphome/components/homeassistant/__init__.py b/esphome/components/homeassistant/__init__.py index 69d759b977..c151abc250 100644 --- a/esphome/components/homeassistant/__init__.py +++ b/esphome/components/homeassistant/__init__.py @@ -1,4 +1,4 @@ import esphome.codegen as cg -CODEOWNERS = ['@OttoWinter'] -homeassistant_ns = cg.esphome_ns.namespace('homeassistant') +CODEOWNERS = ["@OttoWinter"] +homeassistant_ns = cg.esphome_ns.namespace("homeassistant") diff --git a/esphome/components/homeassistant/binary_sensor/__init__.py b/esphome/components/homeassistant/binary_sensor/__init__.py index 88e2f2fcb2..850f239d6f 100644 --- a/esphome/components/homeassistant/binary_sensor/__init__.py +++ b/esphome/components/homeassistant/binary_sensor/__init__.py @@ -4,15 +4,17 @@ from esphome.components import binary_sensor from esphome.const import CONF_ENTITY_ID, CONF_ID from .. import homeassistant_ns -DEPENDENCIES = ['api'] -HomeassistantBinarySensor = homeassistant_ns.class_('HomeassistantBinarySensor', - binary_sensor.BinarySensor, - cg.Component) +DEPENDENCIES = ["api"] +HomeassistantBinarySensor = homeassistant_ns.class_( + "HomeassistantBinarySensor", binary_sensor.BinarySensor, cg.Component +) -CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(HomeassistantBinarySensor), - cv.Required(CONF_ENTITY_ID): cv.entity_id, -}).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(HomeassistantBinarySensor), + cv.Required(CONF_ENTITY_ID): cv.entity_id, + } +).extend(cv.COMPONENT_SCHEMA) def to_code(config): diff --git a/esphome/components/homeassistant/sensor/__init__.py b/esphome/components/homeassistant/sensor/__init__.py index a413518e07..9c19c7867f 100644 --- a/esphome/components/homeassistant/sensor/__init__.py +++ b/esphome/components/homeassistant/sensor/__init__.py @@ -1,18 +1,29 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor -from esphome.const import CONF_ENTITY_ID, CONF_ID, ICON_EMPTY, UNIT_EMPTY, DEVICE_CLASS_EMPTY +from esphome.const import ( + CONF_ENTITY_ID, + CONF_ID, + ICON_EMPTY, + UNIT_EMPTY, + DEVICE_CLASS_EMPTY, +) from .. import homeassistant_ns -DEPENDENCIES = ['api'] +DEPENDENCIES = ["api"] -HomeassistantSensor = homeassistant_ns.class_('HomeassistantSensor', sensor.Sensor, - cg.Component) +HomeassistantSensor = homeassistant_ns.class_( + "HomeassistantSensor", sensor.Sensor, cg.Component +) -CONFIG_SCHEMA = sensor.sensor_schema(UNIT_EMPTY, ICON_EMPTY, 1, DEVICE_CLASS_EMPTY).extend({ - cv.GenerateID(): cv.declare_id(HomeassistantSensor), - cv.Required(CONF_ENTITY_ID): cv.entity_id, -}) +CONFIG_SCHEMA = sensor.sensor_schema( + UNIT_EMPTY, ICON_EMPTY, 1, DEVICE_CLASS_EMPTY +).extend( + { + cv.GenerateID(): cv.declare_id(HomeassistantSensor), + cv.Required(CONF_ENTITY_ID): cv.entity_id, + } +) def to_code(config): diff --git a/esphome/components/homeassistant/text_sensor/__init__.py b/esphome/components/homeassistant/text_sensor/__init__.py index 2d06473f3c..478bd1c3d2 100644 --- a/esphome/components/homeassistant/text_sensor/__init__.py +++ b/esphome/components/homeassistant/text_sensor/__init__.py @@ -4,15 +4,18 @@ from esphome.components import text_sensor from esphome.const import CONF_ENTITY_ID, CONF_ID from .. import homeassistant_ns -DEPENDENCIES = ['api'] +DEPENDENCIES = ["api"] -HomeassistantTextSensor = homeassistant_ns.class_('HomeassistantTextSensor', - text_sensor.TextSensor, cg.Component) +HomeassistantTextSensor = homeassistant_ns.class_( + "HomeassistantTextSensor", text_sensor.TextSensor, cg.Component +) -CONFIG_SCHEMA = text_sensor.TEXT_SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(HomeassistantTextSensor), - cv.Required(CONF_ENTITY_ID): cv.entity_id, -}) +CONFIG_SCHEMA = text_sensor.TEXT_SENSOR_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(HomeassistantTextSensor), + cv.Required(CONF_ENTITY_ID): cv.entity_id, + } +) def to_code(config): diff --git a/esphome/components/homeassistant/time/__init__.py b/esphome/components/homeassistant/time/__init__.py index fd40de68d9..e995421302 100644 --- a/esphome/components/homeassistant/time/__init__.py +++ b/esphome/components/homeassistant/time/__init__.py @@ -4,17 +4,19 @@ import esphome.codegen as cg from esphome.const import CONF_ID from .. import homeassistant_ns -DEPENDENCIES = ['api'] +DEPENDENCIES = ["api"] -HomeassistantTime = homeassistant_ns.class_('HomeassistantTime', time_.RealTimeClock) +HomeassistantTime = homeassistant_ns.class_("HomeassistantTime", time_.RealTimeClock) -CONFIG_SCHEMA = time_.TIME_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(HomeassistantTime), -}).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = time_.TIME_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(HomeassistantTime), + } +).extend(cv.COMPONENT_SCHEMA) def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) yield time_.register_time(var, config) yield cg.register_component(var, config) - cg.add_define('USE_HOMEASSISTANT_TIME') + cg.add_define("USE_HOMEASSISTANT_TIME") diff --git a/esphome/components/http_request/__init__.py b/esphome/components/http_request/__init__.py index f7ec3cdbbf..0cac088f3d 100644 --- a/esphome/components/http_request/__init__.py +++ b/esphome/components/http_request/__init__.py @@ -3,39 +3,54 @@ import urllib.parse as urlparse import esphome.codegen as cg import esphome.config_validation as cv from esphome import automation -from esphome.const import CONF_ID, CONF_TIMEOUT, CONF_ESPHOME, CONF_METHOD, \ - CONF_ARDUINO_VERSION, ARDUINO_VERSION_ESP8266, CONF_URL +from esphome.const import ( + CONF_ID, + CONF_TIMEOUT, + CONF_ESPHOME, + CONF_METHOD, + CONF_ARDUINO_VERSION, + ARDUINO_VERSION_ESP8266, + CONF_URL, +) from esphome.core import CORE, Lambda from esphome.core_config import PLATFORMIO_ESP8266_LUT -DEPENDENCIES = ['network'] -AUTO_LOAD = ['json'] +DEPENDENCIES = ["network"] +AUTO_LOAD = ["json"] -http_request_ns = cg.esphome_ns.namespace('http_request') -HttpRequestComponent = http_request_ns.class_('HttpRequestComponent', cg.Component) -HttpRequestSendAction = http_request_ns.class_('HttpRequestSendAction', automation.Action) +http_request_ns = cg.esphome_ns.namespace("http_request") +HttpRequestComponent = http_request_ns.class_("HttpRequestComponent", cg.Component) +HttpRequestSendAction = http_request_ns.class_( + "HttpRequestSendAction", automation.Action +) -CONF_HEADERS = 'headers' -CONF_USERAGENT = 'useragent' -CONF_BODY = 'body' -CONF_JSON = 'json' -CONF_VERIFY_SSL = 'verify_ssl' +CONF_HEADERS = "headers" +CONF_USERAGENT = "useragent" +CONF_BODY = "body" +CONF_JSON = "json" +CONF_VERIFY_SSL = "verify_ssl" def validate_framework(config): if CORE.is_esp32: return config - version = 'RECOMMENDED' + version = "RECOMMENDED" if CONF_ARDUINO_VERSION in CORE.raw_config[CONF_ESPHOME]: version = CORE.raw_config[CONF_ESPHOME][CONF_ARDUINO_VERSION] - if version in ['LATEST', 'DEV']: + if version in ["LATEST", "DEV"]: return config - framework = PLATFORMIO_ESP8266_LUT[version] if version in PLATFORMIO_ESP8266_LUT else version - if framework < ARDUINO_VERSION_ESP8266['2.5.1']: - raise cv.Invalid('This component is not supported on arduino framework version below 2.5.1') + framework = ( + PLATFORMIO_ESP8266_LUT[version] + if version in PLATFORMIO_ESP8266_LUT + else version + ) + if framework < ARDUINO_VERSION_ESP8266["2.5.1"]: + raise cv.Invalid( + "This component is not supported on arduino framework version below 2.5.1" + ) return config @@ -44,34 +59,47 @@ def validate_url(value): try: parsed = list(urlparse.urlparse(value)) except Exception as err: - raise cv.Invalid('Invalid URL') from err + raise cv.Invalid("Invalid URL") from err if not parsed[0] or not parsed[1]: - raise cv.Invalid('URL must have a URL scheme and host') + raise cv.Invalid("URL must have a URL scheme and host") - if parsed[0] not in ['http', 'https']: - raise cv.Invalid('Scheme must be http or https') + if parsed[0] not in ["http", "https"]: + raise cv.Invalid("Scheme must be http or https") if not parsed[2]: - parsed[2] = '/' + parsed[2] = "/" return urlparse.urlunparse(parsed) def validate_secure_url(config): url_ = config[CONF_URL] - if config.get(CONF_VERIFY_SSL) and not isinstance(url_, Lambda) \ - and url_.lower().startswith('https:'): - raise cv.Invalid('Currently ESPHome doesn\'t support SSL verification. ' - 'Set \'verify_ssl: false\' to make insecure HTTPS requests.') + if ( + config.get(CONF_VERIFY_SSL) + and not isinstance(url_, Lambda) + and url_.lower().startswith("https:") + ): + raise cv.Invalid( + "Currently ESPHome doesn't support SSL verification. " + "Set 'verify_ssl: false' to make insecure HTTPS requests." + ) return config -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(HttpRequestComponent), - cv.Optional(CONF_USERAGENT, 'ESPHome'): cv.string, - cv.Optional(CONF_TIMEOUT, default='5s'): cv.positive_time_period_milliseconds, -}).add_extra(validate_framework).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(HttpRequestComponent), + cv.Optional(CONF_USERAGENT, "ESPHome"): cv.string, + cv.Optional( + CONF_TIMEOUT, default="5s" + ): cv.positive_time_period_milliseconds, + } + ) + .add_extra(validate_framework) + .extend(cv.COMPONENT_SCHEMA) +) def to_code(config): @@ -81,41 +109,60 @@ def to_code(config): yield cg.register_component(var, config) -HTTP_REQUEST_ACTION_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.use_id(HttpRequestComponent), - cv.Required(CONF_URL): cv.templatable(validate_url), - cv.Optional(CONF_HEADERS): cv.All(cv.Schema({cv.string: cv.templatable(cv.string)})), - cv.Optional(CONF_VERIFY_SSL, default=True): cv.boolean, -}).add_extra(validate_secure_url) +HTTP_REQUEST_ACTION_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.use_id(HttpRequestComponent), + cv.Required(CONF_URL): cv.templatable(validate_url), + cv.Optional(CONF_HEADERS): cv.All( + cv.Schema({cv.string: cv.templatable(cv.string)}) + ), + cv.Optional(CONF_VERIFY_SSL, default=True): cv.boolean, + } +).add_extra(validate_secure_url) HTTP_REQUEST_GET_ACTION_SCHEMA = automation.maybe_conf( - CONF_URL, HTTP_REQUEST_ACTION_SCHEMA.extend({ - cv.Optional(CONF_METHOD, default='GET'): cv.one_of('GET', upper=True), - }) + CONF_URL, + HTTP_REQUEST_ACTION_SCHEMA.extend( + { + cv.Optional(CONF_METHOD, default="GET"): cv.one_of("GET", upper=True), + } + ), ) HTTP_REQUEST_POST_ACTION_SCHEMA = automation.maybe_conf( - CONF_URL, HTTP_REQUEST_ACTION_SCHEMA.extend({ - cv.Optional(CONF_METHOD, default='POST'): cv.one_of('POST', upper=True), - cv.Exclusive(CONF_BODY, 'body'): cv.templatable(cv.string), - cv.Exclusive(CONF_JSON, 'body'): cv.Any( - cv.lambda_, cv.All(cv.Schema({cv.string: cv.templatable(cv.string_strict)})), - ), - }) -) -HTTP_REQUEST_SEND_ACTION_SCHEMA = HTTP_REQUEST_ACTION_SCHEMA.extend({ - cv.Required(CONF_METHOD): cv.one_of('GET', 'POST', 'PUT', 'DELETE', 'PATCH', upper=True), - cv.Exclusive(CONF_BODY, 'body'): cv.templatable(cv.string), - cv.Exclusive(CONF_JSON, 'body'): cv.Any( - cv.lambda_, cv.All(cv.Schema({cv.string: cv.templatable(cv.string_strict)})), + CONF_URL, + HTTP_REQUEST_ACTION_SCHEMA.extend( + { + cv.Optional(CONF_METHOD, default="POST"): cv.one_of("POST", upper=True), + cv.Exclusive(CONF_BODY, "body"): cv.templatable(cv.string), + cv.Exclusive(CONF_JSON, "body"): cv.Any( + cv.lambda_, + cv.All(cv.Schema({cv.string: cv.templatable(cv.string_strict)})), + ), + } ), -}) +) +HTTP_REQUEST_SEND_ACTION_SCHEMA = HTTP_REQUEST_ACTION_SCHEMA.extend( + { + cv.Required(CONF_METHOD): cv.one_of( + "GET", "POST", "PUT", "DELETE", "PATCH", upper=True + ), + cv.Exclusive(CONF_BODY, "body"): cv.templatable(cv.string), + cv.Exclusive(CONF_JSON, "body"): cv.Any( + cv.lambda_, + cv.All(cv.Schema({cv.string: cv.templatable(cv.string_strict)})), + ), + } +) -@automation.register_action('http_request.get', HttpRequestSendAction, - HTTP_REQUEST_GET_ACTION_SCHEMA) -@automation.register_action('http_request.post', HttpRequestSendAction, - HTTP_REQUEST_POST_ACTION_SCHEMA) -@automation.register_action('http_request.send', HttpRequestSendAction, - HTTP_REQUEST_SEND_ACTION_SCHEMA) +@automation.register_action( + "http_request.get", HttpRequestSendAction, HTTP_REQUEST_GET_ACTION_SCHEMA +) +@automation.register_action( + "http_request.post", HttpRequestSendAction, HTTP_REQUEST_POST_ACTION_SCHEMA +) +@automation.register_action( + "http_request.send", HttpRequestSendAction, HTTP_REQUEST_SEND_ACTION_SCHEMA +) def http_request_action_to_code(config, action_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) var = cg.new_Pvariable(action_id, template_arg, paren) @@ -129,7 +176,7 @@ def http_request_action_to_code(config, action_id, template_arg, args): if CONF_JSON in config: json_ = config[CONF_JSON] if isinstance(json_, Lambda): - args_ = args + [(cg.JsonObjectRef, 'root')] + args_ = args + [(cg.JsonObjectRef, "root")] lambda_ = yield cg.process_lambda(json_, args_, return_type=cg.void) cg.add(var.set_json(lambda_)) else: @@ -137,7 +184,9 @@ def http_request_action_to_code(config, action_id, template_arg, args): template_ = yield cg.templatable(json_[key], args, cg.std_string) cg.add(var.add_json(key, template_)) for key in config.get(CONF_HEADERS, []): - template_ = yield cg.templatable(config[CONF_HEADERS][key], args, cg.const_char_ptr) + template_ = yield cg.templatable( + config[CONF_HEADERS][key], args, cg.const_char_ptr + ) cg.add(var.add_header(key, template_)) yield var diff --git a/esphome/components/htu21d/sensor.py b/esphome/components/htu21d/sensor.py index 358eb33d6d..258681a5aa 100644 --- a/esphome/components/htu21d/sensor.py +++ b/esphome/components/htu21d/sensor.py @@ -1,21 +1,39 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import CONF_HUMIDITY, CONF_ID, CONF_TEMPERATURE, DEVICE_CLASS_HUMIDITY, \ - DEVICE_CLASS_TEMPERATURE, ICON_EMPTY, UNIT_CELSIUS, UNIT_PERCENT +from esphome.const import ( + CONF_HUMIDITY, + CONF_ID, + CONF_TEMPERATURE, + DEVICE_CLASS_HUMIDITY, + DEVICE_CLASS_TEMPERATURE, + ICON_EMPTY, + UNIT_CELSIUS, + UNIT_PERCENT, +) -DEPENDENCIES = ['i2c'] +DEPENDENCIES = ["i2c"] -htu21d_ns = cg.esphome_ns.namespace('htu21d') -HTU21DComponent = htu21d_ns.class_('HTU21DComponent', cg.PollingComponent, i2c.I2CDevice) +htu21d_ns = cg.esphome_ns.namespace("htu21d") +HTU21DComponent = htu21d_ns.class_( + "HTU21DComponent", cg.PollingComponent, i2c.I2CDevice +) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(HTU21DComponent), - cv.Required(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, - DEVICE_CLASS_TEMPERATURE), - cv.Required(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 1, - DEVICE_CLASS_HUMIDITY), -}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x40)) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(HTU21DComponent), + cv.Required(CONF_TEMPERATURE): sensor.sensor_schema( + UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE + ), + cv.Required(CONF_HUMIDITY): sensor.sensor_schema( + UNIT_PERCENT, ICON_EMPTY, 1, DEVICE_CLASS_HUMIDITY + ), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x40)) +) def to_code(config): diff --git a/esphome/components/hx711/sensor.py b/esphome/components/hx711/sensor.py index ad060646dc..191a7386e6 100644 --- a/esphome/components/hx711/sensor.py +++ b/esphome/components/hx711/sensor.py @@ -2,27 +2,39 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins from esphome.components import sensor -from esphome.const import CONF_CLK_PIN, CONF_GAIN, CONF_ID, DEVICE_CLASS_EMPTY, ICON_SCALE, \ - UNIT_EMPTY +from esphome.const import ( + CONF_CLK_PIN, + CONF_GAIN, + CONF_ID, + DEVICE_CLASS_EMPTY, + ICON_SCALE, + UNIT_EMPTY, +) -hx711_ns = cg.esphome_ns.namespace('hx711') -HX711Sensor = hx711_ns.class_('HX711Sensor', sensor.Sensor, cg.PollingComponent) +hx711_ns = cg.esphome_ns.namespace("hx711") +HX711Sensor = hx711_ns.class_("HX711Sensor", sensor.Sensor, cg.PollingComponent) -CONF_DOUT_PIN = 'dout_pin' +CONF_DOUT_PIN = "dout_pin" -HX711Gain = hx711_ns.enum('HX711Gain') +HX711Gain = hx711_ns.enum("HX711Gain") GAINS = { 128: HX711Gain.HX711_GAIN_128, 32: HX711Gain.HX711_GAIN_32, 64: HX711Gain.HX711_GAIN_64, } -CONFIG_SCHEMA = sensor.sensor_schema(UNIT_EMPTY, ICON_SCALE, 0, DEVICE_CLASS_EMPTY).extend({ - cv.GenerateID(): cv.declare_id(HX711Sensor), - cv.Required(CONF_DOUT_PIN): pins.gpio_input_pin_schema, - cv.Required(CONF_CLK_PIN): pins.gpio_output_pin_schema, - cv.Optional(CONF_GAIN, default=128): cv.enum(GAINS, int=True), -}).extend(cv.polling_component_schema('60s')) +CONFIG_SCHEMA = ( + sensor.sensor_schema(UNIT_EMPTY, ICON_SCALE, 0, DEVICE_CLASS_EMPTY) + .extend( + { + cv.GenerateID(): cv.declare_id(HX711Sensor), + cv.Required(CONF_DOUT_PIN): pins.gpio_input_pin_schema, + cv.Required(CONF_CLK_PIN): pins.gpio_output_pin_schema, + cv.Optional(CONF_GAIN, default=128): cv.enum(GAINS, int=True), + } + ) + .extend(cv.polling_component_schema("60s")) +) def to_code(config): diff --git a/esphome/components/i2c/__init__.py b/esphome/components/i2c/__init__.py index 91c6a97190..1446835904 100644 --- a/esphome/components/i2c/__init__.py +++ b/esphome/components/i2c/__init__.py @@ -1,24 +1,34 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins -from esphome.const import CONF_FREQUENCY, CONF_ID, CONF_SCAN, CONF_SCL, CONF_SDA, CONF_ADDRESS, \ - CONF_I2C_ID +from esphome.const import ( + CONF_FREQUENCY, + CONF_ID, + CONF_SCAN, + CONF_SCL, + CONF_SDA, + CONF_ADDRESS, + CONF_I2C_ID, +) from esphome.core import coroutine, coroutine_with_priority -CODEOWNERS = ['@esphome/core'] -i2c_ns = cg.esphome_ns.namespace('i2c') -I2CComponent = i2c_ns.class_('I2CComponent', cg.Component) -I2CDevice = i2c_ns.class_('I2CDevice') +CODEOWNERS = ["@esphome/core"] +i2c_ns = cg.esphome_ns.namespace("i2c") +I2CComponent = i2c_ns.class_("I2CComponent", cg.Component) +I2CDevice = i2c_ns.class_("I2CDevice") MULTI_CONF = True -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(I2CComponent), - cv.Optional(CONF_SDA, default='SDA'): pins.input_pin, - cv.Optional(CONF_SCL, default='SCL'): pins.input_pin, - cv.Optional(CONF_FREQUENCY, default='50kHz'): - cv.All(cv.frequency, cv.Range(min=0, min_included=False)), - cv.Optional(CONF_SCAN, default=True): cv.boolean, -}).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(I2CComponent), + cv.Optional(CONF_SDA, default="SDA"): pins.input_pin, + cv.Optional(CONF_SCL, default="SCL"): pins.input_pin, + cv.Optional(CONF_FREQUENCY, default="50kHz"): cv.All( + cv.frequency, cv.Range(min=0, min_included=False) + ), + cv.Optional(CONF_SCAN, default=True): cv.boolean, + } +).extend(cv.COMPONENT_SCHEMA) @coroutine_with_priority(1.0) @@ -31,7 +41,7 @@ def to_code(config): cg.add(var.set_scl_pin(config[CONF_SCL])) cg.add(var.set_frequency(int(config[CONF_FREQUENCY]))) cg.add(var.set_scan(config[CONF_SCAN])) - cg.add_library('Wire', None) + cg.add_library("Wire", None) def i2c_device_schema(default_address): diff --git a/esphome/components/ili9341/display.py b/esphome/components/ili9341/display.py index 0a3e9e16cc..8c1a28ea5d 100644 --- a/esphome/components/ili9341/display.py +++ b/esphome/components/ili9341/display.py @@ -2,42 +2,55 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins from esphome.components import display, spi -from esphome.const import CONF_DC_PIN, \ - CONF_ID, CONF_LAMBDA, CONF_MODEL, CONF_PAGES, CONF_RESET_PIN +from esphome.const import ( + CONF_DC_PIN, + CONF_ID, + CONF_LAMBDA, + CONF_MODEL, + CONF_PAGES, + CONF_RESET_PIN, +) -DEPENDENCIES = ['spi'] +DEPENDENCIES = ["spi"] -CONF_LED_PIN = 'led_pin' +CONF_LED_PIN = "led_pin" -ili9341_ns = cg.esphome_ns.namespace('ili9341') -ili9341 = ili9341_ns.class_('ILI9341Display', cg.PollingComponent, spi.SPIDevice, - display.DisplayBuffer) -ILI9341M5Stack = ili9341_ns.class_('ILI9341M5Stack', ili9341) -ILI9341TFT24 = ili9341_ns.class_('ILI9341TFT24', ili9341) +ili9341_ns = cg.esphome_ns.namespace("ili9341") +ili9341 = ili9341_ns.class_( + "ILI9341Display", cg.PollingComponent, spi.SPIDevice, display.DisplayBuffer +) +ILI9341M5Stack = ili9341_ns.class_("ILI9341M5Stack", ili9341) +ILI9341TFT24 = ili9341_ns.class_("ILI9341TFT24", ili9341) -ILI9341Model = ili9341_ns.enum('ILI9341Model') +ILI9341Model = ili9341_ns.enum("ILI9341Model") MODELS = { - 'M5STACK': ILI9341Model.M5STACK, - 'TFT_2.4': ILI9341Model.TFT_24, + "M5STACK": ILI9341Model.M5STACK, + "TFT_2.4": ILI9341Model.TFT_24, } ILI9341_MODEL = cv.enum(MODELS, upper=True, space="_") -CONFIG_SCHEMA = cv.All(display.FULL_DISPLAY_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(ili9341), - cv.Required(CONF_MODEL): ILI9341_MODEL, - cv.Required(CONF_DC_PIN): pins.gpio_output_pin_schema, - cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, - cv.Optional(CONF_LED_PIN): pins.gpio_output_pin_schema, -}).extend(cv.polling_component_schema('1s')).extend(spi.spi_device_schema()), - cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA)) +CONFIG_SCHEMA = cv.All( + display.FULL_DISPLAY_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(ili9341), + cv.Required(CONF_MODEL): ILI9341_MODEL, + cv.Required(CONF_DC_PIN): pins.gpio_output_pin_schema, + cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, + cv.Optional(CONF_LED_PIN): pins.gpio_output_pin_schema, + } + ) + .extend(cv.polling_component_schema("1s")) + .extend(spi.spi_device_schema()), + cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA), +) def to_code(config): - if config[CONF_MODEL] == 'M5STACK': + if config[CONF_MODEL] == "M5STACK": lcd_type = ILI9341M5Stack - if config[CONF_MODEL] == 'TFT_2.4': + if config[CONF_MODEL] == "TFT_2.4": lcd_type = ILI9341TFT24 rhs = lcd_type.new() var = cg.Pvariable(config[CONF_ID], rhs) @@ -50,8 +63,9 @@ def to_code(config): cg.add(var.set_dc_pin(dc)) if CONF_LAMBDA in config: - lambda_ = yield cg.process_lambda(config[CONF_LAMBDA], [(display.DisplayBufferRef, 'it')], - return_type=cg.void) + lambda_ = yield cg.process_lambda( + config[CONF_LAMBDA], [(display.DisplayBufferRef, "it")], return_type=cg.void + ) cg.add(var.set_writer(lambda_)) if CONF_RESET_PIN in config: reset = yield cg.gpio_pin_expression(config[CONF_RESET_PIN]) diff --git a/esphome/components/image/__init__.py b/esphome/components/image/__init__.py index 3558d9660e..b629779690 100644 --- a/esphome/components/image/__init__.py +++ b/esphome/components/image/__init__.py @@ -9,28 +9,32 @@ from esphome.core import CORE, HexInt _LOGGER = logging.getLogger(__name__) -DEPENDENCIES = ['display'] +DEPENDENCIES = ["display"] MULTI_CONF = True -ImageType = display.display_ns.enum('ImageType') +ImageType = display.display_ns.enum("ImageType") IMAGE_TYPE = { - 'BINARY': ImageType.IMAGE_TYPE_BINARY, - 'GRAYSCALE': ImageType.IMAGE_TYPE_GRAYSCALE, - 'RGB24': ImageType.IMAGE_TYPE_RGB24, + "BINARY": ImageType.IMAGE_TYPE_BINARY, + "GRAYSCALE": ImageType.IMAGE_TYPE_GRAYSCALE, + "RGB24": ImageType.IMAGE_TYPE_RGB24, } -Image_ = display.display_ns.class_('Image') +Image_ = display.display_ns.class_("Image") -CONF_RAW_DATA_ID = 'raw_data_id' +CONF_RAW_DATA_ID = "raw_data_id" -IMAGE_SCHEMA = cv.Schema({ - cv.Required(CONF_ID): cv.declare_id(Image_), - cv.Required(CONF_FILE): cv.file_, - cv.Optional(CONF_RESIZE): cv.dimensions, - cv.Optional(CONF_TYPE, default='BINARY'): cv.enum(IMAGE_TYPE, upper=True), - cv.Optional(CONF_DITHER, default='NONE'): cv.one_of("NONE", "FLOYDSTEINBERG", upper=True), - cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_id(cg.uint8), -}) +IMAGE_SCHEMA = cv.Schema( + { + cv.Required(CONF_ID): cv.declare_id(Image_), + cv.Required(CONF_FILE): cv.file_, + cv.Optional(CONF_RESIZE): cv.dimensions, + cv.Optional(CONF_TYPE, default="BINARY"): cv.enum(IMAGE_TYPE, upper=True), + cv.Optional(CONF_DITHER, default="NONE"): cv.one_of( + "NONE", "FLOYDSTEINBERG", upper=True + ), + cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_id(cg.uint8), + } +) CONFIG_SCHEMA = cv.All(font.validate_pillow_installed, IMAGE_SCHEMA) @@ -51,12 +55,14 @@ def to_code(config): width, height = image.size else: if width > 500 or height > 500: - _LOGGER.warning("The image you requested is very big. Please consider using" - " the resize parameter.") + _LOGGER.warning( + "The image you requested is very big. Please consider using" + " the resize parameter." + ) - dither = Image.NONE if config[CONF_DITHER] == 'NONE' else Image.FLOYDSTEINBERG - if config[CONF_TYPE] == 'GRAYSCALE': - image = image.convert('L', dither=dither) + dither = Image.NONE if config[CONF_DITHER] == "NONE" else Image.FLOYDSTEINBERG + if config[CONF_TYPE] == "GRAYSCALE": + image = image.convert("L", dither=dither) pixels = list(image.getdata()) data = [0 for _ in range(height * width)] pos = 0 @@ -64,8 +70,8 @@ def to_code(config): data[pos] = pix pos += 1 - elif config[CONF_TYPE] == 'RGB24': - image = image.convert('RGB') + elif config[CONF_TYPE] == "RGB24": + image = image.convert("RGB") pixels = list(image.getdata()) data = [0 for _ in range(height * width * 3)] pos = 0 @@ -77,8 +83,8 @@ def to_code(config): data[pos] = pix[2] pos += 1 - elif config[CONF_TYPE] == 'BINARY': - image = image.convert('1', dither=dither) + elif config[CONF_TYPE] == "BINARY": + image = image.convert("1", dither=dither) width8 = ((width + 7) // 8) * 8 data = [0 for _ in range(height * width8 // 8)] for y in range(height): @@ -90,5 +96,6 @@ def to_code(config): rhs = [HexInt(x) for x in data] prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs) - cg.new_Pvariable(config[CONF_ID], prog_arr, width, height, - IMAGE_TYPE[config[CONF_TYPE]]) + cg.new_Pvariable( + config[CONF_ID], prog_arr, width, height, IMAGE_TYPE[config[CONF_TYPE]] + ) diff --git a/esphome/components/ina219/sensor.py b/esphome/components/ina219/sensor.py index 6229dad0d8..d122754b88 100644 --- a/esphome/components/ina219/sensor.py +++ b/esphome/components/ina219/sensor.py @@ -1,29 +1,61 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import CONF_BUS_VOLTAGE, CONF_CURRENT, CONF_ID, CONF_MAX_CURRENT, \ - CONF_MAX_VOLTAGE, CONF_POWER, CONF_SHUNT_RESISTANCE, CONF_SHUNT_VOLTAGE, DEVICE_CLASS_CURRENT, \ - DEVICE_CLASS_POWER, DEVICE_CLASS_VOLTAGE, ICON_EMPTY, UNIT_VOLT, UNIT_AMPERE, UNIT_WATT +from esphome.const import ( + CONF_BUS_VOLTAGE, + CONF_CURRENT, + CONF_ID, + CONF_MAX_CURRENT, + CONF_MAX_VOLTAGE, + CONF_POWER, + CONF_SHUNT_RESISTANCE, + CONF_SHUNT_VOLTAGE, + DEVICE_CLASS_CURRENT, + DEVICE_CLASS_POWER, + DEVICE_CLASS_VOLTAGE, + ICON_EMPTY, + UNIT_VOLT, + UNIT_AMPERE, + UNIT_WATT, +) -DEPENDENCIES = ['i2c'] +DEPENDENCIES = ["i2c"] -ina219_ns = cg.esphome_ns.namespace('ina219') -INA219Component = ina219_ns.class_('INA219Component', cg.PollingComponent, i2c.I2CDevice) +ina219_ns = cg.esphome_ns.namespace("ina219") +INA219Component = ina219_ns.class_( + "INA219Component", cg.PollingComponent, i2c.I2CDevice +) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(INA219Component), - cv.Optional(CONF_BUS_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_EMPTY, 2, - DEVICE_CLASS_VOLTAGE), - cv.Optional(CONF_SHUNT_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_EMPTY, 2, - DEVICE_CLASS_VOLTAGE), - cv.Optional(CONF_CURRENT): sensor.sensor_schema(UNIT_AMPERE, ICON_EMPTY, 3, - DEVICE_CLASS_CURRENT), - cv.Optional(CONF_POWER): sensor.sensor_schema(UNIT_WATT, ICON_EMPTY, 2, DEVICE_CLASS_POWER), - cv.Optional(CONF_SHUNT_RESISTANCE, default=0.1): cv.All(cv.resistance, - cv.Range(min=0.0, max=32.0)), - cv.Optional(CONF_MAX_VOLTAGE, default=32.0): cv.All(cv.voltage, cv.Range(min=0.0, max=32.0)), - cv.Optional(CONF_MAX_CURRENT, default=3.2): cv.All(cv.current, cv.Range(min=0.0)), -}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x40)) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(INA219Component), + cv.Optional(CONF_BUS_VOLTAGE): sensor.sensor_schema( + UNIT_VOLT, ICON_EMPTY, 2, DEVICE_CLASS_VOLTAGE + ), + cv.Optional(CONF_SHUNT_VOLTAGE): sensor.sensor_schema( + UNIT_VOLT, ICON_EMPTY, 2, DEVICE_CLASS_VOLTAGE + ), + cv.Optional(CONF_CURRENT): sensor.sensor_schema( + UNIT_AMPERE, ICON_EMPTY, 3, DEVICE_CLASS_CURRENT + ), + cv.Optional(CONF_POWER): sensor.sensor_schema( + UNIT_WATT, ICON_EMPTY, 2, DEVICE_CLASS_POWER + ), + cv.Optional(CONF_SHUNT_RESISTANCE, default=0.1): cv.All( + cv.resistance, cv.Range(min=0.0, max=32.0) + ), + cv.Optional(CONF_MAX_VOLTAGE, default=32.0): cv.All( + cv.voltage, cv.Range(min=0.0, max=32.0) + ), + cv.Optional(CONF_MAX_CURRENT, default=3.2): cv.All( + cv.current, cv.Range(min=0.0) + ), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x40)) +) def to_code(config): diff --git a/esphome/components/ina226/sensor.py b/esphome/components/ina226/sensor.py index b36a74755f..2b7346fbe2 100644 --- a/esphome/components/ina226/sensor.py +++ b/esphome/components/ina226/sensor.py @@ -1,27 +1,57 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import CONF_BUS_VOLTAGE, CONF_CURRENT, CONF_ID, CONF_MAX_CURRENT, CONF_POWER, \ - CONF_SHUNT_RESISTANCE, CONF_SHUNT_VOLTAGE, DEVICE_CLASS_VOLTAGE, DEVICE_CLASS_CURRENT, \ - DEVICE_CLASS_POWER, ICON_EMPTY, UNIT_VOLT, UNIT_AMPERE, UNIT_WATT +from esphome.const import ( + CONF_BUS_VOLTAGE, + CONF_CURRENT, + CONF_ID, + CONF_MAX_CURRENT, + CONF_POWER, + CONF_SHUNT_RESISTANCE, + CONF_SHUNT_VOLTAGE, + DEVICE_CLASS_VOLTAGE, + DEVICE_CLASS_CURRENT, + DEVICE_CLASS_POWER, + ICON_EMPTY, + UNIT_VOLT, + UNIT_AMPERE, + UNIT_WATT, +) -DEPENDENCIES = ['i2c'] +DEPENDENCIES = ["i2c"] -ina226_ns = cg.esphome_ns.namespace('ina226') -INA226Component = ina226_ns.class_('INA226Component', cg.PollingComponent, i2c.I2CDevice) +ina226_ns = cg.esphome_ns.namespace("ina226") +INA226Component = ina226_ns.class_( + "INA226Component", cg.PollingComponent, i2c.I2CDevice +) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(INA226Component), - cv.Optional(CONF_BUS_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_EMPTY, 2, - DEVICE_CLASS_VOLTAGE), - cv.Optional(CONF_SHUNT_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_EMPTY, 2, - DEVICE_CLASS_VOLTAGE), - cv.Optional(CONF_CURRENT): sensor.sensor_schema(UNIT_AMPERE, ICON_EMPTY, 3, - DEVICE_CLASS_CURRENT), - cv.Optional(CONF_POWER): sensor.sensor_schema(UNIT_WATT, ICON_EMPTY, 2, DEVICE_CLASS_POWER), - cv.Optional(CONF_SHUNT_RESISTANCE, default=0.1): cv.All(cv.resistance, cv.Range(min=0.0)), - cv.Optional(CONF_MAX_CURRENT, default=3.2): cv.All(cv.current, cv.Range(min=0.0)), -}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x40)) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(INA226Component), + cv.Optional(CONF_BUS_VOLTAGE): sensor.sensor_schema( + UNIT_VOLT, ICON_EMPTY, 2, DEVICE_CLASS_VOLTAGE + ), + cv.Optional(CONF_SHUNT_VOLTAGE): sensor.sensor_schema( + UNIT_VOLT, ICON_EMPTY, 2, DEVICE_CLASS_VOLTAGE + ), + cv.Optional(CONF_CURRENT): sensor.sensor_schema( + UNIT_AMPERE, ICON_EMPTY, 3, DEVICE_CLASS_CURRENT + ), + cv.Optional(CONF_POWER): sensor.sensor_schema( + UNIT_WATT, ICON_EMPTY, 2, DEVICE_CLASS_POWER + ), + cv.Optional(CONF_SHUNT_RESISTANCE, default=0.1): cv.All( + cv.resistance, cv.Range(min=0.0) + ), + cv.Optional(CONF_MAX_CURRENT, default=3.2): cv.All( + cv.current, cv.Range(min=0.0) + ), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x40)) +) def to_code(config): diff --git a/esphome/components/ina3221/sensor.py b/esphome/components/ina3221/sensor.py index e71f9f5d9b..c055e149b7 100644 --- a/esphome/components/ina3221/sensor.py +++ b/esphome/components/ina3221/sensor.py @@ -1,37 +1,65 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import CONF_BUS_VOLTAGE, CONF_CURRENT, CONF_ID, CONF_POWER, \ - CONF_SHUNT_RESISTANCE, CONF_SHUNT_VOLTAGE, DEVICE_CLASS_VOLTAGE, DEVICE_CLASS_CURRENT, \ - DEVICE_CLASS_POWER, ICON_EMPTY, UNIT_VOLT, UNIT_AMPERE, UNIT_WATT +from esphome.const import ( + CONF_BUS_VOLTAGE, + CONF_CURRENT, + CONF_ID, + CONF_POWER, + CONF_SHUNT_RESISTANCE, + CONF_SHUNT_VOLTAGE, + DEVICE_CLASS_VOLTAGE, + DEVICE_CLASS_CURRENT, + DEVICE_CLASS_POWER, + ICON_EMPTY, + UNIT_VOLT, + UNIT_AMPERE, + UNIT_WATT, +) -DEPENDENCIES = ['i2c'] +DEPENDENCIES = ["i2c"] -CONF_CHANNEL_1 = 'channel_1' -CONF_CHANNEL_2 = 'channel_2' -CONF_CHANNEL_3 = 'channel_3' +CONF_CHANNEL_1 = "channel_1" +CONF_CHANNEL_2 = "channel_2" +CONF_CHANNEL_3 = "channel_3" -ina3221_ns = cg.esphome_ns.namespace('ina3221') -INA3221Component = ina3221_ns.class_('INA3221Component', cg.PollingComponent, i2c.I2CDevice) +ina3221_ns = cg.esphome_ns.namespace("ina3221") +INA3221Component = ina3221_ns.class_( + "INA3221Component", cg.PollingComponent, i2c.I2CDevice +) -INA3221_CHANNEL_SCHEMA = cv.Schema({ - cv.Optional(CONF_BUS_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_EMPTY, 2, - DEVICE_CLASS_VOLTAGE), - cv.Optional(CONF_SHUNT_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_EMPTY, 2, - DEVICE_CLASS_VOLTAGE), - cv.Optional(CONF_CURRENT): sensor.sensor_schema(UNIT_AMPERE, ICON_EMPTY, 2, - DEVICE_CLASS_CURRENT), - cv.Optional(CONF_POWER): sensor.sensor_schema(UNIT_WATT, ICON_EMPTY, 2, DEVICE_CLASS_POWER), - cv.Optional(CONF_SHUNT_RESISTANCE, default=0.1): cv.All(cv.resistance, - cv.Range(min=0.0, max=32.0)), -}) +INA3221_CHANNEL_SCHEMA = cv.Schema( + { + cv.Optional(CONF_BUS_VOLTAGE): sensor.sensor_schema( + UNIT_VOLT, ICON_EMPTY, 2, DEVICE_CLASS_VOLTAGE + ), + cv.Optional(CONF_SHUNT_VOLTAGE): sensor.sensor_schema( + UNIT_VOLT, ICON_EMPTY, 2, DEVICE_CLASS_VOLTAGE + ), + cv.Optional(CONF_CURRENT): sensor.sensor_schema( + UNIT_AMPERE, ICON_EMPTY, 2, DEVICE_CLASS_CURRENT + ), + cv.Optional(CONF_POWER): sensor.sensor_schema( + UNIT_WATT, ICON_EMPTY, 2, DEVICE_CLASS_POWER + ), + cv.Optional(CONF_SHUNT_RESISTANCE, default=0.1): cv.All( + cv.resistance, cv.Range(min=0.0, max=32.0) + ), + } +) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(INA3221Component), - cv.Optional(CONF_CHANNEL_1): INA3221_CHANNEL_SCHEMA, - cv.Optional(CONF_CHANNEL_2): INA3221_CHANNEL_SCHEMA, - cv.Optional(CONF_CHANNEL_3): INA3221_CHANNEL_SCHEMA, -}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x40)) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(INA3221Component), + cv.Optional(CONF_CHANNEL_1): INA3221_CHANNEL_SCHEMA, + cv.Optional(CONF_CHANNEL_2): INA3221_CHANNEL_SCHEMA, + cv.Optional(CONF_CHANNEL_3): INA3221_CHANNEL_SCHEMA, + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x40)) +) def to_code(config): diff --git a/esphome/components/inkbird_ibsth1_mini/sensor.py b/esphome/components/inkbird_ibsth1_mini/sensor.py index 57a16f2883..c67acb8595 100644 --- a/esphome/components/inkbird_ibsth1_mini/sensor.py +++ b/esphome/components/inkbird_ibsth1_mini/sensor.py @@ -1,27 +1,47 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, esp32_ble_tracker -from esphome.const import CONF_BATTERY_LEVEL, CONF_HUMIDITY, CONF_MAC_ADDRESS, CONF_TEMPERATURE, \ - DEVICE_CLASS_BATTERY, DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_TEMPERATURE, ICON_EMPTY, \ - UNIT_CELSIUS, UNIT_PERCENT, CONF_ID +from esphome.const import ( + CONF_BATTERY_LEVEL, + CONF_HUMIDITY, + CONF_MAC_ADDRESS, + CONF_TEMPERATURE, + DEVICE_CLASS_BATTERY, + DEVICE_CLASS_HUMIDITY, + DEVICE_CLASS_TEMPERATURE, + ICON_EMPTY, + UNIT_CELSIUS, + UNIT_PERCENT, + CONF_ID, +) -CODEOWNERS = ['@fkirill'] -DEPENDENCIES = ['esp32_ble_tracker'] +CODEOWNERS = ["@fkirill"] +DEPENDENCIES = ["esp32_ble_tracker"] -inkbird_ibsth1_mini_ns = cg.esphome_ns.namespace('inkbird_ibsth1_mini') +inkbird_ibsth1_mini_ns = cg.esphome_ns.namespace("inkbird_ibsth1_mini") InkbirdUBSTH1_MINI = inkbird_ibsth1_mini_ns.class_( - 'InkbirdIBSTH1_MINI', esp32_ble_tracker.ESPBTDeviceListener, cg.Component) + "InkbirdIBSTH1_MINI", esp32_ble_tracker.ESPBTDeviceListener, cg.Component +) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(InkbirdUBSTH1_MINI), - cv.Required(CONF_MAC_ADDRESS): cv.mac_address, - cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, - DEVICE_CLASS_TEMPERATURE), - cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 1, - DEVICE_CLASS_HUMIDITY), - cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 0, - DEVICE_CLASS_BATTERY), -}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(InkbirdUBSTH1_MINI), + cv.Required(CONF_MAC_ADDRESS): cv.mac_address, + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( + UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE + ), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema( + UNIT_PERCENT, ICON_EMPTY, 1, DEVICE_CLASS_HUMIDITY + ), + cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema( + UNIT_PERCENT, ICON_EMPTY, 0, DEVICE_CLASS_BATTERY + ), + } + ) + .extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA) + .extend(cv.COMPONENT_SCHEMA) +) def to_code(config): diff --git a/esphome/components/inkplate6/__init__.py b/esphome/components/inkplate6/__init__.py index ba7653988b..b1de57df8f 100644 --- a/esphome/components/inkplate6/__init__.py +++ b/esphome/components/inkplate6/__init__.py @@ -1 +1 @@ -CODEOWNERS = ['@jesserockz'] +CODEOWNERS = ["@jesserockz"] diff --git a/esphome/components/inkplate6/display.py b/esphome/components/inkplate6/display.py index 4f35901bd4..cd2b5ac51b 100644 --- a/esphome/components/inkplate6/display.py +++ b/esphome/components/inkplate6/display.py @@ -2,67 +2,94 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins from esphome.components import display, i2c -from esphome.const import CONF_FULL_UPDATE_EVERY, CONF_ID, CONF_LAMBDA, CONF_PAGES, \ - CONF_WAKEUP_PIN, ESP_PLATFORM_ESP32 +from esphome.const import ( + CONF_FULL_UPDATE_EVERY, + CONF_ID, + CONF_LAMBDA, + CONF_PAGES, + CONF_WAKEUP_PIN, + ESP_PLATFORM_ESP32, +) -DEPENDENCIES = ['i2c'] +DEPENDENCIES = ["i2c"] ESP_PLATFORMS = [ESP_PLATFORM_ESP32] -CONF_DISPLAY_DATA_0_PIN = 'display_data_0_pin' -CONF_DISPLAY_DATA_1_PIN = 'display_data_1_pin' -CONF_DISPLAY_DATA_2_PIN = 'display_data_2_pin' -CONF_DISPLAY_DATA_3_PIN = 'display_data_3_pin' -CONF_DISPLAY_DATA_4_PIN = 'display_data_4_pin' -CONF_DISPLAY_DATA_5_PIN = 'display_data_5_pin' -CONF_DISPLAY_DATA_6_PIN = 'display_data_6_pin' -CONF_DISPLAY_DATA_7_PIN = 'display_data_7_pin' +CONF_DISPLAY_DATA_0_PIN = "display_data_0_pin" +CONF_DISPLAY_DATA_1_PIN = "display_data_1_pin" +CONF_DISPLAY_DATA_2_PIN = "display_data_2_pin" +CONF_DISPLAY_DATA_3_PIN = "display_data_3_pin" +CONF_DISPLAY_DATA_4_PIN = "display_data_4_pin" +CONF_DISPLAY_DATA_5_PIN = "display_data_5_pin" +CONF_DISPLAY_DATA_6_PIN = "display_data_6_pin" +CONF_DISPLAY_DATA_7_PIN = "display_data_7_pin" -CONF_CL_PIN = 'cl_pin' -CONF_CKV_PIN = 'ckv_pin' -CONF_GREYSCALE = 'greyscale' -CONF_GMOD_PIN = 'gmod_pin' -CONF_GPIO0_ENABLE_PIN = 'gpio0_enable_pin' -CONF_LE_PIN = 'le_pin' -CONF_OE_PIN = 'oe_pin' -CONF_PARTIAL_UPDATING = 'partial_updating' -CONF_POWERUP_PIN = 'powerup_pin' -CONF_SPH_PIN = 'sph_pin' -CONF_SPV_PIN = 'spv_pin' -CONF_VCOM_PIN = 'vcom_pin' +CONF_CL_PIN = "cl_pin" +CONF_CKV_PIN = "ckv_pin" +CONF_GREYSCALE = "greyscale" +CONF_GMOD_PIN = "gmod_pin" +CONF_GPIO0_ENABLE_PIN = "gpio0_enable_pin" +CONF_LE_PIN = "le_pin" +CONF_OE_PIN = "oe_pin" +CONF_PARTIAL_UPDATING = "partial_updating" +CONF_POWERUP_PIN = "powerup_pin" +CONF_SPH_PIN = "sph_pin" +CONF_SPV_PIN = "spv_pin" +CONF_VCOM_PIN = "vcom_pin" -inkplate6_ns = cg.esphome_ns.namespace('inkplate6') -Inkplate6 = inkplate6_ns.class_('Inkplate6', cg.PollingComponent, i2c.I2CDevice, - display.DisplayBuffer) +inkplate6_ns = cg.esphome_ns.namespace("inkplate6") +Inkplate6 = inkplate6_ns.class_( + "Inkplate6", cg.PollingComponent, i2c.I2CDevice, display.DisplayBuffer +) -CONFIG_SCHEMA = cv.All(display.FULL_DISPLAY_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(Inkplate6), - cv.Optional(CONF_GREYSCALE, default=False): cv.boolean, - cv.Optional(CONF_PARTIAL_UPDATING, default=True): cv.boolean, - cv.Optional(CONF_FULL_UPDATE_EVERY, default=10): cv.uint32_t, - # Control pins - cv.Required(CONF_CKV_PIN): pins.gpio_output_pin_schema, - cv.Required(CONF_GMOD_PIN): pins.gpio_output_pin_schema, - cv.Required(CONF_GPIO0_ENABLE_PIN): pins.gpio_output_pin_schema, - cv.Required(CONF_OE_PIN): pins.gpio_output_pin_schema, - cv.Required(CONF_POWERUP_PIN): pins.gpio_output_pin_schema, - cv.Required(CONF_SPH_PIN): pins.gpio_output_pin_schema, - cv.Required(CONF_SPV_PIN): pins.gpio_output_pin_schema, - cv.Required(CONF_VCOM_PIN): pins.gpio_output_pin_schema, - cv.Required(CONF_WAKEUP_PIN): pins.gpio_output_pin_schema, - cv.Optional(CONF_CL_PIN, default=0): pins.internal_gpio_output_pin_schema, - cv.Optional(CONF_LE_PIN, default=2): pins.internal_gpio_output_pin_schema, - # Data pins - cv.Optional(CONF_DISPLAY_DATA_0_PIN, default=4): pins.internal_gpio_output_pin_schema, - cv.Optional(CONF_DISPLAY_DATA_1_PIN, default=5): pins.internal_gpio_output_pin_schema, - cv.Optional(CONF_DISPLAY_DATA_2_PIN, default=18): pins.internal_gpio_output_pin_schema, - cv.Optional(CONF_DISPLAY_DATA_3_PIN, default=19): pins.internal_gpio_output_pin_schema, - cv.Optional(CONF_DISPLAY_DATA_4_PIN, default=23): pins.internal_gpio_output_pin_schema, - cv.Optional(CONF_DISPLAY_DATA_5_PIN, default=25): pins.internal_gpio_output_pin_schema, - cv.Optional(CONF_DISPLAY_DATA_6_PIN, default=26): pins.internal_gpio_output_pin_schema, - cv.Optional(CONF_DISPLAY_DATA_7_PIN, default=27): pins.internal_gpio_output_pin_schema, -}).extend(cv.polling_component_schema('5s').extend(i2c.i2c_device_schema(0x48))), - cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA)) +CONFIG_SCHEMA = cv.All( + display.FULL_DISPLAY_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(Inkplate6), + cv.Optional(CONF_GREYSCALE, default=False): cv.boolean, + cv.Optional(CONF_PARTIAL_UPDATING, default=True): cv.boolean, + cv.Optional(CONF_FULL_UPDATE_EVERY, default=10): cv.uint32_t, + # Control pins + cv.Required(CONF_CKV_PIN): pins.gpio_output_pin_schema, + cv.Required(CONF_GMOD_PIN): pins.gpio_output_pin_schema, + cv.Required(CONF_GPIO0_ENABLE_PIN): pins.gpio_output_pin_schema, + cv.Required(CONF_OE_PIN): pins.gpio_output_pin_schema, + cv.Required(CONF_POWERUP_PIN): pins.gpio_output_pin_schema, + cv.Required(CONF_SPH_PIN): pins.gpio_output_pin_schema, + cv.Required(CONF_SPV_PIN): pins.gpio_output_pin_schema, + cv.Required(CONF_VCOM_PIN): pins.gpio_output_pin_schema, + cv.Required(CONF_WAKEUP_PIN): pins.gpio_output_pin_schema, + cv.Optional(CONF_CL_PIN, default=0): pins.internal_gpio_output_pin_schema, + cv.Optional(CONF_LE_PIN, default=2): pins.internal_gpio_output_pin_schema, + # Data pins + cv.Optional( + CONF_DISPLAY_DATA_0_PIN, default=4 + ): pins.internal_gpio_output_pin_schema, + cv.Optional( + CONF_DISPLAY_DATA_1_PIN, default=5 + ): pins.internal_gpio_output_pin_schema, + cv.Optional( + CONF_DISPLAY_DATA_2_PIN, default=18 + ): pins.internal_gpio_output_pin_schema, + cv.Optional( + CONF_DISPLAY_DATA_3_PIN, default=19 + ): pins.internal_gpio_output_pin_schema, + cv.Optional( + CONF_DISPLAY_DATA_4_PIN, default=23 + ): pins.internal_gpio_output_pin_schema, + cv.Optional( + CONF_DISPLAY_DATA_5_PIN, default=25 + ): pins.internal_gpio_output_pin_schema, + cv.Optional( + CONF_DISPLAY_DATA_6_PIN, default=26 + ): pins.internal_gpio_output_pin_schema, + cv.Optional( + CONF_DISPLAY_DATA_7_PIN, default=27 + ): pins.internal_gpio_output_pin_schema, + } + ).extend(cv.polling_component_schema("5s").extend(i2c.i2c_device_schema(0x48))), + cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA), +) def to_code(config): @@ -73,8 +100,9 @@ def to_code(config): yield i2c.register_i2c_device(var, config) if CONF_LAMBDA in config: - lambda_ = yield cg.process_lambda(config[CONF_LAMBDA], [(display.DisplayBufferRef, 'it')], - return_type=cg.void) + lambda_ = yield cg.process_lambda( + config[CONF_LAMBDA], [(display.DisplayBufferRef, "it")], return_type=cg.void + ) cg.add(var.set_writer(lambda_)) cg.add(var.set_greyscale(config[CONF_GREYSCALE])) @@ -138,4 +166,4 @@ def to_code(config): display_data_7 = yield cg.gpio_pin_expression(config[CONF_DISPLAY_DATA_7_PIN]) cg.add(var.set_display_data_7_pin(display_data_7)) - cg.add_build_flag('-DBOARD_HAS_PSRAM') + cg.add_build_flag("-DBOARD_HAS_PSRAM") diff --git a/esphome/components/integration/__init__.py b/esphome/components/integration/__init__.py index 6f14e10033..71a87b6ae5 100644 --- a/esphome/components/integration/__init__.py +++ b/esphome/components/integration/__init__.py @@ -1 +1 @@ -CODEOWNERS = ['@OttoWinter'] +CODEOWNERS = ["@OttoWinter"] diff --git a/esphome/components/integration/sensor.py b/esphome/components/integration/sensor.py index a354ab7433..81b588610e 100644 --- a/esphome/components/integration/sensor.py +++ b/esphome/components/integration/sensor.py @@ -4,36 +4,41 @@ from esphome import automation from esphome.components import sensor from esphome.const import CONF_ID, CONF_SENSOR, CONF_RESTORE -integration_ns = cg.esphome_ns.namespace('integration') -IntegrationSensor = integration_ns.class_('IntegrationSensor', sensor.Sensor, cg.Component) -ResetAction = integration_ns.class_('ResetAction', automation.Action) +integration_ns = cg.esphome_ns.namespace("integration") +IntegrationSensor = integration_ns.class_( + "IntegrationSensor", sensor.Sensor, cg.Component +) +ResetAction = integration_ns.class_("ResetAction", automation.Action) -IntegrationSensorTime = integration_ns.enum('IntegrationSensorTime') +IntegrationSensorTime = integration_ns.enum("IntegrationSensorTime") INTEGRATION_TIMES = { - 'ms': IntegrationSensorTime.INTEGRATION_SENSOR_TIME_MILLISECOND, - 's': IntegrationSensorTime.INTEGRATION_SENSOR_TIME_SECOND, - 'min': IntegrationSensorTime.INTEGRATION_SENSOR_TIME_MINUTE, - 'h': IntegrationSensorTime.INTEGRATION_SENSOR_TIME_HOUR, - 'd': IntegrationSensorTime.INTEGRATION_SENSOR_TIME_DAY, + "ms": IntegrationSensorTime.INTEGRATION_SENSOR_TIME_MILLISECOND, + "s": IntegrationSensorTime.INTEGRATION_SENSOR_TIME_SECOND, + "min": IntegrationSensorTime.INTEGRATION_SENSOR_TIME_MINUTE, + "h": IntegrationSensorTime.INTEGRATION_SENSOR_TIME_HOUR, + "d": IntegrationSensorTime.INTEGRATION_SENSOR_TIME_DAY, } -IntegrationMethod = integration_ns.enum('IntegrationMethod') +IntegrationMethod = integration_ns.enum("IntegrationMethod") INTEGRATION_METHODS = { - 'trapezoid': IntegrationMethod.INTEGRATION_METHOD_TRAPEZOID, - 'left': IntegrationMethod.INTEGRATION_METHOD_LEFT, - 'right': IntegrationMethod.INTEGRATION_METHOD_RIGHT, + "trapezoid": IntegrationMethod.INTEGRATION_METHOD_TRAPEZOID, + "left": IntegrationMethod.INTEGRATION_METHOD_LEFT, + "right": IntegrationMethod.INTEGRATION_METHOD_RIGHT, } -CONF_TIME_UNIT = 'time_unit' -CONF_INTEGRATION_METHOD = 'integration_method' +CONF_TIME_UNIT = "time_unit" +CONF_INTEGRATION_METHOD = "integration_method" -CONFIG_SCHEMA = sensor.SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(IntegrationSensor), - cv.Required(CONF_SENSOR): cv.use_id(sensor.Sensor), - cv.Required(CONF_TIME_UNIT): cv.enum(INTEGRATION_TIMES, lower=True), - cv.Optional(CONF_INTEGRATION_METHOD, default='trapezoid'): - cv.enum(INTEGRATION_METHODS, lower=True), - cv.Optional(CONF_RESTORE, default=False): cv.boolean, -}).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = sensor.SENSOR_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(IntegrationSensor), + cv.Required(CONF_SENSOR): cv.use_id(sensor.Sensor), + cv.Required(CONF_TIME_UNIT): cv.enum(INTEGRATION_TIMES, lower=True), + cv.Optional(CONF_INTEGRATION_METHOD, default="trapezoid"): cv.enum( + INTEGRATION_METHODS, lower=True + ), + cv.Optional(CONF_RESTORE, default=False): cv.boolean, + } +).extend(cv.COMPONENT_SCHEMA) def to_code(config): @@ -49,9 +54,15 @@ def to_code(config): cg.add(var.set_restore(config[CONF_RESTORE])) -@automation.register_action('sensor.integration.reset', ResetAction, automation.maybe_simple_id({ - cv.Required(CONF_ID): cv.use_id(IntegrationSensor), -})) +@automation.register_action( + "sensor.integration.reset", + ResetAction, + automation.maybe_simple_id( + { + cv.Required(CONF_ID): cv.use_id(IntegrationSensor), + } + ), +) def sensor_integration_reset_to_code(config, action_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) yield cg.new_Pvariable(action_id, template_arg, paren) diff --git a/esphome/components/interval/__init__.py b/esphome/components/interval/__init__.py index be37526ad1..7d04a9e9ab 100644 --- a/esphome/components/interval/__init__.py +++ b/esphome/components/interval/__init__.py @@ -3,15 +3,20 @@ import esphome.config_validation as cv from esphome import automation from esphome.const import CONF_ID, CONF_INTERVAL -CODEOWNERS = ['@esphome/core'] -interval_ns = cg.esphome_ns.namespace('interval') -IntervalTrigger = interval_ns.class_('IntervalTrigger', automation.Trigger.template(), - cg.PollingComponent) +CODEOWNERS = ["@esphome/core"] +interval_ns = cg.esphome_ns.namespace("interval") +IntervalTrigger = interval_ns.class_( + "IntervalTrigger", automation.Trigger.template(), cg.PollingComponent +) -CONFIG_SCHEMA = automation.validate_automation(cv.Schema({ - cv.GenerateID(): cv.declare_id(IntervalTrigger), - cv.Required(CONF_INTERVAL): cv.positive_time_period_milliseconds, -}).extend(cv.COMPONENT_SCHEMA)) +CONFIG_SCHEMA = automation.validate_automation( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(IntervalTrigger), + cv.Required(CONF_INTERVAL): cv.positive_time_period_milliseconds, + } + ).extend(cv.COMPONENT_SCHEMA) +) def to_code(config): diff --git a/esphome/components/json/__init__.py b/esphome/components/json/__init__.py index 63bc16dfa2..f73fcb53fb 100644 --- a/esphome/components/json/__init__.py +++ b/esphome/components/json/__init__.py @@ -1,12 +1,12 @@ import esphome.codegen as cg from esphome.core import coroutine_with_priority -CODEOWNERS = ['@OttoWinter'] -json_ns = cg.esphome_ns.namespace('json') +CODEOWNERS = ["@OttoWinter"] +json_ns = cg.esphome_ns.namespace("json") @coroutine_with_priority(1.0) def to_code(config): - cg.add_library('ArduinoJson-esphomelib', '5.13.3') - cg.add_define('USE_JSON') + cg.add_library("ArduinoJson-esphomelib", "5.13.3") + cg.add_define("USE_JSON") cg.add_global(json_ns.using) diff --git a/esphome/components/lcd_base/__init__.py b/esphome/components/lcd_base/__init__.py index bff194578c..c577d7f8be 100644 --- a/esphome/components/lcd_base/__init__.py +++ b/esphome/components/lcd_base/__init__.py @@ -4,8 +4,8 @@ from esphome.components import display from esphome.const import CONF_DIMENSIONS from esphome.core import coroutine -lcd_base_ns = cg.esphome_ns.namespace('lcd_base') -LCDDisplay = lcd_base_ns.class_('LCDDisplay', cg.PollingComponent) +lcd_base_ns = cg.esphome_ns.namespace("lcd_base") +LCDDisplay = lcd_base_ns.class_("LCDDisplay", cg.PollingComponent) def validate_lcd_dimensions(value): @@ -17,9 +17,11 @@ def validate_lcd_dimensions(value): return value -LCD_SCHEMA = display.BASIC_DISPLAY_SCHEMA.extend({ - cv.Required(CONF_DIMENSIONS): validate_lcd_dimensions, -}).extend(cv.polling_component_schema('1s')) +LCD_SCHEMA = display.BASIC_DISPLAY_SCHEMA.extend( + { + cv.Required(CONF_DIMENSIONS): validate_lcd_dimensions, + } +).extend(cv.polling_component_schema("1s")) @coroutine diff --git a/esphome/components/lcd_gpio/display.py b/esphome/components/lcd_gpio/display.py index 91498d59c9..28c6df2efd 100644 --- a/esphome/components/lcd_gpio/display.py +++ b/esphome/components/lcd_gpio/display.py @@ -2,29 +2,41 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins from esphome.components import lcd_base -from esphome.const import CONF_DATA_PINS, CONF_ENABLE_PIN, CONF_RS_PIN, CONF_RW_PIN, CONF_ID, \ - CONF_LAMBDA +from esphome.const import ( + CONF_DATA_PINS, + CONF_ENABLE_PIN, + CONF_RS_PIN, + CONF_RW_PIN, + CONF_ID, + CONF_LAMBDA, +) -AUTO_LOAD = ['lcd_base'] +AUTO_LOAD = ["lcd_base"] -lcd_gpio_ns = cg.esphome_ns.namespace('lcd_gpio') -GPIOLCDDisplay = lcd_gpio_ns.class_('GPIOLCDDisplay', lcd_base.LCDDisplay) +lcd_gpio_ns = cg.esphome_ns.namespace("lcd_gpio") +GPIOLCDDisplay = lcd_gpio_ns.class_("GPIOLCDDisplay", lcd_base.LCDDisplay) def validate_pin_length(value): if len(value) != 4 and len(value) != 8: - raise cv.Invalid("LCD Displays can either operate in 4-pin or 8-pin mode," - "not {}-pin mode".format(len(value))) + raise cv.Invalid( + "LCD Displays can either operate in 4-pin or 8-pin mode," + "not {}-pin mode".format(len(value)) + ) return value -CONFIG_SCHEMA = lcd_base.LCD_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(GPIOLCDDisplay), - cv.Required(CONF_DATA_PINS): cv.All([pins.gpio_output_pin_schema], validate_pin_length), - cv.Required(CONF_ENABLE_PIN): pins.gpio_output_pin_schema, - cv.Required(CONF_RS_PIN): pins.gpio_output_pin_schema, - cv.Optional(CONF_RW_PIN): pins.gpio_output_pin_schema, -}) +CONFIG_SCHEMA = lcd_base.LCD_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(GPIOLCDDisplay), + cv.Required(CONF_DATA_PINS): cv.All( + [pins.gpio_output_pin_schema], validate_pin_length + ), + cv.Required(CONF_ENABLE_PIN): pins.gpio_output_pin_schema, + cv.Required(CONF_RS_PIN): pins.gpio_output_pin_schema, + cv.Optional(CONF_RW_PIN): pins.gpio_output_pin_schema, + } +) def to_code(config): @@ -45,7 +57,9 @@ def to_code(config): cg.add(var.set_rw_pin(rw)) if CONF_LAMBDA in config: - lambda_ = yield cg.process_lambda(config[CONF_LAMBDA], - [(GPIOLCDDisplay.operator('ref'), 'it')], - return_type=cg.void) + lambda_ = yield cg.process_lambda( + config[CONF_LAMBDA], + [(GPIOLCDDisplay.operator("ref"), "it")], + return_type=cg.void, + ) cg.add(var.set_writer(lambda_)) diff --git a/esphome/components/lcd_pcf8574/display.py b/esphome/components/lcd_pcf8574/display.py index 2bbb3a2f7b..6499a8369e 100644 --- a/esphome/components/lcd_pcf8574/display.py +++ b/esphome/components/lcd_pcf8574/display.py @@ -3,15 +3,19 @@ import esphome.config_validation as cv from esphome.components import lcd_base, i2c from esphome.const import CONF_ID, CONF_LAMBDA -DEPENDENCIES = ['i2c'] -AUTO_LOAD = ['lcd_base'] +DEPENDENCIES = ["i2c"] +AUTO_LOAD = ["lcd_base"] -lcd_pcf8574_ns = cg.esphome_ns.namespace('lcd_pcf8574') -PCF8574LCDDisplay = lcd_pcf8574_ns.class_('PCF8574LCDDisplay', lcd_base.LCDDisplay, i2c.I2CDevice) +lcd_pcf8574_ns = cg.esphome_ns.namespace("lcd_pcf8574") +PCF8574LCDDisplay = lcd_pcf8574_ns.class_( + "PCF8574LCDDisplay", lcd_base.LCDDisplay, i2c.I2CDevice +) -CONFIG_SCHEMA = lcd_base.LCD_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(PCF8574LCDDisplay), -}).extend(i2c.i2c_device_schema(0x3F)) +CONFIG_SCHEMA = lcd_base.LCD_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(PCF8574LCDDisplay), + } +).extend(i2c.i2c_device_schema(0x3F)) def to_code(config): @@ -20,7 +24,9 @@ def to_code(config): yield i2c.register_i2c_device(var, config) if CONF_LAMBDA in config: - lambda_ = yield cg.process_lambda(config[CONF_LAMBDA], - [(PCF8574LCDDisplay.operator('ref'), 'it')], - return_type=cg.void) + lambda_ = yield cg.process_lambda( + config[CONF_LAMBDA], + [(PCF8574LCDDisplay.operator("ref"), "it")], + return_type=cg.void, + ) cg.add(var.set_writer(lambda_)) diff --git a/esphome/components/ledc/__init__.py b/esphome/components/ledc/__init__.py index 6f14e10033..71a87b6ae5 100644 --- a/esphome/components/ledc/__init__.py +++ b/esphome/components/ledc/__init__.py @@ -1 +1 @@ -CODEOWNERS = ['@OttoWinter'] +CODEOWNERS = ["@OttoWinter"] diff --git a/esphome/components/ledc/output.py b/esphome/components/ledc/output.py index b608e9bbf7..df746fff4f 100644 --- a/esphome/components/ledc/output.py +++ b/esphome/components/ledc/output.py @@ -2,19 +2,25 @@ from esphome import pins, automation from esphome.components import output import esphome.config_validation as cv import esphome.codegen as cg -from esphome.const import CONF_BIT_DEPTH, CONF_CHANNEL, CONF_FREQUENCY, \ - CONF_ID, CONF_PIN, ESP_PLATFORM_ESP32 +from esphome.const import ( + CONF_BIT_DEPTH, + CONF_CHANNEL, + CONF_FREQUENCY, + CONF_ID, + CONF_PIN, + ESP_PLATFORM_ESP32, +) ESP_PLATFORMS = [ESP_PLATFORM_ESP32] def calc_max_frequency(bit_depth): - return 80e6 / (2**bit_depth) + return 80e6 / (2 ** bit_depth) def calc_min_frequency(bit_depth): - max_div_num = ((2**20) - 1) / 256.0 - return 80e6 / (max_div_num * (2**bit_depth)) + max_div_num = ((2 ** 20) - 1) / 256.0 + return 80e6 / (max_div_num * (2 ** bit_depth)) def validate_frequency(value): @@ -22,27 +28,34 @@ def validate_frequency(value): min_freq = calc_min_frequency(20) max_freq = calc_max_frequency(1) if value < min_freq: - raise cv.Invalid("This frequency setting is not possible, please choose a higher " - "frequency (at least {}Hz)".format(int(min_freq))) + raise cv.Invalid( + "This frequency setting is not possible, please choose a higher " + "frequency (at least {}Hz)".format(int(min_freq)) + ) if value > max_freq: - raise cv.Invalid("This frequency setting is not possible, please choose a lower " - "frequency (at most {}Hz)".format(int(max_freq))) + raise cv.Invalid( + "This frequency setting is not possible, please choose a lower " + "frequency (at most {}Hz)".format(int(max_freq)) + ) return value -ledc_ns = cg.esphome_ns.namespace('ledc') -LEDCOutput = ledc_ns.class_('LEDCOutput', output.FloatOutput, cg.Component) -SetFrequencyAction = ledc_ns.class_('SetFrequencyAction', automation.Action) +ledc_ns = cg.esphome_ns.namespace("ledc") +LEDCOutput = ledc_ns.class_("LEDCOutput", output.FloatOutput, cg.Component) +SetFrequencyAction = ledc_ns.class_("SetFrequencyAction", automation.Action) -CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend({ - cv.Required(CONF_ID): cv.declare_id(LEDCOutput), - cv.Required(CONF_PIN): pins.internal_gpio_output_pin_schema, - cv.Optional(CONF_FREQUENCY, default='1kHz'): cv.frequency, - cv.Optional(CONF_CHANNEL): cv.int_range(min=0, max=15), - - cv.Optional(CONF_BIT_DEPTH): cv.invalid("The bit_depth option has been removed in v1.14, the " - "best bit depth is now automatically calculated."), -}).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend( + { + cv.Required(CONF_ID): cv.declare_id(LEDCOutput), + cv.Required(CONF_PIN): pins.internal_gpio_output_pin_schema, + cv.Optional(CONF_FREQUENCY, default="1kHz"): cv.frequency, + cv.Optional(CONF_CHANNEL): cv.int_range(min=0, max=15), + cv.Optional(CONF_BIT_DEPTH): cv.invalid( + "The bit_depth option has been removed in v1.14, the " + "best bit depth is now automatically calculated." + ), + } +).extend(cv.COMPONENT_SCHEMA) def to_code(config): @@ -55,10 +68,16 @@ def to_code(config): cg.add(var.set_frequency(config[CONF_FREQUENCY])) -@automation.register_action('output.ledc.set_frequency', SetFrequencyAction, cv.Schema({ - cv.Required(CONF_ID): cv.use_id(LEDCOutput), - cv.Required(CONF_FREQUENCY): cv.templatable(validate_frequency), -})) +@automation.register_action( + "output.ledc.set_frequency", + SetFrequencyAction, + cv.Schema( + { + cv.Required(CONF_ID): cv.use_id(LEDCOutput), + cv.Required(CONF_FREQUENCY): cv.templatable(validate_frequency), + } + ), +) def ledc_set_frequency_to_code(config, action_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) var = cg.new_Pvariable(action_id, template_arg, paren) diff --git a/esphome/components/light/__init__.py b/esphome/components/light/__init__.py index 9d58fa47bf..034dbdaf34 100644 --- a/esphome/components/light/__init__.py +++ b/esphome/components/light/__init__.py @@ -2,62 +2,104 @@ import esphome.codegen as cg import esphome.config_validation as cv import esphome.automation as auto from esphome.components import mqtt, power_supply -from esphome.const import CONF_COLOR_CORRECT, \ - CONF_DEFAULT_TRANSITION_LENGTH, CONF_EFFECTS, CONF_GAMMA_CORRECT, CONF_ID, \ - CONF_INTERNAL, CONF_NAME, CONF_MQTT_ID, CONF_POWER_SUPPLY, CONF_RESTORE_MODE, \ - CONF_ON_TURN_OFF, CONF_ON_TURN_ON, CONF_TRIGGER_ID +from esphome.const import ( + CONF_COLOR_CORRECT, + CONF_DEFAULT_TRANSITION_LENGTH, + CONF_EFFECTS, + CONF_GAMMA_CORRECT, + CONF_ID, + CONF_INTERNAL, + CONF_NAME, + CONF_MQTT_ID, + CONF_POWER_SUPPLY, + CONF_RESTORE_MODE, + CONF_ON_TURN_OFF, + CONF_ON_TURN_ON, + CONF_TRIGGER_ID, +) from esphome.core import coroutine, coroutine_with_priority from .automation import light_control_to_code # noqa -from .effects import validate_effects, BINARY_EFFECTS, \ - MONOCHROMATIC_EFFECTS, RGB_EFFECTS, ADDRESSABLE_EFFECTS, EFFECTS_REGISTRY +from .effects import ( + validate_effects, + BINARY_EFFECTS, + MONOCHROMATIC_EFFECTS, + RGB_EFFECTS, + ADDRESSABLE_EFFECTS, + EFFECTS_REGISTRY, +) from .types import ( # noqa - LightState, AddressableLightState, light_ns, LightOutput, AddressableLight, \ - LightTurnOnTrigger, LightTurnOffTrigger) + LightState, + AddressableLightState, + light_ns, + LightOutput, + AddressableLight, + LightTurnOnTrigger, + LightTurnOffTrigger, +) -CODEOWNERS = ['@esphome/core'] +CODEOWNERS = ["@esphome/core"] IS_PLATFORM_COMPONENT = True -LightRestoreMode = light_ns.enum('LightRestoreMode') +LightRestoreMode = light_ns.enum("LightRestoreMode") RESTORE_MODES = { - 'RESTORE_DEFAULT_OFF': LightRestoreMode.LIGHT_RESTORE_DEFAULT_OFF, - 'RESTORE_DEFAULT_ON': LightRestoreMode.LIGHT_RESTORE_DEFAULT_ON, - 'ALWAYS_OFF': LightRestoreMode.LIGHT_ALWAYS_OFF, - 'ALWAYS_ON': LightRestoreMode.LIGHT_ALWAYS_ON, + "RESTORE_DEFAULT_OFF": LightRestoreMode.LIGHT_RESTORE_DEFAULT_OFF, + "RESTORE_DEFAULT_ON": LightRestoreMode.LIGHT_RESTORE_DEFAULT_ON, + "ALWAYS_OFF": LightRestoreMode.LIGHT_ALWAYS_OFF, + "ALWAYS_ON": LightRestoreMode.LIGHT_ALWAYS_ON, } -LIGHT_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(LightState), - cv.OnlyWith(CONF_MQTT_ID, 'mqtt'): cv.declare_id(mqtt.MQTTJSONLightComponent), - cv.Optional(CONF_RESTORE_MODE, default='restore_default_off'): - cv.enum(RESTORE_MODES, upper=True, space='_'), - cv.Optional(CONF_ON_TURN_ON): auto.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(LightTurnOnTrigger), - }), - cv.Optional(CONF_ON_TURN_OFF): auto.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(LightTurnOffTrigger), - }), -}) +LIGHT_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(LightState), + cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTJSONLightComponent), + cv.Optional(CONF_RESTORE_MODE, default="restore_default_off"): cv.enum( + RESTORE_MODES, upper=True, space="_" + ), + cv.Optional(CONF_ON_TURN_ON): auto.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(LightTurnOnTrigger), + } + ), + cv.Optional(CONF_ON_TURN_OFF): auto.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(LightTurnOffTrigger), + } + ), + } +) -BINARY_LIGHT_SCHEMA = LIGHT_SCHEMA.extend({ - cv.Optional(CONF_EFFECTS): validate_effects(BINARY_EFFECTS), -}) +BINARY_LIGHT_SCHEMA = LIGHT_SCHEMA.extend( + { + cv.Optional(CONF_EFFECTS): validate_effects(BINARY_EFFECTS), + } +) -BRIGHTNESS_ONLY_LIGHT_SCHEMA = LIGHT_SCHEMA.extend({ - cv.Optional(CONF_GAMMA_CORRECT, default=2.8): cv.positive_float, - cv.Optional(CONF_DEFAULT_TRANSITION_LENGTH, default='1s'): cv.positive_time_period_milliseconds, - cv.Optional(CONF_EFFECTS): validate_effects(MONOCHROMATIC_EFFECTS), -}) +BRIGHTNESS_ONLY_LIGHT_SCHEMA = LIGHT_SCHEMA.extend( + { + cv.Optional(CONF_GAMMA_CORRECT, default=2.8): cv.positive_float, + cv.Optional( + CONF_DEFAULT_TRANSITION_LENGTH, default="1s" + ): cv.positive_time_period_milliseconds, + cv.Optional(CONF_EFFECTS): validate_effects(MONOCHROMATIC_EFFECTS), + } +) -RGB_LIGHT_SCHEMA = BRIGHTNESS_ONLY_LIGHT_SCHEMA.extend({ - cv.Optional(CONF_EFFECTS): validate_effects(RGB_EFFECTS), -}) +RGB_LIGHT_SCHEMA = BRIGHTNESS_ONLY_LIGHT_SCHEMA.extend( + { + cv.Optional(CONF_EFFECTS): validate_effects(RGB_EFFECTS), + } +) -ADDRESSABLE_LIGHT_SCHEMA = RGB_LIGHT_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(AddressableLightState), - cv.Optional(CONF_EFFECTS): validate_effects(ADDRESSABLE_EFFECTS), - cv.Optional(CONF_COLOR_CORRECT): cv.All([cv.percentage], cv.Length(min=3, max=4)), - cv.Optional(CONF_POWER_SUPPLY): cv.use_id(power_supply.PowerSupply), -}) +ADDRESSABLE_LIGHT_SCHEMA = RGB_LIGHT_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(AddressableLightState), + cv.Optional(CONF_EFFECTS): validate_effects(ADDRESSABLE_EFFECTS), + cv.Optional(CONF_COLOR_CORRECT): cv.All( + [cv.percentage], cv.Length(min=3, max=4) + ), + cv.Optional(CONF_POWER_SUPPLY): cv.use_id(power_supply.PowerSupply), + } +) @coroutine @@ -66,10 +108,16 @@ def setup_light_core_(light_var, output_var, config): if CONF_INTERNAL in config: cg.add(light_var.set_internal(config[CONF_INTERNAL])) if CONF_DEFAULT_TRANSITION_LENGTH in config: - cg.add(light_var.set_default_transition_length(config[CONF_DEFAULT_TRANSITION_LENGTH])) + cg.add( + light_var.set_default_transition_length( + config[CONF_DEFAULT_TRANSITION_LENGTH] + ) + ) if CONF_GAMMA_CORRECT in config: cg.add(light_var.set_gamma_correct(config[CONF_GAMMA_CORRECT])) - effects = yield cg.build_registry_list(EFFECTS_REGISTRY, config.get(CONF_EFFECTS, [])) + effects = yield cg.build_registry_list( + EFFECTS_REGISTRY, config.get(CONF_EFFECTS, []) + ) cg.add(light_var.add_effects(effects)) for conf in config.get(CONF_ON_TURN_ON, []): @@ -101,5 +149,5 @@ def register_light(output_var, config): @coroutine_with_priority(100.0) def to_code(config): - cg.add_define('USE_LIGHT') + cg.add_define("USE_LIGHT") cg.add_global(light_ns.using) diff --git a/esphome/components/light/automation.py b/esphome/components/light/automation.py index 9e14246c0f..7f1c6d9cb9 100644 --- a/esphome/components/light/automation.py +++ b/esphome/components/light/automation.py @@ -1,54 +1,102 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import automation -from esphome.const import CONF_ID, CONF_TRANSITION_LENGTH, CONF_STATE, CONF_FLASH_LENGTH, \ - CONF_EFFECT, CONF_BRIGHTNESS, CONF_RED, CONF_GREEN, CONF_BLUE, CONF_WHITE, \ - CONF_COLOR_TEMPERATURE, CONF_RANGE_FROM, CONF_RANGE_TO -from .types import DimRelativeAction, ToggleAction, LightState, LightControlAction, \ - AddressableLightState, AddressableSet, LightIsOnCondition, LightIsOffCondition +from esphome.const import ( + CONF_ID, + CONF_TRANSITION_LENGTH, + CONF_STATE, + CONF_FLASH_LENGTH, + CONF_EFFECT, + CONF_BRIGHTNESS, + CONF_RED, + CONF_GREEN, + CONF_BLUE, + CONF_WHITE, + CONF_COLOR_TEMPERATURE, + CONF_RANGE_FROM, + CONF_RANGE_TO, +) +from .types import ( + DimRelativeAction, + ToggleAction, + LightState, + LightControlAction, + AddressableLightState, + AddressableSet, + LightIsOnCondition, + LightIsOffCondition, +) -@automation.register_action('light.toggle', ToggleAction, automation.maybe_simple_id({ - cv.Required(CONF_ID): cv.use_id(LightState), - cv.Optional(CONF_TRANSITION_LENGTH): cv.templatable(cv.positive_time_period_milliseconds), -})) +@automation.register_action( + "light.toggle", + ToggleAction, + automation.maybe_simple_id( + { + cv.Required(CONF_ID): cv.use_id(LightState), + cv.Optional(CONF_TRANSITION_LENGTH): cv.templatable( + cv.positive_time_period_milliseconds + ), + } + ), +) def light_toggle_to_code(config, action_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) var = cg.new_Pvariable(action_id, template_arg, paren) if CONF_TRANSITION_LENGTH in config: - template_ = yield cg.templatable(config[CONF_TRANSITION_LENGTH], args, cg.uint32) + template_ = yield cg.templatable( + config[CONF_TRANSITION_LENGTH], args, cg.uint32 + ) cg.add(var.set_transition_length(template_)) yield var -LIGHT_CONTROL_ACTION_SCHEMA = cv.Schema({ - cv.Required(CONF_ID): cv.use_id(LightState), - cv.Optional(CONF_STATE): cv.templatable(cv.boolean), - cv.Exclusive(CONF_TRANSITION_LENGTH, 'transformer'): - cv.templatable(cv.positive_time_period_milliseconds), - cv.Exclusive(CONF_FLASH_LENGTH, 'transformer'): - cv.templatable(cv.positive_time_period_milliseconds), - cv.Exclusive(CONF_EFFECT, 'transformer'): cv.templatable(cv.string), - cv.Optional(CONF_BRIGHTNESS): cv.templatable(cv.percentage), - cv.Optional(CONF_RED): cv.templatable(cv.percentage), - cv.Optional(CONF_GREEN): cv.templatable(cv.percentage), - cv.Optional(CONF_BLUE): cv.templatable(cv.percentage), - cv.Optional(CONF_WHITE): cv.templatable(cv.percentage), - cv.Optional(CONF_COLOR_TEMPERATURE): cv.templatable(cv.color_temperature), -}) -LIGHT_TURN_OFF_ACTION_SCHEMA = automation.maybe_simple_id({ - cv.Required(CONF_ID): cv.use_id(LightState), - cv.Optional(CONF_TRANSITION_LENGTH): cv.templatable(cv.positive_time_period_milliseconds), - cv.Optional(CONF_STATE, default=False): False, -}) -LIGHT_TURN_ON_ACTION_SCHEMA = automation.maybe_simple_id(LIGHT_CONTROL_ACTION_SCHEMA.extend({ - cv.Optional(CONF_STATE, default=True): True, -})) +LIGHT_CONTROL_ACTION_SCHEMA = cv.Schema( + { + cv.Required(CONF_ID): cv.use_id(LightState), + cv.Optional(CONF_STATE): cv.templatable(cv.boolean), + cv.Exclusive(CONF_TRANSITION_LENGTH, "transformer"): cv.templatable( + cv.positive_time_period_milliseconds + ), + cv.Exclusive(CONF_FLASH_LENGTH, "transformer"): cv.templatable( + cv.positive_time_period_milliseconds + ), + cv.Exclusive(CONF_EFFECT, "transformer"): cv.templatable(cv.string), + cv.Optional(CONF_BRIGHTNESS): cv.templatable(cv.percentage), + cv.Optional(CONF_RED): cv.templatable(cv.percentage), + cv.Optional(CONF_GREEN): cv.templatable(cv.percentage), + cv.Optional(CONF_BLUE): cv.templatable(cv.percentage), + cv.Optional(CONF_WHITE): cv.templatable(cv.percentage), + cv.Optional(CONF_COLOR_TEMPERATURE): cv.templatable(cv.color_temperature), + } +) +LIGHT_TURN_OFF_ACTION_SCHEMA = automation.maybe_simple_id( + { + cv.Required(CONF_ID): cv.use_id(LightState), + cv.Optional(CONF_TRANSITION_LENGTH): cv.templatable( + cv.positive_time_period_milliseconds + ), + cv.Optional(CONF_STATE, default=False): False, + } +) +LIGHT_TURN_ON_ACTION_SCHEMA = automation.maybe_simple_id( + LIGHT_CONTROL_ACTION_SCHEMA.extend( + { + cv.Optional(CONF_STATE, default=True): True, + } + ) +) -@automation.register_action('light.turn_off', LightControlAction, LIGHT_TURN_OFF_ACTION_SCHEMA) -@automation.register_action('light.turn_on', LightControlAction, LIGHT_TURN_ON_ACTION_SCHEMA) -@automation.register_action('light.control', LightControlAction, LIGHT_CONTROL_ACTION_SCHEMA) +@automation.register_action( + "light.turn_off", LightControlAction, LIGHT_TURN_OFF_ACTION_SCHEMA +) +@automation.register_action( + "light.turn_on", LightControlAction, LIGHT_TURN_ON_ACTION_SCHEMA +) +@automation.register_action( + "light.control", LightControlAction, LIGHT_CONTROL_ACTION_SCHEMA +) def light_control_to_code(config, action_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) var = cg.new_Pvariable(action_id, template_arg, paren) @@ -56,7 +104,9 @@ def light_control_to_code(config, action_id, template_arg, args): template_ = yield cg.templatable(config[CONF_STATE], args, bool) cg.add(var.set_state(template_)) if CONF_TRANSITION_LENGTH in config: - template_ = yield cg.templatable(config[CONF_TRANSITION_LENGTH], args, cg.uint32) + template_ = yield cg.templatable( + config[CONF_TRANSITION_LENGTH], args, cg.uint32 + ) cg.add(var.set_transition_length(template_)) if CONF_FLASH_LENGTH in config: template_ = yield cg.templatable(config[CONF_FLASH_LENGTH], args, cg.uint32) @@ -85,16 +135,23 @@ def light_control_to_code(config, action_id, template_arg, args): yield var -CONF_RELATIVE_BRIGHTNESS = 'relative_brightness' -LIGHT_DIM_RELATIVE_ACTION_SCHEMA = cv.Schema({ - cv.Required(CONF_ID): cv.use_id(LightState), - cv.Required(CONF_RELATIVE_BRIGHTNESS): cv.templatable(cv.possibly_negative_percentage), - cv.Optional(CONF_TRANSITION_LENGTH): cv.templatable(cv.positive_time_period_milliseconds), -}) +CONF_RELATIVE_BRIGHTNESS = "relative_brightness" +LIGHT_DIM_RELATIVE_ACTION_SCHEMA = cv.Schema( + { + cv.Required(CONF_ID): cv.use_id(LightState), + cv.Required(CONF_RELATIVE_BRIGHTNESS): cv.templatable( + cv.possibly_negative_percentage + ), + cv.Optional(CONF_TRANSITION_LENGTH): cv.templatable( + cv.positive_time_period_milliseconds + ), + } +) -@automation.register_action('light.dim_relative', DimRelativeAction, - LIGHT_DIM_RELATIVE_ACTION_SCHEMA) +@automation.register_action( + "light.dim_relative", DimRelativeAction, LIGHT_DIM_RELATIVE_ACTION_SCHEMA +) def light_dim_relative_to_code(config, action_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) var = cg.new_Pvariable(action_id, template_arg, paren) @@ -106,19 +163,22 @@ def light_dim_relative_to_code(config, action_id, template_arg, args): yield var -LIGHT_ADDRESSABLE_SET_ACTION_SCHEMA = cv.Schema({ - cv.Required(CONF_ID): cv.use_id(AddressableLightState), - cv.Optional(CONF_RANGE_FROM): cv.templatable(cv.positive_int), - cv.Optional(CONF_RANGE_TO): cv.templatable(cv.positive_int), - cv.Optional(CONF_RED): cv.templatable(cv.percentage), - cv.Optional(CONF_GREEN): cv.templatable(cv.percentage), - cv.Optional(CONF_BLUE): cv.templatable(cv.percentage), - cv.Optional(CONF_WHITE): cv.templatable(cv.percentage), -}) +LIGHT_ADDRESSABLE_SET_ACTION_SCHEMA = cv.Schema( + { + cv.Required(CONF_ID): cv.use_id(AddressableLightState), + cv.Optional(CONF_RANGE_FROM): cv.templatable(cv.positive_int), + cv.Optional(CONF_RANGE_TO): cv.templatable(cv.positive_int), + cv.Optional(CONF_RED): cv.templatable(cv.percentage), + cv.Optional(CONF_GREEN): cv.templatable(cv.percentage), + cv.Optional(CONF_BLUE): cv.templatable(cv.percentage), + cv.Optional(CONF_WHITE): cv.templatable(cv.percentage), + } +) -@automation.register_action('light.addressable_set', AddressableSet, - LIGHT_ADDRESSABLE_SET_ACTION_SCHEMA) +@automation.register_action( + "light.addressable_set", AddressableSet, LIGHT_ADDRESSABLE_SET_ACTION_SCHEMA +) def light_addressable_set_to_code(config, action_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) var = cg.new_Pvariable(action_id, template_arg, paren) @@ -133,28 +193,46 @@ def light_addressable_set_to_code(config, action_id, template_arg, args): return int(round(x * 255)) if CONF_RED in config: - templ = yield cg.templatable(config[CONF_RED], args, cg.uint8, to_exp=rgbw_to_exp) + templ = yield cg.templatable( + config[CONF_RED], args, cg.uint8, to_exp=rgbw_to_exp + ) cg.add(var.set_red(templ)) if CONF_GREEN in config: - templ = yield cg.templatable(config[CONF_GREEN], args, cg.uint8, to_exp=rgbw_to_exp) + templ = yield cg.templatable( + config[CONF_GREEN], args, cg.uint8, to_exp=rgbw_to_exp + ) cg.add(var.set_green(templ)) if CONF_BLUE in config: - templ = yield cg.templatable(config[CONF_BLUE], args, cg.uint8, to_exp=rgbw_to_exp) + templ = yield cg.templatable( + config[CONF_BLUE], args, cg.uint8, to_exp=rgbw_to_exp + ) cg.add(var.set_blue(templ)) if CONF_WHITE in config: - templ = yield cg.templatable(config[CONF_WHITE], args, cg.uint8, to_exp=rgbw_to_exp) + templ = yield cg.templatable( + config[CONF_WHITE], args, cg.uint8, to_exp=rgbw_to_exp + ) cg.add(var.set_white(templ)) yield var -@automation.register_condition('light.is_on', LightIsOnCondition, - automation.maybe_simple_id({ - cv.Required(CONF_ID): cv.use_id(LightState), - })) -@automation.register_condition('light.is_off', LightIsOffCondition, - automation.maybe_simple_id({ - cv.Required(CONF_ID): cv.use_id(LightState), - })) +@automation.register_condition( + "light.is_on", + LightIsOnCondition, + automation.maybe_simple_id( + { + cv.Required(CONF_ID): cv.use_id(LightState), + } + ), +) +@automation.register_condition( + "light.is_off", + LightIsOffCondition, + automation.maybe_simple_id( + { + cv.Required(CONF_ID): cv.use_id(LightState), + } + ), +) def light_is_on_off_to_code(config, condition_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) yield cg.new_Pvariable(condition_id, template_arg, paren) diff --git a/esphome/components/light/effects.py b/esphome/components/light/effects.py index edb7a39ed9..9f017de98b 100644 --- a/esphome/components/light/effects.py +++ b/esphome/components/light/effects.py @@ -2,38 +2,69 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import automation -from esphome.const import CONF_NAME, CONF_LAMBDA, CONF_UPDATE_INTERVAL, CONF_TRANSITION_LENGTH, \ - CONF_COLORS, CONF_STATE, CONF_DURATION, CONF_BRIGHTNESS, CONF_RED, CONF_GREEN, CONF_BLUE, \ - CONF_WHITE, CONF_ALPHA, CONF_INTENSITY, CONF_SPEED, CONF_WIDTH, CONF_NUM_LEDS, CONF_RANDOM, \ - CONF_SEQUENCE +from esphome.const import ( + CONF_NAME, + CONF_LAMBDA, + CONF_UPDATE_INTERVAL, + CONF_TRANSITION_LENGTH, + CONF_COLORS, + CONF_STATE, + CONF_DURATION, + CONF_BRIGHTNESS, + CONF_RED, + CONF_GREEN, + CONF_BLUE, + CONF_WHITE, + CONF_ALPHA, + CONF_INTENSITY, + CONF_SPEED, + CONF_WIDTH, + CONF_NUM_LEDS, + CONF_RANDOM, + CONF_SEQUENCE, +) from esphome.util import Registry -from .types import LambdaLightEffect, RandomLightEffect, StrobeLightEffect, \ - StrobeLightEffectColor, LightColorValues, AddressableLightRef, AddressableLambdaLightEffect, \ - FlickerLightEffect, AddressableRainbowLightEffect, AddressableColorWipeEffect, \ - AddressableColorWipeEffectColor, AddressableScanEffect, AddressableTwinkleEffect, \ - AddressableRandomTwinkleEffect, AddressableFireworksEffect, AddressableFlickerEffect, \ - AutomationLightEffect, Color +from .types import ( + LambdaLightEffect, + RandomLightEffect, + StrobeLightEffect, + StrobeLightEffectColor, + LightColorValues, + AddressableLightRef, + AddressableLambdaLightEffect, + FlickerLightEffect, + AddressableRainbowLightEffect, + AddressableColorWipeEffect, + AddressableColorWipeEffectColor, + AddressableScanEffect, + AddressableTwinkleEffect, + AddressableRandomTwinkleEffect, + AddressableFireworksEffect, + AddressableFlickerEffect, + AutomationLightEffect, + Color, +) -CONF_ADD_LED_INTERVAL = 'add_led_interval' -CONF_REVERSE = 'reverse' -CONF_MOVE_INTERVAL = 'move_interval' -CONF_SCAN_WIDTH = 'scan_width' -CONF_TWINKLE_PROBABILITY = 'twinkle_probability' -CONF_PROGRESS_INTERVAL = 'progress_interval' -CONF_SPARK_PROBABILITY = 'spark_probability' -CONF_USE_RANDOM_COLOR = 'use_random_color' -CONF_FADE_OUT_RATE = 'fade_out_rate' -CONF_STROBE = 'strobe' -CONF_FLICKER = 'flicker' -CONF_ADDRESSABLE_LAMBDA = 'addressable_lambda' -CONF_ADDRESSABLE_RAINBOW = 'addressable_rainbow' -CONF_ADDRESSABLE_COLOR_WIPE = 'addressable_color_wipe' -CONF_ADDRESSABLE_SCAN = 'addressable_scan' -CONF_ADDRESSABLE_TWINKLE = 'addressable_twinkle' -CONF_ADDRESSABLE_RANDOM_TWINKLE = 'addressable_random_twinkle' -CONF_ADDRESSABLE_FIREWORKS = 'addressable_fireworks' -CONF_ADDRESSABLE_FLICKER = 'addressable_flicker' -CONF_AUTOMATION = 'automation' +CONF_ADD_LED_INTERVAL = "add_led_interval" +CONF_REVERSE = "reverse" +CONF_MOVE_INTERVAL = "move_interval" +CONF_SCAN_WIDTH = "scan_width" +CONF_TWINKLE_PROBABILITY = "twinkle_probability" +CONF_PROGRESS_INTERVAL = "progress_interval" +CONF_SPARK_PROBABILITY = "spark_probability" +CONF_USE_RANDOM_COLOR = "use_random_color" +CONF_FADE_OUT_RATE = "fade_out_rate" +CONF_STROBE = "strobe" +CONF_FLICKER = "flicker" +CONF_ADDRESSABLE_LAMBDA = "addressable_lambda" +CONF_ADDRESSABLE_RAINBOW = "addressable_rainbow" +CONF_ADDRESSABLE_COLOR_WIPE = "addressable_color_wipe" +CONF_ADDRESSABLE_SCAN = "addressable_scan" +CONF_ADDRESSABLE_TWINKLE = "addressable_twinkle" +CONF_ADDRESSABLE_RANDOM_TWINKLE = "addressable_random_twinkle" +CONF_ADDRESSABLE_FIREWORKS = "addressable_fireworks" +CONF_ADDRESSABLE_FLICKER = "addressable_flicker" +CONF_AUTOMATION = "automation" BINARY_EFFECTS = [] MONOCHROMATIC_EFFECTS = [] @@ -44,9 +75,11 @@ EFFECTS_REGISTRY = Registry() def register_effect(name, effect_type, default_name, schema, *extra_validators): - schema = cv.Schema(schema).extend({ - cv.Optional(CONF_NAME, default=default_name): cv.string_strict, - }) + schema = cv.Schema(schema).extend( + { + cv.Optional(CONF_NAME, default=default_name): cv.string_strict, + } + ) validator = cv.All(schema, *extra_validators) return EFFECTS_REGISTRY.register(name, effect_type, validator) @@ -61,7 +94,9 @@ def register_binary_effect(name, effect_type, default_name, schema, *extra_valid return register_effect(name, effect_type, default_name, schema, *extra_validators) -def register_monochromatic_effect(name, effect_type, default_name, schema, *extra_validators): +def register_monochromatic_effect( + name, effect_type, default_name, schema, *extra_validators +): # monochromatic effect can be used for all lights expect binary MONOCHROMATIC_EFFECTS.append(name) RGB_EFFECTS.append(name) @@ -78,36 +113,58 @@ def register_rgb_effect(name, effect_type, default_name, schema, *extra_validato return register_effect(name, effect_type, default_name, schema, *extra_validators) -def register_addressable_effect(name, effect_type, default_name, schema, *extra_validators): +def register_addressable_effect( + name, effect_type, default_name, schema, *extra_validators +): # addressable effect can be used only in addressable ADDRESSABLE_EFFECTS.append(name) return register_effect(name, effect_type, default_name, schema, *extra_validators) -@register_binary_effect('lambda', LambdaLightEffect, "Lambda", { - cv.Required(CONF_LAMBDA): cv.lambda_, - cv.Optional(CONF_UPDATE_INTERVAL, default='0ms'): cv.update_interval, -}) +@register_binary_effect( + "lambda", + LambdaLightEffect, + "Lambda", + { + cv.Required(CONF_LAMBDA): cv.lambda_, + cv.Optional(CONF_UPDATE_INTERVAL, default="0ms"): cv.update_interval, + }, +) def lambda_effect_to_code(config, effect_id): lambda_ = yield cg.process_lambda(config[CONF_LAMBDA], [], return_type=cg.void) - yield cg.new_Pvariable(effect_id, config[CONF_NAME], lambda_, - config[CONF_UPDATE_INTERVAL]) + yield cg.new_Pvariable( + effect_id, config[CONF_NAME], lambda_, config[CONF_UPDATE_INTERVAL] + ) -@register_binary_effect('automation', AutomationLightEffect, "Automation", { - cv.Required(CONF_SEQUENCE): automation.validate_automation(single=True), -}) +@register_binary_effect( + "automation", + AutomationLightEffect, + "Automation", + { + cv.Required(CONF_SEQUENCE): automation.validate_automation(single=True), + }, +) def automation_effect_to_code(config, effect_id): var = yield cg.new_Pvariable(effect_id, config[CONF_NAME]) yield automation.build_automation(var.get_trig(), [], config[CONF_SEQUENCE]) yield var -@register_rgb_effect('random', RandomLightEffect, "Random", { - cv.Optional(CONF_TRANSITION_LENGTH, default='7.5s'): cv.positive_time_period_milliseconds, - cv.Optional(CONF_UPDATE_INTERVAL, default='10s'): cv.positive_time_period_milliseconds, -}) +@register_rgb_effect( + "random", + RandomLightEffect, + "Random", + { + cv.Optional( + CONF_TRANSITION_LENGTH, default="7.5s" + ): cv.positive_time_period_milliseconds, + cv.Optional( + CONF_UPDATE_INTERVAL, default="10s" + ): cv.positive_time_period_milliseconds, + }, +) def random_effect_to_code(config, effect_id): effect = cg.new_Pvariable(effect_id, config[CONF_NAME]) cg.add(effect.set_transition_length(config[CONF_TRANSITION_LENGTH])) @@ -115,40 +172,79 @@ def random_effect_to_code(config, effect_id): yield effect -@register_binary_effect('strobe', StrobeLightEffect, "Strobe", { - cv.Optional(CONF_COLORS, default=[ - {CONF_STATE: True, CONF_DURATION: '0.5s'}, - {CONF_STATE: False, CONF_DURATION: '0.5s'}, - ]): cv.All(cv.ensure_list(cv.Schema({ - cv.Optional(CONF_STATE, default=True): cv.boolean, - cv.Optional(CONF_BRIGHTNESS, default=1.0): cv.percentage, - cv.Optional(CONF_RED, default=1.0): cv.percentage, - cv.Optional(CONF_GREEN, default=1.0): cv.percentage, - cv.Optional(CONF_BLUE, default=1.0): cv.percentage, - cv.Optional(CONF_WHITE, default=1.0): cv.percentage, - cv.Required(CONF_DURATION): cv.positive_time_period_milliseconds, - }), cv.has_at_least_one_key(CONF_STATE, CONF_BRIGHTNESS, CONF_RED, CONF_GREEN, CONF_BLUE, - CONF_WHITE)), cv.Length(min=2)), -}) +@register_binary_effect( + "strobe", + StrobeLightEffect, + "Strobe", + { + cv.Optional( + CONF_COLORS, + default=[ + {CONF_STATE: True, CONF_DURATION: "0.5s"}, + {CONF_STATE: False, CONF_DURATION: "0.5s"}, + ], + ): cv.All( + cv.ensure_list( + cv.Schema( + { + cv.Optional(CONF_STATE, default=True): cv.boolean, + cv.Optional(CONF_BRIGHTNESS, default=1.0): cv.percentage, + cv.Optional(CONF_RED, default=1.0): cv.percentage, + cv.Optional(CONF_GREEN, default=1.0): cv.percentage, + cv.Optional(CONF_BLUE, default=1.0): cv.percentage, + cv.Optional(CONF_WHITE, default=1.0): cv.percentage, + cv.Required( + CONF_DURATION + ): cv.positive_time_period_milliseconds, + } + ), + cv.has_at_least_one_key( + CONF_STATE, + CONF_BRIGHTNESS, + CONF_RED, + CONF_GREEN, + CONF_BLUE, + CONF_WHITE, + ), + ), + cv.Length(min=2), + ), + }, +) def strobe_effect_to_code(config, effect_id): var = cg.new_Pvariable(effect_id, config[CONF_NAME]) colors = [] for color in config.get(CONF_COLORS, []): - colors.append(cg.StructInitializer( - StrobeLightEffectColor, - ('color', LightColorValues(color[CONF_STATE], color[CONF_BRIGHTNESS], - color[CONF_RED], color[CONF_GREEN], color[CONF_BLUE], - color[CONF_WHITE])), - ('duration', color[CONF_DURATION]), - )) + colors.append( + cg.StructInitializer( + StrobeLightEffectColor, + ( + "color", + LightColorValues( + color[CONF_STATE], + color[CONF_BRIGHTNESS], + color[CONF_RED], + color[CONF_GREEN], + color[CONF_BLUE], + color[CONF_WHITE], + ), + ), + ("duration", color[CONF_DURATION]), + ) + ) cg.add(var.set_colors(colors)) yield var -@register_monochromatic_effect('flicker', FlickerLightEffect, "Flicker", { - cv.Optional(CONF_ALPHA, default=0.95): cv.percentage, - cv.Optional(CONF_INTENSITY, default=0.015): cv.percentage, -}) +@register_monochromatic_effect( + "flicker", + FlickerLightEffect, + "Flicker", + { + cv.Optional(CONF_ALPHA, default=0.95): cv.percentage, + cv.Optional(CONF_INTENSITY, default=0.015): cv.percentage, + }, +) def flicker_effect_to_code(config, effect_id): var = cg.new_Pvariable(effect_id, config[CONF_NAME]) cg.add(var.set_alpha(config[CONF_ALPHA])) @@ -157,23 +253,38 @@ def flicker_effect_to_code(config, effect_id): @register_addressable_effect( - 'addressable_lambda', AddressableLambdaLightEffect, "Addressable Lambda", { + "addressable_lambda", + AddressableLambdaLightEffect, + "Addressable Lambda", + { cv.Required(CONF_LAMBDA): cv.lambda_, - cv.Optional(CONF_UPDATE_INTERVAL, default='0ms'): cv.positive_time_period_milliseconds, - } + cv.Optional( + CONF_UPDATE_INTERVAL, default="0ms" + ): cv.positive_time_period_milliseconds, + }, ) def addressable_lambda_effect_to_code(config, effect_id): - args = [(AddressableLightRef, 'it'), (Color, 'current_color'), (bool, 'initial_run')] + args = [ + (AddressableLightRef, "it"), + (Color, "current_color"), + (bool, "initial_run"), + ] lambda_ = yield cg.process_lambda(config[CONF_LAMBDA], args, return_type=cg.void) - var = cg.new_Pvariable(effect_id, config[CONF_NAME], lambda_, - config[CONF_UPDATE_INTERVAL]) + var = cg.new_Pvariable( + effect_id, config[CONF_NAME], lambda_, config[CONF_UPDATE_INTERVAL] + ) yield var -@register_addressable_effect('addressable_rainbow', AddressableRainbowLightEffect, "Rainbow", { - cv.Optional(CONF_SPEED, default=10): cv.uint32_t, - cv.Optional(CONF_WIDTH, default=50): cv.uint32_t, -}) +@register_addressable_effect( + "addressable_rainbow", + AddressableRainbowLightEffect, + "Rainbow", + { + cv.Optional(CONF_SPEED, default=10): cv.uint32_t, + cv.Optional(CONF_WIDTH, default=50): cv.uint32_t, + }, +) def addressable_rainbow_effect_to_code(config, effect_id): var = cg.new_Pvariable(effect_id, config[CONF_NAME]) cg.add(var.set_speed(config[CONF_SPEED])) @@ -181,41 +292,61 @@ def addressable_rainbow_effect_to_code(config, effect_id): yield var -@register_addressable_effect('addressable_color_wipe', AddressableColorWipeEffect, "Color Wipe", { - cv.Optional(CONF_COLORS, default=[{CONF_NUM_LEDS: 1, CONF_RANDOM: True}]): cv.ensure_list({ - cv.Optional(CONF_RED, default=1.0): cv.percentage, - cv.Optional(CONF_GREEN, default=1.0): cv.percentage, - cv.Optional(CONF_BLUE, default=1.0): cv.percentage, - cv.Optional(CONF_WHITE, default=1.0): cv.percentage, - cv.Optional(CONF_RANDOM, default=False): cv.boolean, - cv.Required(CONF_NUM_LEDS): cv.All(cv.uint32_t, cv.Range(min=1)), - }), - cv.Optional(CONF_ADD_LED_INTERVAL, default='0.1s'): cv.positive_time_period_milliseconds, - cv.Optional(CONF_REVERSE, default=False): cv.boolean, -}) +@register_addressable_effect( + "addressable_color_wipe", + AddressableColorWipeEffect, + "Color Wipe", + { + cv.Optional( + CONF_COLORS, default=[{CONF_NUM_LEDS: 1, CONF_RANDOM: True}] + ): cv.ensure_list( + { + cv.Optional(CONF_RED, default=1.0): cv.percentage, + cv.Optional(CONF_GREEN, default=1.0): cv.percentage, + cv.Optional(CONF_BLUE, default=1.0): cv.percentage, + cv.Optional(CONF_WHITE, default=1.0): cv.percentage, + cv.Optional(CONF_RANDOM, default=False): cv.boolean, + cv.Required(CONF_NUM_LEDS): cv.All(cv.uint32_t, cv.Range(min=1)), + } + ), + cv.Optional( + CONF_ADD_LED_INTERVAL, default="0.1s" + ): cv.positive_time_period_milliseconds, + cv.Optional(CONF_REVERSE, default=False): cv.boolean, + }, +) def addressable_color_wipe_effect_to_code(config, effect_id): var = cg.new_Pvariable(effect_id, config[CONF_NAME]) cg.add(var.set_add_led_interval(config[CONF_ADD_LED_INTERVAL])) cg.add(var.set_reverse(config[CONF_REVERSE])) colors = [] for color in config.get(CONF_COLORS, []): - colors.append(cg.StructInitializer( - AddressableColorWipeEffectColor, - ('r', int(round(color[CONF_RED] * 255))), - ('g', int(round(color[CONF_GREEN] * 255))), - ('b', int(round(color[CONF_BLUE] * 255))), - ('w', int(round(color[CONF_WHITE] * 255))), - ('random', color[CONF_RANDOM]), - ('num_leds', color[CONF_NUM_LEDS]), - )) + colors.append( + cg.StructInitializer( + AddressableColorWipeEffectColor, + ("r", int(round(color[CONF_RED] * 255))), + ("g", int(round(color[CONF_GREEN] * 255))), + ("b", int(round(color[CONF_BLUE] * 255))), + ("w", int(round(color[CONF_WHITE] * 255))), + ("random", color[CONF_RANDOM]), + ("num_leds", color[CONF_NUM_LEDS]), + ) + ) cg.add(var.set_colors(colors)) yield var -@register_addressable_effect('addressable_scan', AddressableScanEffect, "Scan", { - cv.Optional(CONF_MOVE_INTERVAL, default='0.1s'): cv.positive_time_period_milliseconds, - cv.Optional(CONF_SCAN_WIDTH, default=1): cv.int_range(min=1), -}) +@register_addressable_effect( + "addressable_scan", + AddressableScanEffect, + "Scan", + { + cv.Optional( + CONF_MOVE_INTERVAL, default="0.1s" + ): cv.positive_time_period_milliseconds, + cv.Optional(CONF_SCAN_WIDTH, default=1): cv.int_range(min=1), + }, +) def addressable_scan_effect_to_code(config, effect_id): var = cg.new_Pvariable(effect_id, config[CONF_NAME]) cg.add(var.set_move_interval(config[CONF_MOVE_INTERVAL])) @@ -223,10 +354,17 @@ def addressable_scan_effect_to_code(config, effect_id): yield var -@register_addressable_effect('addressable_twinkle', AddressableTwinkleEffect, "Twinkle", { - cv.Optional(CONF_TWINKLE_PROBABILITY, default='5%'): cv.percentage, - cv.Optional(CONF_PROGRESS_INTERVAL, default='4ms'): cv.positive_time_period_milliseconds, -}) +@register_addressable_effect( + "addressable_twinkle", + AddressableTwinkleEffect, + "Twinkle", + { + cv.Optional(CONF_TWINKLE_PROBABILITY, default="5%"): cv.percentage, + cv.Optional( + CONF_PROGRESS_INTERVAL, default="4ms" + ): cv.positive_time_period_milliseconds, + }, +) def addressable_twinkle_effect_to_code(config, effect_id): var = cg.new_Pvariable(effect_id, config[CONF_NAME]) cg.add(var.set_twinkle_probability(config[CONF_TWINKLE_PROBABILITY])) @@ -235,10 +373,15 @@ def addressable_twinkle_effect_to_code(config, effect_id): @register_addressable_effect( - 'addressable_random_twinkle', AddressableRandomTwinkleEffect, "Random Twinkle", { - cv.Optional(CONF_TWINKLE_PROBABILITY, default='5%'): cv.percentage, - cv.Optional(CONF_PROGRESS_INTERVAL, default='32ms'): cv.positive_time_period_milliseconds, - } + "addressable_random_twinkle", + AddressableRandomTwinkleEffect, + "Random Twinkle", + { + cv.Optional(CONF_TWINKLE_PROBABILITY, default="5%"): cv.percentage, + cv.Optional( + CONF_PROGRESS_INTERVAL, default="32ms" + ): cv.positive_time_period_milliseconds, + }, ) def addressable_random_twinkle_effect_to_code(config, effect_id): var = cg.new_Pvariable(effect_id, config[CONF_NAME]) @@ -247,12 +390,19 @@ def addressable_random_twinkle_effect_to_code(config, effect_id): yield var -@register_addressable_effect('addressable_fireworks', AddressableFireworksEffect, "Fireworks", { - cv.Optional(CONF_UPDATE_INTERVAL, default='32ms'): cv.positive_time_period_milliseconds, - cv.Optional(CONF_SPARK_PROBABILITY, default='10%'): cv.percentage, - cv.Optional(CONF_USE_RANDOM_COLOR, default=False): cv.boolean, - cv.Optional(CONF_FADE_OUT_RATE, default=120): cv.uint8_t, -}) +@register_addressable_effect( + "addressable_fireworks", + AddressableFireworksEffect, + "Fireworks", + { + cv.Optional( + CONF_UPDATE_INTERVAL, default="32ms" + ): cv.positive_time_period_milliseconds, + cv.Optional(CONF_SPARK_PROBABILITY, default="10%"): cv.percentage, + cv.Optional(CONF_USE_RANDOM_COLOR, default=False): cv.boolean, + cv.Optional(CONF_FADE_OUT_RATE, default=120): cv.uint8_t, + }, +) def addressable_fireworks_effect_to_code(config, effect_id): var = cg.new_Pvariable(effect_id, config[CONF_NAME]) cg.add(var.set_update_interval(config[CONF_UPDATE_INTERVAL])) @@ -263,10 +413,15 @@ def addressable_fireworks_effect_to_code(config, effect_id): @register_addressable_effect( - 'addressable_flicker', AddressableFlickerEffect, "Addressable Flicker", { - cv.Optional(CONF_UPDATE_INTERVAL, default='16ms'): cv.positive_time_period_milliseconds, - cv.Optional(CONF_INTENSITY, default='5%'): cv.percentage, - } + "addressable_flicker", + AddressableFlickerEffect, + "Addressable Flicker", + { + cv.Optional( + CONF_UPDATE_INTERVAL, default="16ms" + ): cv.positive_time_period_milliseconds, + cv.Optional(CONF_INTENSITY, default="5%"): cv.percentage, + }, ) def addressable_flicker_effect_to_code(config, effect_id): var = cg.new_Pvariable(effect_id, config[CONF_NAME]) @@ -277,22 +432,28 @@ def addressable_flicker_effect_to_code(config, effect_id): def validate_effects(allowed_effects): def validator(value): - value = cv.validate_registry('effect', EFFECTS_REGISTRY)(value) + value = cv.validate_registry("effect", EFFECTS_REGISTRY)(value) errors = [] names = set() for i, x in enumerate(value): key = next(it for it in x.keys()) if key not in allowed_effects: errors.append( - cv.Invalid("The effect '{}' is not allowed for this " - "light type".format(key), [i]) + cv.Invalid( + "The effect '{}' is not allowed for this " + "light type".format(key), + [i], + ) ) continue name = x[key][CONF_NAME] if name in names: errors.append( - cv.Invalid("Found the effect name '{}' twice. All effects must have " - "unique names".format(name), [i]) + cv.Invalid( + "Found the effect name '{}' twice. All effects must have " + "unique names".format(name), + [i], + ) ) continue names.add(name) diff --git a/esphome/components/light/types.py b/esphome/components/light/types.py index 6bcfd5fdb4..4bca266b67 100644 --- a/esphome/components/light/types.py +++ b/esphome/components/light/types.py @@ -2,47 +2,62 @@ import esphome.codegen as cg from esphome import automation # Base -light_ns = cg.esphome_ns.namespace('light') -LightState = light_ns.class_('LightState', cg.Nameable, cg.Component) +light_ns = cg.esphome_ns.namespace("light") +LightState = light_ns.class_("LightState", cg.Nameable, cg.Component) # Fake class for addressable lights -AddressableLightState = light_ns.class_('LightState', LightState) -LightOutput = light_ns.class_('LightOutput') -AddressableLight = light_ns.class_('AddressableLight', cg.Component) -AddressableLightRef = AddressableLight.operator('ref') +AddressableLightState = light_ns.class_("LightState", LightState) +LightOutput = light_ns.class_("LightOutput") +AddressableLight = light_ns.class_("AddressableLight", cg.Component) +AddressableLightRef = AddressableLight.operator("ref") -Color = cg.esphome_ns.class_('Color') -LightColorValues = light_ns.class_('LightColorValues') +Color = cg.esphome_ns.class_("Color") +LightColorValues = light_ns.class_("LightColorValues") # Actions -ToggleAction = light_ns.class_('ToggleAction', automation.Action) -LightControlAction = light_ns.class_('LightControlAction', automation.Action) -DimRelativeAction = light_ns.class_('DimRelativeAction', automation.Action) -AddressableSet = light_ns.class_('AddressableSet', automation.Action) -LightIsOnCondition = light_ns.class_('LightIsOnCondition', automation.Condition) -LightIsOffCondition = light_ns.class_('LightIsOffCondition', automation.Condition) +ToggleAction = light_ns.class_("ToggleAction", automation.Action) +LightControlAction = light_ns.class_("LightControlAction", automation.Action) +DimRelativeAction = light_ns.class_("DimRelativeAction", automation.Action) +AddressableSet = light_ns.class_("AddressableSet", automation.Action) +LightIsOnCondition = light_ns.class_("LightIsOnCondition", automation.Condition) +LightIsOffCondition = light_ns.class_("LightIsOffCondition", automation.Condition) # Triggers -LightTurnOnTrigger = light_ns.class_('LightTurnOnTrigger', automation.Trigger.template()) -LightTurnOffTrigger = light_ns.class_('LightTurnOffTrigger', automation.Trigger.template()) +LightTurnOnTrigger = light_ns.class_( + "LightTurnOnTrigger", automation.Trigger.template() +) +LightTurnOffTrigger = light_ns.class_( + "LightTurnOffTrigger", automation.Trigger.template() +) # Effects -LightEffect = light_ns.class_('LightEffect') -RandomLightEffect = light_ns.class_('RandomLightEffect', LightEffect) -LambdaLightEffect = light_ns.class_('LambdaLightEffect', LightEffect) -AutomationLightEffect = light_ns.class_('AutomationLightEffect', LightEffect) -StrobeLightEffect = light_ns.class_('StrobeLightEffect', LightEffect) -StrobeLightEffectColor = light_ns.class_('StrobeLightEffectColor', LightEffect) -FlickerLightEffect = light_ns.class_('FlickerLightEffect', LightEffect) -AddressableLightEffect = light_ns.class_('AddressableLightEffect', LightEffect) -AddressableLambdaLightEffect = light_ns.class_('AddressableLambdaLightEffect', - AddressableLightEffect) -AddressableRainbowLightEffect = light_ns.class_('AddressableRainbowLightEffect', - AddressableLightEffect) -AddressableColorWipeEffect = light_ns.class_('AddressableColorWipeEffect', AddressableLightEffect) -AddressableColorWipeEffectColor = light_ns.struct('AddressableColorWipeEffectColor') -AddressableScanEffect = light_ns.class_('AddressableScanEffect', AddressableLightEffect) -AddressableTwinkleEffect = light_ns.class_('AddressableTwinkleEffect', AddressableLightEffect) -AddressableRandomTwinkleEffect = light_ns.class_('AddressableRandomTwinkleEffect', - AddressableLightEffect) -AddressableFireworksEffect = light_ns.class_('AddressableFireworksEffect', AddressableLightEffect) -AddressableFlickerEffect = light_ns.class_('AddressableFlickerEffect', AddressableLightEffect) +LightEffect = light_ns.class_("LightEffect") +RandomLightEffect = light_ns.class_("RandomLightEffect", LightEffect) +LambdaLightEffect = light_ns.class_("LambdaLightEffect", LightEffect) +AutomationLightEffect = light_ns.class_("AutomationLightEffect", LightEffect) +StrobeLightEffect = light_ns.class_("StrobeLightEffect", LightEffect) +StrobeLightEffectColor = light_ns.class_("StrobeLightEffectColor", LightEffect) +FlickerLightEffect = light_ns.class_("FlickerLightEffect", LightEffect) +AddressableLightEffect = light_ns.class_("AddressableLightEffect", LightEffect) +AddressableLambdaLightEffect = light_ns.class_( + "AddressableLambdaLightEffect", AddressableLightEffect +) +AddressableRainbowLightEffect = light_ns.class_( + "AddressableRainbowLightEffect", AddressableLightEffect +) +AddressableColorWipeEffect = light_ns.class_( + "AddressableColorWipeEffect", AddressableLightEffect +) +AddressableColorWipeEffectColor = light_ns.struct("AddressableColorWipeEffectColor") +AddressableScanEffect = light_ns.class_("AddressableScanEffect", AddressableLightEffect) +AddressableTwinkleEffect = light_ns.class_( + "AddressableTwinkleEffect", AddressableLightEffect +) +AddressableRandomTwinkleEffect = light_ns.class_( + "AddressableRandomTwinkleEffect", AddressableLightEffect +) +AddressableFireworksEffect = light_ns.class_( + "AddressableFireworksEffect", AddressableLightEffect +) +AddressableFlickerEffect = light_ns.class_( + "AddressableFlickerEffect", AddressableLightEffect +) diff --git a/esphome/components/logger/__init__.py b/esphome/components/logger/__init__.py index c447b0eee1..5e7383cca7 100644 --- a/esphome/components/logger/__init__.py +++ b/esphome/components/logger/__init__.py @@ -4,49 +4,69 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import automation from esphome.automation import LambdaAction -from esphome.const import CONF_ARGS, CONF_BAUD_RATE, CONF_FORMAT, CONF_HARDWARE_UART, CONF_ID, \ - CONF_LEVEL, CONF_LOGS, CONF_ON_MESSAGE, CONF_TAG, CONF_TRIGGER_ID, CONF_TX_BUFFER_SIZE +from esphome.const import ( + CONF_ARGS, + CONF_BAUD_RATE, + CONF_FORMAT, + CONF_HARDWARE_UART, + CONF_ID, + CONF_LEVEL, + CONF_LOGS, + CONF_ON_MESSAGE, + CONF_TAG, + CONF_TRIGGER_ID, + CONF_TX_BUFFER_SIZE, +) from esphome.core import CORE, EsphomeError, Lambda, coroutine_with_priority -CODEOWNERS = ['@esphome/core'] -logger_ns = cg.esphome_ns.namespace('logger') +CODEOWNERS = ["@esphome/core"] +logger_ns = cg.esphome_ns.namespace("logger") LOG_LEVELS = { - 'NONE': cg.global_ns.ESPHOME_LOG_LEVEL_NONE, - 'ERROR': cg.global_ns.ESPHOME_LOG_LEVEL_ERROR, - 'WARN': cg.global_ns.ESPHOME_LOG_LEVEL_WARN, - 'INFO': cg.global_ns.ESPHOME_LOG_LEVEL_INFO, - 'DEBUG': cg.global_ns.ESPHOME_LOG_LEVEL_DEBUG, - 'VERBOSE': cg.global_ns.ESPHOME_LOG_LEVEL_VERBOSE, - 'VERY_VERBOSE': cg.global_ns.ESPHOME_LOG_LEVEL_VERY_VERBOSE, + "NONE": cg.global_ns.ESPHOME_LOG_LEVEL_NONE, + "ERROR": cg.global_ns.ESPHOME_LOG_LEVEL_ERROR, + "WARN": cg.global_ns.ESPHOME_LOG_LEVEL_WARN, + "INFO": cg.global_ns.ESPHOME_LOG_LEVEL_INFO, + "DEBUG": cg.global_ns.ESPHOME_LOG_LEVEL_DEBUG, + "VERBOSE": cg.global_ns.ESPHOME_LOG_LEVEL_VERBOSE, + "VERY_VERBOSE": cg.global_ns.ESPHOME_LOG_LEVEL_VERY_VERBOSE, } LOG_LEVEL_TO_ESP_LOG = { - 'ERROR': cg.global_ns.ESP_LOGE, - 'WARN': cg.global_ns.ESP_LOGW, - 'INFO': cg.global_ns.ESP_LOGI, - 'DEBUG': cg.global_ns.ESP_LOGD, - 'VERBOSE': cg.global_ns.ESP_LOGV, - 'VERY_VERBOSE': cg.global_ns.ESP_LOGVV, + "ERROR": cg.global_ns.ESP_LOGE, + "WARN": cg.global_ns.ESP_LOGW, + "INFO": cg.global_ns.ESP_LOGI, + "DEBUG": cg.global_ns.ESP_LOGD, + "VERBOSE": cg.global_ns.ESP_LOGV, + "VERY_VERBOSE": cg.global_ns.ESP_LOGVV, } -LOG_LEVEL_SEVERITY = ['NONE', 'ERROR', 'WARN', 'INFO', 'CONFIG', 'DEBUG', 'VERBOSE', 'VERY_VERBOSE'] +LOG_LEVEL_SEVERITY = [ + "NONE", + "ERROR", + "WARN", + "INFO", + "CONFIG", + "DEBUG", + "VERBOSE", + "VERY_VERBOSE", +] -UART_SELECTION_ESP32 = ['UART0', 'UART1', 'UART2'] +UART_SELECTION_ESP32 = ["UART0", "UART1", "UART2"] -UART_SELECTION_ESP8266 = ['UART0', 'UART0_SWAP', 'UART1'] +UART_SELECTION_ESP8266 = ["UART0", "UART0_SWAP", "UART1"] HARDWARE_UART_TO_UART_SELECTION = { - 'UART0': logger_ns.UART_SELECTION_UART0, - 'UART0_SWAP': logger_ns.UART_SELECTION_UART0_SWAP, - 'UART1': logger_ns.UART_SELECTION_UART1, - 'UART2': logger_ns.UART_SELECTION_UART2, + "UART0": logger_ns.UART_SELECTION_UART0, + "UART0_SWAP": logger_ns.UART_SELECTION_UART0_SWAP, + "UART1": logger_ns.UART_SELECTION_UART1, + "UART2": logger_ns.UART_SELECTION_UART2, } HARDWARE_UART_TO_SERIAL = { - 'UART0': cg.global_ns.Serial, - 'UART0_SWAP': cg.global_ns.Serial, - 'UART1': cg.global_ns.Serial1, - 'UART2': cg.global_ns.Serial2, + "UART0": cg.global_ns.Serial, + "UART0_SWAP": cg.global_ns.Serial, + "UART1": cg.global_ns.Serial1, + "UART2": cg.global_ns.Serial2, } is_log_level = cv.one_of(*LOG_LEVELS, upper=True) @@ -61,45 +81,59 @@ def uart_selection(value): def validate_local_no_higher_than_global(value): - global_level = value.get(CONF_LEVEL, 'DEBUG') + global_level = value.get(CONF_LEVEL, "DEBUG") for tag, level in value.get(CONF_LOGS, {}).items(): if LOG_LEVEL_SEVERITY.index(level) > LOG_LEVEL_SEVERITY.index(global_level): - raise EsphomeError("The local log level {} for {} must be less severe than the " - "global log level {}.".format(level, tag, global_level)) + raise EsphomeError( + "The local log level {} for {} must be less severe than the " + "global log level {}.".format(level, tag, global_level) + ) return value -Logger = logger_ns.class_('Logger', cg.Component) -LoggerMessageTrigger = logger_ns.class_('LoggerMessageTrigger', - automation.Trigger.template(cg.int_, cg.const_char_ptr, - cg.const_char_ptr)) +Logger = logger_ns.class_("Logger", cg.Component) +LoggerMessageTrigger = logger_ns.class_( + "LoggerMessageTrigger", + automation.Trigger.template(cg.int_, cg.const_char_ptr, cg.const_char_ptr), +) -CONF_ESP8266_STORE_LOG_STRINGS_IN_FLASH = 'esp8266_store_log_strings_in_flash' -CONFIG_SCHEMA = cv.All(cv.Schema({ - cv.GenerateID(): cv.declare_id(Logger), - cv.Optional(CONF_BAUD_RATE, default=115200): cv.positive_int, - cv.Optional(CONF_TX_BUFFER_SIZE, default=512): cv.validate_bytes, - cv.Optional(CONF_HARDWARE_UART, default='UART0'): uart_selection, - cv.Optional(CONF_LEVEL, default='DEBUG'): is_log_level, - cv.Optional(CONF_LOGS, default={}): cv.Schema({ - cv.string: is_log_level, - }), - cv.Optional(CONF_ON_MESSAGE): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(LoggerMessageTrigger), - cv.Optional(CONF_LEVEL, default='WARN'): is_log_level, - }), - - cv.SplitDefault(CONF_ESP8266_STORE_LOG_STRINGS_IN_FLASH, esp8266=True): - cv.All(cv.only_on_esp8266, cv.boolean), -}).extend(cv.COMPONENT_SCHEMA), validate_local_no_higher_than_global) +CONF_ESP8266_STORE_LOG_STRINGS_IN_FLASH = "esp8266_store_log_strings_in_flash" +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(Logger), + cv.Optional(CONF_BAUD_RATE, default=115200): cv.positive_int, + cv.Optional(CONF_TX_BUFFER_SIZE, default=512): cv.validate_bytes, + cv.Optional(CONF_HARDWARE_UART, default="UART0"): uart_selection, + cv.Optional(CONF_LEVEL, default="DEBUG"): is_log_level, + cv.Optional(CONF_LOGS, default={}): cv.Schema( + { + cv.string: is_log_level, + } + ), + cv.Optional(CONF_ON_MESSAGE): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(LoggerMessageTrigger), + cv.Optional(CONF_LEVEL, default="WARN"): is_log_level, + } + ), + cv.SplitDefault( + CONF_ESP8266_STORE_LOG_STRINGS_IN_FLASH, esp8266=True + ): cv.All(cv.only_on_esp8266, cv.boolean), + } + ).extend(cv.COMPONENT_SCHEMA), + validate_local_no_higher_than_global, +) @coroutine_with_priority(90.0) def to_code(config): baud_rate = config[CONF_BAUD_RATE] - rhs = Logger.new(baud_rate, - config[CONF_TX_BUFFER_SIZE], - HARDWARE_UART_TO_UART_SELECTION[config[CONF_HARDWARE_UART]]) + rhs = Logger.new( + baud_rate, + config[CONF_TX_BUFFER_SIZE], + HARDWARE_UART_TO_UART_SELECTION[config[CONF_HARDWARE_UART]], + ) log = cg.Pvariable(config[CONF_ID], rhs) cg.add(log.pre_setup()) @@ -107,12 +141,12 @@ def to_code(config): cg.add(log.set_log_level(tag, LOG_LEVELS[level])) level = config[CONF_LEVEL] - cg.add_define('USE_LOGGER') + cg.add_define("USE_LOGGER") this_severity = LOG_LEVEL_SEVERITY.index(level) - cg.add_build_flag('-DESPHOME_LOG_LEVEL={}'.format(LOG_LEVELS[level])) + cg.add_build_flag("-DESPHOME_LOG_LEVEL={}".format(LOG_LEVELS[level])) - verbose_severity = LOG_LEVEL_SEVERITY.index('VERBOSE') - very_verbose_severity = LOG_LEVEL_SEVERITY.index('VERY_VERBOSE') + verbose_severity = LOG_LEVEL_SEVERITY.index("VERBOSE") + very_verbose_severity = LOG_LEVEL_SEVERITY.index("VERY_VERBOSE") is_at_least_verbose = this_severity >= verbose_severity is_at_least_very_verbose = this_severity >= very_verbose_severity has_serial_logging = baud_rate != 0 @@ -122,35 +156,42 @@ def to_code(config): cg.add_build_flag(f"-DDEBUG_ESP_PORT={debug_serial_port}") cg.add_build_flag("-DLWIP_DEBUG") DEBUG_COMPONENTS = { - 'HTTP_CLIENT', - 'HTTP_SERVER', - 'HTTP_UPDATE', - 'OTA', - 'SSL', - 'TLS_MEM', - 'UPDATER', - 'WIFI', + "HTTP_CLIENT", + "HTTP_SERVER", + "HTTP_UPDATE", + "OTA", + "SSL", + "TLS_MEM", + "UPDATER", + "WIFI", # Spams logs too much: # 'MDNS_RESPONDER', } for comp in DEBUG_COMPONENTS: cg.add_build_flag(f"-DDEBUG_ESP_{comp}") if CORE.is_esp32 and is_at_least_verbose: - cg.add_build_flag('-DCORE_DEBUG_LEVEL=5') + cg.add_build_flag("-DCORE_DEBUG_LEVEL=5") if CORE.is_esp32 and is_at_least_very_verbose: - cg.add_build_flag('-DENABLE_I2C_DEBUG_BUFFER') + cg.add_build_flag("-DENABLE_I2C_DEBUG_BUFFER") if config.get(CONF_ESP8266_STORE_LOG_STRINGS_IN_FLASH): - cg.add_build_flag('-DUSE_STORE_LOG_STR_IN_FLASH') + cg.add_build_flag("-DUSE_STORE_LOG_STR_IN_FLASH") # Register at end for safe mode yield cg.register_component(log, config) for conf in config.get(CONF_ON_MESSAGE, []): - trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], log, - LOG_LEVEL_SEVERITY.index(conf[CONF_LEVEL])) - yield automation.build_automation(trigger, [(cg.int_, 'level'), - (cg.const_char_ptr, 'tag'), - (cg.const_char_ptr, 'message')], conf) + trigger = cg.new_Pvariable( + conf[CONF_TRIGGER_ID], log, LOG_LEVEL_SEVERITY.index(conf[CONF_LEVEL]) + ) + yield automation.build_automation( + trigger, + [ + (cg.int_, "level"), + (cg.const_char_ptr, "tag"), + (cg.const_char_ptr, "message"), + ], + conf, + ) def maybe_simple_message(schema): @@ -179,18 +220,27 @@ def validate_printf(value): """ # noqa matches = re.findall(cfmt, value[CONF_FORMAT], flags=re.X) if len(matches) != len(value[CONF_ARGS]): - raise cv.Invalid("Found {} printf-patterns ({}), but {} args were given!" - "".format(len(matches), ', '.join(matches), len(value[CONF_ARGS]))) + raise cv.Invalid( + "Found {} printf-patterns ({}), but {} args were given!" + "".format(len(matches), ", ".join(matches), len(value[CONF_ARGS])) + ) return value -CONF_LOGGER_LOG = 'logger.log' -LOGGER_LOG_ACTION_SCHEMA = cv.All(maybe_simple_message({ - cv.Required(CONF_FORMAT): cv.string, - cv.Optional(CONF_ARGS, default=list): cv.ensure_list(cv.lambda_), - cv.Optional(CONF_LEVEL, default="DEBUG"): cv.one_of(*LOG_LEVEL_TO_ESP_LOG, upper=True), - cv.Optional(CONF_TAG, default="main"): cv.string, -}), validate_printf) +CONF_LOGGER_LOG = "logger.log" +LOGGER_LOG_ACTION_SCHEMA = cv.All( + maybe_simple_message( + { + cv.Required(CONF_FORMAT): cv.string, + cv.Optional(CONF_ARGS, default=list): cv.ensure_list(cv.lambda_), + cv.Optional(CONF_LEVEL, default="DEBUG"): cv.one_of( + *LOG_LEVEL_TO_ESP_LOG, upper=True + ), + cv.Optional(CONF_TAG, default="main"): cv.string, + } + ), + validate_printf, +) @automation.register_action(CONF_LOGGER_LOG, LambdaAction, LOGGER_LOG_ACTION_SCHEMA) diff --git a/esphome/components/max31855/sensor.py b/esphome/components/max31855/sensor.py index 9f105272a2..5d3fa461b1 100644 --- a/esphome/components/max31855/sensor.py +++ b/esphome/components/max31855/sensor.py @@ -1,18 +1,32 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, spi -from esphome.const import CONF_ID, CONF_REFERENCE_TEMPERATURE, DEVICE_CLASS_TEMPERATURE, \ - ICON_EMPTY, UNIT_CELSIUS +from esphome.const import ( + CONF_ID, + CONF_REFERENCE_TEMPERATURE, + DEVICE_CLASS_TEMPERATURE, + ICON_EMPTY, + UNIT_CELSIUS, +) -max31855_ns = cg.esphome_ns.namespace('max31855') -MAX31855Sensor = max31855_ns.class_('MAX31855Sensor', sensor.Sensor, cg.PollingComponent, - spi.SPIDevice) +max31855_ns = cg.esphome_ns.namespace("max31855") +MAX31855Sensor = max31855_ns.class_( + "MAX31855Sensor", sensor.Sensor, cg.PollingComponent, spi.SPIDevice +) -CONFIG_SCHEMA = sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE).extend({ - cv.GenerateID(): cv.declare_id(MAX31855Sensor), - cv.Optional(CONF_REFERENCE_TEMPERATURE): - sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 2, DEVICE_CLASS_TEMPERATURE), -}).extend(cv.polling_component_schema('60s')).extend(spi.spi_device_schema()) +CONFIG_SCHEMA = ( + sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE) + .extend( + { + cv.GenerateID(): cv.declare_id(MAX31855Sensor), + cv.Optional(CONF_REFERENCE_TEMPERATURE): sensor.sensor_schema( + UNIT_CELSIUS, ICON_EMPTY, 2, DEVICE_CLASS_TEMPERATURE + ), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(spi.spi_device_schema()) +) def to_code(config): diff --git a/esphome/components/max31856/sensor.py b/esphome/components/max31856/sensor.py index 95da4e54ad..afbb637609 100644 --- a/esphome/components/max31856/sensor.py +++ b/esphome/components/max31856/sensor.py @@ -1,23 +1,38 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, spi -from esphome.const import CONF_ID, CONF_MAINS_FILTER, DEVICE_CLASS_TEMPERATURE, ICON_EMPTY, \ - UNIT_CELSIUS +from esphome.const import ( + CONF_ID, + CONF_MAINS_FILTER, + DEVICE_CLASS_TEMPERATURE, + ICON_EMPTY, + UNIT_CELSIUS, +) -max31856_ns = cg.esphome_ns.namespace('max31856') -MAX31856Sensor = max31856_ns.class_('MAX31856Sensor', sensor.Sensor, cg.PollingComponent, - spi.SPIDevice) +max31856_ns = cg.esphome_ns.namespace("max31856") +MAX31856Sensor = max31856_ns.class_( + "MAX31856Sensor", sensor.Sensor, cg.PollingComponent, spi.SPIDevice +) -MAX31865ConfigFilter = max31856_ns.enum('MAX31856ConfigFilter') +MAX31865ConfigFilter = max31856_ns.enum("MAX31856ConfigFilter") FILTER = { - '50HZ': MAX31865ConfigFilter.FILTER_50HZ, - '60HZ': MAX31865ConfigFilter.FILTER_60HZ, + "50HZ": MAX31865ConfigFilter.FILTER_50HZ, + "60HZ": MAX31865ConfigFilter.FILTER_60HZ, } -CONFIG_SCHEMA = sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE).extend({ - cv.GenerateID(): cv.declare_id(MAX31856Sensor), - cv.Optional(CONF_MAINS_FILTER, default='60HZ'): cv.enum(FILTER, upper=True, space=''), -}).extend(cv.polling_component_schema('60s')).extend(spi.spi_device_schema()) +CONFIG_SCHEMA = ( + sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE) + .extend( + { + cv.GenerateID(): cv.declare_id(MAX31856Sensor), + cv.Optional(CONF_MAINS_FILTER, default="60HZ"): cv.enum( + FILTER, upper=True, space="" + ), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(spi.spi_device_schema()) +) def to_code(config): diff --git a/esphome/components/max31865/sensor.py b/esphome/components/max31865/sensor.py index 70913cc8f7..bebc42c95e 100644 --- a/esphome/components/max31865/sensor.py +++ b/esphome/components/max31865/sensor.py @@ -1,26 +1,48 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, spi -from esphome.const import CONF_ID, CONF_MAINS_FILTER, CONF_REFERENCE_RESISTANCE, \ - CONF_RTD_NOMINAL_RESISTANCE, CONF_RTD_WIRES, DEVICE_CLASS_TEMPERATURE, ICON_EMPTY, UNIT_CELSIUS +from esphome.const import ( + CONF_ID, + CONF_MAINS_FILTER, + CONF_REFERENCE_RESISTANCE, + CONF_RTD_NOMINAL_RESISTANCE, + CONF_RTD_WIRES, + DEVICE_CLASS_TEMPERATURE, + ICON_EMPTY, + UNIT_CELSIUS, +) -max31865_ns = cg.esphome_ns.namespace('max31865') -MAX31865Sensor = max31865_ns.class_('MAX31865Sensor', sensor.Sensor, cg.PollingComponent, - spi.SPIDevice) +max31865_ns = cg.esphome_ns.namespace("max31865") +MAX31865Sensor = max31865_ns.class_( + "MAX31865Sensor", sensor.Sensor, cg.PollingComponent, spi.SPIDevice +) -MAX31865ConfigFilter = max31865_ns.enum('MAX31865ConfigFilter') +MAX31865ConfigFilter = max31865_ns.enum("MAX31865ConfigFilter") FILTER = { - '50HZ': MAX31865ConfigFilter.FILTER_50HZ, - '60HZ': MAX31865ConfigFilter.FILTER_60HZ, + "50HZ": MAX31865ConfigFilter.FILTER_50HZ, + "60HZ": MAX31865ConfigFilter.FILTER_60HZ, } -CONFIG_SCHEMA = sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 2, DEVICE_CLASS_TEMPERATURE).extend({ - cv.GenerateID(): cv.declare_id(MAX31865Sensor), - cv.Required(CONF_REFERENCE_RESISTANCE): cv.All(cv.resistance, cv.Range(min=100, max=10000)), - cv.Required(CONF_RTD_NOMINAL_RESISTANCE): cv.All(cv.resistance, cv.Range(min=100, max=1000)), - cv.Optional(CONF_MAINS_FILTER, default='60HZ'): cv.enum(FILTER, upper=True, space=''), - cv.Optional(CONF_RTD_WIRES, default=4): cv.int_range(min=2, max=4), -}).extend(cv.polling_component_schema('60s')).extend(spi.spi_device_schema()) +CONFIG_SCHEMA = ( + sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 2, DEVICE_CLASS_TEMPERATURE) + .extend( + { + cv.GenerateID(): cv.declare_id(MAX31865Sensor), + cv.Required(CONF_REFERENCE_RESISTANCE): cv.All( + cv.resistance, cv.Range(min=100, max=10000) + ), + cv.Required(CONF_RTD_NOMINAL_RESISTANCE): cv.All( + cv.resistance, cv.Range(min=100, max=1000) + ), + cv.Optional(CONF_MAINS_FILTER, default="60HZ"): cv.enum( + FILTER, upper=True, space="" + ), + cv.Optional(CONF_RTD_WIRES, default=4): cv.int_range(min=2, max=4), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(spi.spi_device_schema()) +) def to_code(config): diff --git a/esphome/components/max6675/sensor.py b/esphome/components/max6675/sensor.py index f5b537ace9..0713654f84 100644 --- a/esphome/components/max6675/sensor.py +++ b/esphome/components/max6675/sensor.py @@ -3,13 +3,21 @@ import esphome.config_validation as cv from esphome.components import sensor, spi from esphome.const import CONF_ID, DEVICE_CLASS_TEMPERATURE, ICON_EMPTY, UNIT_CELSIUS -max6675_ns = cg.esphome_ns.namespace('max6675') -MAX6675Sensor = max6675_ns.class_('MAX6675Sensor', sensor.Sensor, cg.PollingComponent, - spi.SPIDevice) +max6675_ns = cg.esphome_ns.namespace("max6675") +MAX6675Sensor = max6675_ns.class_( + "MAX6675Sensor", sensor.Sensor, cg.PollingComponent, spi.SPIDevice +) -CONFIG_SCHEMA = sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE).extend({ - cv.GenerateID(): cv.declare_id(MAX6675Sensor), -}).extend(cv.polling_component_schema('60s')).extend(spi.spi_device_schema()) +CONFIG_SCHEMA = ( + sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE) + .extend( + { + cv.GenerateID(): cv.declare_id(MAX6675Sensor), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(spi.spi_device_schema()) +) def to_code(config): diff --git a/esphome/components/max7219/display.py b/esphome/components/max7219/display.py index 05b383004d..fba4d7ca76 100644 --- a/esphome/components/max7219/display.py +++ b/esphome/components/max7219/display.py @@ -3,21 +3,28 @@ import esphome.config_validation as cv from esphome.components import display, spi from esphome.const import CONF_ID, CONF_INTENSITY, CONF_LAMBDA, CONF_NUM_CHIPS -DEPENDENCIES = ['spi'] +DEPENDENCIES = ["spi"] -max7219_ns = cg.esphome_ns.namespace('max7219') -MAX7219Component = max7219_ns.class_('MAX7219Component', cg.PollingComponent, spi.SPIDevice) -MAX7219ComponentRef = MAX7219Component.operator('ref') +max7219_ns = cg.esphome_ns.namespace("max7219") +MAX7219Component = max7219_ns.class_( + "MAX7219Component", cg.PollingComponent, spi.SPIDevice +) +MAX7219ComponentRef = MAX7219Component.operator("ref") -CONF_REVERSE_ENABLE = 'reverse_enable' +CONF_REVERSE_ENABLE = "reverse_enable" -CONFIG_SCHEMA = display.BASIC_DISPLAY_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(MAX7219Component), - - cv.Optional(CONF_NUM_CHIPS, default=1): cv.int_range(min=1, max=255), - cv.Optional(CONF_INTENSITY, default=15): cv.int_range(min=0, max=15), - cv.Optional(CONF_REVERSE_ENABLE, default=False): cv.boolean, -}).extend(cv.polling_component_schema('1s')).extend(spi.spi_device_schema()) +CONFIG_SCHEMA = ( + display.BASIC_DISPLAY_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(MAX7219Component), + cv.Optional(CONF_NUM_CHIPS, default=1): cv.int_range(min=1, max=255), + cv.Optional(CONF_INTENSITY, default=15): cv.int_range(min=0, max=15), + cv.Optional(CONF_REVERSE_ENABLE, default=False): cv.boolean, + } + ) + .extend(cv.polling_component_schema("1s")) + .extend(spi.spi_device_schema()) +) def to_code(config): @@ -31,6 +38,7 @@ def to_code(config): cg.add(var.set_reverse(config[CONF_REVERSE_ENABLE])) if CONF_LAMBDA in config: - lambda_ = yield cg.process_lambda(config[CONF_LAMBDA], [(MAX7219ComponentRef, 'it')], - return_type=cg.void) + lambda_ = yield cg.process_lambda( + config[CONF_LAMBDA], [(MAX7219ComponentRef, "it")], return_type=cg.void + ) cg.add(var.set_writer(lambda_)) diff --git a/esphome/components/mcp23008/__init__.py b/esphome/components/mcp23008/__init__.py index 858b37b60a..4736765e64 100644 --- a/esphome/components/mcp23008/__init__.py +++ b/esphome/components/mcp23008/__init__.py @@ -2,26 +2,38 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins from esphome.components import i2c -from esphome.const import CONF_ID, CONF_NUMBER, CONF_MODE, CONF_INVERTED, CONF_OPEN_DRAIN_INTERRUPT +from esphome.const import ( + CONF_ID, + CONF_NUMBER, + CONF_MODE, + CONF_INVERTED, + CONF_OPEN_DRAIN_INTERRUPT, +) -DEPENDENCIES = ['i2c'] +DEPENDENCIES = ["i2c"] MULTI_CONF = True -mcp23008_ns = cg.esphome_ns.namespace('mcp23008') -MCP23008GPIOMode = mcp23008_ns.enum('MCP23008GPIOMode') +mcp23008_ns = cg.esphome_ns.namespace("mcp23008") +MCP23008GPIOMode = mcp23008_ns.enum("MCP23008GPIOMode") MCP23008_GPIO_MODES = { - 'INPUT': MCP23008GPIOMode.MCP23008_INPUT, - 'INPUT_PULLUP': MCP23008GPIOMode.MCP23008_INPUT_PULLUP, - 'OUTPUT': MCP23008GPIOMode.MCP23008_OUTPUT, + "INPUT": MCP23008GPIOMode.MCP23008_INPUT, + "INPUT_PULLUP": MCP23008GPIOMode.MCP23008_INPUT_PULLUP, + "OUTPUT": MCP23008GPIOMode.MCP23008_OUTPUT, } -MCP23008 = mcp23008_ns.class_('MCP23008', cg.Component, i2c.I2CDevice) -MCP23008GPIOPin = mcp23008_ns.class_('MCP23008GPIOPin', cg.GPIOPin) +MCP23008 = mcp23008_ns.class_("MCP23008", cg.Component, i2c.I2CDevice) +MCP23008GPIOPin = mcp23008_ns.class_("MCP23008GPIOPin", cg.GPIOPin) -CONFIG_SCHEMA = cv.Schema({ - cv.Required(CONF_ID): cv.declare_id(MCP23008), - cv.Optional(CONF_OPEN_DRAIN_INTERRUPT, default=False): cv.boolean, -}).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(0x20)) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.Required(CONF_ID): cv.declare_id(MCP23008), + cv.Optional(CONF_OPEN_DRAIN_INTERRUPT, default=False): cv.boolean, + } + ) + .extend(cv.COMPONENT_SCHEMA) + .extend(i2c.i2c_device_schema(0x20)) +) def to_code(config): @@ -31,23 +43,34 @@ def to_code(config): cg.add(var.set_open_drain_ints(config[CONF_OPEN_DRAIN_INTERRUPT])) -CONF_MCP23008 = 'mcp23008' -MCP23008_OUTPUT_PIN_SCHEMA = cv.Schema({ - cv.Required(CONF_MCP23008): cv.use_id(MCP23008), - cv.Required(CONF_NUMBER): cv.int_, - cv.Optional(CONF_MODE, default="OUTPUT"): cv.enum(MCP23008_GPIO_MODES, upper=True), - cv.Optional(CONF_INVERTED, default=False): cv.boolean, -}) -MCP23008_INPUT_PIN_SCHEMA = cv.Schema({ - cv.Required(CONF_MCP23008): cv.use_id(MCP23008), - cv.Required(CONF_NUMBER): cv.int_, - cv.Optional(CONF_MODE, default="INPUT"): cv.enum(MCP23008_GPIO_MODES, upper=True), - cv.Optional(CONF_INVERTED, default=False): cv.boolean, -}) +CONF_MCP23008 = "mcp23008" +MCP23008_OUTPUT_PIN_SCHEMA = cv.Schema( + { + cv.Required(CONF_MCP23008): cv.use_id(MCP23008), + cv.Required(CONF_NUMBER): cv.int_, + cv.Optional(CONF_MODE, default="OUTPUT"): cv.enum( + MCP23008_GPIO_MODES, upper=True + ), + cv.Optional(CONF_INVERTED, default=False): cv.boolean, + } +) +MCP23008_INPUT_PIN_SCHEMA = cv.Schema( + { + cv.Required(CONF_MCP23008): cv.use_id(MCP23008), + cv.Required(CONF_NUMBER): cv.int_, + cv.Optional(CONF_MODE, default="INPUT"): cv.enum( + MCP23008_GPIO_MODES, upper=True + ), + cv.Optional(CONF_INVERTED, default=False): cv.boolean, + } +) -@pins.PIN_SCHEMA_REGISTRY.register(CONF_MCP23008, - (MCP23008_OUTPUT_PIN_SCHEMA, MCP23008_INPUT_PIN_SCHEMA)) +@pins.PIN_SCHEMA_REGISTRY.register( + CONF_MCP23008, (MCP23008_OUTPUT_PIN_SCHEMA, MCP23008_INPUT_PIN_SCHEMA) +) def mcp23008_pin_to_code(config): parent = yield cg.get_variable(config[CONF_MCP23008]) - yield MCP23008GPIOPin.new(parent, config[CONF_NUMBER], config[CONF_MODE], config[CONF_INVERTED]) + yield MCP23008GPIOPin.new( + parent, config[CONF_NUMBER], config[CONF_MODE], config[CONF_INVERTED] + ) diff --git a/esphome/components/mcp23016/__init__.py b/esphome/components/mcp23016/__init__.py index 93c3d3843c..2aa29c9cde 100644 --- a/esphome/components/mcp23016/__init__.py +++ b/esphome/components/mcp23016/__init__.py @@ -4,22 +4,28 @@ from esphome import pins from esphome.components import i2c from esphome.const import CONF_ID, CONF_NUMBER, CONF_MODE, CONF_INVERTED -DEPENDENCIES = ['i2c'] +DEPENDENCIES = ["i2c"] MULTI_CONF = True -mcp23016_ns = cg.esphome_ns.namespace('mcp23016') -MCP23016GPIOMode = mcp23016_ns.enum('MCP23016GPIOMode') +mcp23016_ns = cg.esphome_ns.namespace("mcp23016") +MCP23016GPIOMode = mcp23016_ns.enum("MCP23016GPIOMode") MCP23016_GPIO_MODES = { - 'INPUT': MCP23016GPIOMode.MCP23016_INPUT, - 'OUTPUT': MCP23016GPIOMode.MCP23016_OUTPUT, + "INPUT": MCP23016GPIOMode.MCP23016_INPUT, + "OUTPUT": MCP23016GPIOMode.MCP23016_OUTPUT, } -MCP23016 = mcp23016_ns.class_('MCP23016', cg.Component, i2c.I2CDevice) -MCP23016GPIOPin = mcp23016_ns.class_('MCP23016GPIOPin', cg.GPIOPin) +MCP23016 = mcp23016_ns.class_("MCP23016", cg.Component, i2c.I2CDevice) +MCP23016GPIOPin = mcp23016_ns.class_("MCP23016GPIOPin", cg.GPIOPin) -CONFIG_SCHEMA = cv.Schema({ - cv.Required(CONF_ID): cv.declare_id(MCP23016), -}).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(0x20)) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.Required(CONF_ID): cv.declare_id(MCP23016), + } + ) + .extend(cv.COMPONENT_SCHEMA) + .extend(i2c.i2c_device_schema(0x20)) +) def to_code(config): @@ -28,23 +34,34 @@ def to_code(config): yield i2c.register_i2c_device(var, config) -CONF_MCP23016 = 'mcp23016' -MCP23016_OUTPUT_PIN_SCHEMA = cv.Schema({ - cv.Required(CONF_MCP23016): cv.use_id(MCP23016), - cv.Required(CONF_NUMBER): cv.int_, - cv.Optional(CONF_MODE, default="OUTPUT"): cv.enum(MCP23016_GPIO_MODES, upper=True), - cv.Optional(CONF_INVERTED, default=False): cv.boolean, -}) -MCP23016_INPUT_PIN_SCHEMA = cv.Schema({ - cv.Required(CONF_MCP23016): cv.use_id(MCP23016), - cv.Required(CONF_NUMBER): cv.int_, - cv.Optional(CONF_MODE, default="INPUT"): cv.enum(MCP23016_GPIO_MODES, upper=True), - cv.Optional(CONF_INVERTED, default=False): cv.boolean, -}) +CONF_MCP23016 = "mcp23016" +MCP23016_OUTPUT_PIN_SCHEMA = cv.Schema( + { + cv.Required(CONF_MCP23016): cv.use_id(MCP23016), + cv.Required(CONF_NUMBER): cv.int_, + cv.Optional(CONF_MODE, default="OUTPUT"): cv.enum( + MCP23016_GPIO_MODES, upper=True + ), + cv.Optional(CONF_INVERTED, default=False): cv.boolean, + } +) +MCP23016_INPUT_PIN_SCHEMA = cv.Schema( + { + cv.Required(CONF_MCP23016): cv.use_id(MCP23016), + cv.Required(CONF_NUMBER): cv.int_, + cv.Optional(CONF_MODE, default="INPUT"): cv.enum( + MCP23016_GPIO_MODES, upper=True + ), + cv.Optional(CONF_INVERTED, default=False): cv.boolean, + } +) -@pins.PIN_SCHEMA_REGISTRY.register(CONF_MCP23016, - (MCP23016_OUTPUT_PIN_SCHEMA, MCP23016_INPUT_PIN_SCHEMA)) +@pins.PIN_SCHEMA_REGISTRY.register( + CONF_MCP23016, (MCP23016_OUTPUT_PIN_SCHEMA, MCP23016_INPUT_PIN_SCHEMA) +) def mcp23016_pin_to_code(config): parent = yield cg.get_variable(config[CONF_MCP23016]) - yield MCP23016GPIOPin.new(parent, config[CONF_NUMBER], config[CONF_MODE], config[CONF_INVERTED]) + yield MCP23016GPIOPin.new( + parent, config[CONF_NUMBER], config[CONF_MODE], config[CONF_INVERTED] + ) diff --git a/esphome/components/mcp23017/__init__.py b/esphome/components/mcp23017/__init__.py index 34f94b293a..dae7a6fead 100644 --- a/esphome/components/mcp23017/__init__.py +++ b/esphome/components/mcp23017/__init__.py @@ -2,26 +2,38 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins from esphome.components import i2c -from esphome.const import CONF_ID, CONF_NUMBER, CONF_MODE, CONF_INVERTED, CONF_OPEN_DRAIN_INTERRUPT +from esphome.const import ( + CONF_ID, + CONF_NUMBER, + CONF_MODE, + CONF_INVERTED, + CONF_OPEN_DRAIN_INTERRUPT, +) -DEPENDENCIES = ['i2c'] +DEPENDENCIES = ["i2c"] MULTI_CONF = True -mcp23017_ns = cg.esphome_ns.namespace('mcp23017') -MCP23017GPIOMode = mcp23017_ns.enum('MCP23017GPIOMode') +mcp23017_ns = cg.esphome_ns.namespace("mcp23017") +MCP23017GPIOMode = mcp23017_ns.enum("MCP23017GPIOMode") MCP23017_GPIO_MODES = { - 'INPUT': MCP23017GPIOMode.MCP23017_INPUT, - 'INPUT_PULLUP': MCP23017GPIOMode.MCP23017_INPUT_PULLUP, - 'OUTPUT': MCP23017GPIOMode.MCP23017_OUTPUT, + "INPUT": MCP23017GPIOMode.MCP23017_INPUT, + "INPUT_PULLUP": MCP23017GPIOMode.MCP23017_INPUT_PULLUP, + "OUTPUT": MCP23017GPIOMode.MCP23017_OUTPUT, } -MCP23017 = mcp23017_ns.class_('MCP23017', cg.Component, i2c.I2CDevice) -MCP23017GPIOPin = mcp23017_ns.class_('MCP23017GPIOPin', cg.GPIOPin) +MCP23017 = mcp23017_ns.class_("MCP23017", cg.Component, i2c.I2CDevice) +MCP23017GPIOPin = mcp23017_ns.class_("MCP23017GPIOPin", cg.GPIOPin) -CONFIG_SCHEMA = cv.Schema({ - cv.Required(CONF_ID): cv.declare_id(MCP23017), - cv.Optional(CONF_OPEN_DRAIN_INTERRUPT, default=False): cv.boolean, -}).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(0x20)) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.Required(CONF_ID): cv.declare_id(MCP23017), + cv.Optional(CONF_OPEN_DRAIN_INTERRUPT, default=False): cv.boolean, + } + ) + .extend(cv.COMPONENT_SCHEMA) + .extend(i2c.i2c_device_schema(0x20)) +) def to_code(config): @@ -31,23 +43,34 @@ def to_code(config): cg.add(var.set_open_drain_ints(config[CONF_OPEN_DRAIN_INTERRUPT])) -CONF_MCP23017 = 'mcp23017' -MCP23017_OUTPUT_PIN_SCHEMA = cv.Schema({ - cv.Required(CONF_MCP23017): cv.use_id(MCP23017), - cv.Required(CONF_NUMBER): cv.int_, - cv.Optional(CONF_MODE, default="OUTPUT"): cv.enum(MCP23017_GPIO_MODES, upper=True), - cv.Optional(CONF_INVERTED, default=False): cv.boolean, -}) -MCP23017_INPUT_PIN_SCHEMA = cv.Schema({ - cv.Required(CONF_MCP23017): cv.use_id(MCP23017), - cv.Required(CONF_NUMBER): cv.int_, - cv.Optional(CONF_MODE, default="INPUT"): cv.enum(MCP23017_GPIO_MODES, upper=True), - cv.Optional(CONF_INVERTED, default=False): cv.boolean, -}) +CONF_MCP23017 = "mcp23017" +MCP23017_OUTPUT_PIN_SCHEMA = cv.Schema( + { + cv.Required(CONF_MCP23017): cv.use_id(MCP23017), + cv.Required(CONF_NUMBER): cv.int_, + cv.Optional(CONF_MODE, default="OUTPUT"): cv.enum( + MCP23017_GPIO_MODES, upper=True + ), + cv.Optional(CONF_INVERTED, default=False): cv.boolean, + } +) +MCP23017_INPUT_PIN_SCHEMA = cv.Schema( + { + cv.Required(CONF_MCP23017): cv.use_id(MCP23017), + cv.Required(CONF_NUMBER): cv.int_, + cv.Optional(CONF_MODE, default="INPUT"): cv.enum( + MCP23017_GPIO_MODES, upper=True + ), + cv.Optional(CONF_INVERTED, default=False): cv.boolean, + } +) -@pins.PIN_SCHEMA_REGISTRY.register(CONF_MCP23017, - (MCP23017_OUTPUT_PIN_SCHEMA, MCP23017_INPUT_PIN_SCHEMA)) +@pins.PIN_SCHEMA_REGISTRY.register( + CONF_MCP23017, (MCP23017_OUTPUT_PIN_SCHEMA, MCP23017_INPUT_PIN_SCHEMA) +) def mcp23017_pin_to_code(config): parent = yield cg.get_variable(config[CONF_MCP23017]) - yield MCP23017GPIOPin.new(parent, config[CONF_NUMBER], config[CONF_MODE], config[CONF_INVERTED]) + yield MCP23017GPIOPin.new( + parent, config[CONF_NUMBER], config[CONF_MODE], config[CONF_INVERTED] + ) diff --git a/esphome/components/mcp23s08/__init__.py b/esphome/components/mcp23s08/__init__.py index 1440f74f56..b0e047b6ba 100644 --- a/esphome/components/mcp23s08/__init__.py +++ b/esphome/components/mcp23s08/__init__.py @@ -4,28 +4,34 @@ from esphome import pins from esphome.components import spi from esphome.const import CONF_ID, CONF_NUMBER, CONF_MODE, CONF_INVERTED -CODEOWNERS = ['@SenexCrenshaw'] +CODEOWNERS = ["@SenexCrenshaw"] -DEPENDENCIES = ['spi'] +DEPENDENCIES = ["spi"] MULTI_CONF = True CONF_DEVICEADDRESS = "deviceaddress" -mcp23S08_ns = cg.esphome_ns.namespace('mcp23s08') -mcp23S08GPIOMode = mcp23S08_ns.enum('MCP23S08GPIOMode') +mcp23S08_ns = cg.esphome_ns.namespace("mcp23s08") +mcp23S08GPIOMode = mcp23S08_ns.enum("MCP23S08GPIOMode") mcp23S08_GPIO_MODES = { - 'INPUT': mcp23S08GPIOMode.MCP23S08_INPUT, - 'INPUT_PULLUP': mcp23S08GPIOMode.MCP23S08_INPUT_PULLUP, - 'OUTPUT': mcp23S08GPIOMode.MCP23S08_OUTPUT, + "INPUT": mcp23S08GPIOMode.MCP23S08_INPUT, + "INPUT_PULLUP": mcp23S08GPIOMode.MCP23S08_INPUT_PULLUP, + "OUTPUT": mcp23S08GPIOMode.MCP23S08_OUTPUT, } -mcp23S08 = mcp23S08_ns.class_('MCP23S08', cg.Component, spi.SPIDevice) -mcp23S08GPIOPin = mcp23S08_ns.class_('MCP23S08GPIOPin', cg.GPIOPin) +mcp23S08 = mcp23S08_ns.class_("MCP23S08", cg.Component, spi.SPIDevice) +mcp23S08GPIOPin = mcp23S08_ns.class_("MCP23S08GPIOPin", cg.GPIOPin) -CONFIG_SCHEMA = cv.Schema({ - cv.Required(CONF_ID): cv.declare_id(mcp23S08), - cv.Optional(CONF_DEVICEADDRESS, default=0): cv.uint8_t, -}).extend(cv.COMPONENT_SCHEMA).extend(spi.spi_device_schema()) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.Required(CONF_ID): cv.declare_id(mcp23S08), + cv.Optional(CONF_DEVICEADDRESS, default=0): cv.uint8_t, + } + ) + .extend(cv.COMPONENT_SCHEMA) + .extend(spi.spi_device_schema()) +) def to_code(config): @@ -35,24 +41,35 @@ def to_code(config): yield spi.register_spi_device(var, config) -CONF_MCP23S08 = 'mcp23s08' +CONF_MCP23S08 = "mcp23s08" -mcp23S08_OUTPUT_PIN_SCHEMA = cv.Schema({ - cv.GenerateID(CONF_MCP23S08): cv.use_id(mcp23S08), - cv.Required(CONF_NUMBER): cv.int_, - cv.Optional(CONF_MODE, default="OUTPUT"): cv.enum(mcp23S08_GPIO_MODES, upper=True), - cv.Optional(CONF_INVERTED, default=False): cv.boolean, -}) -mcp23S08_INPUT_PIN_SCHEMA = cv.Schema({ - cv.GenerateID(CONF_MCP23S08): cv.use_id(mcp23S08), - cv.Required(CONF_NUMBER): cv.int_range(0, 7), - cv.Optional(CONF_MODE, default="INPUT"): cv.enum(mcp23S08_GPIO_MODES, upper=True), - cv.Optional(CONF_INVERTED, default=False): cv.boolean, -}) +mcp23S08_OUTPUT_PIN_SCHEMA = cv.Schema( + { + cv.GenerateID(CONF_MCP23S08): cv.use_id(mcp23S08), + cv.Required(CONF_NUMBER): cv.int_, + cv.Optional(CONF_MODE, default="OUTPUT"): cv.enum( + mcp23S08_GPIO_MODES, upper=True + ), + cv.Optional(CONF_INVERTED, default=False): cv.boolean, + } +) +mcp23S08_INPUT_PIN_SCHEMA = cv.Schema( + { + cv.GenerateID(CONF_MCP23S08): cv.use_id(mcp23S08), + cv.Required(CONF_NUMBER): cv.int_range(0, 7), + cv.Optional(CONF_MODE, default="INPUT"): cv.enum( + mcp23S08_GPIO_MODES, upper=True + ), + cv.Optional(CONF_INVERTED, default=False): cv.boolean, + } +) -@pins.PIN_SCHEMA_REGISTRY.register(CONF_MCP23S08, - (mcp23S08_OUTPUT_PIN_SCHEMA, mcp23S08_INPUT_PIN_SCHEMA)) +@pins.PIN_SCHEMA_REGISTRY.register( + CONF_MCP23S08, (mcp23S08_OUTPUT_PIN_SCHEMA, mcp23S08_INPUT_PIN_SCHEMA) +) def mcp23S08_pin_to_code(config): parent = yield cg.get_variable(config[CONF_MCP23S08]) - yield mcp23S08GPIOPin.new(parent, config[CONF_NUMBER], config[CONF_MODE], config[CONF_INVERTED]) + yield mcp23S08GPIOPin.new( + parent, config[CONF_NUMBER], config[CONF_MODE], config[CONF_INVERTED] + ) diff --git a/esphome/components/mcp23s17/__init__.py b/esphome/components/mcp23s17/__init__.py index c0c06d495c..85f22874a6 100644 --- a/esphome/components/mcp23s17/__init__.py +++ b/esphome/components/mcp23s17/__init__.py @@ -4,28 +4,34 @@ from esphome import pins from esphome.components import spi from esphome.const import CONF_ID, CONF_NUMBER, CONF_MODE, CONF_INVERTED -CODEOWNERS = ['@SenexCrenshaw'] +CODEOWNERS = ["@SenexCrenshaw"] -DEPENDENCIES = ['spi'] +DEPENDENCIES = ["spi"] MULTI_CONF = True CONF_DEVICEADDRESS = "deviceaddress" -mcp23S17_ns = cg.esphome_ns.namespace('mcp23s17') -mcp23S17GPIOMode = mcp23S17_ns.enum('MCP23S17GPIOMode') +mcp23S17_ns = cg.esphome_ns.namespace("mcp23s17") +mcp23S17GPIOMode = mcp23S17_ns.enum("MCP23S17GPIOMode") mcp23S17_GPIO_MODES = { - 'INPUT': mcp23S17GPIOMode.MCP23S17_INPUT, - 'INPUT_PULLUP': mcp23S17GPIOMode.MCP23S17_INPUT_PULLUP, - 'OUTPUT': mcp23S17GPIOMode.MCP23S17_OUTPUT, + "INPUT": mcp23S17GPIOMode.MCP23S17_INPUT, + "INPUT_PULLUP": mcp23S17GPIOMode.MCP23S17_INPUT_PULLUP, + "OUTPUT": mcp23S17GPIOMode.MCP23S17_OUTPUT, } -mcp23S17 = mcp23S17_ns.class_('MCP23S17', cg.Component, spi.SPIDevice) -mcp23S17GPIOPin = mcp23S17_ns.class_('MCP23S17GPIOPin', cg.GPIOPin) +mcp23S17 = mcp23S17_ns.class_("MCP23S17", cg.Component, spi.SPIDevice) +mcp23S17GPIOPin = mcp23S17_ns.class_("MCP23S17GPIOPin", cg.GPIOPin) -CONFIG_SCHEMA = cv.Schema({ - cv.Required(CONF_ID): cv.declare_id(mcp23S17), - cv.Optional(CONF_DEVICEADDRESS, default=0): cv.uint8_t, -}).extend(cv.COMPONENT_SCHEMA).extend(spi.spi_device_schema()) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.Required(CONF_ID): cv.declare_id(mcp23S17), + cv.Optional(CONF_DEVICEADDRESS, default=0): cv.uint8_t, + } + ) + .extend(cv.COMPONENT_SCHEMA) + .extend(spi.spi_device_schema()) +) def to_code(config): @@ -35,24 +41,35 @@ def to_code(config): yield spi.register_spi_device(var, config) -CONF_MCP23S17 = 'mcp23s17' +CONF_MCP23S17 = "mcp23s17" -mcp23S17_OUTPUT_PIN_SCHEMA = cv.Schema({ - cv.GenerateID(CONF_MCP23S17): cv.use_id(mcp23S17), - cv.Required(CONF_NUMBER): cv.int_, - cv.Optional(CONF_MODE, default="OUTPUT"): cv.enum(mcp23S17_GPIO_MODES, upper=True), - cv.Optional(CONF_INVERTED, default=False): cv.boolean, -}) -mcp23S17_INPUT_PIN_SCHEMA = cv.Schema({ - cv.Required(CONF_MCP23S17): cv.use_id(mcp23S17), - cv.Required(CONF_NUMBER): cv.int_range(0, 15), - cv.Optional(CONF_MODE, default="INPUT"): cv.enum(mcp23S17_GPIO_MODES, upper=True), - cv.Optional(CONF_INVERTED, default=False): cv.boolean, -}) +mcp23S17_OUTPUT_PIN_SCHEMA = cv.Schema( + { + cv.GenerateID(CONF_MCP23S17): cv.use_id(mcp23S17), + cv.Required(CONF_NUMBER): cv.int_, + cv.Optional(CONF_MODE, default="OUTPUT"): cv.enum( + mcp23S17_GPIO_MODES, upper=True + ), + cv.Optional(CONF_INVERTED, default=False): cv.boolean, + } +) +mcp23S17_INPUT_PIN_SCHEMA = cv.Schema( + { + cv.Required(CONF_MCP23S17): cv.use_id(mcp23S17), + cv.Required(CONF_NUMBER): cv.int_range(0, 15), + cv.Optional(CONF_MODE, default="INPUT"): cv.enum( + mcp23S17_GPIO_MODES, upper=True + ), + cv.Optional(CONF_INVERTED, default=False): cv.boolean, + } +) -@pins.PIN_SCHEMA_REGISTRY.register(CONF_MCP23S17, - (mcp23S17_OUTPUT_PIN_SCHEMA, mcp23S17_INPUT_PIN_SCHEMA)) +@pins.PIN_SCHEMA_REGISTRY.register( + CONF_MCP23S17, (mcp23S17_OUTPUT_PIN_SCHEMA, mcp23S17_INPUT_PIN_SCHEMA) +) def mcp23S17_pin_to_code(config): parent = yield cg.get_variable(config[CONF_MCP23S17]) - yield mcp23S17GPIOPin.new(parent, config[CONF_NUMBER], config[CONF_MODE], config[CONF_INVERTED]) + yield mcp23S17GPIOPin.new( + parent, config[CONF_NUMBER], config[CONF_MODE], config[CONF_INVERTED] + ) diff --git a/esphome/components/mcp2515/canbus.py b/esphome/components/mcp2515/canbus.py index a877507e36..68e28d45b8 100644 --- a/esphome/components/mcp2515/canbus.py +++ b/esphome/components/mcp2515/canbus.py @@ -4,33 +4,35 @@ from esphome.components import spi, canbus from esphome.const import CONF_ID, CONF_MODE from esphome.components.canbus import CanbusComponent -CODEOWNERS = ['@mvturnho', '@danielschramm'] -DEPENDENCIES = ['spi'] +CODEOWNERS = ["@mvturnho", "@danielschramm"] +DEPENDENCIES = ["spi"] -CONF_CLOCK = 'clock' +CONF_CLOCK = "clock" -mcp2515_ns = cg.esphome_ns.namespace('mcp2515') -mcp2515 = mcp2515_ns.class_('MCP2515', CanbusComponent, spi.SPIDevice) -CanClock = mcp2515_ns.enum('CAN_CLOCK') -McpMode = mcp2515_ns.enum('CANCTRL_REQOP_MODE') +mcp2515_ns = cg.esphome_ns.namespace("mcp2515") +mcp2515 = mcp2515_ns.class_("MCP2515", CanbusComponent, spi.SPIDevice) +CanClock = mcp2515_ns.enum("CAN_CLOCK") +McpMode = mcp2515_ns.enum("CANCTRL_REQOP_MODE") CAN_CLOCK = { - '8MHZ': CanClock.MCP_8MHZ, - '16MHZ': CanClock.MCP_16MHZ, - '20MHZ': CanClock.MCP_20MHZ, + "8MHZ": CanClock.MCP_8MHZ, + "16MHZ": CanClock.MCP_16MHZ, + "20MHZ": CanClock.MCP_20MHZ, } MCP_MODE = { - 'NORMAL': McpMode.CANCTRL_REQOP_NORMAL, - 'LOOPBACK': McpMode.CANCTRL_REQOP_LOOPBACK, - 'LISTENONLY': McpMode.CANCTRL_REQOP_LISTENONLY, + "NORMAL": McpMode.CANCTRL_REQOP_NORMAL, + "LOOPBACK": McpMode.CANCTRL_REQOP_LOOPBACK, + "LISTENONLY": McpMode.CANCTRL_REQOP_LISTENONLY, } -CONFIG_SCHEMA = canbus.CONFIG_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(mcp2515), - cv.Optional(CONF_CLOCK, default='8MHZ'): cv.enum(CAN_CLOCK, upper=True), - cv.Optional(CONF_MODE, default='NORMAL'): cv.enum(MCP_MODE, upper=True), -}).extend(spi.spi_device_schema(True)) +CONFIG_SCHEMA = canbus.CONFIG_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(mcp2515), + cv.Optional(CONF_CLOCK, default="8MHZ"): cv.enum(CAN_CLOCK, upper=True), + cv.Optional(CONF_MODE, default="NORMAL"): cv.enum(MCP_MODE, upper=True), + } +).extend(spi.spi_device_schema(True)) def to_code(config): diff --git a/esphome/components/mcp3008/__init__.py b/esphome/components/mcp3008/__init__.py index acacc96159..6a0410a75b 100644 --- a/esphome/components/mcp3008/__init__.py +++ b/esphome/components/mcp3008/__init__.py @@ -3,18 +3,20 @@ import esphome.config_validation as cv from esphome.components import spi from esphome.const import CONF_ID -DEPENDENCIES = ['spi'] -AUTO_LOAD = ['sensor'] +DEPENDENCIES = ["spi"] +AUTO_LOAD = ["sensor"] MULTI_CONF = True -CONF_MCP3008 = 'mcp3008' +CONF_MCP3008 = "mcp3008" -mcp3008_ns = cg.esphome_ns.namespace('mcp3008') -MCP3008 = mcp3008_ns.class_('MCP3008', cg.Component, spi.SPIDevice) +mcp3008_ns = cg.esphome_ns.namespace("mcp3008") +MCP3008 = mcp3008_ns.class_("MCP3008", cg.Component, spi.SPIDevice) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(MCP3008), -}).extend(spi.spi_device_schema(cs_pin_required=True)) +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(MCP3008), + } +).extend(spi.spi_device_schema(cs_pin_required=True)) def to_code(config): diff --git a/esphome/components/mcp3008/sensor.py b/esphome/components/mcp3008/sensor.py index 9248d51a42..b146158552 100644 --- a/esphome/components/mcp3008/sensor.py +++ b/esphome/components/mcp3008/sensor.py @@ -4,26 +4,34 @@ from esphome.components import sensor, voltage_sampler from esphome.const import CONF_ID, CONF_NUMBER, CONF_NAME from . import mcp3008_ns, MCP3008 -AUTO_LOAD = ['voltage_sampler'] +AUTO_LOAD = ["voltage_sampler"] -DEPENDENCIES = ['mcp3008'] +DEPENDENCIES = ["mcp3008"] -MCP3008Sensor = mcp3008_ns.class_('MCP3008Sensor', sensor.Sensor, cg.PollingComponent, - voltage_sampler.VoltageSampler) -CONF_REFERENCE_VOLTAGE = 'reference_voltage' -CONF_MCP3008_ID = 'mcp3008_id' +MCP3008Sensor = mcp3008_ns.class_( + "MCP3008Sensor", sensor.Sensor, cg.PollingComponent, voltage_sampler.VoltageSampler +) +CONF_REFERENCE_VOLTAGE = "reference_voltage" +CONF_MCP3008_ID = "mcp3008_id" -CONFIG_SCHEMA = sensor.SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(MCP3008Sensor), - cv.GenerateID(CONF_MCP3008_ID): cv.use_id(MCP3008), - cv.Required(CONF_NUMBER): cv.int_, - cv.Optional(CONF_REFERENCE_VOLTAGE, default='3.3V'): cv.voltage, -}).extend(cv.polling_component_schema('1s')) +CONFIG_SCHEMA = sensor.SENSOR_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(MCP3008Sensor), + cv.GenerateID(CONF_MCP3008_ID): cv.use_id(MCP3008), + cv.Required(CONF_NUMBER): cv.int_, + cv.Optional(CONF_REFERENCE_VOLTAGE, default="3.3V"): cv.voltage, + } +).extend(cv.polling_component_schema("1s")) def to_code(config): parent = yield cg.get_variable(config[CONF_MCP3008_ID]) - var = cg.new_Pvariable(config[CONF_ID], parent, config[CONF_NAME], - config[CONF_NUMBER], config[CONF_REFERENCE_VOLTAGE]) + var = cg.new_Pvariable( + config[CONF_ID], + parent, + config[CONF_NAME], + config[CONF_NUMBER], + config[CONF_REFERENCE_VOLTAGE], + ) yield cg.register_component(var, config) yield sensor.register_sensor(var, config) diff --git a/esphome/components/mcp4725/output.py b/esphome/components/mcp4725/output.py index f9593db1f5..a010ca9970 100644 --- a/esphome/components/mcp4725/output.py +++ b/esphome/components/mcp4725/output.py @@ -3,14 +3,20 @@ import esphome.codegen as cg from esphome.components import output, i2c from esphome.const import CONF_ID -DEPENDENCIES = ['i2c'] +DEPENDENCIES = ["i2c"] -mcp4725 = cg.esphome_ns.namespace('mcp4725') -MCP4725 = mcp4725.class_('MCP4725', output.FloatOutput, cg.Component, i2c.I2CDevice) +mcp4725 = cg.esphome_ns.namespace("mcp4725") +MCP4725 = mcp4725.class_("MCP4725", output.FloatOutput, cg.Component, i2c.I2CDevice) -CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend({ - cv.Required(CONF_ID): cv.declare_id(MCP4725), -}).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(0x60)) +CONFIG_SCHEMA = ( + output.FLOAT_OUTPUT_SCHEMA.extend( + { + cv.Required(CONF_ID): cv.declare_id(MCP4725), + } + ) + .extend(cv.COMPONENT_SCHEMA) + .extend(i2c.i2c_device_schema(0x60)) +) def to_code(config): diff --git a/esphome/components/mcp9808/sensor.py b/esphome/components/mcp9808/sensor.py index 0489dd5e1b..4973d41ff5 100644 --- a/esphome/components/mcp9808/sensor.py +++ b/esphome/components/mcp9808/sensor.py @@ -3,16 +3,24 @@ import esphome.config_validation as cv from esphome.components import i2c, sensor from esphome.const import CONF_ID, DEVICE_CLASS_TEMPERATURE, ICON_EMPTY, UNIT_CELSIUS -CODEOWNERS = ['@k7hpn'] -DEPENDENCIES = ['i2c'] +CODEOWNERS = ["@k7hpn"] +DEPENDENCIES = ["i2c"] -mcp9808_ns = cg.esphome_ns.namespace('mcp9808') -MCP9808Sensor = mcp9808_ns.class_('MCP9808Sensor', sensor.Sensor, cg.PollingComponent, - i2c.I2CDevice) +mcp9808_ns = cg.esphome_ns.namespace("mcp9808") +MCP9808Sensor = mcp9808_ns.class_( + "MCP9808Sensor", sensor.Sensor, cg.PollingComponent, i2c.I2CDevice +) -CONFIG_SCHEMA = sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE).extend({ - cv.GenerateID(): cv.declare_id(MCP9808Sensor), -}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x18)) +CONFIG_SCHEMA = ( + sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE) + .extend( + { + cv.GenerateID(): cv.declare_id(MCP9808Sensor), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x18)) +) def to_code(config): diff --git a/esphome/components/mhz19/sensor.py b/esphome/components/mhz19/sensor.py index aa7db6b775..6989814ada 100644 --- a/esphome/components/mhz19/sensor.py +++ b/esphome/components/mhz19/sensor.py @@ -3,27 +3,46 @@ import esphome.config_validation as cv from esphome import automation from esphome.automation import maybe_simple_id from esphome.components import sensor, uart -from esphome.const import CONF_CO2, CONF_ID, CONF_TEMPERATURE, DEVICE_CLASS_EMPTY, \ - DEVICE_CLASS_TEMPERATURE, ICON_MOLECULE_CO2, UNIT_PARTS_PER_MILLION, UNIT_CELSIUS, ICON_EMPTY +from esphome.const import ( + CONF_CO2, + CONF_ID, + CONF_TEMPERATURE, + DEVICE_CLASS_EMPTY, + DEVICE_CLASS_TEMPERATURE, + ICON_MOLECULE_CO2, + UNIT_PARTS_PER_MILLION, + UNIT_CELSIUS, + ICON_EMPTY, +) -DEPENDENCIES = ['uart'] +DEPENDENCIES = ["uart"] -CONF_AUTOMATIC_BASELINE_CALIBRATION = 'automatic_baseline_calibration' +CONF_AUTOMATIC_BASELINE_CALIBRATION = "automatic_baseline_calibration" -mhz19_ns = cg.esphome_ns.namespace('mhz19') -MHZ19Component = mhz19_ns.class_('MHZ19Component', cg.PollingComponent, uart.UARTDevice) -MHZ19CalibrateZeroAction = mhz19_ns.class_('MHZ19CalibrateZeroAction', automation.Action) -MHZ19ABCEnableAction = mhz19_ns.class_('MHZ19ABCEnableAction', automation.Action) -MHZ19ABCDisableAction = mhz19_ns.class_('MHZ19ABCDisableAction', automation.Action) +mhz19_ns = cg.esphome_ns.namespace("mhz19") +MHZ19Component = mhz19_ns.class_("MHZ19Component", cg.PollingComponent, uart.UARTDevice) +MHZ19CalibrateZeroAction = mhz19_ns.class_( + "MHZ19CalibrateZeroAction", automation.Action +) +MHZ19ABCEnableAction = mhz19_ns.class_("MHZ19ABCEnableAction", automation.Action) +MHZ19ABCDisableAction = mhz19_ns.class_("MHZ19ABCDisableAction", automation.Action) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(MHZ19Component), - cv.Required(CONF_CO2): sensor.sensor_schema( - UNIT_PARTS_PER_MILLION, ICON_MOLECULE_CO2, 0, DEVICE_CLASS_EMPTY), - cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( - UNIT_CELSIUS, ICON_EMPTY, 0, DEVICE_CLASS_TEMPERATURE), - cv.Optional(CONF_AUTOMATIC_BASELINE_CALIBRATION): cv.boolean, -}).extend(cv.polling_component_schema('60s')).extend(uart.UART_DEVICE_SCHEMA) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(MHZ19Component), + cv.Required(CONF_CO2): sensor.sensor_schema( + UNIT_PARTS_PER_MILLION, ICON_MOLECULE_CO2, 0, DEVICE_CLASS_EMPTY + ), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( + UNIT_CELSIUS, ICON_EMPTY, 0, DEVICE_CLASS_TEMPERATURE + ), + cv.Optional(CONF_AUTOMATIC_BASELINE_CALIBRATION): cv.boolean, + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(uart.UART_DEVICE_SCHEMA) +) def to_code(config): @@ -43,17 +62,22 @@ def to_code(config): cg.add(var.set_abc_enabled(config[CONF_AUTOMATIC_BASELINE_CALIBRATION])) -CALIBRATION_ACTION_SCHEMA = maybe_simple_id({ - cv.Required(CONF_ID): cv.use_id(MHZ19Component), -}) +CALIBRATION_ACTION_SCHEMA = maybe_simple_id( + { + cv.Required(CONF_ID): cv.use_id(MHZ19Component), + } +) -@automation.register_action('mhz19.calibrate_zero', MHZ19CalibrateZeroAction, - CALIBRATION_ACTION_SCHEMA) -@automation.register_action('mhz19.abc_enable', MHZ19ABCEnableAction, - CALIBRATION_ACTION_SCHEMA) -@automation.register_action('mhz19.abc_disable', MHZ19ABCDisableAction, - CALIBRATION_ACTION_SCHEMA) +@automation.register_action( + "mhz19.calibrate_zero", MHZ19CalibrateZeroAction, CALIBRATION_ACTION_SCHEMA +) +@automation.register_action( + "mhz19.abc_enable", MHZ19ABCEnableAction, CALIBRATION_ACTION_SCHEMA +) +@automation.register_action( + "mhz19.abc_disable", MHZ19ABCDisableAction, CALIBRATION_ACTION_SCHEMA +) def mhz19_calibration_to_code(config, action_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) yield cg.new_Pvariable(action_id, template_arg, paren) diff --git a/esphome/components/mitsubishi/climate.py b/esphome/components/mitsubishi/climate.py index 933e53baf0..c08baf7e54 100644 --- a/esphome/components/mitsubishi/climate.py +++ b/esphome/components/mitsubishi/climate.py @@ -3,14 +3,16 @@ import esphome.config_validation as cv from esphome.components import climate_ir from esphome.const import CONF_ID -AUTO_LOAD = ['climate_ir'] +AUTO_LOAD = ["climate_ir"] -mitsubishi_ns = cg.esphome_ns.namespace('mitsubishi') -MitsubishiClimate = mitsubishi_ns.class_('MitsubishiClimate', climate_ir.ClimateIR) +mitsubishi_ns = cg.esphome_ns.namespace("mitsubishi") +MitsubishiClimate = mitsubishi_ns.class_("MitsubishiClimate", climate_ir.ClimateIR) -CONFIG_SCHEMA = climate_ir.CLIMATE_IR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(MitsubishiClimate), -}) +CONFIG_SCHEMA = climate_ir.CLIMATE_IR_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(MitsubishiClimate), + } +) def to_code(config): diff --git a/esphome/components/modbus/__init__.py b/esphome/components/modbus/__init__.py index cada835905..e71f196fca 100644 --- a/esphome/components/modbus/__init__.py +++ b/esphome/components/modbus/__init__.py @@ -4,17 +4,23 @@ from esphome.components import uart from esphome.const import CONF_ID, CONF_ADDRESS from esphome.core import coroutine -DEPENDENCIES = ['uart'] +DEPENDENCIES = ["uart"] -modbus_ns = cg.esphome_ns.namespace('modbus') -Modbus = modbus_ns.class_('Modbus', cg.Component, uart.UARTDevice) -ModbusDevice = modbus_ns.class_('ModbusDevice') +modbus_ns = cg.esphome_ns.namespace("modbus") +Modbus = modbus_ns.class_("Modbus", cg.Component, uart.UARTDevice) +ModbusDevice = modbus_ns.class_("ModbusDevice") MULTI_CONF = True -CONF_MODBUS_ID = 'modbus_id' -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(Modbus), -}).extend(cv.COMPONENT_SCHEMA).extend(uart.UART_DEVICE_SCHEMA) +CONF_MODBUS_ID = "modbus_id" +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(Modbus), + } + ) + .extend(cv.COMPONENT_SCHEMA) + .extend(uart.UART_DEVICE_SCHEMA) +) def to_code(config): diff --git a/esphome/components/monochromatic/light.py b/esphome/components/monochromatic/light.py index 79faacff6c..32d9981a57 100644 --- a/esphome/components/monochromatic/light.py +++ b/esphome/components/monochromatic/light.py @@ -3,13 +3,17 @@ import esphome.config_validation as cv from esphome.components import light, output from esphome.const import CONF_OUTPUT_ID, CONF_OUTPUT -monochromatic_ns = cg.esphome_ns.namespace('monochromatic') -MonochromaticLightOutput = monochromatic_ns.class_('MonochromaticLightOutput', light.LightOutput) +monochromatic_ns = cg.esphome_ns.namespace("monochromatic") +MonochromaticLightOutput = monochromatic_ns.class_( + "MonochromaticLightOutput", light.LightOutput +) -CONFIG_SCHEMA = light.BRIGHTNESS_ONLY_LIGHT_SCHEMA.extend({ - cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(MonochromaticLightOutput), - cv.Required(CONF_OUTPUT): cv.use_id(output.FloatOutput), -}) +CONFIG_SCHEMA = light.BRIGHTNESS_ONLY_LIGHT_SCHEMA.extend( + { + cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(MonochromaticLightOutput), + cv.Required(CONF_OUTPUT): cv.use_id(output.FloatOutput), + } +) def to_code(config): diff --git a/esphome/components/mpr121/__init__.py b/esphome/components/mpr121/__init__.py index b1ef9eaef5..eb3043c2b1 100644 --- a/esphome/components/mpr121/__init__.py +++ b/esphome/components/mpr121/__init__.py @@ -8,21 +8,31 @@ CONF_RELEASE_THRESHOLD = "release_threshold" CONF_TOUCH_DEBOUNCE = "touch_debounce" CONF_RELEASE_DEBOUNCE = "release_debounce" -DEPENDENCIES = ['i2c'] -AUTO_LOAD = ['binary_sensor'] +DEPENDENCIES = ["i2c"] +AUTO_LOAD = ["binary_sensor"] -mpr121_ns = cg.esphome_ns.namespace('mpr121') -CONF_MPR121_ID = 'mpr121_id' -MPR121Component = mpr121_ns.class_('MPR121Component', cg.Component, i2c.I2CDevice) +mpr121_ns = cg.esphome_ns.namespace("mpr121") +CONF_MPR121_ID = "mpr121_id" +MPR121Component = mpr121_ns.class_("MPR121Component", cg.Component, i2c.I2CDevice) MULTI_CONF = True -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(MPR121Component), - cv.Optional(CONF_RELEASE_DEBOUNCE, default=0): cv.int_range(min=0, max=7), - cv.Optional(CONF_TOUCH_DEBOUNCE, default=0): cv.int_range(min=0, max=7), - cv.Optional(CONF_TOUCH_THRESHOLD, default=0x0b): cv.int_range(min=0x05, max=0x30), - cv.Optional(CONF_RELEASE_THRESHOLD, default=0x06): cv.int_range(min=0x05, max=0x30), -}).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(0x5A)) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(MPR121Component), + cv.Optional(CONF_RELEASE_DEBOUNCE, default=0): cv.int_range(min=0, max=7), + cv.Optional(CONF_TOUCH_DEBOUNCE, default=0): cv.int_range(min=0, max=7), + cv.Optional(CONF_TOUCH_THRESHOLD, default=0x0B): cv.int_range( + min=0x05, max=0x30 + ), + cv.Optional(CONF_RELEASE_THRESHOLD, default=0x06): cv.int_range( + min=0x05, max=0x30 + ), + } + ) + .extend(cv.COMPONENT_SCHEMA) + .extend(i2c.i2c_device_schema(0x5A)) +) def to_code(config): diff --git a/esphome/components/mpr121/binary_sensor.py b/esphome/components/mpr121/binary_sensor.py index dddfeb40e1..68e56075f5 100644 --- a/esphome/components/mpr121/binary_sensor.py +++ b/esphome/components/mpr121/binary_sensor.py @@ -2,19 +2,26 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import binary_sensor from esphome.const import CONF_CHANNEL, CONF_ID -from . import mpr121_ns, MPR121Component, CONF_MPR121_ID, CONF_TOUCH_THRESHOLD, \ - CONF_RELEASE_THRESHOLD +from . import ( + mpr121_ns, + MPR121Component, + CONF_MPR121_ID, + CONF_TOUCH_THRESHOLD, + CONF_RELEASE_THRESHOLD, +) -DEPENDENCIES = ['mpr121'] -MPR121Channel = mpr121_ns.class_('MPR121Channel', binary_sensor.BinarySensor) +DEPENDENCIES = ["mpr121"] +MPR121Channel = mpr121_ns.class_("MPR121Channel", binary_sensor.BinarySensor) -CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(MPR121Channel), - cv.GenerateID(CONF_MPR121_ID): cv.use_id(MPR121Component), - cv.Required(CONF_CHANNEL): cv.int_range(min=0, max=11), - cv.Optional(CONF_TOUCH_THRESHOLD): cv.int_range(min=0x05, max=0x30), - cv.Optional(CONF_RELEASE_THRESHOLD): cv.int_range(min=0x05, max=0x30), -}) +CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(MPR121Channel), + cv.GenerateID(CONF_MPR121_ID): cv.use_id(MPR121Component), + cv.Required(CONF_CHANNEL): cv.int_range(min=0, max=11), + cv.Optional(CONF_TOUCH_THRESHOLD): cv.int_range(min=0x05, max=0x30), + cv.Optional(CONF_RELEASE_THRESHOLD): cv.int_range(min=0x05, max=0x30), + } +) def to_code(config): diff --git a/esphome/components/mpu6050/sensor.py b/esphome/components/mpu6050/sensor.py index 1a02d2ce7c..11d491006e 100644 --- a/esphome/components/mpu6050/sensor.py +++ b/esphome/components/mpu6050/sensor.py @@ -1,38 +1,59 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import CONF_ID, CONF_TEMPERATURE, DEVICE_CLASS_EMPTY, DEVICE_CLASS_TEMPERATURE, \ - ICON_BRIEFCASE_DOWNLOAD, ICON_EMPTY, UNIT_METER_PER_SECOND_SQUARED, \ - ICON_SCREEN_ROTATION, UNIT_DEGREE_PER_SECOND, UNIT_CELSIUS +from esphome.const import ( + CONF_ID, + CONF_TEMPERATURE, + DEVICE_CLASS_EMPTY, + DEVICE_CLASS_TEMPERATURE, + ICON_BRIEFCASE_DOWNLOAD, + ICON_EMPTY, + UNIT_METER_PER_SECOND_SQUARED, + ICON_SCREEN_ROTATION, + UNIT_DEGREE_PER_SECOND, + UNIT_CELSIUS, +) -DEPENDENCIES = ['i2c'] +DEPENDENCIES = ["i2c"] -CONF_ACCEL_X = 'accel_x' -CONF_ACCEL_Y = 'accel_y' -CONF_ACCEL_Z = 'accel_z' -CONF_GYRO_X = 'gyro_x' -CONF_GYRO_Y = 'gyro_y' -CONF_GYRO_Z = 'gyro_z' +CONF_ACCEL_X = "accel_x" +CONF_ACCEL_Y = "accel_y" +CONF_ACCEL_Z = "accel_z" +CONF_GYRO_X = "gyro_x" +CONF_GYRO_Y = "gyro_y" +CONF_GYRO_Z = "gyro_z" -mpu6050_ns = cg.esphome_ns.namespace('mpu6050') -MPU6050Component = mpu6050_ns.class_('MPU6050Component', cg.PollingComponent, i2c.I2CDevice) +mpu6050_ns = cg.esphome_ns.namespace("mpu6050") +MPU6050Component = mpu6050_ns.class_( + "MPU6050Component", cg.PollingComponent, i2c.I2CDevice +) -accel_schema = sensor.sensor_schema(UNIT_METER_PER_SECOND_SQUARED, ICON_BRIEFCASE_DOWNLOAD, 2, - DEVICE_CLASS_EMPTY) -gyro_schema = sensor.sensor_schema(UNIT_DEGREE_PER_SECOND, ICON_SCREEN_ROTATION, 2, - DEVICE_CLASS_EMPTY) -temperature_schema = sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE) +accel_schema = sensor.sensor_schema( + UNIT_METER_PER_SECOND_SQUARED, ICON_BRIEFCASE_DOWNLOAD, 2, DEVICE_CLASS_EMPTY +) +gyro_schema = sensor.sensor_schema( + UNIT_DEGREE_PER_SECOND, ICON_SCREEN_ROTATION, 2, DEVICE_CLASS_EMPTY +) +temperature_schema = sensor.sensor_schema( + UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE +) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(MPU6050Component), - cv.Optional(CONF_ACCEL_X): accel_schema, - cv.Optional(CONF_ACCEL_Y): accel_schema, - cv.Optional(CONF_ACCEL_Z): accel_schema, - cv.Optional(CONF_GYRO_X): gyro_schema, - cv.Optional(CONF_GYRO_Y): gyro_schema, - cv.Optional(CONF_GYRO_Z): gyro_schema, - cv.Optional(CONF_TEMPERATURE): temperature_schema, -}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x68)) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(MPU6050Component), + cv.Optional(CONF_ACCEL_X): accel_schema, + cv.Optional(CONF_ACCEL_Y): accel_schema, + cv.Optional(CONF_ACCEL_Z): accel_schema, + cv.Optional(CONF_GYRO_X): gyro_schema, + cv.Optional(CONF_GYRO_Y): gyro_schema, + cv.Optional(CONF_GYRO_Z): gyro_schema, + cv.Optional(CONF_TEMPERATURE): temperature_schema, + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x68)) +) def to_code(config): @@ -40,15 +61,15 @@ def to_code(config): yield cg.register_component(var, config) yield i2c.register_i2c_device(var, config) - for d in ['x', 'y', 'z']: - accel_key = f'accel_{d}' + for d in ["x", "y", "z"]: + accel_key = f"accel_{d}" if accel_key in config: sens = yield sensor.new_sensor(config[accel_key]) - cg.add(getattr(var, f'set_accel_{d}_sensor')(sens)) - accel_key = f'gyro_{d}' + cg.add(getattr(var, f"set_accel_{d}_sensor")(sens)) + accel_key = f"gyro_{d}" if accel_key in config: sens = yield sensor.new_sensor(config[accel_key]) - cg.add(getattr(var, f'set_gyro_{d}_sensor')(sens)) + cg.add(getattr(var, f"set_gyro_{d}_sensor")(sens)) if CONF_TEMPERATURE in config: sens = yield sensor.new_sensor(config[CONF_TEMPERATURE]) diff --git a/esphome/components/mqtt/__init__.py b/esphome/components/mqtt/__init__.py index db99334d0b..e90f90cd6a 100644 --- a/esphome/components/mqtt/__init__.py +++ b/esphome/components/mqtt/__init__.py @@ -5,17 +5,42 @@ import esphome.config_validation as cv from esphome import automation from esphome.automation import Condition from esphome.components import logger -from esphome.const import CONF_AVAILABILITY, CONF_BIRTH_MESSAGE, CONF_BROKER, CONF_CLIENT_ID, \ - CONF_COMMAND_TOPIC, CONF_DISCOVERY, CONF_DISCOVERY_PREFIX, CONF_DISCOVERY_RETAIN, \ - CONF_ID, CONF_KEEPALIVE, CONF_LEVEL, CONF_LOG_TOPIC, CONF_ON_JSON_MESSAGE, CONF_ON_MESSAGE, \ - CONF_PASSWORD, CONF_PAYLOAD, CONF_PAYLOAD_AVAILABLE, CONF_PAYLOAD_NOT_AVAILABLE, CONF_PORT, \ - CONF_QOS, CONF_REBOOT_TIMEOUT, CONF_RETAIN, CONF_SHUTDOWN_MESSAGE, CONF_SSL_FINGERPRINTS, \ - CONF_STATE_TOPIC, CONF_TOPIC, CONF_TOPIC_PREFIX, CONF_TRIGGER_ID, CONF_USERNAME, \ - CONF_WILL_MESSAGE +from esphome.const import ( + CONF_AVAILABILITY, + CONF_BIRTH_MESSAGE, + CONF_BROKER, + CONF_CLIENT_ID, + CONF_COMMAND_TOPIC, + CONF_DISCOVERY, + CONF_DISCOVERY_PREFIX, + CONF_DISCOVERY_RETAIN, + CONF_ID, + CONF_KEEPALIVE, + CONF_LEVEL, + CONF_LOG_TOPIC, + CONF_ON_JSON_MESSAGE, + CONF_ON_MESSAGE, + CONF_PASSWORD, + CONF_PAYLOAD, + CONF_PAYLOAD_AVAILABLE, + CONF_PAYLOAD_NOT_AVAILABLE, + CONF_PORT, + CONF_QOS, + CONF_REBOOT_TIMEOUT, + CONF_RETAIN, + CONF_SHUTDOWN_MESSAGE, + CONF_SSL_FINGERPRINTS, + CONF_STATE_TOPIC, + CONF_TOPIC, + CONF_TOPIC_PREFIX, + CONF_TRIGGER_ID, + CONF_USERNAME, + CONF_WILL_MESSAGE, +) from esphome.core import coroutine_with_priority, coroutine, CORE -DEPENDENCIES = ['network'] -AUTO_LOAD = ['json', 'async_tcp'] +DEPENDENCIES = ["network"] +AUTO_LOAD = ["json", "async_tcp"] def validate_message_just_topic(value): @@ -23,39 +48,49 @@ def validate_message_just_topic(value): return MQTT_MESSAGE_BASE({CONF_TOPIC: value}) -MQTT_MESSAGE_BASE = cv.Schema({ - cv.Required(CONF_TOPIC): cv.publish_topic, - cv.Optional(CONF_QOS, default=0): cv.mqtt_qos, - cv.Optional(CONF_RETAIN, default=True): cv.boolean, -}) +MQTT_MESSAGE_BASE = cv.Schema( + { + cv.Required(CONF_TOPIC): cv.publish_topic, + cv.Optional(CONF_QOS, default=0): cv.mqtt_qos, + cv.Optional(CONF_RETAIN, default=True): cv.boolean, + } +) -MQTT_MESSAGE_TEMPLATE_SCHEMA = cv.Any(None, MQTT_MESSAGE_BASE, validate_message_just_topic) +MQTT_MESSAGE_TEMPLATE_SCHEMA = cv.Any( + None, MQTT_MESSAGE_BASE, validate_message_just_topic +) -MQTT_MESSAGE_SCHEMA = cv.Any(None, MQTT_MESSAGE_BASE.extend({ - cv.Required(CONF_PAYLOAD): cv.mqtt_payload, -})) +MQTT_MESSAGE_SCHEMA = cv.Any( + None, + MQTT_MESSAGE_BASE.extend( + { + cv.Required(CONF_PAYLOAD): cv.mqtt_payload, + } + ), +) -mqtt_ns = cg.esphome_ns.namespace('mqtt') -MQTTMessage = mqtt_ns.struct('MQTTMessage') -MQTTClientComponent = mqtt_ns.class_('MQTTClientComponent', cg.Component) -MQTTPublishAction = mqtt_ns.class_('MQTTPublishAction', automation.Action) -MQTTPublishJsonAction = mqtt_ns.class_('MQTTPublishJsonAction', automation.Action) -MQTTMessageTrigger = mqtt_ns.class_('MQTTMessageTrigger', - automation.Trigger.template(cg.std_string), - cg.Component) -MQTTJsonMessageTrigger = mqtt_ns.class_('MQTTJsonMessageTrigger', - automation.Trigger.template(cg.JsonObjectConstRef)) -MQTTComponent = mqtt_ns.class_('MQTTComponent', cg.Component) -MQTTConnectedCondition = mqtt_ns.class_('MQTTConnectedCondition', Condition) +mqtt_ns = cg.esphome_ns.namespace("mqtt") +MQTTMessage = mqtt_ns.struct("MQTTMessage") +MQTTClientComponent = mqtt_ns.class_("MQTTClientComponent", cg.Component) +MQTTPublishAction = mqtt_ns.class_("MQTTPublishAction", automation.Action) +MQTTPublishJsonAction = mqtt_ns.class_("MQTTPublishJsonAction", automation.Action) +MQTTMessageTrigger = mqtt_ns.class_( + "MQTTMessageTrigger", automation.Trigger.template(cg.std_string), cg.Component +) +MQTTJsonMessageTrigger = mqtt_ns.class_( + "MQTTJsonMessageTrigger", automation.Trigger.template(cg.JsonObjectConstRef) +) +MQTTComponent = mqtt_ns.class_("MQTTComponent", cg.Component) +MQTTConnectedCondition = mqtt_ns.class_("MQTTConnectedCondition", Condition) -MQTTBinarySensorComponent = mqtt_ns.class_('MQTTBinarySensorComponent', MQTTComponent) -MQTTClimateComponent = mqtt_ns.class_('MQTTClimateComponent', MQTTComponent) -MQTTCoverComponent = mqtt_ns.class_('MQTTCoverComponent', MQTTComponent) -MQTTFanComponent = mqtt_ns.class_('MQTTFanComponent', MQTTComponent) -MQTTJSONLightComponent = mqtt_ns.class_('MQTTJSONLightComponent', MQTTComponent) -MQTTSensorComponent = mqtt_ns.class_('MQTTSensorComponent', MQTTComponent) -MQTTSwitchComponent = mqtt_ns.class_('MQTTSwitchComponent', MQTTComponent) -MQTTTextSensor = mqtt_ns.class_('MQTTTextSensor', MQTTComponent) +MQTTBinarySensorComponent = mqtt_ns.class_("MQTTBinarySensorComponent", MQTTComponent) +MQTTClimateComponent = mqtt_ns.class_("MQTTClimateComponent", MQTTComponent) +MQTTCoverComponent = mqtt_ns.class_("MQTTCoverComponent", MQTTComponent) +MQTTFanComponent = mqtt_ns.class_("MQTTFanComponent", MQTTComponent) +MQTTJSONLightComponent = mqtt_ns.class_("MQTTJSONLightComponent", MQTTComponent) +MQTTSensorComponent = mqtt_ns.class_("MQTTSensorComponent", MQTTComponent) +MQTTSwitchComponent = mqtt_ns.class_("MQTTSwitchComponent", MQTTComponent) +MQTTTextSensor = mqtt_ns.class_("MQTTTextSensor", MQTTComponent) def validate_config(value): @@ -64,28 +99,28 @@ def validate_config(value): topic_prefix = value[CONF_TOPIC_PREFIX] if CONF_BIRTH_MESSAGE not in value: out[CONF_BIRTH_MESSAGE] = { - CONF_TOPIC: f'{topic_prefix}/status', - CONF_PAYLOAD: 'online', + CONF_TOPIC: f"{topic_prefix}/status", + CONF_PAYLOAD: "online", CONF_QOS: 0, CONF_RETAIN: True, } if CONF_WILL_MESSAGE not in value: out[CONF_WILL_MESSAGE] = { - CONF_TOPIC: f'{topic_prefix}/status', - CONF_PAYLOAD: 'offline', + CONF_TOPIC: f"{topic_prefix}/status", + CONF_PAYLOAD: "offline", CONF_QOS: 0, CONF_RETAIN: True, } if CONF_SHUTDOWN_MESSAGE not in value: out[CONF_SHUTDOWN_MESSAGE] = { - CONF_TOPIC: f'{topic_prefix}/status', - CONF_PAYLOAD: 'offline', + CONF_TOPIC: f"{topic_prefix}/status", + CONF_PAYLOAD: "offline", CONF_QOS: 0, CONF_RETAIN: True, } if CONF_LOG_TOPIC not in value: out[CONF_LOG_TOPIC] = { - CONF_TOPIC: f'{topic_prefix}/debug', + CONF_TOPIC: f"{topic_prefix}/debug", CONF_QOS: 0, CONF_RETAIN: True, } @@ -94,46 +129,68 @@ def validate_config(value): def validate_fingerprint(value): value = cv.string(value) - if re.match(r'^[0-9a-f]{40}$', value) is None: + if re.match(r"^[0-9a-f]{40}$", value) is None: raise cv.Invalid("fingerprint must be valid SHA1 hash") return value -CONFIG_SCHEMA = cv.All(cv.Schema({ - cv.GenerateID(): cv.declare_id(MQTTClientComponent), - cv.Required(CONF_BROKER): cv.string_strict, - cv.Optional(CONF_PORT, default=1883): cv.port, - cv.Optional(CONF_USERNAME, default=''): cv.string, - cv.Optional(CONF_PASSWORD, default=''): cv.string, - cv.Optional(CONF_CLIENT_ID): cv.string, - cv.Optional(CONF_DISCOVERY, default=True): cv.Any(cv.boolean, cv.one_of("CLEAN", upper=True)), - cv.Optional(CONF_DISCOVERY_RETAIN, default=True): cv.boolean, - cv.Optional(CONF_DISCOVERY_PREFIX, default="homeassistant"): cv.publish_topic, - - cv.Optional(CONF_BIRTH_MESSAGE): MQTT_MESSAGE_SCHEMA, - cv.Optional(CONF_WILL_MESSAGE): MQTT_MESSAGE_SCHEMA, - cv.Optional(CONF_SHUTDOWN_MESSAGE): MQTT_MESSAGE_SCHEMA, - cv.Optional(CONF_TOPIC_PREFIX, default=lambda: CORE.name): cv.publish_topic, - cv.Optional(CONF_LOG_TOPIC): cv.Any(None, MQTT_MESSAGE_BASE.extend({ - cv.Optional(CONF_LEVEL): logger.is_log_level, - }), validate_message_just_topic), - - cv.Optional(CONF_SSL_FINGERPRINTS): cv.All(cv.only_on_esp8266, - cv.ensure_list(validate_fingerprint)), - cv.Optional(CONF_KEEPALIVE, default='15s'): cv.positive_time_period_seconds, - cv.Optional(CONF_REBOOT_TIMEOUT, default='15min'): cv.positive_time_period_milliseconds, - cv.Optional(CONF_ON_MESSAGE): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(MQTTMessageTrigger), - cv.Required(CONF_TOPIC): cv.subscribe_topic, - cv.Optional(CONF_QOS, default=0): cv.mqtt_qos, - cv.Optional(CONF_PAYLOAD): cv.string_strict, - }), - cv.Optional(CONF_ON_JSON_MESSAGE): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(MQTTJsonMessageTrigger), - cv.Required(CONF_TOPIC): cv.subscribe_topic, - cv.Optional(CONF_QOS, default=0): cv.mqtt_qos, - }), -}), validate_config) +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(MQTTClientComponent), + cv.Required(CONF_BROKER): cv.string_strict, + cv.Optional(CONF_PORT, default=1883): cv.port, + cv.Optional(CONF_USERNAME, default=""): cv.string, + cv.Optional(CONF_PASSWORD, default=""): cv.string, + cv.Optional(CONF_CLIENT_ID): cv.string, + cv.Optional(CONF_DISCOVERY, default=True): cv.Any( + cv.boolean, cv.one_of("CLEAN", upper=True) + ), + cv.Optional(CONF_DISCOVERY_RETAIN, default=True): cv.boolean, + cv.Optional( + CONF_DISCOVERY_PREFIX, default="homeassistant" + ): cv.publish_topic, + cv.Optional(CONF_BIRTH_MESSAGE): MQTT_MESSAGE_SCHEMA, + cv.Optional(CONF_WILL_MESSAGE): MQTT_MESSAGE_SCHEMA, + cv.Optional(CONF_SHUTDOWN_MESSAGE): MQTT_MESSAGE_SCHEMA, + cv.Optional(CONF_TOPIC_PREFIX, default=lambda: CORE.name): cv.publish_topic, + cv.Optional(CONF_LOG_TOPIC): cv.Any( + None, + MQTT_MESSAGE_BASE.extend( + { + cv.Optional(CONF_LEVEL): logger.is_log_level, + } + ), + validate_message_just_topic, + ), + cv.Optional(CONF_SSL_FINGERPRINTS): cv.All( + cv.only_on_esp8266, cv.ensure_list(validate_fingerprint) + ), + cv.Optional(CONF_KEEPALIVE, default="15s"): cv.positive_time_period_seconds, + cv.Optional( + CONF_REBOOT_TIMEOUT, default="15min" + ): cv.positive_time_period_milliseconds, + cv.Optional(CONF_ON_MESSAGE): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(MQTTMessageTrigger), + cv.Required(CONF_TOPIC): cv.subscribe_topic, + cv.Optional(CONF_QOS, default=0): cv.mqtt_qos, + cv.Optional(CONF_PAYLOAD): cv.string_strict, + } + ), + cv.Optional(CONF_ON_JSON_MESSAGE): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( + MQTTJsonMessageTrigger + ), + cv.Required(CONF_TOPIC): cv.subscribe_topic, + cv.Optional(CONF_QOS, default=0): cv.mqtt_qos, + } + ), + } + ), + validate_config, +) def exp_mqtt_message(config): @@ -141,10 +198,10 @@ def exp_mqtt_message(config): return cg.optional(cg.TemplateArguments(MQTTMessage)) exp = cg.StructInitializer( MQTTMessage, - ('topic', config[CONF_TOPIC]), - ('payload', config.get(CONF_PAYLOAD, "")), - ('qos', config[CONF_QOS]), - ('retain', config[CONF_RETAIN]) + ("topic", config[CONF_TOPIC]), + ("payload", config.get(CONF_PAYLOAD, "")), + ("qos", config[CONF_QOS]), + ("retain", config[CONF_RETAIN]), ) return exp @@ -155,8 +212,8 @@ def to_code(config): yield cg.register_component(var, config) # https://github.com/OttoWinter/async-mqtt-client/blob/master/library.json - cg.add_library('AsyncMqttClient-esphome', '0.8.4') - cg.add_define('USE_MQTT') + cg.add_library("AsyncMqttClient-esphome", "0.8.4") + cg.add_define("USE_MQTT") cg.add_global(mqtt_ns.using) cg.add(var.set_broker_address(config[CONF_BROKER])) @@ -206,9 +263,12 @@ def to_code(config): if CONF_SSL_FINGERPRINTS in config: for fingerprint in config[CONF_SSL_FINGERPRINTS]: - arr = [cg.RawExpression("0x{}".format(fingerprint[i:i + 2])) for i in range(0, 40, 2)] + arr = [ + cg.RawExpression("0x{}".format(fingerprint[i : i + 2])) + for i in range(0, 40, 2) + ] cg.add(var.add_ssl_fingerprint(arr)) - cg.add_build_flag('-DASYNC_TCP_SSL_ENABLED=1') + cg.add_build_flag("-DASYNC_TCP_SSL_ENABLED=1") cg.add(var.set_keep_alive(config[CONF_KEEPALIVE])) @@ -220,23 +280,27 @@ def to_code(config): if CONF_PAYLOAD in conf: cg.add(trig.set_payload(conf[CONF_PAYLOAD])) yield cg.register_component(trig, conf) - yield automation.build_automation(trig, [(cg.std_string, 'x')], conf) + yield automation.build_automation(trig, [(cg.std_string, "x")], conf) for conf in config.get(CONF_ON_JSON_MESSAGE, []): trig = cg.new_Pvariable(conf[CONF_TRIGGER_ID], conf[CONF_TOPIC], conf[CONF_QOS]) - yield automation.build_automation(trig, [(cg.JsonObjectConstRef, 'x')], conf) + yield automation.build_automation(trig, [(cg.JsonObjectConstRef, "x")], conf) -MQTT_PUBLISH_ACTION_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.use_id(MQTTClientComponent), - cv.Required(CONF_TOPIC): cv.templatable(cv.publish_topic), - cv.Required(CONF_PAYLOAD): cv.templatable(cv.mqtt_payload), - cv.Optional(CONF_QOS, default=0): cv.templatable(cv.mqtt_qos), - cv.Optional(CONF_RETAIN, default=False): cv.templatable(cv.boolean), -}) +MQTT_PUBLISH_ACTION_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.use_id(MQTTClientComponent), + cv.Required(CONF_TOPIC): cv.templatable(cv.publish_topic), + cv.Required(CONF_PAYLOAD): cv.templatable(cv.mqtt_payload), + cv.Optional(CONF_QOS, default=0): cv.templatable(cv.mqtt_qos), + cv.Optional(CONF_RETAIN, default=False): cv.templatable(cv.boolean), + } +) -@automation.register_action('mqtt.publish', MQTTPublishAction, MQTT_PUBLISH_ACTION_SCHEMA) +@automation.register_action( + "mqtt.publish", MQTTPublishAction, MQTT_PUBLISH_ACTION_SCHEMA +) def mqtt_publish_action_to_code(config, action_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) var = cg.new_Pvariable(action_id, template_arg, paren) @@ -252,24 +316,27 @@ def mqtt_publish_action_to_code(config, action_id, template_arg, args): yield var -MQTT_PUBLISH_JSON_ACTION_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.use_id(MQTTClientComponent), - cv.Required(CONF_TOPIC): cv.templatable(cv.publish_topic), - cv.Required(CONF_PAYLOAD): cv.lambda_, - cv.Optional(CONF_QOS, default=0): cv.templatable(cv.mqtt_qos), - cv.Optional(CONF_RETAIN, default=False): cv.templatable(cv.boolean), -}) +MQTT_PUBLISH_JSON_ACTION_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.use_id(MQTTClientComponent), + cv.Required(CONF_TOPIC): cv.templatable(cv.publish_topic), + cv.Required(CONF_PAYLOAD): cv.lambda_, + cv.Optional(CONF_QOS, default=0): cv.templatable(cv.mqtt_qos), + cv.Optional(CONF_RETAIN, default=False): cv.templatable(cv.boolean), + } +) -@automation.register_action('mqtt.publish_json', MQTTPublishJsonAction, - MQTT_PUBLISH_JSON_ACTION_SCHEMA) +@automation.register_action( + "mqtt.publish_json", MQTTPublishJsonAction, MQTT_PUBLISH_JSON_ACTION_SCHEMA +) def mqtt_publish_json_action_to_code(config, action_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) var = cg.new_Pvariable(action_id, template_arg, paren) template_ = yield cg.templatable(config[CONF_TOPIC], args, cg.std_string) cg.add(var.set_topic(template_)) - args_ = args + [(cg.JsonObjectRef, 'root')] + args_ = args + [(cg.JsonObjectRef, "root")] lambda_ = yield cg.process_lambda(config[CONF_PAYLOAD], args_, return_type=cg.void) cg.add(var.set_payload(lambda_)) template_ = yield cg.templatable(config[CONF_QOS], args, cg.uint8) @@ -280,10 +347,13 @@ def mqtt_publish_json_action_to_code(config, action_id, template_arg, args): def get_default_topic_for(data, component_type, name, suffix): - allowlist = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_' - sanitized_name = ''.join(x for x in name.lower().replace(' ', '_') if x in allowlist) - return '{}/{}/{}/{}'.format(data.topic_prefix, component_type, - sanitized_name, suffix) + allowlist = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_" + sanitized_name = "".join( + x for x in name.lower().replace(" ", "_") if x in allowlist + ) + return "{}/{}/{}/{}".format( + data.topic_prefix, component_type, sanitized_name, suffix + ) @coroutine @@ -303,14 +373,24 @@ def register_mqtt_component(var, config): if not availability: cg.add(var.disable_availability()) else: - cg.add(var.set_availability(availability[CONF_TOPIC], - availability[CONF_PAYLOAD_AVAILABLE], - availability[CONF_PAYLOAD_NOT_AVAILABLE])) + cg.add( + var.set_availability( + availability[CONF_TOPIC], + availability[CONF_PAYLOAD_AVAILABLE], + availability[CONF_PAYLOAD_NOT_AVAILABLE], + ) + ) -@automation.register_condition('mqtt.connected', MQTTConnectedCondition, cv.Schema({ - cv.GenerateID(): cv.use_id(MQTTClientComponent), -})) +@automation.register_condition( + "mqtt.connected", + MQTTConnectedCondition, + cv.Schema( + { + cv.GenerateID(): cv.use_id(MQTTClientComponent), + } + ), +) def mqtt_connected_to_code(config, condition_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) yield cg.new_Pvariable(condition_id, template_arg, paren) diff --git a/esphome/components/mqtt_subscribe/__init__.py b/esphome/components/mqtt_subscribe/__init__.py index 82e77f2f78..b17f321372 100644 --- a/esphome/components/mqtt_subscribe/__init__.py +++ b/esphome/components/mqtt_subscribe/__init__.py @@ -1,3 +1,3 @@ import esphome.codegen as cg -mqtt_subscribe_ns = cg.esphome_ns.namespace('mqtt_subscribe') +mqtt_subscribe_ns = cg.esphome_ns.namespace("mqtt_subscribe") diff --git a/esphome/components/mqtt_subscribe/sensor/__init__.py b/esphome/components/mqtt_subscribe/sensor/__init__.py index 0ccad5c72d..00c2d324d3 100644 --- a/esphome/components/mqtt_subscribe/sensor/__init__.py +++ b/esphome/components/mqtt_subscribe/sensor/__init__.py @@ -1,20 +1,35 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import mqtt, sensor -from esphome.const import CONF_ID, CONF_QOS, CONF_TOPIC, UNIT_EMPTY, ICON_EMPTY, DEVICE_CLASS_EMPTY +from esphome.const import ( + CONF_ID, + CONF_QOS, + CONF_TOPIC, + UNIT_EMPTY, + ICON_EMPTY, + DEVICE_CLASS_EMPTY, +) from .. import mqtt_subscribe_ns -DEPENDENCIES = ['mqtt'] +DEPENDENCIES = ["mqtt"] -CONF_MQTT_PARENT_ID = 'mqtt_parent_id' -MQTTSubscribeSensor = mqtt_subscribe_ns.class_('MQTTSubscribeSensor', sensor.Sensor, cg.Component) +CONF_MQTT_PARENT_ID = "mqtt_parent_id" +MQTTSubscribeSensor = mqtt_subscribe_ns.class_( + "MQTTSubscribeSensor", sensor.Sensor, cg.Component +) -CONFIG_SCHEMA = sensor.sensor_schema(UNIT_EMPTY, ICON_EMPTY, 1, DEVICE_CLASS_EMPTY).extend({ - cv.GenerateID(): cv.declare_id(MQTTSubscribeSensor), - cv.GenerateID(CONF_MQTT_PARENT_ID): cv.use_id(mqtt.MQTTClientComponent), - cv.Required(CONF_TOPIC): cv.subscribe_topic, - cv.Optional(CONF_QOS, default=0): cv.mqtt_qos, -}).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = ( + sensor.sensor_schema(UNIT_EMPTY, ICON_EMPTY, 1, DEVICE_CLASS_EMPTY) + .extend( + { + cv.GenerateID(): cv.declare_id(MQTTSubscribeSensor), + cv.GenerateID(CONF_MQTT_PARENT_ID): cv.use_id(mqtt.MQTTClientComponent), + cv.Required(CONF_TOPIC): cv.subscribe_topic, + cv.Optional(CONF_QOS, default=0): cv.mqtt_qos, + } + ) + .extend(cv.COMPONENT_SCHEMA) +) def to_code(config): diff --git a/esphome/components/mqtt_subscribe/text_sensor/__init__.py b/esphome/components/mqtt_subscribe/text_sensor/__init__.py index c80909669b..b5f2c4307b 100644 --- a/esphome/components/mqtt_subscribe/text_sensor/__init__.py +++ b/esphome/components/mqtt_subscribe/text_sensor/__init__.py @@ -4,18 +4,21 @@ from esphome.components import text_sensor, mqtt from esphome.const import CONF_ID, CONF_QOS, CONF_TOPIC from .. import mqtt_subscribe_ns -DEPENDENCIES = ['mqtt'] +DEPENDENCIES = ["mqtt"] -CONF_MQTT_PARENT_ID = 'mqtt_parent_id' -MQTTSubscribeTextSensor = mqtt_subscribe_ns.class_('MQTTSubscribeTextSensor', - text_sensor.TextSensor, cg.Component) +CONF_MQTT_PARENT_ID = "mqtt_parent_id" +MQTTSubscribeTextSensor = mqtt_subscribe_ns.class_( + "MQTTSubscribeTextSensor", text_sensor.TextSensor, cg.Component +) -CONFIG_SCHEMA = text_sensor.TEXT_SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(MQTTSubscribeTextSensor), - cv.GenerateID(CONF_MQTT_PARENT_ID): cv.use_id(mqtt.MQTTClientComponent), - cv.Required(CONF_TOPIC): cv.subscribe_topic, - cv.Optional(CONF_QOS, default=0): cv.mqtt_qos, -}).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = text_sensor.TEXT_SENSOR_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(MQTTSubscribeTextSensor), + cv.GenerateID(CONF_MQTT_PARENT_ID): cv.use_id(mqtt.MQTTClientComponent), + cv.Required(CONF_TOPIC): cv.subscribe_topic, + cv.Optional(CONF_QOS, default=0): cv.mqtt_qos, + } +).extend(cv.COMPONENT_SCHEMA) def to_code(config): diff --git a/esphome/components/ms5611/sensor.py b/esphome/components/ms5611/sensor.py index 94ff6cbb0a..d180008140 100644 --- a/esphome/components/ms5611/sensor.py +++ b/esphome/components/ms5611/sensor.py @@ -1,22 +1,40 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import CONF_ID, CONF_PRESSURE, \ - CONF_TEMPERATURE, DEVICE_CLASS_PRESSURE, DEVICE_CLASS_TEMPERATURE, ICON_EMPTY, UNIT_CELSIUS, \ - ICON_GAUGE, UNIT_HECTOPASCAL +from esphome.const import ( + CONF_ID, + CONF_PRESSURE, + CONF_TEMPERATURE, + DEVICE_CLASS_PRESSURE, + DEVICE_CLASS_TEMPERATURE, + ICON_EMPTY, + UNIT_CELSIUS, + ICON_GAUGE, + UNIT_HECTOPASCAL, +) -DEPENDENCIES = ['i2c'] +DEPENDENCIES = ["i2c"] -ms5611_ns = cg.esphome_ns.namespace('ms5611') -MS5611Component = ms5611_ns.class_('MS5611Component', cg.PollingComponent, i2c.I2CDevice) +ms5611_ns = cg.esphome_ns.namespace("ms5611") +MS5611Component = ms5611_ns.class_( + "MS5611Component", cg.PollingComponent, i2c.I2CDevice +) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(MS5611Component), - cv.Required(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, - DEVICE_CLASS_TEMPERATURE), - cv.Required(CONF_PRESSURE): sensor.sensor_schema(UNIT_HECTOPASCAL, ICON_GAUGE, 1, - DEVICE_CLASS_PRESSURE), -}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x77)) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(MS5611Component), + cv.Required(CONF_TEMPERATURE): sensor.sensor_schema( + UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE + ), + cv.Required(CONF_PRESSURE): sensor.sensor_schema( + UNIT_HECTOPASCAL, ICON_GAUGE, 1, DEVICE_CLASS_PRESSURE + ), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x77)) +) def to_code(config): diff --git a/esphome/components/my9231/__init__.py b/esphome/components/my9231/__init__.py index 7ca0a30cab..ed1edd7b2d 100644 --- a/esphome/components/my9231/__init__.py +++ b/esphome/components/my9231/__init__.py @@ -1,22 +1,30 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins -from esphome.const import (CONF_BIT_DEPTH, CONF_CLOCK_PIN, CONF_DATA_PIN, CONF_ID, - CONF_NUM_CHANNELS, CONF_NUM_CHIPS) +from esphome.const import ( + CONF_BIT_DEPTH, + CONF_CLOCK_PIN, + CONF_DATA_PIN, + CONF_ID, + CONF_NUM_CHANNELS, + CONF_NUM_CHIPS, +) -AUTO_LOAD = ['output'] -my9231_ns = cg.esphome_ns.namespace('my9231') -MY9231OutputComponent = my9231_ns.class_('MY9231OutputComponent', cg.Component) +AUTO_LOAD = ["output"] +my9231_ns = cg.esphome_ns.namespace("my9231") +MY9231OutputComponent = my9231_ns.class_("MY9231OutputComponent", cg.Component) MULTI_CONF = True -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(MY9231OutputComponent), - cv.Required(CONF_DATA_PIN): pins.gpio_output_pin_schema, - cv.Required(CONF_CLOCK_PIN): pins.gpio_output_pin_schema, - cv.Optional(CONF_NUM_CHANNELS, default=6): cv.int_range(min=3, max=1020), - cv.Optional(CONF_NUM_CHIPS, default=2): cv.int_range(min=1, max=255), - cv.Optional(CONF_BIT_DEPTH, default=16): cv.one_of(8, 12, 14, 16, int=True), -}).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(MY9231OutputComponent), + cv.Required(CONF_DATA_PIN): pins.gpio_output_pin_schema, + cv.Required(CONF_CLOCK_PIN): pins.gpio_output_pin_schema, + cv.Optional(CONF_NUM_CHANNELS, default=6): cv.int_range(min=3, max=1020), + cv.Optional(CONF_NUM_CHIPS, default=2): cv.int_range(min=1, max=255), + cv.Optional(CONF_BIT_DEPTH, default=16): cv.one_of(8, 12, 14, 16, int=True), + } +).extend(cv.COMPONENT_SCHEMA) def to_code(config): diff --git a/esphome/components/my9231/output.py b/esphome/components/my9231/output.py index c69649fd5e..30db84064d 100644 --- a/esphome/components/my9231/output.py +++ b/esphome/components/my9231/output.py @@ -4,17 +4,18 @@ from esphome.components import output from esphome.const import CONF_CHANNEL, CONF_ID from . import MY9231OutputComponent -DEPENDENCIES = ['my9231'] +DEPENDENCIES = ["my9231"] -Channel = MY9231OutputComponent.class_('Channel', output.FloatOutput) +Channel = MY9231OutputComponent.class_("Channel", output.FloatOutput) -CONF_MY9231_ID = 'my9231_id' -CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend({ - cv.GenerateID(CONF_MY9231_ID): cv.use_id(MY9231OutputComponent), - - cv.Required(CONF_ID): cv.declare_id(Channel), - cv.Required(CONF_CHANNEL): cv.uint16_t, -}).extend(cv.COMPONENT_SCHEMA) +CONF_MY9231_ID = "my9231_id" +CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend( + { + cv.GenerateID(CONF_MY9231_ID): cv.use_id(MY9231OutputComponent), + cv.Required(CONF_ID): cv.declare_id(Channel), + cv.Required(CONF_CHANNEL): cv.uint16_t, + } +).extend(cv.COMPONENT_SCHEMA) def to_code(config): diff --git a/esphome/components/neopixelbus/light.py b/esphome/components/neopixelbus/light.py index 32bfd951e9..0cf8057b8e 100644 --- a/esphome/components/neopixelbus/light.py +++ b/esphome/components/neopixelbus/light.py @@ -2,32 +2,45 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins from esphome.components import light -from esphome.const import CONF_CLOCK_PIN, CONF_DATA_PIN, CONF_METHOD, CONF_NUM_LEDS, CONF_PIN, \ - CONF_TYPE, CONF_VARIANT, CONF_OUTPUT_ID, CONF_INVERT +from esphome.const import ( + CONF_CLOCK_PIN, + CONF_DATA_PIN, + CONF_METHOD, + CONF_NUM_LEDS, + CONF_PIN, + CONF_TYPE, + CONF_VARIANT, + CONF_OUTPUT_ID, + CONF_INVERT, +) from esphome.core import CORE -neopixelbus_ns = cg.esphome_ns.namespace('neopixelbus') -NeoPixelBusLightOutputBase = neopixelbus_ns.class_('NeoPixelBusLightOutputBase', - light.AddressableLight) -NeoPixelRGBLightOutput = neopixelbus_ns.class_('NeoPixelRGBLightOutput', NeoPixelBusLightOutputBase) -NeoPixelRGBWLightOutput = neopixelbus_ns.class_('NeoPixelRGBWLightOutput', - NeoPixelBusLightOutputBase) -ESPNeoPixelOrder = neopixelbus_ns.namespace('ESPNeoPixelOrder') +neopixelbus_ns = cg.esphome_ns.namespace("neopixelbus") +NeoPixelBusLightOutputBase = neopixelbus_ns.class_( + "NeoPixelBusLightOutputBase", light.AddressableLight +) +NeoPixelRGBLightOutput = neopixelbus_ns.class_( + "NeoPixelRGBLightOutput", NeoPixelBusLightOutputBase +) +NeoPixelRGBWLightOutput = neopixelbus_ns.class_( + "NeoPixelRGBWLightOutput", NeoPixelBusLightOutputBase +) +ESPNeoPixelOrder = neopixelbus_ns.namespace("ESPNeoPixelOrder") NeoRgbFeature = cg.global_ns.NeoRgbFeature NeoRgbwFeature = cg.global_ns.NeoRgbwFeature def validate_type(value): value = cv.string(value).upper() - if 'R' not in value: + if "R" not in value: raise cv.Invalid("Must have R in type") - if 'G' not in value: + if "G" not in value: raise cv.Invalid("Must have G in type") - if 'B' not in value: + if "B" not in value: raise cv.Invalid("Must have B in type") - rest = set(value) - set('RGBW') + rest = set(value) - set("RGBW") if rest: - raise cv.Invalid("Type has invalid color: {}".format(', '.join(rest))) + raise cv.Invalid("Type has invalid color: {}".format(", ".join(rest))) if len(set(value)) != len(value): raise cv.Invalid("Type has duplicate color!") return value @@ -35,45 +48,45 @@ def validate_type(value): def validate_variant(value): value = cv.string(value).upper() - if value == 'WS2813': - value = 'WS2812X' - if value == 'WS2812': - value = '800KBPS' - if value == 'LC8812': - value = 'SK6812' + if value == "WS2813": + value = "WS2812X" + if value == "WS2812": + value = "800KBPS" + if value == "LC8812": + value = "SK6812" return cv.one_of(*VARIANTS)(value) def validate_method(value): if value is None: if CORE.is_esp32: - return 'ESP32_I2S_1' + return "ESP32_I2S_1" if CORE.is_esp8266: - return 'ESP8266_DMA' + return "ESP8266_DMA" raise NotImplementedError if CORE.is_esp32: - return cv.one_of(*ESP32_METHODS, upper=True, space='_')(value) + return cv.one_of(*ESP32_METHODS, upper=True, space="_")(value) if CORE.is_esp8266: - return cv.one_of(*ESP8266_METHODS, upper=True, space='_')(value) + return cv.one_of(*ESP8266_METHODS, upper=True, space="_")(value) raise NotImplementedError def validate_method_pin(value): method = value[CONF_METHOD] method_pins = { - 'ESP8266_DMA': [3], - 'ESP8266_UART0': [1], - 'ESP8266_ASYNC_UART0': [1], - 'ESP8266_UART1': [2], - 'ESP8266_ASYNC_UART1': [2], - 'ESP32_I2S_0': list(range(0, 32)), - 'ESP32_I2S_1': list(range(0, 32)), + "ESP8266_DMA": [3], + "ESP8266_UART0": [1], + "ESP8266_ASYNC_UART0": [1], + "ESP8266_UART1": [2], + "ESP8266_ASYNC_UART1": [2], + "ESP32_I2S_0": list(range(0, 32)), + "ESP32_I2S_1": list(range(0, 32)), } if CORE.is_esp8266: - method_pins['BIT_BANG'] = list(range(0, 16)) + method_pins["BIT_BANG"] = list(range(0, 16)) elif CORE.is_esp32: - method_pins['BIT_BANG'] = list(range(0, 32)) + method_pins["BIT_BANG"] = list(range(0, 32)) pins_ = method_pins.get(method) if pins_ is None: # all pins allowed for this method @@ -81,39 +94,42 @@ def validate_method_pin(value): for opt in (CONF_PIN, CONF_CLOCK_PIN, CONF_DATA_PIN): if opt in value and value[opt] not in pins_: - raise cv.Invalid("Method {} only supports pin(s) {}".format( - method, ', '.join(f'GPIO{x}' for x in pins_) - ), path=[CONF_METHOD]) + raise cv.Invalid( + "Method {} only supports pin(s) {}".format( + method, ", ".join(f"GPIO{x}" for x in pins_) + ), + path=[CONF_METHOD], + ) return value VARIANTS = { - 'WS2812X': 'Ws2812x', - 'SK6812': 'Sk6812', - '800KBPS': '800Kbps', - '400KBPS': '400Kbps', + "WS2812X": "Ws2812x", + "SK6812": "Sk6812", + "800KBPS": "800Kbps", + "400KBPS": "400Kbps", } ESP8266_METHODS = { - 'ESP8266_DMA': 'NeoEsp8266Dma{}Method', - 'ESP8266_UART0': 'NeoEsp8266Uart0{}Method', - 'ESP8266_UART1': 'NeoEsp8266Uart1{}Method', - 'ESP8266_ASYNC_UART0': 'NeoEsp8266AsyncUart0{}Method', - 'ESP8266_ASYNC_UART1': 'NeoEsp8266AsyncUart1{}Method', - 'BIT_BANG': 'NeoEsp8266BitBang{}Method', + "ESP8266_DMA": "NeoEsp8266Dma{}Method", + "ESP8266_UART0": "NeoEsp8266Uart0{}Method", + "ESP8266_UART1": "NeoEsp8266Uart1{}Method", + "ESP8266_ASYNC_UART0": "NeoEsp8266AsyncUart0{}Method", + "ESP8266_ASYNC_UART1": "NeoEsp8266AsyncUart1{}Method", + "BIT_BANG": "NeoEsp8266BitBang{}Method", } ESP32_METHODS = { - 'ESP32_I2S_0': 'NeoEsp32I2s0{}Method', - 'ESP32_I2S_1': 'NeoEsp32I2s1{}Method', - 'ESP32_RMT_0': 'NeoEsp32Rmt0{}Method', - 'ESP32_RMT_1': 'NeoEsp32Rmt1{}Method', - 'ESP32_RMT_2': 'NeoEsp32Rmt2{}Method', - 'ESP32_RMT_3': 'NeoEsp32Rmt3{}Method', - 'ESP32_RMT_4': 'NeoEsp32Rmt4{}Method', - 'ESP32_RMT_5': 'NeoEsp32Rmt5{}Method', - 'ESP32_RMT_6': 'NeoEsp32Rmt6{}Method', - 'ESP32_RMT_7': 'NeoEsp32Rmt7{}Method', - 'BIT_BANG': 'NeoEsp32BitBang{}Method', + "ESP32_I2S_0": "NeoEsp32I2s0{}Method", + "ESP32_I2S_1": "NeoEsp32I2s1{}Method", + "ESP32_RMT_0": "NeoEsp32Rmt0{}Method", + "ESP32_RMT_1": "NeoEsp32Rmt1{}Method", + "ESP32_RMT_2": "NeoEsp32Rmt2{}Method", + "ESP32_RMT_3": "NeoEsp32Rmt3{}Method", + "ESP32_RMT_4": "NeoEsp32Rmt4{}Method", + "ESP32_RMT_5": "NeoEsp32Rmt5{}Method", + "ESP32_RMT_6": "NeoEsp32Rmt6{}Method", + "ESP32_RMT_7": "NeoEsp32Rmt7{}Method", + "BIT_BANG": "NeoEsp32BitBang{}Method", } @@ -122,10 +138,10 @@ def format_method(config): method = config[CONF_METHOD] if config[CONF_INVERT]: - if method == 'ESP8266_DMA': - variant = 'Inverted' + variant + if method == "ESP8266_DMA": + variant = "Inverted" + variant else: - variant += 'Inverted' + variant += "Inverted" if CORE.is_esp8266: return ESP8266_METHODS[method].format(variant) @@ -146,23 +162,27 @@ def validate(config): raise cv.Invalid("Must specify at least one of 'pin' or 'clock_pin'+'data_pin'") -CONFIG_SCHEMA = cv.All(light.ADDRESSABLE_LIGHT_SCHEMA.extend({ - cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(NeoPixelBusLightOutputBase), - - cv.Optional(CONF_TYPE, default='GRB'): validate_type, - cv.Optional(CONF_VARIANT, default='800KBPS'): validate_variant, - cv.Optional(CONF_METHOD, default=None): validate_method, - cv.Optional(CONF_INVERT, default='no'): cv.boolean, - cv.Optional(CONF_PIN): pins.output_pin, - cv.Optional(CONF_CLOCK_PIN): pins.output_pin, - cv.Optional(CONF_DATA_PIN): pins.output_pin, - - cv.Required(CONF_NUM_LEDS): cv.positive_not_null_int, -}).extend(cv.COMPONENT_SCHEMA), validate, validate_method_pin) +CONFIG_SCHEMA = cv.All( + light.ADDRESSABLE_LIGHT_SCHEMA.extend( + { + cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(NeoPixelBusLightOutputBase), + cv.Optional(CONF_TYPE, default="GRB"): validate_type, + cv.Optional(CONF_VARIANT, default="800KBPS"): validate_variant, + cv.Optional(CONF_METHOD, default=None): validate_method, + cv.Optional(CONF_INVERT, default="no"): cv.boolean, + cv.Optional(CONF_PIN): pins.output_pin, + cv.Optional(CONF_CLOCK_PIN): pins.output_pin, + cv.Optional(CONF_DATA_PIN): pins.output_pin, + cv.Required(CONF_NUM_LEDS): cv.positive_not_null_int, + } + ).extend(cv.COMPONENT_SCHEMA), + validate, + validate_method_pin, +) def to_code(config): - has_white = 'W' in config[CONF_TYPE] + has_white = "W" in config[CONF_TYPE] template = cg.TemplateArguments(getattr(cg.global_ns, format_method(config))) if has_white: out_type = NeoPixelRGBWLightOutput.template(template) @@ -176,9 +196,13 @@ def to_code(config): if CONF_PIN in config: cg.add(var.add_leds(config[CONF_NUM_LEDS], config[CONF_PIN])) else: - cg.add(var.add_leds(config[CONF_NUM_LEDS], config[CONF_CLOCK_PIN], config[CONF_DATA_PIN])) + cg.add( + var.add_leds( + config[CONF_NUM_LEDS], config[CONF_CLOCK_PIN], config[CONF_DATA_PIN] + ) + ) cg.add(var.set_pixel_order(getattr(ESPNeoPixelOrder, config[CONF_TYPE]))) # https://github.com/Makuna/NeoPixelBus/blob/master/library.json - cg.add_library('NeoPixelBus-esphome', '2.5.7') + cg.add_library("NeoPixelBus-esphome", "2.5.7") diff --git a/esphome/components/network/__init__.py b/esphome/components/network/__init__.py index a380e32bfe..46713d3ffe 100644 --- a/esphome/components/network/__init__.py +++ b/esphome/components/network/__init__.py @@ -1,2 +1,2 @@ # Dummy package to allow components to depend on network -CODEOWNERS = ['@esphome/core'] +CODEOWNERS = ["@esphome/core"] diff --git a/esphome/components/nextion/__init__.py b/esphome/components/nextion/__init__.py index 1d90c92496..67a49df9fa 100644 --- a/esphome/components/nextion/__init__.py +++ b/esphome/components/nextion/__init__.py @@ -1,3 +1,3 @@ import esphome.codegen as cg -nextion_ns = cg.esphome_ns.namespace('nextion') +nextion_ns = cg.esphome_ns.namespace("nextion") diff --git a/esphome/components/nextion/binary_sensor.py b/esphome/components/nextion/binary_sensor.py index 6003c59803..e822b65eb5 100644 --- a/esphome/components/nextion/binary_sensor.py +++ b/esphome/components/nextion/binary_sensor.py @@ -5,19 +5,22 @@ from esphome.const import CONF_COMPONENT_ID, CONF_PAGE_ID, CONF_ID from . import nextion_ns from .display import Nextion -DEPENDENCIES = ['display'] +DEPENDENCIES = ["display"] -CONF_NEXTION_ID = 'nextion_id' +CONF_NEXTION_ID = "nextion_id" -NextionTouchComponent = nextion_ns.class_('NextionTouchComponent', binary_sensor.BinarySensor) +NextionTouchComponent = nextion_ns.class_( + "NextionTouchComponent", binary_sensor.BinarySensor +) -CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(NextionTouchComponent), - cv.GenerateID(CONF_NEXTION_ID): cv.use_id(Nextion), - - cv.Required(CONF_PAGE_ID): cv.uint8_t, - cv.Required(CONF_COMPONENT_ID): cv.uint8_t, -}) +CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(NextionTouchComponent), + cv.GenerateID(CONF_NEXTION_ID): cv.use_id(Nextion), + cv.Required(CONF_PAGE_ID): cv.uint8_t, + cv.Required(CONF_COMPONENT_ID): cv.uint8_t, + } +) def to_code(config): diff --git a/esphome/components/nextion/display.py b/esphome/components/nextion/display.py index 394de69585..483395fe2f 100644 --- a/esphome/components/nextion/display.py +++ b/esphome/components/nextion/display.py @@ -4,16 +4,22 @@ from esphome.components import display, uart from esphome.const import CONF_ID, CONF_LAMBDA, CONF_BRIGHTNESS from . import nextion_ns -DEPENDENCIES = ['uart'] -AUTO_LOAD = ['binary_sensor'] +DEPENDENCIES = ["uart"] +AUTO_LOAD = ["binary_sensor"] -Nextion = nextion_ns.class_('Nextion', cg.PollingComponent, uart.UARTDevice) -NextionRef = Nextion.operator('ref') +Nextion = nextion_ns.class_("Nextion", cg.PollingComponent, uart.UARTDevice) +NextionRef = Nextion.operator("ref") -CONFIG_SCHEMA = display.BASIC_DISPLAY_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(Nextion), - cv.Optional(CONF_BRIGHTNESS, default=1.0): cv.percentage, -}).extend(cv.polling_component_schema('5s')).extend(uart.UART_DEVICE_SCHEMA) +CONFIG_SCHEMA = ( + display.BASIC_DISPLAY_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(Nextion), + cv.Optional(CONF_BRIGHTNESS, default=1.0): cv.percentage, + } + ) + .extend(cv.polling_component_schema("5s")) + .extend(uart.UART_DEVICE_SCHEMA) +) def to_code(config): @@ -24,8 +30,9 @@ def to_code(config): if CONF_BRIGHTNESS in config: cg.add(var.set_brightness(config[CONF_BRIGHTNESS])) if CONF_LAMBDA in config: - lambda_ = yield cg.process_lambda(config[CONF_LAMBDA], [(NextionRef, 'it')], - return_type=cg.void) + lambda_ = yield cg.process_lambda( + config[CONF_LAMBDA], [(NextionRef, "it")], return_type=cg.void + ) cg.add(var.set_writer(lambda_)) yield display.register_display(var, config) diff --git a/esphome/components/nfc/__init__.py b/esphome/components/nfc/__init__.py index ae3c9a4c0a..b795a5d5ca 100644 --- a/esphome/components/nfc/__init__.py +++ b/esphome/components/nfc/__init__.py @@ -1,7 +1,7 @@ import esphome.codegen as cg -CODEOWNERS = ['@jesserockz'] +CODEOWNERS = ["@jesserockz"] -nfc_ns = cg.esphome_ns.namespace('nfc') +nfc_ns = cg.esphome_ns.namespace("nfc") -NfcTag = nfc_ns.class_('NfcTag') +NfcTag = nfc_ns.class_("NfcTag") diff --git a/esphome/components/ntc/sensor.py b/esphome/components/ntc/sensor.py index 5cafecdc5f..f3505eec53 100644 --- a/esphome/components/ntc/sensor.py +++ b/esphome/components/ntc/sensor.py @@ -3,37 +3,50 @@ from math import log import esphome.config_validation as cv import esphome.codegen as cg from esphome.components import sensor -from esphome.const import CONF_CALIBRATION, CONF_ID, CONF_REFERENCE_RESISTANCE, \ - CONF_REFERENCE_TEMPERATURE, CONF_SENSOR, CONF_TEMPERATURE, CONF_VALUE, \ - DEVICE_CLASS_TEMPERATURE, ICON_EMPTY, UNIT_CELSIUS +from esphome.const import ( + CONF_CALIBRATION, + CONF_ID, + CONF_REFERENCE_RESISTANCE, + CONF_REFERENCE_TEMPERATURE, + CONF_SENSOR, + CONF_TEMPERATURE, + CONF_VALUE, + DEVICE_CLASS_TEMPERATURE, + ICON_EMPTY, + UNIT_CELSIUS, +) -ntc_ns = cg.esphome_ns.namespace('ntc') -NTC = ntc_ns.class_('NTC', cg.Component, sensor.Sensor) +ntc_ns = cg.esphome_ns.namespace("ntc") +NTC = ntc_ns.class_("NTC", cg.Component, sensor.Sensor) -CONF_B_CONSTANT = 'b_constant' -CONF_A = 'a' -CONF_B = 'b' -CONF_C = 'c' +CONF_B_CONSTANT = "b_constant" +CONF_A = "a" +CONF_B = "b" +CONF_C = "c" ZERO_POINT = 273.15 def validate_calibration_parameter(value): if isinstance(value, dict): - return cv.Schema({ - cv.Required(CONF_TEMPERATURE): cv.float_, - cv.Required(CONF_VALUE): cv.float_, - })(value) + return cv.Schema( + { + cv.Required(CONF_TEMPERATURE): cv.float_, + cv.Required(CONF_VALUE): cv.float_, + } + )(value) value = cv.string(value) - parts = value.split('->') + parts = value.split("->") if len(parts) != 2: raise cv.Invalid("Calibration parameter must be of form 3000 -> 23°C") voltage = cv.resistance(parts[0].strip()) temperature = cv.temperature(parts[1].strip()) - return validate_calibration_parameter({ - CONF_TEMPERATURE: temperature, - CONF_VALUE: voltage, - }) + return validate_calibration_parameter( + { + CONF_TEMPERATURE: temperature, + CONF_VALUE: voltage, + } + ) def calc_steinhart_hart(value): @@ -48,16 +61,16 @@ def calc_steinhart_hart(value): l2 = log(r2) l3 = log(r3) - y1 = 1/t1 - y2 = 1/t2 - y3 = 1/t3 + y1 = 1 / t1 + y2 = 1 / t2 + y3 = 1 / t3 - g2 = (y2-y1)/(l2-l1) - g3 = (y3-y1)/(l3-l1) + g2 = (y2 - y1) / (l2 - l1) + g3 = (y3 - y1) / (l3 - l1) - c = (g3-g2)/(l3-l2) * 1/(l1+l2+l3) - b = g2 - c*(l1*l1 + l1*l2 + l2*l2) - a = y1 - (b + l1*l1*c) * l1 + c = (g3 - g2) / (l3 - l2) * 1 / (l1 + l2 + l3) + b = g2 - c * (l1 * l1 + l1 * l2 + l2 * l2) + a = y1 - (b + l1 * l1 * c) * l1 return a, b, c @@ -66,8 +79,8 @@ def calc_b(value): t0 = value[CONF_REFERENCE_TEMPERATURE] + ZERO_POINT r0 = value[CONF_REFERENCE_RESISTANCE] - a = (1/t0) - (1/beta) * log(r0) - b = 1/beta + a = (1 / t0) - (1 / beta) * log(r0) + b = 1 / beta c = 0 return a, b, c @@ -75,21 +88,27 @@ def calc_b(value): def process_calibration(value): if isinstance(value, dict): - value = cv.Schema({ - cv.Required(CONF_B_CONSTANT): cv.float_, - cv.Required(CONF_REFERENCE_TEMPERATURE): cv.temperature, - cv.Required(CONF_REFERENCE_RESISTANCE): cv.resistance, - })(value) + value = cv.Schema( + { + cv.Required(CONF_B_CONSTANT): cv.float_, + cv.Required(CONF_REFERENCE_TEMPERATURE): cv.temperature, + cv.Required(CONF_REFERENCE_RESISTANCE): cv.resistance, + } + )(value) a, b, c = calc_b(value) elif isinstance(value, list): if len(value) != 3: - raise cv.Invalid("Steinhart–Hart Calibration must consist of exactly three values") + raise cv.Invalid( + "Steinhart–Hart Calibration must consist of exactly three values" + ) value = cv.Schema([validate_calibration_parameter])(value) a, b, c = calc_steinhart_hart(value) else: - raise cv.Invalid("Calibration parameter accepts either a list for steinhart-hart " - "calibration, or mapping for b-constant calibration, " - "not {}".format(type(value))) + raise cv.Invalid( + "Calibration parameter accepts either a list for steinhart-hart " + "calibration, or mapping for b-constant calibration, " + "not {}".format(type(value)) + ) return { CONF_A: a, @@ -98,11 +117,17 @@ def process_calibration(value): } -CONFIG_SCHEMA = sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE).extend({ - cv.GenerateID(): cv.declare_id(NTC), - cv.Required(CONF_SENSOR): cv.use_id(sensor.Sensor), - cv.Required(CONF_CALIBRATION): process_calibration, -}).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = ( + sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE) + .extend( + { + cv.GenerateID(): cv.declare_id(NTC), + cv.Required(CONF_SENSOR): cv.use_id(sensor.Sensor), + cv.Required(CONF_CALIBRATION): process_calibration, + } + ) + .extend(cv.COMPONENT_SCHEMA) +) def to_code(config): diff --git a/esphome/components/ota/__init__.py b/esphome/components/ota/__init__.py index 8956227c17..25a278f5bf 100644 --- a/esphome/components/ota/__init__.py +++ b/esphome/components/ota/__init__.py @@ -2,25 +2,33 @@ from esphome.cpp_generator import RawExpression import esphome.codegen as cg import esphome.config_validation as cv from esphome.const import ( - CONF_ID, CONF_NUM_ATTEMPTS, CONF_PASSWORD, - CONF_PORT, CONF_REBOOT_TIMEOUT, CONF_SAFE_MODE + CONF_ID, + CONF_NUM_ATTEMPTS, + CONF_PASSWORD, + CONF_PORT, + CONF_REBOOT_TIMEOUT, + CONF_SAFE_MODE, ) from esphome.core import CORE, coroutine_with_priority -CODEOWNERS = ['@esphome/core'] -DEPENDENCIES = ['network'] +CODEOWNERS = ["@esphome/core"] +DEPENDENCIES = ["network"] -ota_ns = cg.esphome_ns.namespace('ota') -OTAComponent = ota_ns.class_('OTAComponent', cg.Component) +ota_ns = cg.esphome_ns.namespace("ota") +OTAComponent = ota_ns.class_("OTAComponent", cg.Component) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(OTAComponent), - cv.Optional(CONF_SAFE_MODE, default=True): cv.boolean, - cv.SplitDefault(CONF_PORT, esp8266=8266, esp32=3232): cv.port, - cv.Optional(CONF_PASSWORD, default=''): cv.string, - cv.Optional(CONF_REBOOT_TIMEOUT, default='5min'): cv.positive_time_period_milliseconds, - cv.Optional(CONF_NUM_ATTEMPTS, default='10'): cv.positive_not_null_int -}).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(OTAComponent), + cv.Optional(CONF_SAFE_MODE, default=True): cv.boolean, + cv.SplitDefault(CONF_PORT, esp8266=8266, esp32=3232): cv.port, + cv.Optional(CONF_PASSWORD, default=""): cv.string, + cv.Optional( + CONF_REBOOT_TIMEOUT, default="5min" + ): cv.positive_time_period_milliseconds, + cv.Optional(CONF_NUM_ATTEMPTS, default="10"): cv.positive_not_null_int, + } +).extend(cv.COMPONENT_SCHEMA) @coroutine_with_priority(50.0) @@ -32,11 +40,12 @@ def to_code(config): yield cg.register_component(var, config) if config[CONF_SAFE_MODE]: - condition = var.should_enter_safe_mode(config[CONF_NUM_ATTEMPTS], - config[CONF_REBOOT_TIMEOUT]) + condition = var.should_enter_safe_mode( + config[CONF_NUM_ATTEMPTS], config[CONF_REBOOT_TIMEOUT] + ) cg.add(RawExpression(f"if ({condition}) return")) if CORE.is_esp8266: - cg.add_library('Update', None) + cg.add_library("Update", None) elif CORE.is_esp32: - cg.add_library('Hash', None) + cg.add_library("Hash", None) diff --git a/esphome/components/output/__init__.py b/esphome/components/output/__init__.py index 34cb7c3f7a..487bd8cba5 100644 --- a/esphome/components/output/__init__.py +++ b/esphome/components/output/__init__.py @@ -3,34 +3,44 @@ import esphome.config_validation as cv from esphome import automation from esphome.automation import maybe_simple_id from esphome.components import power_supply -from esphome.const import CONF_ID, CONF_INVERTED, CONF_LEVEL, CONF_MAX_POWER, \ - CONF_MIN_POWER, CONF_POWER_SUPPLY +from esphome.const import ( + CONF_ID, + CONF_INVERTED, + CONF_LEVEL, + CONF_MAX_POWER, + CONF_MIN_POWER, + CONF_POWER_SUPPLY, +) from esphome.core import CORE, coroutine -CODEOWNERS = ['@esphome/core'] +CODEOWNERS = ["@esphome/core"] IS_PLATFORM_COMPONENT = True -BINARY_OUTPUT_SCHEMA = cv.Schema({ - cv.Optional(CONF_POWER_SUPPLY): cv.use_id(power_supply.PowerSupply), - cv.Optional(CONF_INVERTED): cv.boolean, -}) +BINARY_OUTPUT_SCHEMA = cv.Schema( + { + cv.Optional(CONF_POWER_SUPPLY): cv.use_id(power_supply.PowerSupply), + cv.Optional(CONF_INVERTED): cv.boolean, + } +) -FLOAT_OUTPUT_SCHEMA = BINARY_OUTPUT_SCHEMA.extend({ - cv.Optional(CONF_MAX_POWER): cv.percentage, - cv.Optional(CONF_MIN_POWER): cv.percentage, -}) +FLOAT_OUTPUT_SCHEMA = BINARY_OUTPUT_SCHEMA.extend( + { + cv.Optional(CONF_MAX_POWER): cv.percentage, + cv.Optional(CONF_MIN_POWER): cv.percentage, + } +) -output_ns = cg.esphome_ns.namespace('output') -BinaryOutput = output_ns.class_('BinaryOutput') -BinaryOutputPtr = BinaryOutput.operator('ptr') -FloatOutput = output_ns.class_('FloatOutput', BinaryOutput) -FloatOutputPtr = FloatOutput.operator('ptr') +output_ns = cg.esphome_ns.namespace("output") +BinaryOutput = output_ns.class_("BinaryOutput") +BinaryOutputPtr = BinaryOutput.operator("ptr") +FloatOutput = output_ns.class_("FloatOutput", BinaryOutput) +FloatOutputPtr = FloatOutput.operator("ptr") # Actions -TurnOffAction = output_ns.class_('TurnOffAction', automation.Action) -TurnOnAction = output_ns.class_('TurnOnAction', automation.Action) -SetLevelAction = output_ns.class_('SetLevelAction', automation.Action) +TurnOffAction = output_ns.class_("TurnOffAction", automation.Action) +TurnOnAction = output_ns.class_("TurnOnAction", automation.Action) +SetLevelAction = output_ns.class_("SetLevelAction", automation.Action) @coroutine @@ -53,27 +63,37 @@ def register_output(var, config): yield setup_output_platform_(var, config) -BINARY_OUTPUT_ACTION_SCHEMA = maybe_simple_id({ - cv.Required(CONF_ID): cv.use_id(BinaryOutput), -}) +BINARY_OUTPUT_ACTION_SCHEMA = maybe_simple_id( + { + cv.Required(CONF_ID): cv.use_id(BinaryOutput), + } +) -@automation.register_action('output.turn_on', TurnOnAction, BINARY_OUTPUT_ACTION_SCHEMA) +@automation.register_action("output.turn_on", TurnOnAction, BINARY_OUTPUT_ACTION_SCHEMA) def output_turn_on_to_code(config, action_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) yield cg.new_Pvariable(action_id, template_arg, paren) -@automation.register_action('output.turn_off', TurnOffAction, BINARY_OUTPUT_ACTION_SCHEMA) +@automation.register_action( + "output.turn_off", TurnOffAction, BINARY_OUTPUT_ACTION_SCHEMA +) def output_turn_off_to_code(config, action_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) yield cg.new_Pvariable(action_id, template_arg, paren) -@automation.register_action('output.set_level', SetLevelAction, cv.Schema({ - cv.Required(CONF_ID): cv.use_id(FloatOutput), - cv.Required(CONF_LEVEL): cv.templatable(cv.percentage), -})) +@automation.register_action( + "output.set_level", + SetLevelAction, + cv.Schema( + { + cv.Required(CONF_ID): cv.use_id(FloatOutput), + cv.Required(CONF_LEVEL): cv.templatable(cv.percentage), + } + ), +) def output_set_level_to_code(config, action_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) var = cg.new_Pvariable(action_id, template_arg, paren) diff --git a/esphome/components/output/switch/__init__.py b/esphome/components/output/switch/__init__.py index 5795271f8b..14027de74c 100644 --- a/esphome/components/output/switch/__init__.py +++ b/esphome/components/output/switch/__init__.py @@ -4,12 +4,14 @@ from esphome.components import output, switch from esphome.const import CONF_ID, CONF_OUTPUT from .. import output_ns -OutputSwitch = output_ns.class_('OutputSwitch', switch.Switch, cg.Component) +OutputSwitch = output_ns.class_("OutputSwitch", switch.Switch, cg.Component) -CONFIG_SCHEMA = switch.SWITCH_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(OutputSwitch), - cv.Required(CONF_OUTPUT): cv.use_id(output.BinaryOutput), -}).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = switch.SWITCH_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(OutputSwitch), + cv.Required(CONF_OUTPUT): cv.use_id(output.BinaryOutput), + } +).extend(cv.COMPONENT_SCHEMA) def to_code(config): diff --git a/esphome/components/packages/__init__.py b/esphome/components/packages/__init__.py index 55dfe35e34..8c5c9a0144 100644 --- a/esphome/components/packages/__init__.py +++ b/esphome/components/packages/__init__.py @@ -4,7 +4,6 @@ from esphome.const import CONF_PACKAGES def _merge_package(full_old, full_new): - def merge(old, new): # pylint: disable=no-else-return if isinstance(new, dict): @@ -30,8 +29,10 @@ def do_packages_pass(config: dict): packages = config[CONF_PACKAGES] with cv.prepend_path(CONF_PACKAGES): if not isinstance(packages, dict): - raise cv.Invalid("Packages must be a key to value mapping, got {} instead" - "".format(type(packages))) + raise cv.Invalid( + "Packages must be a key to value mapping, got {} instead" + "".format(type(packages)) + ) for package_name, package_config in packages.items(): with cv.prepend_path(package_name): diff --git a/esphome/components/partition/light.py b/esphome/components/partition/light.py index ba1059e36b..202481b936 100644 --- a/esphome/components/partition/light.py +++ b/esphome/components/partition/light.py @@ -3,34 +3,49 @@ import esphome.config_validation as cv from esphome.components import light from esphome.const import CONF_FROM, CONF_ID, CONF_SEGMENTS, CONF_TO, CONF_OUTPUT_ID -partitions_ns = cg.esphome_ns.namespace('partition') -AddressableSegment = partitions_ns.class_('AddressableSegment') -PartitionLightOutput = partitions_ns.class_('PartitionLightOutput', light.AddressableLight) +partitions_ns = cg.esphome_ns.namespace("partition") +AddressableSegment = partitions_ns.class_("AddressableSegment") +PartitionLightOutput = partitions_ns.class_( + "PartitionLightOutput", light.AddressableLight +) def validate_from_to(value): if value[CONF_FROM] > value[CONF_TO]: - raise cv.Invalid("From ({}) must not be larger than to ({})" - "".format(value[CONF_FROM], value[CONF_TO])) + raise cv.Invalid( + "From ({}) must not be larger than to ({})" + "".format(value[CONF_FROM], value[CONF_TO]) + ) return value -CONFIG_SCHEMA = light.ADDRESSABLE_LIGHT_SCHEMA.extend({ - cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(PartitionLightOutput), - cv.Required(CONF_SEGMENTS): cv.All(cv.ensure_list({ - cv.Required(CONF_ID): cv.use_id(light.AddressableLightState), - cv.Required(CONF_FROM): cv.positive_int, - cv.Required(CONF_TO): cv.positive_int, - }, validate_from_to), cv.Length(min=1)), -}) +CONFIG_SCHEMA = light.ADDRESSABLE_LIGHT_SCHEMA.extend( + { + cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(PartitionLightOutput), + cv.Required(CONF_SEGMENTS): cv.All( + cv.ensure_list( + { + cv.Required(CONF_ID): cv.use_id(light.AddressableLightState), + cv.Required(CONF_FROM): cv.positive_int, + cv.Required(CONF_TO): cv.positive_int, + }, + validate_from_to, + ), + cv.Length(min=1), + ), + } +) def to_code(config): segments = [] for conf in config[CONF_SEGMENTS]: var = yield cg.get_variable(conf[CONF_ID]) - segments.append(AddressableSegment(var, conf[CONF_FROM], - conf[CONF_TO] - conf[CONF_FROM] + 1)) + segments.append( + AddressableSegment( + var, conf[CONF_FROM], conf[CONF_TO] - conf[CONF_FROM] + 1 + ) + ) var = cg.new_Pvariable(config[CONF_OUTPUT_ID], segments) yield cg.register_component(var, config) diff --git a/esphome/components/pca9685/__init__.py b/esphome/components/pca9685/__init__.py index 8e02bd78df..d88012a1cd 100644 --- a/esphome/components/pca9685/__init__.py +++ b/esphome/components/pca9685/__init__.py @@ -3,17 +3,24 @@ import esphome.config_validation as cv from esphome.components import i2c from esphome.const import CONF_FREQUENCY, CONF_ID -DEPENDENCIES = ['i2c'] +DEPENDENCIES = ["i2c"] MULTI_CONF = True -pca9685_ns = cg.esphome_ns.namespace('pca9685') -PCA9685Output = pca9685_ns.class_('PCA9685Output', cg.Component, i2c.I2CDevice) +pca9685_ns = cg.esphome_ns.namespace("pca9685") +PCA9685Output = pca9685_ns.class_("PCA9685Output", cg.Component, i2c.I2CDevice) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(PCA9685Output), - cv.Required(CONF_FREQUENCY): cv.All(cv.frequency, - cv.Range(min=23.84, max=1525.88)), -}).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(0x40)) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(PCA9685Output), + cv.Required(CONF_FREQUENCY): cv.All( + cv.frequency, cv.Range(min=23.84, max=1525.88) + ), + } + ) + .extend(cv.COMPONENT_SCHEMA) + .extend(i2c.i2c_device_schema(0x40)) +) def to_code(config): diff --git a/esphome/components/pca9685/output.py b/esphome/components/pca9685/output.py index b5f4805611..c3cb88eeaf 100644 --- a/esphome/components/pca9685/output.py +++ b/esphome/components/pca9685/output.py @@ -4,17 +4,18 @@ from esphome.components import output from esphome.const import CONF_CHANNEL, CONF_ID from . import PCA9685Output, pca9685_ns -DEPENDENCIES = ['pca9685'] +DEPENDENCIES = ["pca9685"] -PCA9685Channel = pca9685_ns.class_('PCA9685Channel', output.FloatOutput) -CONF_PCA9685_ID = 'pca9685_id' +PCA9685Channel = pca9685_ns.class_("PCA9685Channel", output.FloatOutput) +CONF_PCA9685_ID = "pca9685_id" -CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend({ - cv.Required(CONF_ID): cv.declare_id(PCA9685Channel), - cv.GenerateID(CONF_PCA9685_ID): cv.use_id(PCA9685Output), - - cv.Required(CONF_CHANNEL): cv.int_range(min=0, max=15), -}) +CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend( + { + cv.Required(CONF_ID): cv.declare_id(PCA9685Channel), + cv.GenerateID(CONF_PCA9685_ID): cv.use_id(PCA9685Output), + cv.Required(CONF_CHANNEL): cv.int_range(min=0, max=15), + } +) def to_code(config): diff --git a/esphome/components/pcd8544/display.py b/esphome/components/pcd8544/display.py index f4b625fe8b..50cc1b02a8 100644 --- a/esphome/components/pcd8544/display.py +++ b/esphome/components/pcd8544/display.py @@ -3,23 +3,37 @@ import esphome.config_validation as cv from esphome import pins from esphome.components import display, spi from esphome.const import ( - CONF_DC_PIN, CONF_ID, CONF_LAMBDA, CONF_PAGES, CONF_RESET_PIN, CONF_CS_PIN, CONF_CONTRAST + CONF_DC_PIN, + CONF_ID, + CONF_LAMBDA, + CONF_PAGES, + CONF_RESET_PIN, + CONF_CS_PIN, + CONF_CONTRAST, ) -DEPENDENCIES = ['spi'] +DEPENDENCIES = ["spi"] -pcd8544_ns = cg.esphome_ns.namespace('pcd8544') -PCD8544 = pcd8544_ns.class_('PCD8544', cg.PollingComponent, display.DisplayBuffer, spi.SPIDevice) +pcd8544_ns = cg.esphome_ns.namespace("pcd8544") +PCD8544 = pcd8544_ns.class_( + "PCD8544", cg.PollingComponent, display.DisplayBuffer, spi.SPIDevice +) -CONFIG_SCHEMA = cv.All(display.FULL_DISPLAY_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(PCD8544), - cv.Required(CONF_DC_PIN): pins.gpio_output_pin_schema, - cv.Required(CONF_RESET_PIN): pins.gpio_output_pin_schema, - cv.Required(CONF_CS_PIN): pins.gpio_output_pin_schema, # CE - cv.Optional(CONF_CONTRAST, default=0x7f): cv.int_, -}).extend(cv.polling_component_schema('1s')).extend(spi.spi_device_schema()), - cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA)) +CONFIG_SCHEMA = cv.All( + display.FULL_DISPLAY_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(PCD8544), + cv.Required(CONF_DC_PIN): pins.gpio_output_pin_schema, + cv.Required(CONF_RESET_PIN): pins.gpio_output_pin_schema, + cv.Required(CONF_CS_PIN): pins.gpio_output_pin_schema, # CE + cv.Optional(CONF_CONTRAST, default=0x7F): cv.int_, + } + ) + .extend(cv.polling_component_schema("1s")) + .extend(spi.spi_device_schema()), + cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA), +) def to_code(config): @@ -37,6 +51,7 @@ def to_code(config): cg.add(var.set_contrast(config[CONF_CONTRAST])) if CONF_LAMBDA in config: - lambda_ = yield cg.process_lambda(config[CONF_LAMBDA], [(display.DisplayBufferRef, 'it')], - return_type=cg.void) + lambda_ = yield cg.process_lambda( + config[CONF_LAMBDA], [(display.DisplayBufferRef, "it")], return_type=cg.void + ) cg.add(var.set_writer(lambda_)) diff --git a/esphome/components/pcf8574/__init__.py b/esphome/components/pcf8574/__init__.py index daf367c089..8c028fabc1 100644 --- a/esphome/components/pcf8574/__init__.py +++ b/esphome/components/pcf8574/__init__.py @@ -4,25 +4,31 @@ from esphome import pins from esphome.components import i2c from esphome.const import CONF_ID, CONF_NUMBER, CONF_MODE, CONF_INVERTED -DEPENDENCIES = ['i2c'] +DEPENDENCIES = ["i2c"] MULTI_CONF = True -pcf8574_ns = cg.esphome_ns.namespace('pcf8574') -PCF8574GPIOMode = pcf8574_ns.enum('PCF8574GPIOMode') +pcf8574_ns = cg.esphome_ns.namespace("pcf8574") +PCF8574GPIOMode = pcf8574_ns.enum("PCF8574GPIOMode") PCF8674_GPIO_MODES = { - 'INPUT': PCF8574GPIOMode.PCF8574_INPUT, - 'OUTPUT': PCF8574GPIOMode.PCF8574_OUTPUT, + "INPUT": PCF8574GPIOMode.PCF8574_INPUT, + "OUTPUT": PCF8574GPIOMode.PCF8574_OUTPUT, } -PCF8574Component = pcf8574_ns.class_('PCF8574Component', cg.Component, i2c.I2CDevice) -PCF8574GPIOPin = pcf8574_ns.class_('PCF8574GPIOPin', cg.GPIOPin) +PCF8574Component = pcf8574_ns.class_("PCF8574Component", cg.Component, i2c.I2CDevice) +PCF8574GPIOPin = pcf8574_ns.class_("PCF8574GPIOPin", cg.GPIOPin) -CONF_PCF8574 = 'pcf8574' -CONF_PCF8575 = 'pcf8575' -CONFIG_SCHEMA = cv.Schema({ - cv.Required(CONF_ID): cv.declare_id(PCF8574Component), - cv.Optional(CONF_PCF8575, default=False): cv.boolean, -}).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(0x21)) +CONF_PCF8574 = "pcf8574" +CONF_PCF8575 = "pcf8575" +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.Required(CONF_ID): cv.declare_id(PCF8574Component), + cv.Optional(CONF_PCF8575, default=False): cv.boolean, + } + ) + .extend(cv.COMPONENT_SCHEMA) + .extend(i2c.i2c_device_schema(0x21)) +) def to_code(config): @@ -34,27 +40,37 @@ def to_code(config): def validate_pcf8574_gpio_mode(value): value = cv.string(value) - if value.upper() == 'INPUT_PULLUP': - raise cv.Invalid("INPUT_PULLUP mode has been removed in 1.14 and been combined into " - "INPUT mode (they were the same thing). Please use INPUT instead.") + if value.upper() == "INPUT_PULLUP": + raise cv.Invalid( + "INPUT_PULLUP mode has been removed in 1.14 and been combined into " + "INPUT mode (they were the same thing). Please use INPUT instead." + ) return cv.enum(PCF8674_GPIO_MODES, upper=True)(value) -PCF8574_OUTPUT_PIN_SCHEMA = cv.Schema({ - cv.Required(CONF_PCF8574): cv.use_id(PCF8574Component), - cv.Required(CONF_NUMBER): cv.int_, - cv.Optional(CONF_MODE, default="OUTPUT"): validate_pcf8574_gpio_mode, - cv.Optional(CONF_INVERTED, default=False): cv.boolean, -}) -PCF8574_INPUT_PIN_SCHEMA = cv.Schema({ - cv.Required(CONF_PCF8574): cv.use_id(PCF8574Component), - cv.Required(CONF_NUMBER): cv.int_, - cv.Optional(CONF_MODE, default="INPUT"): validate_pcf8574_gpio_mode, - cv.Optional(CONF_INVERTED, default=False): cv.boolean, -}) +PCF8574_OUTPUT_PIN_SCHEMA = cv.Schema( + { + cv.Required(CONF_PCF8574): cv.use_id(PCF8574Component), + cv.Required(CONF_NUMBER): cv.int_, + cv.Optional(CONF_MODE, default="OUTPUT"): validate_pcf8574_gpio_mode, + cv.Optional(CONF_INVERTED, default=False): cv.boolean, + } +) +PCF8574_INPUT_PIN_SCHEMA = cv.Schema( + { + cv.Required(CONF_PCF8574): cv.use_id(PCF8574Component), + cv.Required(CONF_NUMBER): cv.int_, + cv.Optional(CONF_MODE, default="INPUT"): validate_pcf8574_gpio_mode, + cv.Optional(CONF_INVERTED, default=False): cv.boolean, + } +) -@pins.PIN_SCHEMA_REGISTRY.register('pcf8574', (PCF8574_OUTPUT_PIN_SCHEMA, PCF8574_INPUT_PIN_SCHEMA)) +@pins.PIN_SCHEMA_REGISTRY.register( + "pcf8574", (PCF8574_OUTPUT_PIN_SCHEMA, PCF8574_INPUT_PIN_SCHEMA) +) def pcf8574_pin_to_code(config): parent = yield cg.get_variable(config[CONF_PCF8574]) - yield PCF8574GPIOPin.new(parent, config[CONF_NUMBER], config[CONF_MODE], config[CONF_INVERTED]) + yield PCF8574GPIOPin.new( + parent, config[CONF_NUMBER], config[CONF_MODE], config[CONF_INVERTED] + ) diff --git a/esphome/components/pid/__init__.py b/esphome/components/pid/__init__.py index 6f14e10033..71a87b6ae5 100644 --- a/esphome/components/pid/__init__.py +++ b/esphome/components/pid/__init__.py @@ -1 +1 @@ -CODEOWNERS = ['@OttoWinter'] +CODEOWNERS = ["@OttoWinter"] diff --git a/esphome/components/pid/climate.py b/esphome/components/pid/climate.py index 446c614f14..c16f1726ae 100644 --- a/esphome/components/pid/climate.py +++ b/esphome/components/pid/climate.py @@ -4,40 +4,51 @@ from esphome import automation from esphome.components import climate, sensor, output from esphome.const import CONF_ID, CONF_SENSOR -pid_ns = cg.esphome_ns.namespace('pid') -PIDClimate = pid_ns.class_('PIDClimate', climate.Climate, cg.Component) -PIDAutotuneAction = pid_ns.class_('PIDAutotuneAction', automation.Action) -PIDResetIntegralTermAction = pid_ns.class_('PIDResetIntegralTermAction', automation.Action) -PIDSetControlParametersAction = pid_ns.class_('PIDSetControlParametersAction', automation.Action) +pid_ns = cg.esphome_ns.namespace("pid") +PIDClimate = pid_ns.class_("PIDClimate", climate.Climate, cg.Component) +PIDAutotuneAction = pid_ns.class_("PIDAutotuneAction", automation.Action) +PIDResetIntegralTermAction = pid_ns.class_( + "PIDResetIntegralTermAction", automation.Action +) +PIDSetControlParametersAction = pid_ns.class_( + "PIDSetControlParametersAction", automation.Action +) -CONF_DEFAULT_TARGET_TEMPERATURE = 'default_target_temperature' +CONF_DEFAULT_TARGET_TEMPERATURE = "default_target_temperature" -CONF_KP = 'kp' -CONF_KI = 'ki' -CONF_KD = 'kd' -CONF_CONTROL_PARAMETERS = 'control_parameters' -CONF_COOL_OUTPUT = 'cool_output' -CONF_HEAT_OUTPUT = 'heat_output' -CONF_NOISEBAND = 'noiseband' -CONF_POSITIVE_OUTPUT = 'positive_output' -CONF_NEGATIVE_OUTPUT = 'negative_output' -CONF_MIN_INTEGRAL = 'min_integral' -CONF_MAX_INTEGRAL = 'max_integral' +CONF_KP = "kp" +CONF_KI = "ki" +CONF_KD = "kd" +CONF_CONTROL_PARAMETERS = "control_parameters" +CONF_COOL_OUTPUT = "cool_output" +CONF_HEAT_OUTPUT = "heat_output" +CONF_NOISEBAND = "noiseband" +CONF_POSITIVE_OUTPUT = "positive_output" +CONF_NEGATIVE_OUTPUT = "negative_output" +CONF_MIN_INTEGRAL = "min_integral" +CONF_MAX_INTEGRAL = "max_integral" -CONFIG_SCHEMA = cv.All(climate.CLIMATE_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(PIDClimate), - cv.Required(CONF_SENSOR): cv.use_id(sensor.Sensor), - cv.Required(CONF_DEFAULT_TARGET_TEMPERATURE): cv.temperature, - cv.Optional(CONF_COOL_OUTPUT): cv.use_id(output.FloatOutput), - cv.Optional(CONF_HEAT_OUTPUT): cv.use_id(output.FloatOutput), - cv.Required(CONF_CONTROL_PARAMETERS): cv.Schema({ - cv.Required(CONF_KP): cv.float_, - cv.Optional(CONF_KI, default=0.0): cv.float_, - cv.Optional(CONF_KD, default=0.0): cv.float_, - cv.Optional(CONF_MIN_INTEGRAL, default=-1): cv.float_, - cv.Optional(CONF_MAX_INTEGRAL, default=1): cv.float_, - }), -}), cv.has_at_least_one_key(CONF_COOL_OUTPUT, CONF_HEAT_OUTPUT)) +CONFIG_SCHEMA = cv.All( + climate.CLIMATE_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(PIDClimate), + cv.Required(CONF_SENSOR): cv.use_id(sensor.Sensor), + cv.Required(CONF_DEFAULT_TARGET_TEMPERATURE): cv.temperature, + cv.Optional(CONF_COOL_OUTPUT): cv.use_id(output.FloatOutput), + cv.Optional(CONF_HEAT_OUTPUT): cv.use_id(output.FloatOutput), + cv.Required(CONF_CONTROL_PARAMETERS): cv.Schema( + { + cv.Required(CONF_KP): cv.float_, + cv.Optional(CONF_KI, default=0.0): cv.float_, + cv.Optional(CONF_KD, default=0.0): cv.float_, + cv.Optional(CONF_MIN_INTEGRAL, default=-1): cv.float_, + cv.Optional(CONF_MAX_INTEGRAL, default=1): cv.float_, + } + ), + } + ), + cv.has_at_least_one_key(CONF_COOL_OUTPUT, CONF_HEAT_OUTPUT), +) def to_code(config): @@ -67,23 +78,35 @@ def to_code(config): @automation.register_action( - 'climate.pid.reset_integral_term', + "climate.pid.reset_integral_term", PIDResetIntegralTermAction, - automation.maybe_simple_id({ - cv.Required(CONF_ID): cv.use_id(PIDClimate), - }) + automation.maybe_simple_id( + { + cv.Required(CONF_ID): cv.use_id(PIDClimate), + } + ), ) def pid_reset_integral_term(config, action_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) yield cg.new_Pvariable(action_id, template_arg, paren) -@automation.register_action('climate.pid.autotune', PIDAutotuneAction, automation.maybe_simple_id({ - cv.Required(CONF_ID): cv.use_id(PIDClimate), - cv.Optional(CONF_NOISEBAND, default=0.25): cv.float_, - cv.Optional(CONF_POSITIVE_OUTPUT, default=1.0): cv.possibly_negative_percentage, - cv.Optional(CONF_NEGATIVE_OUTPUT, default=-1.0): cv.possibly_negative_percentage, -})) +@automation.register_action( + "climate.pid.autotune", + PIDAutotuneAction, + automation.maybe_simple_id( + { + cv.Required(CONF_ID): cv.use_id(PIDClimate), + cv.Optional(CONF_NOISEBAND, default=0.25): cv.float_, + cv.Optional( + CONF_POSITIVE_OUTPUT, default=1.0 + ): cv.possibly_negative_percentage, + cv.Optional( + CONF_NEGATIVE_OUTPUT, default=-1.0 + ): cv.possibly_negative_percentage, + } + ), +) def esp8266_set_frequency_to_code(config, action_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) var = cg.new_Pvariable(action_id, template_arg, paren) @@ -94,14 +117,16 @@ def esp8266_set_frequency_to_code(config, action_id, template_arg, args): @automation.register_action( - 'climate.pid.set_control_parameters', + "climate.pid.set_control_parameters", PIDSetControlParametersAction, - automation.maybe_simple_id({ - cv.Required(CONF_ID): cv.use_id(PIDClimate), - cv.Required(CONF_KP): cv.templatable(cv.float_), - cv.Optional(CONF_KI, default=0.0): cv.templatable(cv.float_), - cv.Optional(CONF_KD, default=0.0): cv.templatable(cv.float_), - }) + automation.maybe_simple_id( + { + cv.Required(CONF_ID): cv.use_id(PIDClimate), + cv.Required(CONF_KP): cv.templatable(cv.float_), + cv.Optional(CONF_KI, default=0.0): cv.templatable(cv.float_), + cv.Optional(CONF_KD, default=0.0): cv.templatable(cv.float_), + } + ), ) def set_control_parameters(config, action_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) diff --git a/esphome/components/pid/sensor/__init__.py b/esphome/components/pid/sensor/__init__.py index e655480a46..d29fb6b662 100644 --- a/esphome/components/pid/sensor/__init__.py +++ b/esphome/components/pid/sensor/__init__.py @@ -1,32 +1,43 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor -from esphome.const import CONF_ID, DEVICE_CLASS_EMPTY, UNIT_PERCENT, ICON_GAUGE, CONF_TYPE +from esphome.const import ( + CONF_ID, + DEVICE_CLASS_EMPTY, + UNIT_PERCENT, + ICON_GAUGE, + CONF_TYPE, +) from ..climate import pid_ns, PIDClimate -PIDClimateSensor = pid_ns.class_('PIDClimateSensor', sensor.Sensor, cg.Component) -PIDClimateSensorType = pid_ns.enum('PIDClimateSensorType') +PIDClimateSensor = pid_ns.class_("PIDClimateSensor", sensor.Sensor, cg.Component) +PIDClimateSensorType = pid_ns.enum("PIDClimateSensorType") PID_CLIMATE_SENSOR_TYPES = { - 'RESULT': PIDClimateSensorType.PID_SENSOR_TYPE_RESULT, - 'ERROR': PIDClimateSensorType.PID_SENSOR_TYPE_ERROR, - 'PROPORTIONAL': PIDClimateSensorType.PID_SENSOR_TYPE_PROPORTIONAL, - 'INTEGRAL': PIDClimateSensorType.PID_SENSOR_TYPE_INTEGRAL, - 'DERIVATIVE': PIDClimateSensorType.PID_SENSOR_TYPE_DERIVATIVE, - 'HEAT': PIDClimateSensorType.PID_SENSOR_TYPE_HEAT, - 'COOL': PIDClimateSensorType.PID_SENSOR_TYPE_COOL, - 'KP': PIDClimateSensorType.PID_SENSOR_TYPE_KP, - 'KI': PIDClimateSensorType.PID_SENSOR_TYPE_KI, - 'KD': PIDClimateSensorType.PID_SENSOR_TYPE_KD, + "RESULT": PIDClimateSensorType.PID_SENSOR_TYPE_RESULT, + "ERROR": PIDClimateSensorType.PID_SENSOR_TYPE_ERROR, + "PROPORTIONAL": PIDClimateSensorType.PID_SENSOR_TYPE_PROPORTIONAL, + "INTEGRAL": PIDClimateSensorType.PID_SENSOR_TYPE_INTEGRAL, + "DERIVATIVE": PIDClimateSensorType.PID_SENSOR_TYPE_DERIVATIVE, + "HEAT": PIDClimateSensorType.PID_SENSOR_TYPE_HEAT, + "COOL": PIDClimateSensorType.PID_SENSOR_TYPE_COOL, + "KP": PIDClimateSensorType.PID_SENSOR_TYPE_KP, + "KI": PIDClimateSensorType.PID_SENSOR_TYPE_KI, + "KD": PIDClimateSensorType.PID_SENSOR_TYPE_KD, } -CONF_CLIMATE_ID = 'climate_id' -CONFIG_SCHEMA = sensor.sensor_schema(UNIT_PERCENT, ICON_GAUGE, 1, DEVICE_CLASS_EMPTY).extend({ - cv.GenerateID(): cv.declare_id(PIDClimateSensor), - cv.GenerateID(CONF_CLIMATE_ID): cv.use_id(PIDClimate), - - cv.Required(CONF_TYPE): cv.enum(PID_CLIMATE_SENSOR_TYPES, upper=True), -}).extend(cv.COMPONENT_SCHEMA) +CONF_CLIMATE_ID = "climate_id" +CONFIG_SCHEMA = ( + sensor.sensor_schema(UNIT_PERCENT, ICON_GAUGE, 1, DEVICE_CLASS_EMPTY) + .extend( + { + cv.GenerateID(): cv.declare_id(PIDClimateSensor), + cv.GenerateID(CONF_CLIMATE_ID): cv.use_id(PIDClimate), + cv.Required(CONF_TYPE): cv.enum(PID_CLIMATE_SENSOR_TYPES, upper=True), + } + ) + .extend(cv.COMPONENT_SCHEMA) +) def to_code(config): diff --git a/esphome/components/pmsx003/sensor.py b/esphome/components/pmsx003/sensor.py index aa1be62ad0..05becfb71f 100644 --- a/esphome/components/pmsx003/sensor.py +++ b/esphome/components/pmsx003/sensor.py @@ -1,22 +1,36 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, uart -from esphome.const import CONF_FORMALDEHYDE, CONF_HUMIDITY, CONF_ID, CONF_PM_10_0, \ - CONF_PM_1_0, CONF_PM_2_5, CONF_TEMPERATURE, CONF_TYPE, DEVICE_CLASS_EMPTY, \ - DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_TEMPERATURE, ICON_CHEMICAL_WEAPON, ICON_EMPTY, \ - UNIT_MICROGRAMS_PER_CUBIC_METER, UNIT_CELSIUS, UNIT_PERCENT +from esphome.const import ( + CONF_FORMALDEHYDE, + CONF_HUMIDITY, + CONF_ID, + CONF_PM_10_0, + CONF_PM_1_0, + CONF_PM_2_5, + CONF_TEMPERATURE, + CONF_TYPE, + DEVICE_CLASS_EMPTY, + DEVICE_CLASS_HUMIDITY, + DEVICE_CLASS_TEMPERATURE, + ICON_CHEMICAL_WEAPON, + ICON_EMPTY, + UNIT_MICROGRAMS_PER_CUBIC_METER, + UNIT_CELSIUS, + UNIT_PERCENT, +) -DEPENDENCIES = ['uart'] +DEPENDENCIES = ["uart"] -pmsx003_ns = cg.esphome_ns.namespace('pmsx003') -PMSX003Component = pmsx003_ns.class_('PMSX003Component', uart.UARTDevice, cg.Component) -PMSX003Sensor = pmsx003_ns.class_('PMSX003Sensor', sensor.Sensor) +pmsx003_ns = cg.esphome_ns.namespace("pmsx003") +PMSX003Component = pmsx003_ns.class_("PMSX003Component", uart.UARTDevice, cg.Component) +PMSX003Sensor = pmsx003_ns.class_("PMSX003Sensor", sensor.Sensor) -TYPE_PMSX003 = 'PMSX003' -TYPE_PMS5003T = 'PMS5003T' -TYPE_PMS5003ST = 'PMS5003ST' +TYPE_PMSX003 = "PMSX003" +TYPE_PMS5003T = "PMS5003T" +TYPE_PMS5003ST = "PMS5003ST" -PMSX003Type = pmsx003_ns.enum('PMSX003Type') +PMSX003Type = pmsx003_ns.enum("PMSX003Type") PMSX003_TYPES = { TYPE_PMSX003: PMSX003Type.PMSX003_TYPE_X003, TYPE_PMS5003T: PMSX003Type.PMSX003_TYPE_5003T, @@ -36,31 +50,52 @@ SENSORS_TO_TYPE = { def validate_pmsx003_sensors(value): for key, types in SENSORS_TO_TYPE.items(): if key in value and value[CONF_TYPE] not in types: - raise cv.Invalid("{} does not have {} sensor!".format(value[CONF_TYPE], key)) + raise cv.Invalid( + "{} does not have {} sensor!".format(value[CONF_TYPE], key) + ) return value -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(PMSX003Component), - cv.Required(CONF_TYPE): cv.enum(PMSX003_TYPES, upper=True), - - cv.Optional(CONF_PM_1_0): - sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_CHEMICAL_WEAPON, 0, - DEVICE_CLASS_EMPTY), - cv.Optional(CONF_PM_2_5): - sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_CHEMICAL_WEAPON, 0, - DEVICE_CLASS_EMPTY), - cv.Optional(CONF_PM_10_0): - sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_CHEMICAL_WEAPON, 0, - DEVICE_CLASS_EMPTY), - cv.Optional(CONF_TEMPERATURE): - sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE), - cv.Optional(CONF_HUMIDITY): - sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 1, DEVICE_CLASS_HUMIDITY), - cv.Optional(CONF_FORMALDEHYDE): - sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_CHEMICAL_WEAPON, 0, - DEVICE_CLASS_EMPTY), -}).extend(cv.COMPONENT_SCHEMA).extend(uart.UART_DEVICE_SCHEMA) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(PMSX003Component), + cv.Required(CONF_TYPE): cv.enum(PMSX003_TYPES, upper=True), + cv.Optional(CONF_PM_1_0): sensor.sensor_schema( + UNIT_MICROGRAMS_PER_CUBIC_METER, + ICON_CHEMICAL_WEAPON, + 0, + DEVICE_CLASS_EMPTY, + ), + cv.Optional(CONF_PM_2_5): sensor.sensor_schema( + UNIT_MICROGRAMS_PER_CUBIC_METER, + ICON_CHEMICAL_WEAPON, + 0, + DEVICE_CLASS_EMPTY, + ), + cv.Optional(CONF_PM_10_0): sensor.sensor_schema( + UNIT_MICROGRAMS_PER_CUBIC_METER, + ICON_CHEMICAL_WEAPON, + 0, + DEVICE_CLASS_EMPTY, + ), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( + UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE + ), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema( + UNIT_PERCENT, ICON_EMPTY, 1, DEVICE_CLASS_HUMIDITY + ), + cv.Optional(CONF_FORMALDEHYDE): sensor.sensor_schema( + UNIT_MICROGRAMS_PER_CUBIC_METER, + ICON_CHEMICAL_WEAPON, + 0, + DEVICE_CLASS_EMPTY, + ), + } + ) + .extend(cv.COMPONENT_SCHEMA) + .extend(uart.UART_DEVICE_SCHEMA) +) def to_code(config): diff --git a/esphome/components/pn532/__init__.py b/esphome/components/pn532/__init__.py index c96ebe2b4d..5403ebe5cd 100644 --- a/esphome/components/pn532/__init__.py +++ b/esphome/components/pn532/__init__.py @@ -5,38 +5,52 @@ from esphome.components import nfc from esphome.const import CONF_ID, CONF_ON_TAG, CONF_TRIGGER_ID from esphome.core import coroutine -CODEOWNERS = ['@OttoWinter', '@jesserockz'] -AUTO_LOAD = ['binary_sensor', 'nfc'] +CODEOWNERS = ["@OttoWinter", "@jesserockz"] +AUTO_LOAD = ["binary_sensor", "nfc"] MULTI_CONF = True -CONF_PN532_ID = 'pn532_id' -CONF_ON_FINISHED_WRITE = 'on_finished_write' +CONF_PN532_ID = "pn532_id" +CONF_ON_FINISHED_WRITE = "on_finished_write" -pn532_ns = cg.esphome_ns.namespace('pn532') -PN532 = pn532_ns.class_('PN532', cg.PollingComponent) +pn532_ns = cg.esphome_ns.namespace("pn532") +PN532 = pn532_ns.class_("PN532", cg.PollingComponent) -PN532OnTagTrigger = pn532_ns.class_('PN532OnTagTrigger', - automation.Trigger.template(cg.std_string, nfc.NfcTag)) -PN532OnFinishedWriteTrigger = pn532_ns.class_('PN532OnFinishedWriteTrigger', - automation.Trigger.template()) +PN532OnTagTrigger = pn532_ns.class_( + "PN532OnTagTrigger", automation.Trigger.template(cg.std_string, nfc.NfcTag) +) +PN532OnFinishedWriteTrigger = pn532_ns.class_( + "PN532OnFinishedWriteTrigger", automation.Trigger.template() +) -PN532IsWritingCondition = pn532_ns.class_('PN532IsWritingCondition', automation.Condition) +PN532IsWritingCondition = pn532_ns.class_( + "PN532IsWritingCondition", automation.Condition +) -PN532_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(PN532), - cv.Optional(CONF_ON_TAG): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PN532OnTagTrigger), - }), - cv.Optional(CONF_ON_FINISHED_WRITE): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PN532OnFinishedWriteTrigger), - }), -}).extend(cv.polling_component_schema('1s')) +PN532_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(PN532), + cv.Optional(CONF_ON_TAG): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PN532OnTagTrigger), + } + ), + cv.Optional(CONF_ON_FINISHED_WRITE): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( + PN532OnFinishedWriteTrigger + ), + } + ), + } +).extend(cv.polling_component_schema("1s")) def CONFIG_SCHEMA(conf): if conf: - raise cv.Invalid("This component has been moved in 1.16, please see the docs for updated " - "instructions. https://esphome.io/components/binary_sensor/pn532.html") + raise cv.Invalid( + "This component has been moved in 1.16, please see the docs for updated " + "instructions. https://esphome.io/components/binary_sensor/pn532.html" + ) @coroutine @@ -46,17 +60,24 @@ def setup_pn532(var, config): for conf in config.get(CONF_ON_TAG, []): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID]) cg.add(var.register_trigger(trigger)) - yield automation.build_automation(trigger, [(cg.std_string, 'x'), (nfc.NfcTag, 'tag')], - conf) + yield automation.build_automation( + trigger, [(cg.std_string, "x"), (nfc.NfcTag, "tag")], conf + ) for conf in config.get(CONF_ON_FINISHED_WRITE, []): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) yield automation.build_automation(trigger, [], conf) -@automation.register_condition('pn532.is_writing', PN532IsWritingCondition, cv.Schema({ - cv.GenerateID(): cv.use_id(PN532), -})) +@automation.register_condition( + "pn532.is_writing", + PN532IsWritingCondition, + cv.Schema( + { + cv.GenerateID(): cv.use_id(PN532), + } + ), +) def pn532_is_writing_to_code(config, condition_id, template_arg, args): var = cg.new_Pvariable(condition_id, template_arg) yield cg.register_parented(var, config[CONF_ID]) diff --git a/esphome/components/pn532/binary_sensor.py b/esphome/components/pn532/binary_sensor.py index 2404bc9e99..5e6bf6c2b9 100644 --- a/esphome/components/pn532/binary_sensor.py +++ b/esphome/components/pn532/binary_sensor.py @@ -5,31 +5,39 @@ from esphome.const import CONF_UID, CONF_ID from esphome.core import HexInt from . import pn532_ns, PN532, CONF_PN532_ID -DEPENDENCIES = ['pn532'] +DEPENDENCIES = ["pn532"] def validate_uid(value): value = cv.string_strict(value) - for x in value.split('-'): + for x in value.split("-"): if len(x) != 2: - raise cv.Invalid("Each part (separated by '-') of the UID must be two characters " - "long.") + raise cv.Invalid( + "Each part (separated by '-') of the UID must be two characters " + "long." + ) try: x = int(x, 16) except ValueError as err: - raise cv.Invalid("Valid characters for parts of a UID are 0123456789ABCDEF.") from err + raise cv.Invalid( + "Valid characters for parts of a UID are 0123456789ABCDEF." + ) from err if x < 0 or x > 255: - raise cv.Invalid("Valid values for UID parts (separated by '-') are 00 to FF") + raise cv.Invalid( + "Valid values for UID parts (separated by '-') are 00 to FF" + ) return value -PN532BinarySensor = pn532_ns.class_('PN532BinarySensor', binary_sensor.BinarySensor) +PN532BinarySensor = pn532_ns.class_("PN532BinarySensor", binary_sensor.BinarySensor) -CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(PN532BinarySensor), - cv.GenerateID(CONF_PN532_ID): cv.use_id(PN532), - cv.Required(CONF_UID): validate_uid, -}) +CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(PN532BinarySensor), + cv.GenerateID(CONF_PN532_ID): cv.use_id(PN532), + cv.Required(CONF_UID): validate_uid, + } +) def to_code(config): @@ -38,5 +46,5 @@ def to_code(config): hub = yield cg.get_variable(config[CONF_PN532_ID]) cg.add(hub.register_tag(var)) - addr = [HexInt(int(x, 16)) for x in config[CONF_UID].split('-')] + addr = [HexInt(int(x, 16)) for x in config[CONF_UID].split("-")] cg.add(var.set_uid(addr)) diff --git a/esphome/components/pn532_i2c/__init__.py b/esphome/components/pn532_i2c/__init__.py index f1c50adf45..80995bd018 100644 --- a/esphome/components/pn532_i2c/__init__.py +++ b/esphome/components/pn532_i2c/__init__.py @@ -3,16 +3,20 @@ import esphome.config_validation as cv from esphome.components import i2c, pn532 from esphome.const import CONF_ID -AUTO_LOAD = ['pn532'] -CODEOWNERS = ['@OttoWinter', '@jesserockz'] -DEPENDENCIES = ['i2c'] +AUTO_LOAD = ["pn532"] +CODEOWNERS = ["@OttoWinter", "@jesserockz"] +DEPENDENCIES = ["i2c"] -pn532_i2c_ns = cg.esphome_ns.namespace('pn532_i2c') -PN532I2C = pn532_i2c_ns.class_('PN532I2C', pn532.PN532, i2c.I2CDevice) +pn532_i2c_ns = cg.esphome_ns.namespace("pn532_i2c") +PN532I2C = pn532_i2c_ns.class_("PN532I2C", pn532.PN532, i2c.I2CDevice) -CONFIG_SCHEMA = cv.All(pn532.PN532_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(PN532I2C), -}).extend(i2c.i2c_device_schema(0x24))) +CONFIG_SCHEMA = cv.All( + pn532.PN532_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(PN532I2C), + } + ).extend(i2c.i2c_device_schema(0x24)) +) def to_code(config): diff --git a/esphome/components/pn532_spi/__init__.py b/esphome/components/pn532_spi/__init__.py index e378b96c2d..2abe436291 100644 --- a/esphome/components/pn532_spi/__init__.py +++ b/esphome/components/pn532_spi/__init__.py @@ -3,16 +3,20 @@ import esphome.config_validation as cv from esphome.components import spi, pn532 from esphome.const import CONF_ID -AUTO_LOAD = ['pn532'] -CODEOWNERS = ['@OttoWinter', '@jesserockz'] -DEPENDENCIES = ['spi'] +AUTO_LOAD = ["pn532"] +CODEOWNERS = ["@OttoWinter", "@jesserockz"] +DEPENDENCIES = ["spi"] -pn532_spi_ns = cg.esphome_ns.namespace('pn532_spi') -PN532Spi = pn532_spi_ns.class_('PN532Spi', pn532.PN532, spi.SPIDevice) +pn532_spi_ns = cg.esphome_ns.namespace("pn532_spi") +PN532Spi = pn532_spi_ns.class_("PN532Spi", pn532.PN532, spi.SPIDevice) -CONFIG_SCHEMA = cv.All(pn532.PN532_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(PN532Spi), -}).extend(spi.spi_device_schema(cs_pin_required=True))) +CONFIG_SCHEMA = cv.All( + pn532.PN532_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(PN532Spi), + } + ).extend(spi.spi_device_schema(cs_pin_required=True)) +) def to_code(config): diff --git a/esphome/components/power_supply/__init__.py b/esphome/components/power_supply/__init__.py index d502788637..efed5e0d81 100644 --- a/esphome/components/power_supply/__init__.py +++ b/esphome/components/power_supply/__init__.py @@ -3,17 +3,23 @@ import esphome.config_validation as cv from esphome import pins from esphome.const import CONF_ENABLE_TIME, CONF_ID, CONF_KEEP_ON_TIME, CONF_PIN -CODEOWNERS = ['@esphome/core'] -power_supply_ns = cg.esphome_ns.namespace('power_supply') -PowerSupply = power_supply_ns.class_('PowerSupply', cg.Component) +CODEOWNERS = ["@esphome/core"] +power_supply_ns = cg.esphome_ns.namespace("power_supply") +PowerSupply = power_supply_ns.class_("PowerSupply", cg.Component) MULTI_CONF = True -CONFIG_SCHEMA = cv.Schema({ - cv.Required(CONF_ID): cv.declare_id(PowerSupply), - cv.Required(CONF_PIN): pins.gpio_output_pin_schema, - cv.Optional(CONF_ENABLE_TIME, default='20ms'): cv.positive_time_period_milliseconds, - cv.Optional(CONF_KEEP_ON_TIME, default='10s'): cv.positive_time_period_milliseconds, -}).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = cv.Schema( + { + cv.Required(CONF_ID): cv.declare_id(PowerSupply), + cv.Required(CONF_PIN): pins.gpio_output_pin_schema, + cv.Optional( + CONF_ENABLE_TIME, default="20ms" + ): cv.positive_time_period_milliseconds, + cv.Optional( + CONF_KEEP_ON_TIME, default="10s" + ): cv.positive_time_period_milliseconds, + } +).extend(cv.COMPONENT_SCHEMA) def to_code(config): @@ -25,4 +31,4 @@ def to_code(config): cg.add(var.set_enable_time(config[CONF_ENABLE_TIME])) cg.add(var.set_keep_on_time(config[CONF_KEEP_ON_TIME])) - cg.add_define('USE_POWER_SUPPLY') + cg.add_define("USE_POWER_SUPPLY") diff --git a/esphome/components/prometheus/__init__.py b/esphome/components/prometheus/__init__.py index 9c3deef73d..a1b376c763 100644 --- a/esphome/components/prometheus/__init__.py +++ b/esphome/components/prometheus/__init__.py @@ -4,21 +4,25 @@ from esphome.const import CONF_ID from esphome.components.web_server_base import CONF_WEB_SERVER_BASE_ID from esphome.components import web_server_base -AUTO_LOAD = ['web_server_base'] +AUTO_LOAD = ["web_server_base"] -prometheus_ns = cg.esphome_ns.namespace('prometheus') -PrometheusHandler = prometheus_ns.class_('PrometheusHandler', cg.Component) +prometheus_ns = cg.esphome_ns.namespace("prometheus") +PrometheusHandler = prometheus_ns.class_("PrometheusHandler", cg.Component) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(PrometheusHandler), - cv.GenerateID(CONF_WEB_SERVER_BASE_ID): cv.use_id(web_server_base.WebServerBase), -}).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(PrometheusHandler), + cv.GenerateID(CONF_WEB_SERVER_BASE_ID): cv.use_id( + web_server_base.WebServerBase + ), + } +).extend(cv.COMPONENT_SCHEMA) def to_code(config): paren = yield cg.get_variable(config[CONF_WEB_SERVER_BASE_ID]) - cg.add_define('USE_PROMETHEUS') + cg.add_define("USE_PROMETHEUS") var = cg.new_Pvariable(config[CONF_ID], paren) yield cg.register_component(var, config) diff --git a/esphome/components/pulse_counter/sensor.py b/esphome/components/pulse_counter/sensor.py index a6f54872d4..0dff2959e8 100644 --- a/esphome/components/pulse_counter/sensor.py +++ b/esphome/components/pulse_counter/sensor.py @@ -2,23 +2,35 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins from esphome.components import sensor -from esphome.const import CONF_COUNT_MODE, CONF_FALLING_EDGE, CONF_ID, CONF_INTERNAL_FILTER, \ - CONF_PIN, CONF_RISING_EDGE, CONF_NUMBER, CONF_TOTAL, DEVICE_CLASS_EMPTY, \ - ICON_PULSE, UNIT_PULSES_PER_MINUTE, UNIT_PULSES +from esphome.const import ( + CONF_COUNT_MODE, + CONF_FALLING_EDGE, + CONF_ID, + CONF_INTERNAL_FILTER, + CONF_PIN, + CONF_RISING_EDGE, + CONF_NUMBER, + CONF_TOTAL, + DEVICE_CLASS_EMPTY, + ICON_PULSE, + UNIT_PULSES_PER_MINUTE, + UNIT_PULSES, +) from esphome.core import CORE -pulse_counter_ns = cg.esphome_ns.namespace('pulse_counter') -PulseCounterCountMode = pulse_counter_ns.enum('PulseCounterCountMode') +pulse_counter_ns = cg.esphome_ns.namespace("pulse_counter") +PulseCounterCountMode = pulse_counter_ns.enum("PulseCounterCountMode") COUNT_MODES = { - 'DISABLE': PulseCounterCountMode.PULSE_COUNTER_DISABLE, - 'INCREMENT': PulseCounterCountMode.PULSE_COUNTER_INCREMENT, - 'DECREMENT': PulseCounterCountMode.PULSE_COUNTER_DECREMENT, + "DISABLE": PulseCounterCountMode.PULSE_COUNTER_DISABLE, + "INCREMENT": PulseCounterCountMode.PULSE_COUNTER_INCREMENT, + "DECREMENT": PulseCounterCountMode.PULSE_COUNTER_DECREMENT, } COUNT_MODE_SCHEMA = cv.enum(COUNT_MODES, upper=True) -PulseCounterSensor = pulse_counter_ns.class_('PulseCounterSensor', - sensor.Sensor, cg.PollingComponent) +PulseCounterSensor = pulse_counter_ns.class_( + "PulseCounterSensor", sensor.Sensor, cg.PollingComponent +) def validate_internal_filter(value): @@ -34,35 +46,52 @@ def validate_internal_filter(value): def validate_pulse_counter_pin(value): value = pins.internal_gpio_input_pin_schema(value) if CORE.is_esp8266 and value[CONF_NUMBER] >= 16: - raise cv.Invalid("Pins GPIO16 and GPIO17 cannot be used as pulse counters on ESP8266.") + raise cv.Invalid( + "Pins GPIO16 and GPIO17 cannot be used as pulse counters on ESP8266." + ) return value def validate_count_mode(value): rising_edge = value[CONF_RISING_EDGE] falling_edge = value[CONF_FALLING_EDGE] - if rising_edge == 'DISABLE' and falling_edge == 'DISABLE': - raise cv.Invalid("Can't set both count modes to DISABLE! This means no counting occurs at " - "all!") + if rising_edge == "DISABLE" and falling_edge == "DISABLE": + raise cv.Invalid( + "Can't set both count modes to DISABLE! This means no counting occurs at " + "all!" + ) return value -CONFIG_SCHEMA = sensor.sensor_schema( - UNIT_PULSES_PER_MINUTE, ICON_PULSE, 2, DEVICE_CLASS_EMPTY -).extend({ - cv.GenerateID(): cv.declare_id(PulseCounterSensor), - cv.Required(CONF_PIN): validate_pulse_counter_pin, - cv.Optional(CONF_COUNT_MODE, default={ - CONF_RISING_EDGE: 'INCREMENT', - CONF_FALLING_EDGE: 'DISABLE', - }): cv.All(cv.Schema({ - cv.Required(CONF_RISING_EDGE): COUNT_MODE_SCHEMA, - cv.Required(CONF_FALLING_EDGE): COUNT_MODE_SCHEMA, - }), validate_count_mode), - cv.Optional(CONF_INTERNAL_FILTER, default='13us'): validate_internal_filter, - cv.Optional(CONF_TOTAL): sensor.sensor_schema(UNIT_PULSES, ICON_PULSE, 0, DEVICE_CLASS_EMPTY), - -}).extend(cv.polling_component_schema('60s')) +CONFIG_SCHEMA = ( + sensor.sensor_schema(UNIT_PULSES_PER_MINUTE, ICON_PULSE, 2, DEVICE_CLASS_EMPTY) + .extend( + { + cv.GenerateID(): cv.declare_id(PulseCounterSensor), + cv.Required(CONF_PIN): validate_pulse_counter_pin, + cv.Optional( + CONF_COUNT_MODE, + default={ + CONF_RISING_EDGE: "INCREMENT", + CONF_FALLING_EDGE: "DISABLE", + }, + ): cv.All( + cv.Schema( + { + cv.Required(CONF_RISING_EDGE): COUNT_MODE_SCHEMA, + cv.Required(CONF_FALLING_EDGE): COUNT_MODE_SCHEMA, + } + ), + validate_count_mode, + ), + cv.Optional(CONF_INTERNAL_FILTER, default="13us"): validate_internal_filter, + cv.Optional(CONF_TOTAL): sensor.sensor_schema( + UNIT_PULSES, ICON_PULSE, 0, DEVICE_CLASS_EMPTY + ), + } + ) + .extend(cv.polling_component_schema("60s")) +) def to_code(config): diff --git a/esphome/components/pulse_width/sensor.py b/esphome/components/pulse_width/sensor.py index 01a1c05247..228c5f8dfe 100644 --- a/esphome/components/pulse_width/sensor.py +++ b/esphome/components/pulse_width/sensor.py @@ -4,15 +4,24 @@ from esphome import pins from esphome.components import sensor from esphome.const import CONF_ID, CONF_PIN, DEVICE_CLASS_EMPTY, UNIT_SECOND, ICON_TIMER -pulse_width_ns = cg.esphome_ns.namespace('pulse_width') +pulse_width_ns = cg.esphome_ns.namespace("pulse_width") -PulseWidthSensor = pulse_width_ns.class_('PulseWidthSensor', sensor.Sensor, cg.PollingComponent) +PulseWidthSensor = pulse_width_ns.class_( + "PulseWidthSensor", sensor.Sensor, cg.PollingComponent +) -CONFIG_SCHEMA = sensor.sensor_schema(UNIT_SECOND, ICON_TIMER, 3, DEVICE_CLASS_EMPTY).extend({ - cv.GenerateID(): cv.declare_id(PulseWidthSensor), - cv.Required(CONF_PIN): cv.All(pins.internal_gpio_input_pin_schema, - pins.validate_has_interrupt), -}).extend(cv.polling_component_schema('60s')) +CONFIG_SCHEMA = ( + sensor.sensor_schema(UNIT_SECOND, ICON_TIMER, 3, DEVICE_CLASS_EMPTY) + .extend( + { + cv.GenerateID(): cv.declare_id(PulseWidthSensor), + cv.Required(CONF_PIN): cv.All( + pins.internal_gpio_input_pin_schema, pins.validate_has_interrupt + ), + } + ) + .extend(cv.polling_component_schema("60s")) +) def to_code(config): diff --git a/esphome/components/pzem004t/sensor.py b/esphome/components/pzem004t/sensor.py index ed7158c057..1228fc4ab6 100644 --- a/esphome/components/pzem004t/sensor.py +++ b/esphome/components/pzem004t/sensor.py @@ -1,25 +1,49 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, uart -from esphome.const import CONF_CURRENT, CONF_ID, CONF_POWER, CONF_VOLTAGE, CONF_ENERGY, \ - DEVICE_CLASS_CURRENT, DEVICE_CLASS_ENERGY, DEVICE_CLASS_POWER, DEVICE_CLASS_VOLTAGE, \ - ICON_EMPTY, UNIT_VOLT, UNIT_AMPERE, UNIT_WATT, UNIT_WATT_HOURS +from esphome.const import ( + CONF_CURRENT, + CONF_ID, + CONF_POWER, + CONF_VOLTAGE, + CONF_ENERGY, + DEVICE_CLASS_CURRENT, + DEVICE_CLASS_ENERGY, + DEVICE_CLASS_POWER, + DEVICE_CLASS_VOLTAGE, + ICON_EMPTY, + UNIT_VOLT, + UNIT_AMPERE, + UNIT_WATT, + UNIT_WATT_HOURS, +) -DEPENDENCIES = ['uart'] +DEPENDENCIES = ["uart"] -pzem004t_ns = cg.esphome_ns.namespace('pzem004t') -PZEM004T = pzem004t_ns.class_('PZEM004T', cg.PollingComponent, uart.UARTDevice) +pzem004t_ns = cg.esphome_ns.namespace("pzem004t") +PZEM004T = pzem004t_ns.class_("PZEM004T", cg.PollingComponent, uart.UARTDevice) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(PZEM004T), - - cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE), - cv.Optional(CONF_CURRENT): sensor.sensor_schema(UNIT_AMPERE, ICON_EMPTY, 2, - DEVICE_CLASS_CURRENT), - cv.Optional(CONF_POWER): sensor.sensor_schema(UNIT_WATT, ICON_EMPTY, 0, DEVICE_CLASS_POWER), - cv.Optional(CONF_ENERGY): sensor.sensor_schema(UNIT_WATT_HOURS, ICON_EMPTY, 0, - DEVICE_CLASS_ENERGY) -}).extend(cv.polling_component_schema('60s')).extend(uart.UART_DEVICE_SCHEMA) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(PZEM004T), + cv.Optional(CONF_VOLTAGE): sensor.sensor_schema( + UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE + ), + cv.Optional(CONF_CURRENT): sensor.sensor_schema( + UNIT_AMPERE, ICON_EMPTY, 2, DEVICE_CLASS_CURRENT + ), + cv.Optional(CONF_POWER): sensor.sensor_schema( + UNIT_WATT, ICON_EMPTY, 0, DEVICE_CLASS_POWER + ), + cv.Optional(CONF_ENERGY): sensor.sensor_schema( + UNIT_WATT_HOURS, ICON_EMPTY, 0, DEVICE_CLASS_ENERGY + ), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(uart.UART_DEVICE_SCHEMA) +) def to_code(config): diff --git a/esphome/components/pzemac/sensor.py b/esphome/components/pzemac/sensor.py index 1004d49172..aa30549c25 100644 --- a/esphome/components/pzemac/sensor.py +++ b/esphome/components/pzemac/sensor.py @@ -1,30 +1,62 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, modbus -from esphome.const import CONF_CURRENT, CONF_ENERGY, CONF_ID, CONF_POWER, CONF_VOLTAGE, \ - CONF_FREQUENCY, CONF_POWER_FACTOR, DEVICE_CLASS_EMPTY, DEVICE_CLASS_POWER_FACTOR, \ - DEVICE_CLASS_VOLTAGE, DEVICE_CLASS_CURRENT, DEVICE_CLASS_POWER, DEVICE_CLASS_ENERGY, \ - ICON_EMPTY, ICON_CURRENT_AC, UNIT_HERTZ, UNIT_VOLT, UNIT_AMPERE, UNIT_WATT, UNIT_EMPTY, \ - UNIT_WATT_HOURS +from esphome.const import ( + CONF_CURRENT, + CONF_ENERGY, + CONF_ID, + CONF_POWER, + CONF_VOLTAGE, + CONF_FREQUENCY, + CONF_POWER_FACTOR, + DEVICE_CLASS_EMPTY, + DEVICE_CLASS_POWER_FACTOR, + DEVICE_CLASS_VOLTAGE, + DEVICE_CLASS_CURRENT, + DEVICE_CLASS_POWER, + DEVICE_CLASS_ENERGY, + ICON_EMPTY, + ICON_CURRENT_AC, + UNIT_HERTZ, + UNIT_VOLT, + UNIT_AMPERE, + UNIT_WATT, + UNIT_EMPTY, + UNIT_WATT_HOURS, +) -AUTO_LOAD = ['modbus'] +AUTO_LOAD = ["modbus"] -pzemac_ns = cg.esphome_ns.namespace('pzemac') -PZEMAC = pzemac_ns.class_('PZEMAC', cg.PollingComponent, modbus.ModbusDevice) +pzemac_ns = cg.esphome_ns.namespace("pzemac") +PZEMAC = pzemac_ns.class_("PZEMAC", cg.PollingComponent, modbus.ModbusDevice) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(PZEMAC), - cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE), - cv.Optional(CONF_CURRENT): sensor.sensor_schema(UNIT_AMPERE, ICON_EMPTY, 3, - DEVICE_CLASS_CURRENT), - cv.Optional(CONF_POWER): sensor.sensor_schema(UNIT_WATT, ICON_EMPTY, 2, DEVICE_CLASS_POWER), - cv.Optional(CONF_ENERGY): sensor.sensor_schema(UNIT_WATT_HOURS, ICON_EMPTY, 0, - DEVICE_CLASS_ENERGY), - cv.Optional(CONF_FREQUENCY): sensor.sensor_schema(UNIT_HERTZ, ICON_CURRENT_AC, 1, - DEVICE_CLASS_EMPTY), - cv.Optional(CONF_POWER_FACTOR): sensor.sensor_schema(UNIT_EMPTY, ICON_EMPTY, 2, - DEVICE_CLASS_POWER_FACTOR), -}).extend(cv.polling_component_schema('60s')).extend(modbus.modbus_device_schema(0x01)) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(PZEMAC), + cv.Optional(CONF_VOLTAGE): sensor.sensor_schema( + UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE + ), + cv.Optional(CONF_CURRENT): sensor.sensor_schema( + UNIT_AMPERE, ICON_EMPTY, 3, DEVICE_CLASS_CURRENT + ), + cv.Optional(CONF_POWER): sensor.sensor_schema( + UNIT_WATT, ICON_EMPTY, 2, DEVICE_CLASS_POWER + ), + cv.Optional(CONF_ENERGY): sensor.sensor_schema( + UNIT_WATT_HOURS, ICON_EMPTY, 0, DEVICE_CLASS_ENERGY + ), + cv.Optional(CONF_FREQUENCY): sensor.sensor_schema( + UNIT_HERTZ, ICON_CURRENT_AC, 1, DEVICE_CLASS_EMPTY + ), + cv.Optional(CONF_POWER_FACTOR): sensor.sensor_schema( + UNIT_EMPTY, ICON_EMPTY, 2, DEVICE_CLASS_POWER_FACTOR + ), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(modbus.modbus_device_schema(0x01)) +) def to_code(config): diff --git a/esphome/components/pzemdc/sensor.py b/esphome/components/pzemdc/sensor.py index a4fc9ab26f..962c970359 100644 --- a/esphome/components/pzemdc/sensor.py +++ b/esphome/components/pzemdc/sensor.py @@ -1,22 +1,43 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, modbus -from esphome.const import CONF_CURRENT, CONF_ID, CONF_POWER, CONF_VOLTAGE, DEVICE_CLASS_CURRENT, \ - DEVICE_CLASS_POWER, DEVICE_CLASS_VOLTAGE, ICON_EMPTY, UNIT_VOLT, UNIT_AMPERE, UNIT_WATT +from esphome.const import ( + CONF_CURRENT, + CONF_ID, + CONF_POWER, + CONF_VOLTAGE, + DEVICE_CLASS_CURRENT, + DEVICE_CLASS_POWER, + DEVICE_CLASS_VOLTAGE, + ICON_EMPTY, + UNIT_VOLT, + UNIT_AMPERE, + UNIT_WATT, +) -AUTO_LOAD = ['modbus'] +AUTO_LOAD = ["modbus"] -pzemdc_ns = cg.esphome_ns.namespace('pzemdc') -PZEMDC = pzemdc_ns.class_('PZEMDC', cg.PollingComponent, modbus.ModbusDevice) +pzemdc_ns = cg.esphome_ns.namespace("pzemdc") +PZEMDC = pzemdc_ns.class_("PZEMDC", cg.PollingComponent, modbus.ModbusDevice) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(PZEMDC), - cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE), - cv.Optional(CONF_CURRENT): sensor.sensor_schema(UNIT_AMPERE, ICON_EMPTY, 3, - DEVICE_CLASS_CURRENT), - cv.Optional(CONF_POWER): sensor.sensor_schema(UNIT_WATT, ICON_EMPTY, 1, - DEVICE_CLASS_POWER), -}).extend(cv.polling_component_schema('60s')).extend(modbus.modbus_device_schema(0x01)) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(PZEMDC), + cv.Optional(CONF_VOLTAGE): sensor.sensor_schema( + UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE + ), + cv.Optional(CONF_CURRENT): sensor.sensor_schema( + UNIT_AMPERE, ICON_EMPTY, 3, DEVICE_CLASS_CURRENT + ), + cv.Optional(CONF_POWER): sensor.sensor_schema( + UNIT_WATT, ICON_EMPTY, 1, DEVICE_CLASS_POWER + ), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(modbus.modbus_device_schema(0x01)) +) def to_code(config): diff --git a/esphome/components/qmc5883l/sensor.py b/esphome/components/qmc5883l/sensor.py index 3beaa8bd7b..4359427628 100644 --- a/esphome/components/qmc5883l/sensor.py +++ b/esphome/components/qmc5883l/sensor.py @@ -1,23 +1,33 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import CONF_ADDRESS, CONF_ID, CONF_OVERSAMPLING, CONF_RANGE, \ - DEVICE_CLASS_EMPTY, ICON_MAGNET, UNIT_MICROTESLA, UNIT_DEGREES, ICON_SCREEN_ROTATION, \ - CONF_UPDATE_INTERVAL +from esphome.const import ( + CONF_ADDRESS, + CONF_ID, + CONF_OVERSAMPLING, + CONF_RANGE, + DEVICE_CLASS_EMPTY, + ICON_MAGNET, + UNIT_MICROTESLA, + UNIT_DEGREES, + ICON_SCREEN_ROTATION, + CONF_UPDATE_INTERVAL, +) -DEPENDENCIES = ['i2c'] +DEPENDENCIES = ["i2c"] -qmc5883l_ns = cg.esphome_ns.namespace('qmc5883l') +qmc5883l_ns = cg.esphome_ns.namespace("qmc5883l") -CONF_FIELD_STRENGTH_X = 'field_strength_x' -CONF_FIELD_STRENGTH_Y = 'field_strength_y' -CONF_FIELD_STRENGTH_Z = 'field_strength_z' -CONF_HEADING = 'heading' +CONF_FIELD_STRENGTH_X = "field_strength_x" +CONF_FIELD_STRENGTH_Y = "field_strength_y" +CONF_FIELD_STRENGTH_Z = "field_strength_z" +CONF_HEADING = "heading" QMC5883LComponent = qmc5883l_ns.class_( - 'QMC5883LComponent', cg.PollingComponent, i2c.I2CDevice) + "QMC5883LComponent", cg.PollingComponent, i2c.I2CDevice +) -QMC5883LDatarate = qmc5883l_ns.enum('QMC5883LDatarate') +QMC5883LDatarate = qmc5883l_ns.enum("QMC5883LDatarate") QMC5883LDatarates = { 10: QMC5883LDatarate.QMC5883L_DATARATE_10_HZ, 50: QMC5883LDatarate.QMC5883L_DATARATE_50_HZ, @@ -25,13 +35,13 @@ QMC5883LDatarates = { 200: QMC5883LDatarate.QMC5883L_DATARATE_200_HZ, } -QMC5883LRange = qmc5883l_ns.enum('QMC5883LRange') +QMC5883LRange = qmc5883l_ns.enum("QMC5883LRange") QMC5883L_RANGES = { 200: QMC5883LRange.QMC5883L_RANGE_200_UT, 800: QMC5883LRange.QMC5883L_RANGE_800_UT, } -QMC5883LOversampling = qmc5883l_ns.enum('QMC5883LOversampling') +QMC5883LOversampling = qmc5883l_ns.enum("QMC5883LOversampling") QMC5883LOversamplings = { 512: QMC5883LOversampling.QMC5883L_SAMPLING_512, 256: QMC5883LOversampling.QMC5883L_SAMPLING_256, @@ -51,30 +61,45 @@ def validate_enum(enum_values, units=None, int=True): value = cv.string(value) for unit in _units: if value.endswith(unit): - value = value[:-len(unit)] + value = value[: -len(unit)] break return enum_bound(value) + return validate_enum_bound -field_strength_schema = sensor.sensor_schema(UNIT_MICROTESLA, ICON_MAGNET, 1, DEVICE_CLASS_EMPTY) -heading_schema = sensor.sensor_schema(UNIT_DEGREES, ICON_SCREEN_ROTATION, 1, DEVICE_CLASS_EMPTY) +field_strength_schema = sensor.sensor_schema( + UNIT_MICROTESLA, ICON_MAGNET, 1, DEVICE_CLASS_EMPTY +) +heading_schema = sensor.sensor_schema( + UNIT_DEGREES, ICON_SCREEN_ROTATION, 1, DEVICE_CLASS_EMPTY +) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(QMC5883LComponent), - cv.Optional(CONF_ADDRESS): cv.i2c_address, - cv.Optional(CONF_RANGE, default='200µT'): validate_enum(QMC5883L_RANGES, units=["uT", "µT"]), - cv.Optional(CONF_OVERSAMPLING, default="512x"): validate_enum(QMC5883LOversamplings, units="x"), - cv.Optional(CONF_FIELD_STRENGTH_X): field_strength_schema, - cv.Optional(CONF_FIELD_STRENGTH_Y): field_strength_schema, - cv.Optional(CONF_FIELD_STRENGTH_Z): field_strength_schema, - cv.Optional(CONF_HEADING): heading_schema, -}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x0D)) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(QMC5883LComponent), + cv.Optional(CONF_ADDRESS): cv.i2c_address, + cv.Optional(CONF_RANGE, default="200µT"): validate_enum( + QMC5883L_RANGES, units=["uT", "µT"] + ), + cv.Optional(CONF_OVERSAMPLING, default="512x"): validate_enum( + QMC5883LOversamplings, units="x" + ), + cv.Optional(CONF_FIELD_STRENGTH_X): field_strength_schema, + cv.Optional(CONF_FIELD_STRENGTH_Y): field_strength_schema, + cv.Optional(CONF_FIELD_STRENGTH_Z): field_strength_schema, + cv.Optional(CONF_HEADING): heading_schema, + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x0D)) +) def auto_data_rate(config): interval_sec = config[CONF_UPDATE_INTERVAL].seconds - interval_hz = 1.0/interval_sec + interval_hz = 1.0 / interval_sec for datarate in sorted(QMC5883LDatarates.keys()): if float(datarate) >= interval_hz: return QMC5883LDatarates[datarate] diff --git a/esphome/components/rc522/__init__.py b/esphome/components/rc522/__init__.py index 7b4df37ce2..970b867e79 100644 --- a/esphome/components/rc522/__init__.py +++ b/esphome/components/rc522/__init__.py @@ -5,23 +5,29 @@ from esphome.components import i2c from esphome.const import CONF_ON_TAG, CONF_TRIGGER_ID, CONF_RESET_PIN from esphome.core import coroutine -CODEOWNERS = ['@glmnet'] -AUTO_LOAD = ['binary_sensor'] +CODEOWNERS = ["@glmnet"] +AUTO_LOAD = ["binary_sensor"] MULTI_CONF = True -CONF_RC522_ID = 'rc522_id' +CONF_RC522_ID = "rc522_id" -rc522_ns = cg.esphome_ns.namespace('rc522') -RC522 = rc522_ns.class_('RC522', cg.PollingComponent, i2c.I2CDevice) -RC522Trigger = rc522_ns.class_('RC522Trigger', automation.Trigger.template(cg.std_string)) +rc522_ns = cg.esphome_ns.namespace("rc522") +RC522 = rc522_ns.class_("RC522", cg.PollingComponent, i2c.I2CDevice) +RC522Trigger = rc522_ns.class_( + "RC522Trigger", automation.Trigger.template(cg.std_string) +) -RC522_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(RC522), - cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, - cv.Optional(CONF_ON_TAG): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(RC522Trigger), - }), -}).extend(cv.polling_component_schema('1s')) +RC522_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(RC522), + cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, + cv.Optional(CONF_ON_TAG): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(RC522Trigger), + } + ), + } +).extend(cv.polling_component_schema("1s")) @coroutine @@ -35,4 +41,4 @@ def setup_rc522(var, config): for conf in config.get(CONF_ON_TAG, []): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID]) cg.add(var.register_trigger(trigger)) - yield automation.build_automation(trigger, [(cg.std_string, 'x')], conf) + yield automation.build_automation(trigger, [(cg.std_string, "x")], conf) diff --git a/esphome/components/rc522/binary_sensor.py b/esphome/components/rc522/binary_sensor.py index 675db2f130..89eef2f976 100644 --- a/esphome/components/rc522/binary_sensor.py +++ b/esphome/components/rc522/binary_sensor.py @@ -5,31 +5,39 @@ from esphome.const import CONF_UID, CONF_ID from esphome.core import HexInt, coroutine from . import rc522_ns, RC522, CONF_RC522_ID -DEPENDENCIES = ['rc522'] +DEPENDENCIES = ["rc522"] def validate_uid(value): value = cv.string_strict(value) - for x in value.split('-'): + for x in value.split("-"): if len(x) != 2: - raise cv.Invalid("Each part (separated by '-') of the UID must be two characters " - "long.") + raise cv.Invalid( + "Each part (separated by '-') of the UID must be two characters " + "long." + ) try: x = int(x, 16) except ValueError as err: - raise cv.Invalid("Valid characters for parts of a UID are 0123456789ABCDEF.") from err + raise cv.Invalid( + "Valid characters for parts of a UID are 0123456789ABCDEF." + ) from err if x < 0 or x > 255: - raise cv.Invalid("Valid values for UID parts (separated by '-') are 00 to FF") + raise cv.Invalid( + "Valid values for UID parts (separated by '-') are 00 to FF" + ) return value -RC522BinarySensor = rc522_ns.class_('RC522BinarySensor', binary_sensor.BinarySensor) +RC522BinarySensor = rc522_ns.class_("RC522BinarySensor", binary_sensor.BinarySensor) -CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(RC522BinarySensor), - cv.GenerateID(CONF_RC522_ID): cv.use_id(RC522), - cv.Required(CONF_UID): validate_uid, -}) +CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(RC522BinarySensor), + cv.GenerateID(CONF_RC522_ID): cv.use_id(RC522), + cv.Required(CONF_UID): validate_uid, + } +) @coroutine @@ -39,5 +47,5 @@ def to_code(config): hub = yield cg.get_variable(config[CONF_RC522_ID]) cg.add(hub.register_tag(var)) - addr = [HexInt(int(x, 16)) for x in config[CONF_UID].split('-')] + addr = [HexInt(int(x, 16)) for x in config[CONF_UID].split("-")] cg.add(var.set_uid(addr)) diff --git a/esphome/components/rc522_i2c/__init__.py b/esphome/components/rc522_i2c/__init__.py index c5bb72ee53..532adfce79 100644 --- a/esphome/components/rc522_i2c/__init__.py +++ b/esphome/components/rc522_i2c/__init__.py @@ -3,17 +3,21 @@ import esphome.config_validation as cv from esphome.components import i2c, rc522 from esphome.const import CONF_ID -CODEOWNERS = ['@glmnet'] -DEPENDENCIES = ['i2c'] -AUTO_LOAD = ['rc522'] +CODEOWNERS = ["@glmnet"] +DEPENDENCIES = ["i2c"] +AUTO_LOAD = ["rc522"] -rc522_i2c_ns = cg.esphome_ns.namespace('rc522_i2c') -RC522I2C = rc522_i2c_ns.class_('RC522I2C', rc522.RC522, i2c.I2CDevice) +rc522_i2c_ns = cg.esphome_ns.namespace("rc522_i2c") +RC522I2C = rc522_i2c_ns.class_("RC522I2C", rc522.RC522, i2c.I2CDevice) -CONFIG_SCHEMA = cv.All(rc522.RC522_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(RC522I2C), -}).extend(i2c.i2c_device_schema(0x2c))) +CONFIG_SCHEMA = cv.All( + rc522.RC522_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(RC522I2C), + } + ).extend(i2c.i2c_device_schema(0x2C)) +) def to_code(config): diff --git a/esphome/components/rc522_spi/__init__.py b/esphome/components/rc522_spi/__init__.py index 37f78b66d0..6ae163bcb4 100644 --- a/esphome/components/rc522_spi/__init__.py +++ b/esphome/components/rc522_spi/__init__.py @@ -3,16 +3,20 @@ import esphome.config_validation as cv from esphome.components import spi, rc522 from esphome.const import CONF_ID -CODEOWNERS = ['@glmnet'] -DEPENDENCIES = ['spi'] -AUTO_LOAD = ['rc522'] +CODEOWNERS = ["@glmnet"] +DEPENDENCIES = ["spi"] +AUTO_LOAD = ["rc522"] -rc522_spi_ns = cg.esphome_ns.namespace('rc522_spi') -RC522Spi = rc522_spi_ns.class_('RC522Spi', rc522.RC522, spi.SPIDevice) +rc522_spi_ns = cg.esphome_ns.namespace("rc522_spi") +RC522Spi = rc522_spi_ns.class_("RC522Spi", rc522.RC522, spi.SPIDevice) -CONFIG_SCHEMA = cv.All(rc522.RC522_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(RC522Spi), -}).extend(spi.spi_device_schema(cs_pin_required=True))) +CONFIG_SCHEMA = cv.All( + rc522.RC522_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(RC522Spi), + } + ).extend(spi.spi_device_schema(cs_pin_required=True)) +) def to_code(config): diff --git a/esphome/components/rc522_spi/binary_sensor.py b/esphome/components/rc522_spi/binary_sensor.py index 1f0eafe6af..f5400a2331 100644 --- a/esphome/components/rc522_spi/binary_sensor.py +++ b/esphome/components/rc522_spi/binary_sensor.py @@ -1,6 +1,6 @@ import esphome.components.rc522.binary_sensor as rc522_binary_sensor -DEPENDENCIES = ['rc522'] +DEPENDENCIES = ["rc522"] CONFIG_SCHEMA = rc522_binary_sensor.CONFIG_SCHEMA diff --git a/esphome/components/rdm6300/__init__.py b/esphome/components/rdm6300/__init__.py index ee5077c315..a416d95a12 100644 --- a/esphome/components/rdm6300/__init__.py +++ b/esphome/components/rdm6300/__init__.py @@ -4,19 +4,29 @@ from esphome import automation from esphome.components import uart from esphome.const import CONF_ID, CONF_ON_TAG, CONF_TRIGGER_ID -DEPENDENCIES = ['uart'] -AUTO_LOAD = ['binary_sensor'] +DEPENDENCIES = ["uart"] +AUTO_LOAD = ["binary_sensor"] -rdm6300_ns = cg.esphome_ns.namespace('rdm6300') -RDM6300Component = rdm6300_ns.class_('RDM6300Component', cg.Component, uart.UARTDevice) -RDM6300Trigger = rdm6300_ns.class_('RDM6300Trigger', automation.Trigger.template(cg.uint32)) +rdm6300_ns = cg.esphome_ns.namespace("rdm6300") +RDM6300Component = rdm6300_ns.class_("RDM6300Component", cg.Component, uart.UARTDevice) +RDM6300Trigger = rdm6300_ns.class_( + "RDM6300Trigger", automation.Trigger.template(cg.uint32) +) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(RDM6300Component), - cv.Optional(CONF_ON_TAG): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(RDM6300Trigger), - }), -}).extend(cv.COMPONENT_SCHEMA).extend(uart.UART_DEVICE_SCHEMA) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(RDM6300Component), + cv.Optional(CONF_ON_TAG): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(RDM6300Trigger), + } + ), + } + ) + .extend(cv.COMPONENT_SCHEMA) + .extend(uart.UART_DEVICE_SCHEMA) +) def to_code(config): @@ -27,4 +37,4 @@ def to_code(config): for conf in config.get(CONF_ON_TAG, []): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID]) cg.add(var.register_trigger(trigger)) - yield automation.build_automation(trigger, [(cg.uint32, 'x')], conf) + yield automation.build_automation(trigger, [(cg.uint32, "x")], conf) diff --git a/esphome/components/rdm6300/binary_sensor.py b/esphome/components/rdm6300/binary_sensor.py index 81b24bed0e..02e0b6ceb6 100644 --- a/esphome/components/rdm6300/binary_sensor.py +++ b/esphome/components/rdm6300/binary_sensor.py @@ -4,16 +4,20 @@ from esphome.components import binary_sensor, rdm6300 from esphome.const import CONF_UID, CONF_ID from . import rdm6300_ns -DEPENDENCIES = ['rdm6300'] +DEPENDENCIES = ["rdm6300"] -CONF_RDM6300_ID = 'rdm6300_id' -RDM6300BinarySensor = rdm6300_ns.class_('RDM6300BinarySensor', binary_sensor.BinarySensor) +CONF_RDM6300_ID = "rdm6300_id" +RDM6300BinarySensor = rdm6300_ns.class_( + "RDM6300BinarySensor", binary_sensor.BinarySensor +) -CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(RDM6300BinarySensor), - cv.GenerateID(CONF_RDM6300_ID): cv.use_id(rdm6300.RDM6300Component), - cv.Required(CONF_UID): cv.uint32_t, -}) +CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(RDM6300BinarySensor), + cv.GenerateID(CONF_RDM6300_ID): cv.use_id(rdm6300.RDM6300Component), + cv.Required(CONF_UID): cv.uint32_t, + } +) def to_code(config): diff --git a/esphome/components/remote_base/__init__.py b/esphome/components/remote_base/__init__.py index cc48ffc5b2..7e81daa1e1 100644 --- a/esphome/components/remote_base/__init__.py +++ b/esphome/components/remote_base/__init__.py @@ -2,29 +2,55 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import automation from esphome.components import binary_sensor -from esphome.const import CONF_DATA, CONF_TRIGGER_ID, CONF_NBITS, CONF_ADDRESS, \ - CONF_COMMAND, CONF_CODE, CONF_PULSE_LENGTH, CONF_SYNC, CONF_ZERO, CONF_ONE, CONF_INVERTED, \ - CONF_PROTOCOL, CONF_GROUP, CONF_DEVICE, CONF_STATE, CONF_CHANNEL, CONF_FAMILY, CONF_REPEAT, \ - CONF_WAIT_TIME, CONF_TIMES, CONF_TYPE_ID, CONF_CARRIER_FREQUENCY, CONF_RC_CODE_1, CONF_RC_CODE_2 +from esphome.const import ( + CONF_DATA, + CONF_TRIGGER_ID, + CONF_NBITS, + CONF_ADDRESS, + CONF_COMMAND, + CONF_CODE, + CONF_PULSE_LENGTH, + CONF_SYNC, + CONF_ZERO, + CONF_ONE, + CONF_INVERTED, + CONF_PROTOCOL, + CONF_GROUP, + CONF_DEVICE, + CONF_STATE, + CONF_CHANNEL, + CONF_FAMILY, + CONF_REPEAT, + CONF_WAIT_TIME, + CONF_TIMES, + CONF_TYPE_ID, + CONF_CARRIER_FREQUENCY, + CONF_RC_CODE_1, + CONF_RC_CODE_2, +) from esphome.core import coroutine from esphome.util import Registry, SimpleRegistry -AUTO_LOAD = ['binary_sensor'] +AUTO_LOAD = ["binary_sensor"] -CONF_RECEIVER_ID = 'receiver_id' -CONF_TRANSMITTER_ID = 'transmitter_id' +CONF_RECEIVER_ID = "receiver_id" +CONF_TRANSMITTER_ID = "transmitter_id" -ns = remote_base_ns = cg.esphome_ns.namespace('remote_base') -RemoteProtocol = ns.class_('RemoteProtocol') -RemoteReceiverListener = ns.class_('RemoteReceiverListener') -RemoteReceiverBinarySensorBase = ns.class_('RemoteReceiverBinarySensorBase', - binary_sensor.BinarySensor, cg.Component) -RemoteReceiverTrigger = ns.class_('RemoteReceiverTrigger', automation.Trigger, - RemoteReceiverListener) -RemoteTransmitterDumper = ns.class_('RemoteTransmitterDumper') -RemoteTransmitterActionBase = ns.class_('RemoteTransmitterActionBase', automation.Action) -RemoteReceiverBase = ns.class_('RemoteReceiverBase') -RemoteTransmitterBase = ns.class_('RemoteTransmitterBase') +ns = remote_base_ns = cg.esphome_ns.namespace("remote_base") +RemoteProtocol = ns.class_("RemoteProtocol") +RemoteReceiverListener = ns.class_("RemoteReceiverListener") +RemoteReceiverBinarySensorBase = ns.class_( + "RemoteReceiverBinarySensorBase", binary_sensor.BinarySensor, cg.Component +) +RemoteReceiverTrigger = ns.class_( + "RemoteReceiverTrigger", automation.Trigger, RemoteReceiverListener +) +RemoteTransmitterDumper = ns.class_("RemoteTransmitterDumper") +RemoteTransmitterActionBase = ns.class_( + "RemoteTransmitterActionBase", automation.Action +) +RemoteReceiverBase = ns.class_("RemoteReceiverBase") +RemoteTransmitterBase = ns.class_("RemoteTransmitterBase") def templatize(value): @@ -47,11 +73,13 @@ def register_binary_sensor(name, type, schema): def register_trigger(name, type, data_type): - validator = automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(type), - cv.GenerateID(CONF_RECEIVER_ID): cv.use_id(RemoteReceiverBase), - }) - registerer = TRIGGER_REGISTRY.register(f'on_{name}', validator) + validator = automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(type), + cv.GenerateID(CONF_RECEIVER_ID): cv.use_id(RemoteReceiverBase), + } + ) + registerer = TRIGGER_REGISTRY.register(f"on_{name}", validator) def decorator(func): @coroutine @@ -59,7 +87,7 @@ def register_trigger(name, type, data_type): var = cg.new_Pvariable(config[CONF_TRIGGER_ID]) yield register_listener(var, config) yield coroutine(func)(var, config) - yield automation.build_automation(var, [(data_type, 'x')], config) + yield automation.build_automation(var, [(data_type, "x")], config) yield var return registerer(new_func) @@ -84,21 +112,27 @@ def register_dumper(name, type): def validate_repeat(value): if isinstance(value, dict): - return cv.Schema({ - cv.Required(CONF_TIMES): cv.templatable(cv.positive_int), - cv.Optional(CONF_WAIT_TIME, default='25ms'): - cv.templatable(cv.positive_time_period_microseconds), - })(value) + return cv.Schema( + { + cv.Required(CONF_TIMES): cv.templatable(cv.positive_int), + cv.Optional(CONF_WAIT_TIME, default="25ms"): cv.templatable( + cv.positive_time_period_microseconds + ), + } + )(value) return validate_repeat({CONF_TIMES: value}) def register_action(name, type_, schema): - validator = templatize(schema).extend({ - cv.GenerateID(CONF_TRANSMITTER_ID): cv.use_id(RemoteTransmitterBase), - cv.Optional(CONF_REPEAT): validate_repeat, - }) - registerer = automation.register_action(f'remote_transmitter.transmit_{name}', - type_, validator) + validator = templatize(schema).extend( + { + cv.GenerateID(CONF_TRANSMITTER_ID): cv.use_id(RemoteTransmitterBase), + cv.Optional(CONF_REPEAT): validate_repeat, + } + ) + registerer = automation.register_action( + f"remote_transmitter.transmit_{name}", type_, validator + ) def decorator(func): @coroutine @@ -121,28 +155,36 @@ def register_action(name, type_, schema): def declare_protocol(name): - data = ns.struct(f'{name}Data') - binary_sensor_ = ns.class_(f'{name}BinarySensor', RemoteReceiverBinarySensorBase) - trigger = ns.class_(f'{name}Trigger', RemoteReceiverTrigger) - action = ns.class_(f'{name}Action', RemoteTransmitterActionBase) - dumper = ns.class_(f'{name}Dumper', RemoteTransmitterDumper) + data = ns.struct(f"{name}Data") + binary_sensor_ = ns.class_(f"{name}BinarySensor", RemoteReceiverBinarySensorBase) + trigger = ns.class_(f"{name}Trigger", RemoteReceiverTrigger) + action = ns.class_(f"{name}Action", RemoteTransmitterActionBase) + dumper = ns.class_(f"{name}Dumper", RemoteTransmitterDumper) return data, binary_sensor_, trigger, action, dumper -BINARY_SENSOR_REGISTRY = Registry(binary_sensor.BINARY_SENSOR_SCHEMA.extend({ - cv.GenerateID(CONF_RECEIVER_ID): cv.use_id(RemoteReceiverBase), -})) -validate_binary_sensor = cv.validate_registry_entry('remote receiver', BINARY_SENSOR_REGISTRY) +BINARY_SENSOR_REGISTRY = Registry( + binary_sensor.BINARY_SENSOR_SCHEMA.extend( + { + cv.GenerateID(CONF_RECEIVER_ID): cv.use_id(RemoteReceiverBase), + } + ) +) +validate_binary_sensor = cv.validate_registry_entry( + "remote receiver", BINARY_SENSOR_REGISTRY +) TRIGGER_REGISTRY = SimpleRegistry() -DUMPER_REGISTRY = Registry({ - cv.GenerateID(CONF_RECEIVER_ID): cv.use_id(RemoteReceiverBase), -}) +DUMPER_REGISTRY = Registry( + { + cv.GenerateID(CONF_RECEIVER_ID): cv.use_id(RemoteReceiverBase), + } +) def validate_dumpers(value): - if isinstance(value, str) and value.lower() == 'all': + if isinstance(value, str) and value.lower() == "all": return validate_dumpers(list(DUMPER_REGISTRY.keys())) - return cv.validate_registry('dumper', DUMPER_REGISTRY)(value) + return cv.validate_registry("dumper", DUMPER_REGISTRY)(value) def validate_triggers(base_schema): @@ -160,7 +202,9 @@ def validate_triggers(base_schema): @coroutine def build_binary_sensor(full_config): - registry_entry, config = cg.extract_registry_entry_config(BINARY_SENSOR_REGISTRY, full_config) + registry_entry, config = cg.extract_registry_entry_config( + BINARY_SENSOR_REGISTRY, full_config + ) type_id = full_config[CONF_TYPE_ID] builder = registry_entry.coroutine_fun var = cg.new_Pvariable(type_id) @@ -190,62 +234,72 @@ def build_dumpers(config): # JVC -JVCData, JVCBinarySensor, JVCTrigger, JVCAction, JVCDumper = declare_protocol('JVC') +JVCData, JVCBinarySensor, JVCTrigger, JVCAction, JVCDumper = declare_protocol("JVC") JVC_SCHEMA = cv.Schema({cv.Required(CONF_DATA): cv.hex_uint32_t}) -@register_binary_sensor('jvc', JVCBinarySensor, JVC_SCHEMA) +@register_binary_sensor("jvc", JVCBinarySensor, JVC_SCHEMA) def jvc_binary_sensor(var, config): - cg.add(var.set_data(cg.StructInitializer( - JVCData, - ('data', config[CONF_DATA]), - ))) + cg.add( + var.set_data( + cg.StructInitializer( + JVCData, + ("data", config[CONF_DATA]), + ) + ) + ) -@register_trigger('jvc', JVCTrigger, JVCData) +@register_trigger("jvc", JVCTrigger, JVCData) def jvc_trigger(var, config): pass -@register_dumper('jvc', JVCDumper) +@register_dumper("jvc", JVCDumper) def jvc_dumper(var, config): pass -@register_action('jvc', JVCAction, JVC_SCHEMA) +@register_action("jvc", JVCAction, JVC_SCHEMA) def jvc_action(var, config, args): template_ = yield cg.templatable(config[CONF_DATA], args, cg.uint32) cg.add(var.set_data(template_)) # LG -LGData, LGBinarySensor, LGTrigger, LGAction, LGDumper = declare_protocol('LG') -LG_SCHEMA = cv.Schema({ - cv.Required(CONF_DATA): cv.hex_uint32_t, - cv.Optional(CONF_NBITS, default=28): cv.one_of(28, 32, int=True), -}) +LGData, LGBinarySensor, LGTrigger, LGAction, LGDumper = declare_protocol("LG") +LG_SCHEMA = cv.Schema( + { + cv.Required(CONF_DATA): cv.hex_uint32_t, + cv.Optional(CONF_NBITS, default=28): cv.one_of(28, 32, int=True), + } +) -@register_binary_sensor('lg', LGBinarySensor, LG_SCHEMA) +@register_binary_sensor("lg", LGBinarySensor, LG_SCHEMA) def lg_binary_sensor(var, config): - cg.add(var.set_data(cg.StructInitializer( - LGData, - ('data', config[CONF_DATA]), - ('nbits', config[CONF_NBITS]), - ))) + cg.add( + var.set_data( + cg.StructInitializer( + LGData, + ("data", config[CONF_DATA]), + ("nbits", config[CONF_NBITS]), + ) + ) + ) -@register_trigger('lg', LGTrigger, LGData) +@register_trigger("lg", LGTrigger, LGData) def lg_trigger(var, config): pass -@register_dumper('lg', LGDumper) +@register_dumper("lg", LGDumper) def lg_dumper(var, config): pass -@register_action('lg', LGAction, LG_SCHEMA) +@register_action("lg", LGAction, LG_SCHEMA) def lg_action(var, config, args): template_ = yield cg.templatable(config[CONF_DATA], args, cg.uint32) cg.add(var.set_data(template_)) @@ -254,33 +308,39 @@ def lg_action(var, config, args): # NEC -NECData, NECBinarySensor, NECTrigger, NECAction, NECDumper = declare_protocol('NEC') -NEC_SCHEMA = cv.Schema({ - cv.Required(CONF_ADDRESS): cv.hex_uint16_t, - cv.Required(CONF_COMMAND): cv.hex_uint16_t, -}) +NECData, NECBinarySensor, NECTrigger, NECAction, NECDumper = declare_protocol("NEC") +NEC_SCHEMA = cv.Schema( + { + cv.Required(CONF_ADDRESS): cv.hex_uint16_t, + cv.Required(CONF_COMMAND): cv.hex_uint16_t, + } +) -@register_binary_sensor('nec', NECBinarySensor, NEC_SCHEMA) +@register_binary_sensor("nec", NECBinarySensor, NEC_SCHEMA) def nec_binary_sensor(var, config): - cg.add(var.set_data(cg.StructInitializer( - NECData, - ('address', config[CONF_ADDRESS]), - ('command', config[CONF_COMMAND]), - ))) + cg.add( + var.set_data( + cg.StructInitializer( + NECData, + ("address", config[CONF_ADDRESS]), + ("command", config[CONF_COMMAND]), + ) + ) + ) -@register_trigger('nec', NECTrigger, NECData) +@register_trigger("nec", NECTrigger, NECData) def nec_trigger(var, config): pass -@register_dumper('nec', NECDumper) +@register_dumper("nec", NECDumper) def nec_dumper(var, config): pass -@register_action('nec', NECAction, NEC_SCHEMA) +@register_action("nec", NECAction, NEC_SCHEMA) def nec_action(var, config, args): template_ = yield cg.templatable(config[CONF_ADDRESS], args, cg.uint16) cg.add(var.set_address(template_)) @@ -289,34 +349,45 @@ def nec_action(var, config, args): # Pioneer -(PioneerData, PioneerBinarySensor, PioneerTrigger, PioneerAction, - PioneerDumper) = declare_protocol('Pioneer') -PIONEER_SCHEMA = cv.Schema({ - cv.Required(CONF_RC_CODE_1): cv.hex_uint16_t, - cv.Optional(CONF_RC_CODE_2, default=0): cv.hex_uint16_t, -}) +( + PioneerData, + PioneerBinarySensor, + PioneerTrigger, + PioneerAction, + PioneerDumper, +) = declare_protocol("Pioneer") +PIONEER_SCHEMA = cv.Schema( + { + cv.Required(CONF_RC_CODE_1): cv.hex_uint16_t, + cv.Optional(CONF_RC_CODE_2, default=0): cv.hex_uint16_t, + } +) -@register_binary_sensor('pioneer', PioneerBinarySensor, PIONEER_SCHEMA) +@register_binary_sensor("pioneer", PioneerBinarySensor, PIONEER_SCHEMA) def pioneer_binary_sensor(var, config): - cg.add(var.set_data(cg.StructInitializer( - PioneerData, - ('rc_code_1', config[CONF_RC_CODE_1]), - ('rc_code_2', config[CONF_RC_CODE_2]), - ))) + cg.add( + var.set_data( + cg.StructInitializer( + PioneerData, + ("rc_code_1", config[CONF_RC_CODE_1]), + ("rc_code_2", config[CONF_RC_CODE_2]), + ) + ) + ) -@register_trigger('pioneer', PioneerTrigger, PioneerData) +@register_trigger("pioneer", PioneerTrigger, PioneerData) def pioneer_trigger(var, config): pass -@register_dumper('pioneer', PioneerDumper) +@register_dumper("pioneer", PioneerDumper) def pioneer_dumper(var, config): pass -@register_action('pioneer', PioneerAction, PIONEER_SCHEMA) +@register_action("pioneer", PioneerAction, PIONEER_SCHEMA) def pioneer_action(var, config, args): template_ = yield cg.templatable(config[CONF_RC_CODE_1], args, cg.uint16) cg.add(var.set_rc_code_1(template_)) @@ -325,33 +396,41 @@ def pioneer_action(var, config, args): # Sony -SonyData, SonyBinarySensor, SonyTrigger, SonyAction, SonyDumper = declare_protocol('Sony') -SONY_SCHEMA = cv.Schema({ - cv.Required(CONF_DATA): cv.hex_uint32_t, - cv.Optional(CONF_NBITS, default=12): cv.one_of(12, 15, 20, int=True), -}) +SonyData, SonyBinarySensor, SonyTrigger, SonyAction, SonyDumper = declare_protocol( + "Sony" +) +SONY_SCHEMA = cv.Schema( + { + cv.Required(CONF_DATA): cv.hex_uint32_t, + cv.Optional(CONF_NBITS, default=12): cv.one_of(12, 15, 20, int=True), + } +) -@register_binary_sensor('sony', SonyBinarySensor, SONY_SCHEMA) +@register_binary_sensor("sony", SonyBinarySensor, SONY_SCHEMA) def sony_binary_sensor(var, config): - cg.add(var.set_data(cg.StructInitializer( - SonyData, - ('data', config[CONF_DATA]), - ('nbits', config[CONF_NBITS]), - ))) + cg.add( + var.set_data( + cg.StructInitializer( + SonyData, + ("data", config[CONF_DATA]), + ("nbits", config[CONF_NBITS]), + ) + ) + ) -@register_trigger('sony', SonyTrigger, SonyData) +@register_trigger("sony", SonyTrigger, SonyData) def sony_trigger(var, config): pass -@register_dumper('sony', SonyDumper) +@register_dumper("sony", SonyDumper) def sony_dumper(var, config): pass -@register_action('sony', SonyAction, SONY_SCHEMA) +@register_action("sony", SonyAction, SONY_SCHEMA) def sony_action(var, config, args): template_ = yield cg.templatable(config[CONF_DATA], args, cg.uint16) cg.add(var.set_data(template_)) @@ -367,22 +446,30 @@ def validate_raw_alternating(value): this_negative = val < 0 if i != 0: if this_negative == last_negative: - raise cv.Invalid("Values must alternate between being positive and negative, " - "please see index {} and {}".format(i, i + 1), [i]) + raise cv.Invalid( + "Values must alternate between being positive and negative, " + "please see index {} and {}".format(i, i + 1), + [i], + ) last_negative = this_negative return value -RawData, RawBinarySensor, RawTrigger, RawAction, RawDumper = declare_protocol('Raw') -CONF_CODE_STORAGE_ID = 'code_storage_id' -RAW_SCHEMA = cv.Schema({ - cv.Required(CONF_CODE): cv.All([cv.Any(cv.int_, cv.time_period_microseconds)], - cv.Length(min=1), validate_raw_alternating), - cv.GenerateID(CONF_CODE_STORAGE_ID): cv.declare_id(cg.int32), -}) +RawData, RawBinarySensor, RawTrigger, RawAction, RawDumper = declare_protocol("Raw") +CONF_CODE_STORAGE_ID = "code_storage_id" +RAW_SCHEMA = cv.Schema( + { + cv.Required(CONF_CODE): cv.All( + [cv.Any(cv.int_, cv.time_period_microseconds)], + cv.Length(min=1), + validate_raw_alternating, + ), + cv.GenerateID(CONF_CODE_STORAGE_ID): cv.declare_id(cg.int32), + } +) -@register_binary_sensor('raw', RawBinarySensor, RAW_SCHEMA) +@register_binary_sensor("raw", RawBinarySensor, RAW_SCHEMA) def raw_binary_sensor(var, config): code_ = config[CONF_CODE] arr = cg.progmem_array(config[CONF_CODE_STORAGE_ID], code_) @@ -390,19 +477,27 @@ def raw_binary_sensor(var, config): cg.add(var.set_len(len(code_))) -@register_trigger('raw', RawTrigger, cg.std_vector.template(cg.int32)) +@register_trigger("raw", RawTrigger, cg.std_vector.template(cg.int32)) def raw_trigger(var, config): pass -@register_dumper('raw', RawDumper) +@register_dumper("raw", RawDumper) def raw_dumper(var, config): pass -@register_action('raw', RawAction, RAW_SCHEMA.extend({ - cv.Optional(CONF_CARRIER_FREQUENCY, default='0Hz'): cv.All(cv.frequency, cv.int_), -})) +@register_action( + "raw", + RawAction, + RAW_SCHEMA.extend( + { + cv.Optional(CONF_CARRIER_FREQUENCY, default="0Hz"): cv.All( + cv.frequency, cv.int_ + ), + } + ), +) def raw_action(var, config, args): code_ = config[CONF_CODE] if cg.is_template(code_): @@ -417,33 +512,39 @@ def raw_action(var, config, args): # RC5 -RC5Data, RC5BinarySensor, RC5Trigger, RC5Action, RC5Dumper = declare_protocol('RC5') -RC5_SCHEMA = cv.Schema({ - cv.Required(CONF_ADDRESS): cv.All(cv.hex_int, cv.Range(min=0, max=0x1F)), - cv.Required(CONF_COMMAND): cv.All(cv.hex_int, cv.Range(min=0, max=0x7F)), -}) +RC5Data, RC5BinarySensor, RC5Trigger, RC5Action, RC5Dumper = declare_protocol("RC5") +RC5_SCHEMA = cv.Schema( + { + cv.Required(CONF_ADDRESS): cv.All(cv.hex_int, cv.Range(min=0, max=0x1F)), + cv.Required(CONF_COMMAND): cv.All(cv.hex_int, cv.Range(min=0, max=0x7F)), + } +) -@register_binary_sensor('rc5', RC5BinarySensor, RC5_SCHEMA) +@register_binary_sensor("rc5", RC5BinarySensor, RC5_SCHEMA) def rc5_binary_sensor(var, config): - cg.add(var.set_data(cg.StructInitializer( - RC5Data, - ('address', config[CONF_ADDRESS]), - ('command', config[CONF_COMMAND]), - ))) + cg.add( + var.set_data( + cg.StructInitializer( + RC5Data, + ("address", config[CONF_ADDRESS]), + ("command", config[CONF_COMMAND]), + ) + ) + ) -@register_trigger('rc5', RC5Trigger, RC5Data) +@register_trigger("rc5", RC5Trigger, RC5Data) def rc5_trigger(var, config): pass -@register_dumper('rc5', RC5Dumper) +@register_dumper("rc5", RC5Dumper) def rc5_dumper(var, config): pass -@register_action('rc5', RC5Action, RC5_SCHEMA) +@register_action("rc5", RC5Action, RC5_SCHEMA) def rc5_action(var, config, args): template_ = yield cg.templatable(config[CONF_ADDRESS], args, cg.uint8) cg.add(var.set_address(template_)) @@ -456,13 +557,15 @@ RC_SWITCH_TIMING_SCHEMA = cv.All([cv.uint8_t], cv.Length(min=2, max=2)) RC_SWITCH_PROTOCOL_SCHEMA = cv.Any( cv.int_range(min=1, max=8), - cv.Schema({ - cv.Required(CONF_PULSE_LENGTH): cv.uint32_t, - cv.Optional(CONF_SYNC, default=[1, 31]): RC_SWITCH_TIMING_SCHEMA, - cv.Optional(CONF_ZERO, default=[1, 3]): RC_SWITCH_TIMING_SCHEMA, - cv.Optional(CONF_ONE, default=[3, 1]): RC_SWITCH_TIMING_SCHEMA, - cv.Optional(CONF_INVERTED, default=False): cv.boolean, - }) + cv.Schema( + { + cv.Required(CONF_PULSE_LENGTH): cv.uint32_t, + cv.Optional(CONF_SYNC, default=[1, 31]): RC_SWITCH_TIMING_SCHEMA, + cv.Optional(CONF_ZERO, default=[1, 3]): RC_SWITCH_TIMING_SCHEMA, + cv.Optional(CONF_ONE, default=[3, 1]): RC_SWITCH_TIMING_SCHEMA, + cv.Optional(CONF_INVERTED, default=False): cv.boolean, + } + ), ) @@ -470,12 +573,16 @@ def validate_rc_switch_code(value): if not isinstance(value, (str, str)): raise cv.Invalid("All RCSwitch codes must be in quotes ('')") for c in value: - if c not in ('0', '1'): - raise cv.Invalid("Invalid RCSwitch code character '{}'. Only '0' and '1' are allowed" - "".format(c)) + if c not in ("0", "1"): + raise cv.Invalid( + "Invalid RCSwitch code character '{}'. Only '0' and '1' are allowed" + "".format(c) + ) if len(value) > 64: - raise cv.Invalid("Maximum length for RCSwitch codes is 64, code '{}' has length {}" - "".format(value, len(value))) + raise cv.Invalid( + "Maximum length for RCSwitch codes is 64, code '{}' has length {}" + "".format(value, len(value)) + ) if not value: raise cv.Invalid("RCSwitch code must not be empty") return value @@ -485,13 +592,17 @@ def validate_rc_switch_raw_code(value): if not isinstance(value, (str, str)): raise cv.Invalid("All RCSwitch raw codes must be in quotes ('')") for c in value: - if c not in ('0', '1', 'x'): + if c not in ("0", "1", "x"): raise cv.Invalid( - "Invalid RCSwitch raw code character '{}'.Only '0', '1' and 'x' are allowed" - .format(c)) + "Invalid RCSwitch raw code character '{}'.Only '0', '1' and 'x' are allowed".format( + c + ) + ) if len(value) > 64: - raise cv.Invalid("Maximum length for RCSwitch raw codes is 64, code '{}' has length {}" - "".format(value, len(value))) + raise cv.Invalid( + "Maximum length for RCSwitch raw codes is 64, code '{}' has length {}" + "".format(value, len(value)) + ) if not value: raise cv.Invalid("RCSwitch raw code must not be empty") return value @@ -501,220 +612,332 @@ def build_rc_switch_protocol(config): if isinstance(config, int): return rc_switch_protocols[config] pl = config[CONF_PULSE_LENGTH] - return RCSwitchBase(config[CONF_SYNC][0] * pl, config[CONF_SYNC][1] * pl, - config[CONF_ZERO][0] * pl, config[CONF_ZERO][1] * pl, - config[CONF_ONE][0] * pl, config[CONF_ONE][1] * pl, - config[CONF_INVERTED]) + return RCSwitchBase( + config[CONF_SYNC][0] * pl, + config[CONF_SYNC][1] * pl, + config[CONF_ZERO][0] * pl, + config[CONF_ZERO][1] * pl, + config[CONF_ONE][0] * pl, + config[CONF_ONE][1] * pl, + config[CONF_INVERTED], + ) -RC_SWITCH_RAW_SCHEMA = cv.Schema({ - cv.Required(CONF_CODE): validate_rc_switch_raw_code, - cv.Optional(CONF_PROTOCOL, default=1): RC_SWITCH_PROTOCOL_SCHEMA, -}) -RC_SWITCH_TYPE_A_SCHEMA = cv.Schema({ - cv.Required(CONF_GROUP): cv.All(validate_rc_switch_code, cv.Length(min=5, max=5)), - cv.Required(CONF_DEVICE): cv.All(validate_rc_switch_code, cv.Length(min=5, max=5)), - cv.Required(CONF_STATE): cv.boolean, - cv.Optional(CONF_PROTOCOL, default=1): RC_SWITCH_PROTOCOL_SCHEMA, -}) -RC_SWITCH_TYPE_B_SCHEMA = cv.Schema({ - cv.Required(CONF_ADDRESS): cv.int_range(min=1, max=4), - cv.Required(CONF_CHANNEL): cv.int_range(min=1, max=4), - cv.Required(CONF_STATE): cv.boolean, - cv.Optional(CONF_PROTOCOL, default=1): RC_SWITCH_PROTOCOL_SCHEMA, -}) -RC_SWITCH_TYPE_C_SCHEMA = cv.Schema({ - cv.Required(CONF_FAMILY): cv.one_of('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', - 'l', 'm', 'n', 'o', 'p', lower=True), - cv.Required(CONF_GROUP): cv.int_range(min=1, max=4), - cv.Required(CONF_DEVICE): cv.int_range(min=1, max=4), - cv.Required(CONF_STATE): cv.boolean, - cv.Optional(CONF_PROTOCOL, default=1): RC_SWITCH_PROTOCOL_SCHEMA, -}) -RC_SWITCH_TYPE_D_SCHEMA = cv.Schema({ - cv.Required(CONF_GROUP): cv.one_of('a', 'b', 'c', 'd', lower=True), - cv.Required(CONF_DEVICE): cv.int_range(min=1, max=3), - cv.Required(CONF_STATE): cv.boolean, - cv.Optional(CONF_PROTOCOL, default=1): RC_SWITCH_PROTOCOL_SCHEMA, -}) -RC_SWITCH_TRANSMITTER = cv.Schema({ - cv.Optional(CONF_REPEAT, default={CONF_TIMES: 5}): cv.Schema({ - cv.Required(CONF_TIMES): cv.templatable(cv.positive_int), - cv.Optional(CONF_WAIT_TIME, default='0us'): - cv.templatable(cv.positive_time_period_microseconds), - }), -}) +RC_SWITCH_RAW_SCHEMA = cv.Schema( + { + cv.Required(CONF_CODE): validate_rc_switch_raw_code, + cv.Optional(CONF_PROTOCOL, default=1): RC_SWITCH_PROTOCOL_SCHEMA, + } +) +RC_SWITCH_TYPE_A_SCHEMA = cv.Schema( + { + cv.Required(CONF_GROUP): cv.All( + validate_rc_switch_code, cv.Length(min=5, max=5) + ), + cv.Required(CONF_DEVICE): cv.All( + validate_rc_switch_code, cv.Length(min=5, max=5) + ), + cv.Required(CONF_STATE): cv.boolean, + cv.Optional(CONF_PROTOCOL, default=1): RC_SWITCH_PROTOCOL_SCHEMA, + } +) +RC_SWITCH_TYPE_B_SCHEMA = cv.Schema( + { + cv.Required(CONF_ADDRESS): cv.int_range(min=1, max=4), + cv.Required(CONF_CHANNEL): cv.int_range(min=1, max=4), + cv.Required(CONF_STATE): cv.boolean, + cv.Optional(CONF_PROTOCOL, default=1): RC_SWITCH_PROTOCOL_SCHEMA, + } +) +RC_SWITCH_TYPE_C_SCHEMA = cv.Schema( + { + cv.Required(CONF_FAMILY): cv.one_of( + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + lower=True, + ), + cv.Required(CONF_GROUP): cv.int_range(min=1, max=4), + cv.Required(CONF_DEVICE): cv.int_range(min=1, max=4), + cv.Required(CONF_STATE): cv.boolean, + cv.Optional(CONF_PROTOCOL, default=1): RC_SWITCH_PROTOCOL_SCHEMA, + } +) +RC_SWITCH_TYPE_D_SCHEMA = cv.Schema( + { + cv.Required(CONF_GROUP): cv.one_of("a", "b", "c", "d", lower=True), + cv.Required(CONF_DEVICE): cv.int_range(min=1, max=3), + cv.Required(CONF_STATE): cv.boolean, + cv.Optional(CONF_PROTOCOL, default=1): RC_SWITCH_PROTOCOL_SCHEMA, + } +) +RC_SWITCH_TRANSMITTER = cv.Schema( + { + cv.Optional(CONF_REPEAT, default={CONF_TIMES: 5}): cv.Schema( + { + cv.Required(CONF_TIMES): cv.templatable(cv.positive_int), + cv.Optional(CONF_WAIT_TIME, default="0us"): cv.templatable( + cv.positive_time_period_microseconds + ), + } + ), + } +) rc_switch_protocols = ns.rc_switch_protocols -RCSwitchData = ns.struct('RCSwitchData') -RCSwitchBase = ns.class_('RCSwitchBase') -RCSwitchTrigger = ns.class_('RCSwitchTrigger', RemoteReceiverTrigger) -RCSwitchDumper = ns.class_('RCSwitchDumper', RemoteTransmitterDumper) -RCSwitchRawAction = ns.class_('RCSwitchRawAction', RemoteTransmitterActionBase) -RCSwitchTypeAAction = ns.class_('RCSwitchTypeAAction', RemoteTransmitterActionBase) -RCSwitchTypeBAction = ns.class_('RCSwitchTypeBAction', RemoteTransmitterActionBase) -RCSwitchTypeCAction = ns.class_('RCSwitchTypeCAction', RemoteTransmitterActionBase) -RCSwitchTypeDAction = ns.class_('RCSwitchTypeDAction', RemoteTransmitterActionBase) -RCSwitchRawReceiver = ns.class_('RCSwitchRawReceiver', RemoteReceiverBinarySensorBase) +RCSwitchData = ns.struct("RCSwitchData") +RCSwitchBase = ns.class_("RCSwitchBase") +RCSwitchTrigger = ns.class_("RCSwitchTrigger", RemoteReceiverTrigger) +RCSwitchDumper = ns.class_("RCSwitchDumper", RemoteTransmitterDumper) +RCSwitchRawAction = ns.class_("RCSwitchRawAction", RemoteTransmitterActionBase) +RCSwitchTypeAAction = ns.class_("RCSwitchTypeAAction", RemoteTransmitterActionBase) +RCSwitchTypeBAction = ns.class_("RCSwitchTypeBAction", RemoteTransmitterActionBase) +RCSwitchTypeCAction = ns.class_("RCSwitchTypeCAction", RemoteTransmitterActionBase) +RCSwitchTypeDAction = ns.class_("RCSwitchTypeDAction", RemoteTransmitterActionBase) +RCSwitchRawReceiver = ns.class_("RCSwitchRawReceiver", RemoteReceiverBinarySensorBase) -@register_binary_sensor('rc_switch_raw', RCSwitchRawReceiver, RC_SWITCH_RAW_SCHEMA) +@register_binary_sensor("rc_switch_raw", RCSwitchRawReceiver, RC_SWITCH_RAW_SCHEMA) def rc_switch_raw_binary_sensor(var, config): cg.add(var.set_protocol(build_rc_switch_protocol(config[CONF_PROTOCOL]))) cg.add(var.set_code(config[CONF_CODE])) -@register_action('rc_switch_raw', RCSwitchRawAction, - RC_SWITCH_RAW_SCHEMA.extend(RC_SWITCH_TRANSMITTER)) +@register_action( + "rc_switch_raw", + RCSwitchRawAction, + RC_SWITCH_RAW_SCHEMA.extend(RC_SWITCH_TRANSMITTER), +) def rc_switch_raw_action(var, config, args): - proto = yield cg.templatable(config[CONF_PROTOCOL], args, RCSwitchBase, - to_exp=build_rc_switch_protocol) + proto = yield cg.templatable( + config[CONF_PROTOCOL], args, RCSwitchBase, to_exp=build_rc_switch_protocol + ) cg.add(var.set_protocol(proto)) cg.add(var.set_code((yield cg.templatable(config[CONF_CODE], args, cg.std_string)))) -@register_binary_sensor('rc_switch_type_a', RCSwitchRawReceiver, RC_SWITCH_TYPE_A_SCHEMA) +@register_binary_sensor( + "rc_switch_type_a", RCSwitchRawReceiver, RC_SWITCH_TYPE_A_SCHEMA +) def rc_switch_type_a_binary_sensor(var, config): cg.add(var.set_protocol(build_rc_switch_protocol(config[CONF_PROTOCOL]))) cg.add(var.set_type_a(config[CONF_GROUP], config[CONF_DEVICE], config[CONF_STATE])) -@register_action('rc_switch_type_a', RCSwitchTypeAAction, - RC_SWITCH_TYPE_A_SCHEMA.extend(RC_SWITCH_TRANSMITTER)) +@register_action( + "rc_switch_type_a", + RCSwitchTypeAAction, + RC_SWITCH_TYPE_A_SCHEMA.extend(RC_SWITCH_TRANSMITTER), +) def rc_switch_type_a_action(var, config, args): - proto = yield cg.templatable(config[CONF_PROTOCOL], args, RCSwitchBase, - to_exp=build_rc_switch_protocol) + proto = yield cg.templatable( + config[CONF_PROTOCOL], args, RCSwitchBase, to_exp=build_rc_switch_protocol + ) cg.add(var.set_protocol(proto)) - cg.add(var.set_group((yield cg.templatable(config[CONF_GROUP], args, cg.std_string)))) - cg.add(var.set_device((yield cg.templatable(config[CONF_DEVICE], args, cg.std_string)))) + cg.add( + var.set_group((yield cg.templatable(config[CONF_GROUP], args, cg.std_string))) + ) + cg.add( + var.set_device((yield cg.templatable(config[CONF_DEVICE], args, cg.std_string))) + ) cg.add(var.set_state((yield cg.templatable(config[CONF_STATE], args, bool)))) -@register_binary_sensor('rc_switch_type_b', RCSwitchRawReceiver, RC_SWITCH_TYPE_B_SCHEMA) +@register_binary_sensor( + "rc_switch_type_b", RCSwitchRawReceiver, RC_SWITCH_TYPE_B_SCHEMA +) def rc_switch_type_b_binary_sensor(var, config): cg.add(var.set_protocol(build_rc_switch_protocol(config[CONF_PROTOCOL]))) - cg.add(var.set_type_b(config[CONF_ADDRESS], config[CONF_CHANNEL], config[CONF_STATE])) + cg.add( + var.set_type_b(config[CONF_ADDRESS], config[CONF_CHANNEL], config[CONF_STATE]) + ) -@register_action('rc_switch_type_b', RCSwitchTypeBAction, - RC_SWITCH_TYPE_B_SCHEMA.extend(RC_SWITCH_TRANSMITTER)) +@register_action( + "rc_switch_type_b", + RCSwitchTypeBAction, + RC_SWITCH_TYPE_B_SCHEMA.extend(RC_SWITCH_TRANSMITTER), +) def rc_switch_type_b_action(var, config, args): - proto = yield cg.templatable(config[CONF_PROTOCOL], args, RCSwitchBase, - to_exp=build_rc_switch_protocol) + proto = yield cg.templatable( + config[CONF_PROTOCOL], args, RCSwitchBase, to_exp=build_rc_switch_protocol + ) cg.add(var.set_protocol(proto)) - cg.add(var.set_address((yield cg.templatable(config[CONF_ADDRESS], args, cg.uint8)))) - cg.add(var.set_channel((yield cg.templatable(config[CONF_CHANNEL], args, cg.uint8)))) + cg.add( + var.set_address((yield cg.templatable(config[CONF_ADDRESS], args, cg.uint8))) + ) + cg.add( + var.set_channel((yield cg.templatable(config[CONF_CHANNEL], args, cg.uint8))) + ) cg.add(var.set_state((yield cg.templatable(config[CONF_STATE], args, bool)))) -@register_binary_sensor('rc_switch_type_c', RCSwitchRawReceiver, RC_SWITCH_TYPE_C_SCHEMA) +@register_binary_sensor( + "rc_switch_type_c", RCSwitchRawReceiver, RC_SWITCH_TYPE_C_SCHEMA +) def rc_switch_type_c_binary_sensor(var, config): cg.add(var.set_protocol(build_rc_switch_protocol(config[CONF_PROTOCOL]))) - cg.add(var.set_type_c(config[CONF_FAMILY], config[CONF_GROUP], config[CONF_DEVICE], - config[CONF_STATE])) + cg.add( + var.set_type_c( + config[CONF_FAMILY], + config[CONF_GROUP], + config[CONF_DEVICE], + config[CONF_STATE], + ) + ) -@register_action('rc_switch_type_c', RCSwitchTypeCAction, - RC_SWITCH_TYPE_C_SCHEMA.extend(RC_SWITCH_TRANSMITTER)) +@register_action( + "rc_switch_type_c", + RCSwitchTypeCAction, + RC_SWITCH_TYPE_C_SCHEMA.extend(RC_SWITCH_TRANSMITTER), +) def rc_switch_type_c_action(var, config, args): - proto = yield cg.templatable(config[CONF_PROTOCOL], args, RCSwitchBase, - to_exp=build_rc_switch_protocol) + proto = yield cg.templatable( + config[CONF_PROTOCOL], args, RCSwitchBase, to_exp=build_rc_switch_protocol + ) cg.add(var.set_protocol(proto)) - cg.add(var.set_family((yield cg.templatable(config[CONF_FAMILY], args, cg.std_string)))) + cg.add( + var.set_family((yield cg.templatable(config[CONF_FAMILY], args, cg.std_string))) + ) cg.add(var.set_group((yield cg.templatable(config[CONF_GROUP], args, cg.uint8)))) cg.add(var.set_device((yield cg.templatable(config[CONF_DEVICE], args, cg.uint8)))) cg.add(var.set_state((yield cg.templatable(config[CONF_STATE], args, bool)))) -@register_binary_sensor('rc_switch_type_d', RCSwitchRawReceiver, - RC_SWITCH_TYPE_D_SCHEMA.extend(RC_SWITCH_TRANSMITTER)) +@register_binary_sensor( + "rc_switch_type_d", + RCSwitchRawReceiver, + RC_SWITCH_TYPE_D_SCHEMA.extend(RC_SWITCH_TRANSMITTER), +) def rc_switch_type_d_binary_sensor(var, config): cg.add(var.set_protocol(build_rc_switch_protocol(config[CONF_PROTOCOL]))) cg.add(var.set_type_d(config[CONF_GROUP], config[CONF_DEVICE], config[CONF_STATE])) -@register_action('rc_switch_type_d', RCSwitchTypeDAction, - RC_SWITCH_TYPE_D_SCHEMA.extend(RC_SWITCH_TRANSMITTER)) +@register_action( + "rc_switch_type_d", + RCSwitchTypeDAction, + RC_SWITCH_TYPE_D_SCHEMA.extend(RC_SWITCH_TRANSMITTER), +) def rc_switch_type_d_action(var, config, args): - proto = yield cg.templatable(config[CONF_PROTOCOL], args, RCSwitchBase, - to_exp=build_rc_switch_protocol) + proto = yield cg.templatable( + config[CONF_PROTOCOL], args, RCSwitchBase, to_exp=build_rc_switch_protocol + ) cg.add(var.set_protocol(proto)) - cg.add(var.set_group((yield cg.templatable(config[CONF_GROUP], args, cg.std_string)))) + cg.add( + var.set_group((yield cg.templatable(config[CONF_GROUP], args, cg.std_string))) + ) cg.add(var.set_device((yield cg.templatable(config[CONF_DEVICE], args, cg.uint8)))) cg.add(var.set_state((yield cg.templatable(config[CONF_STATE], args, bool)))) -@register_trigger('rc_switch', RCSwitchTrigger, RCSwitchData) +@register_trigger("rc_switch", RCSwitchTrigger, RCSwitchData) def rc_switch_trigger(var, config): pass -@register_dumper('rc_switch', RCSwitchDumper) +@register_dumper("rc_switch", RCSwitchDumper) def rc_switch_dumper(var, config): pass # Samsung -(SamsungData, SamsungBinarySensor, SamsungTrigger, SamsungAction, - SamsungDumper) = declare_protocol('Samsung') -SAMSUNG_SCHEMA = cv.Schema({ - cv.Required(CONF_DATA): cv.hex_uint32_t, -}) +( + SamsungData, + SamsungBinarySensor, + SamsungTrigger, + SamsungAction, + SamsungDumper, +) = declare_protocol("Samsung") +SAMSUNG_SCHEMA = cv.Schema( + { + cv.Required(CONF_DATA): cv.hex_uint32_t, + } +) -@register_binary_sensor('samsung', SamsungBinarySensor, SAMSUNG_SCHEMA) +@register_binary_sensor("samsung", SamsungBinarySensor, SAMSUNG_SCHEMA) def samsung_binary_sensor(var, config): - cg.add(var.set_data(cg.StructInitializer( - SamsungData, - ('data', config[CONF_DATA]), - ))) + cg.add( + var.set_data( + cg.StructInitializer( + SamsungData, + ("data", config[CONF_DATA]), + ) + ) + ) -@register_trigger('samsung', SamsungTrigger, SamsungData) +@register_trigger("samsung", SamsungTrigger, SamsungData) def samsung_trigger(var, config): pass -@register_dumper('samsung', SamsungDumper) +@register_dumper("samsung", SamsungDumper) def samsung_dumper(var, config): pass -@register_action('samsung', SamsungAction, SAMSUNG_SCHEMA) +@register_action("samsung", SamsungAction, SAMSUNG_SCHEMA) def samsung_action(var, config, args): template_ = yield cg.templatable(config[CONF_DATA], args, cg.uint32) cg.add(var.set_data(template_)) # Samsung36 -(Samsung36Data, Samsung36BinarySensor, Samsung36Trigger, Samsung36Action, - Samsung36Dumper) = declare_protocol('Samsung36') -SAMSUNG36_SCHEMA = cv.Schema({ - cv.Required(CONF_ADDRESS): cv.hex_uint16_t, - cv.Required(CONF_COMMAND): cv.hex_uint32_t, -}) +( + Samsung36Data, + Samsung36BinarySensor, + Samsung36Trigger, + Samsung36Action, + Samsung36Dumper, +) = declare_protocol("Samsung36") +SAMSUNG36_SCHEMA = cv.Schema( + { + cv.Required(CONF_ADDRESS): cv.hex_uint16_t, + cv.Required(CONF_COMMAND): cv.hex_uint32_t, + } +) -@register_binary_sensor('samsung36', Samsung36BinarySensor, SAMSUNG36_SCHEMA) +@register_binary_sensor("samsung36", Samsung36BinarySensor, SAMSUNG36_SCHEMA) def samsung36_binary_sensor(var, config): - cg.add(var.set_data(cg.StructInitializer( - Samsung36Data, - ('address', config[CONF_ADDRESS]), - ('command', config[CONF_COMMAND]), - ))) + cg.add( + var.set_data( + cg.StructInitializer( + Samsung36Data, + ("address", config[CONF_ADDRESS]), + ("command", config[CONF_COMMAND]), + ) + ) + ) -@register_trigger('samsung36', Samsung36Trigger, Samsung36Data) +@register_trigger("samsung36", Samsung36Trigger, Samsung36Data) def samsung36_trigger(var, config): pass -@register_dumper('samsung36', Samsung36Dumper) +@register_dumper("samsung36", Samsung36Dumper) def samsung36_dumper(var, config): pass -@register_action('samsung36', Samsung36Action, SAMSUNG36_SCHEMA) +@register_action("samsung36", Samsung36Action, SAMSUNG36_SCHEMA) def samsung36_action(var, config, args): template_ = yield cg.templatable(config[CONF_ADDRESS], args, cg.uint16) cg.add(var.set_address(template_)) @@ -723,34 +946,45 @@ def samsung36_action(var, config, args): # Panasonic -(PanasonicData, PanasonicBinarySensor, PanasonicTrigger, PanasonicAction, - PanasonicDumper) = declare_protocol('Panasonic') -PANASONIC_SCHEMA = cv.Schema({ - cv.Required(CONF_ADDRESS): cv.hex_uint16_t, - cv.Required(CONF_COMMAND): cv.hex_uint32_t, -}) +( + PanasonicData, + PanasonicBinarySensor, + PanasonicTrigger, + PanasonicAction, + PanasonicDumper, +) = declare_protocol("Panasonic") +PANASONIC_SCHEMA = cv.Schema( + { + cv.Required(CONF_ADDRESS): cv.hex_uint16_t, + cv.Required(CONF_COMMAND): cv.hex_uint32_t, + } +) -@register_binary_sensor('panasonic', PanasonicBinarySensor, PANASONIC_SCHEMA) +@register_binary_sensor("panasonic", PanasonicBinarySensor, PANASONIC_SCHEMA) def panasonic_binary_sensor(var, config): - cg.add(var.set_data(cg.StructInitializer( - PanasonicData, - ('address', config[CONF_ADDRESS]), - ('command', config[CONF_COMMAND]), - ))) + cg.add( + var.set_data( + cg.StructInitializer( + PanasonicData, + ("address", config[CONF_ADDRESS]), + ("command", config[CONF_COMMAND]), + ) + ) + ) -@register_trigger('panasonic', PanasonicTrigger, PanasonicData) +@register_trigger("panasonic", PanasonicTrigger, PanasonicData) def panasonic_trigger(var, config): pass -@register_dumper('panasonic', PanasonicDumper) +@register_dumper("panasonic", PanasonicDumper) def panasonic_dumper(var, config): pass -@register_action('panasonic', PanasonicAction, PANASONIC_SCHEMA) +@register_action("panasonic", PanasonicAction, PANASONIC_SCHEMA) def panasonic_action(var, config, args): template_ = yield cg.templatable(config[CONF_ADDRESS], args, cg.uint16) cg.add(var.set_address(template_)) diff --git a/esphome/components/remote_receiver/__init__.py b/esphome/components/remote_receiver/__init__.py index 28570a7c62..f29c59b3f8 100644 --- a/esphome/components/remote_receiver/__init__.py +++ b/esphome/components/remote_receiver/__init__.py @@ -2,28 +2,49 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins from esphome.components import remote_base -from esphome.const import CONF_BUFFER_SIZE, CONF_DUMP, CONF_FILTER, CONF_ID, CONF_IDLE, \ - CONF_PIN, CONF_TOLERANCE, CONF_MEMORY_BLOCKS +from esphome.const import ( + CONF_BUFFER_SIZE, + CONF_DUMP, + CONF_FILTER, + CONF_ID, + CONF_IDLE, + CONF_PIN, + CONF_TOLERANCE, + CONF_MEMORY_BLOCKS, +) from esphome.core import CORE -AUTO_LOAD = ['remote_base'] -remote_receiver_ns = cg.esphome_ns.namespace('remote_receiver') -RemoteReceiverComponent = remote_receiver_ns.class_('RemoteReceiverComponent', - remote_base.RemoteReceiverBase, - cg.Component) +AUTO_LOAD = ["remote_base"] +remote_receiver_ns = cg.esphome_ns.namespace("remote_receiver") +RemoteReceiverComponent = remote_receiver_ns.class_( + "RemoteReceiverComponent", remote_base.RemoteReceiverBase, cg.Component +) MULTI_CONF = True -CONFIG_SCHEMA = remote_base.validate_triggers(cv.Schema({ - cv.GenerateID(): cv.declare_id(RemoteReceiverComponent), - cv.Required(CONF_PIN): cv.All(pins.internal_gpio_input_pin_schema, - pins.validate_has_interrupt), - cv.Optional(CONF_DUMP, default=[]): remote_base.validate_dumpers, - cv.Optional(CONF_TOLERANCE, default=25): cv.All(cv.percentage_int, cv.Range(min=0)), - cv.SplitDefault(CONF_BUFFER_SIZE, esp32='10000b', esp8266='1000b'): cv.validate_bytes, - cv.Optional(CONF_FILTER, default='50us'): cv.positive_time_period_microseconds, - cv.Optional(CONF_IDLE, default='10ms'): cv.positive_time_period_microseconds, - cv.Optional(CONF_MEMORY_BLOCKS, default=3): cv.Range(min=1, max=8), -}).extend(cv.COMPONENT_SCHEMA)) +CONFIG_SCHEMA = remote_base.validate_triggers( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(RemoteReceiverComponent), + cv.Required(CONF_PIN): cv.All( + pins.internal_gpio_input_pin_schema, pins.validate_has_interrupt + ), + cv.Optional(CONF_DUMP, default=[]): remote_base.validate_dumpers, + cv.Optional(CONF_TOLERANCE, default=25): cv.All( + cv.percentage_int, cv.Range(min=0) + ), + cv.SplitDefault( + CONF_BUFFER_SIZE, esp32="10000b", esp8266="1000b" + ): cv.validate_bytes, + cv.Optional( + CONF_FILTER, default="50us" + ): cv.positive_time_period_microseconds, + cv.Optional( + CONF_IDLE, default="10ms" + ): cv.positive_time_period_microseconds, + cv.Optional(CONF_MEMORY_BLOCKS, default=3): cv.Range(min=1, max=8), + } + ).extend(cv.COMPONENT_SCHEMA) +) def to_code(config): diff --git a/esphome/components/remote_receiver/binary_sensor.py b/esphome/components/remote_receiver/binary_sensor.py index 7be64e5a54..522333584c 100644 --- a/esphome/components/remote_receiver/binary_sensor.py +++ b/esphome/components/remote_receiver/binary_sensor.py @@ -2,7 +2,7 @@ import esphome.codegen as cg from esphome.components import binary_sensor, remote_base from esphome.const import CONF_NAME -DEPENDENCIES = ['remote_receiver'] +DEPENDENCIES = ["remote_receiver"] CONFIG_SCHEMA = remote_base.validate_binary_sensor diff --git a/esphome/components/remote_transmitter/__init__.py b/esphome/components/remote_transmitter/__init__.py index 5e217de608..123af7db97 100644 --- a/esphome/components/remote_transmitter/__init__.py +++ b/esphome/components/remote_transmitter/__init__.py @@ -4,18 +4,22 @@ from esphome import pins from esphome.components import remote_base from esphome.const import CONF_CARRIER_DUTY_PERCENT, CONF_ID, CONF_PIN -AUTO_LOAD = ['remote_base'] -remote_transmitter_ns = cg.esphome_ns.namespace('remote_transmitter') -RemoteTransmitterComponent = remote_transmitter_ns.class_('RemoteTransmitterComponent', - remote_base.RemoteTransmitterBase, - cg.Component) +AUTO_LOAD = ["remote_base"] +remote_transmitter_ns = cg.esphome_ns.namespace("remote_transmitter") +RemoteTransmitterComponent = remote_transmitter_ns.class_( + "RemoteTransmitterComponent", remote_base.RemoteTransmitterBase, cg.Component +) MULTI_CONF = True -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(RemoteTransmitterComponent), - cv.Required(CONF_PIN): pins.gpio_output_pin_schema, - cv.Required(CONF_CARRIER_DUTY_PERCENT): cv.All(cv.percentage_int, cv.Range(min=1, max=100)), -}).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(RemoteTransmitterComponent), + cv.Required(CONF_PIN): pins.gpio_output_pin_schema, + cv.Required(CONF_CARRIER_DUTY_PERCENT): cv.All( + cv.percentage_int, cv.Range(min=1, max=100) + ), + } +).extend(cv.COMPONENT_SCHEMA) def to_code(config): diff --git a/esphome/components/remote_transmitter/switch.py b/esphome/components/remote_transmitter/switch.py index 5e0be04d7a..3a2e43a31a 100644 --- a/esphome/components/remote_transmitter/switch.py +++ b/esphome/components/remote_transmitter/switch.py @@ -5,26 +5,29 @@ from esphome.util import OrderedDict def show_new(value): from esphome import yaml_util + for key in BINARY_SENSOR_REGISTRY: if key in value: break else: - raise cv.Invalid("This platform has been removed in 1.13, please see the docs for updated " - "instructions.") + raise cv.Invalid( + "This platform has been removed in 1.13, please see the docs for updated " + "instructions." + ) val = value[key] - args = [('platform', 'template')] - if 'id' in value: - args.append(('id', value['id'])) - if 'name' in value: - args.append(('name', value['name'])) - args.append(('turn_on_action', { - f'remote_transmitter.transmit_{key}': val - })) + args = [("platform", "template")] + if "id" in value: + args.append(("id", value["id"])) + if "name" in value: + args.append(("name", value["name"])) + args.append(("turn_on_action", {f"remote_transmitter.transmit_{key}": val})) text = yaml_util.dump([OrderedDict(args)]) - raise cv.Invalid("This platform has been removed in 1.13, please change to:\n\n{}\n\n." - "".format(text)) + raise cv.Invalid( + "This platform has been removed in 1.13, please change to:\n\n{}\n\n." + "".format(text) + ) CONFIG_SCHEMA = show_new diff --git a/esphome/components/resistance/sensor.py b/esphome/components/resistance/sensor.py index 373fa3a8ae..92a564f68e 100644 --- a/esphome/components/resistance/sensor.py +++ b/esphome/components/resistance/sensor.py @@ -3,26 +3,32 @@ import esphome.config_validation as cv from esphome.components import sensor from esphome.const import CONF_SENSOR, DEVICE_CLASS_EMPTY, UNIT_OHM, ICON_FLASH, CONF_ID -resistance_ns = cg.esphome_ns.namespace('resistance') -ResistanceSensor = resistance_ns.class_('ResistanceSensor', cg.Component, sensor.Sensor) +resistance_ns = cg.esphome_ns.namespace("resistance") +ResistanceSensor = resistance_ns.class_("ResistanceSensor", cg.Component, sensor.Sensor) -CONF_REFERENCE_VOLTAGE = 'reference_voltage' -CONF_CONFIGURATION = 'configuration' -CONF_RESISTOR = 'resistor' +CONF_REFERENCE_VOLTAGE = "reference_voltage" +CONF_CONFIGURATION = "configuration" +CONF_RESISTOR = "resistor" -ResistanceConfiguration = resistance_ns.enum('ResistanceConfiguration') +ResistanceConfiguration = resistance_ns.enum("ResistanceConfiguration") CONFIGURATIONS = { - 'DOWNSTREAM': ResistanceConfiguration.DOWNSTREAM, - 'UPSTREAM': ResistanceConfiguration.UPSTREAM, + "DOWNSTREAM": ResistanceConfiguration.DOWNSTREAM, + "UPSTREAM": ResistanceConfiguration.UPSTREAM, } -CONFIG_SCHEMA = sensor.sensor_schema(UNIT_OHM, ICON_FLASH, 1, DEVICE_CLASS_EMPTY).extend({ - cv.GenerateID(): cv.declare_id(ResistanceSensor), - cv.Required(CONF_SENSOR): cv.use_id(sensor.Sensor), - cv.Required(CONF_CONFIGURATION): cv.enum(CONFIGURATIONS, upper=True), - cv.Required(CONF_RESISTOR): cv.resistance, - cv.Optional(CONF_REFERENCE_VOLTAGE, default='3.3V'): cv.voltage, -}).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = ( + sensor.sensor_schema(UNIT_OHM, ICON_FLASH, 1, DEVICE_CLASS_EMPTY) + .extend( + { + cv.GenerateID(): cv.declare_id(ResistanceSensor), + cv.Required(CONF_SENSOR): cv.use_id(sensor.Sensor), + cv.Required(CONF_CONFIGURATION): cv.enum(CONFIGURATIONS, upper=True), + cv.Required(CONF_RESISTOR): cv.resistance, + cv.Optional(CONF_REFERENCE_VOLTAGE, default="3.3V"): cv.voltage, + } + ) + .extend(cv.COMPONENT_SCHEMA) +) def to_code(config): diff --git a/esphome/components/restart/__init__.py b/esphome/components/restart/__init__.py index 63db7aee2e..f70ffa9520 100644 --- a/esphome/components/restart/__init__.py +++ b/esphome/components/restart/__init__.py @@ -1 +1 @@ -CODEOWNERS = ['@esphome/core'] +CODEOWNERS = ["@esphome/core"] diff --git a/esphome/components/restart/switch.py b/esphome/components/restart/switch.py index 9517302d33..fe170aee61 100644 --- a/esphome/components/restart/switch.py +++ b/esphome/components/restart/switch.py @@ -3,15 +3,18 @@ import esphome.config_validation as cv from esphome.components import switch from esphome.const import CONF_ID, CONF_INVERTED, CONF_ICON, ICON_RESTART -restart_ns = cg.esphome_ns.namespace('restart') -RestartSwitch = restart_ns.class_('RestartSwitch', switch.Switch, cg.Component) +restart_ns = cg.esphome_ns.namespace("restart") +RestartSwitch = restart_ns.class_("RestartSwitch", switch.Switch, cg.Component) -CONFIG_SCHEMA = switch.SWITCH_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(RestartSwitch), - cv.Optional(CONF_INVERTED): cv.invalid("Restart switches do not support inverted mode!"), - - cv.Optional(CONF_ICON, default=ICON_RESTART): switch.icon, -}).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = switch.SWITCH_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(RestartSwitch), + cv.Optional(CONF_INVERTED): cv.invalid( + "Restart switches do not support inverted mode!" + ), + cv.Optional(CONF_ICON, default=ICON_RESTART): switch.icon, + } +).extend(cv.COMPONENT_SCHEMA) def to_code(config): diff --git a/esphome/components/rf_bridge/__init__.py b/esphome/components/rf_bridge/__init__.py index a0a910118b..b24e19b4bf 100644 --- a/esphome/components/rf_bridge/__init__.py +++ b/esphome/components/rf_bridge/__init__.py @@ -14,47 +14,69 @@ from esphome.const import ( CONF_TRIGGER_ID, ) -DEPENDENCIES = ['uart'] -CODEOWNERS = ['@jesserockz'] +DEPENDENCIES = ["uart"] +CODEOWNERS = ["@jesserockz"] -rf_bridge_ns = cg.esphome_ns.namespace('rf_bridge') -RFBridgeComponent = rf_bridge_ns.class_('RFBridgeComponent', cg.Component, uart.UARTDevice) +rf_bridge_ns = cg.esphome_ns.namespace("rf_bridge") +RFBridgeComponent = rf_bridge_ns.class_( + "RFBridgeComponent", cg.Component, uart.UARTDevice +) -RFBridgeData = rf_bridge_ns.struct('RFBridgeData') -RFBridgeAdvancedData = rf_bridge_ns.struct('RFBridgeAdvancedData') +RFBridgeData = rf_bridge_ns.struct("RFBridgeData") +RFBridgeAdvancedData = rf_bridge_ns.struct("RFBridgeAdvancedData") -RFBridgeReceivedCodeTrigger = rf_bridge_ns.class_('RFBridgeReceivedCodeTrigger', - automation.Trigger.template(RFBridgeData)) +RFBridgeReceivedCodeTrigger = rf_bridge_ns.class_( + "RFBridgeReceivedCodeTrigger", automation.Trigger.template(RFBridgeData) +) RFBridgeReceivedAdvancedCodeTrigger = rf_bridge_ns.class_( - 'RFBridgeReceivedAdvancedCodeTrigger', + "RFBridgeReceivedAdvancedCodeTrigger", automation.Trigger.template(RFBridgeAdvancedData), ) -RFBridgeSendCodeAction = rf_bridge_ns.class_('RFBridgeSendCodeAction', automation.Action) +RFBridgeSendCodeAction = rf_bridge_ns.class_( + "RFBridgeSendCodeAction", automation.Action +) RFBridgeSendAdvancedCodeAction = rf_bridge_ns.class_( - 'RFBridgeSendAdvancedCodeAction', automation.Action) + "RFBridgeSendAdvancedCodeAction", automation.Action +) -RFBridgeLearnAction = rf_bridge_ns.class_('RFBridgeLearnAction', automation.Action) +RFBridgeLearnAction = rf_bridge_ns.class_("RFBridgeLearnAction", automation.Action) RFBridgeStartAdvancedSniffingAction = rf_bridge_ns.class_( - 'RFBridgeStartAdvancedSniffingAction', automation.Action) + "RFBridgeStartAdvancedSniffingAction", automation.Action +) RFBridgeStopAdvancedSniffingAction = rf_bridge_ns.class_( - 'RFBridgeStopAdvancedSniffingAction', automation.Action) + "RFBridgeStopAdvancedSniffingAction", automation.Action +) -RFBridgeSendRawAction = rf_bridge_ns.class_('RFBridgeSendRawAction', automation.Action) +RFBridgeSendRawAction = rf_bridge_ns.class_("RFBridgeSendRawAction", automation.Action) -CONF_ON_CODE_RECEIVED = 'on_code_received' -CONF_ON_ADVANCED_CODE_RECEIVED = 'on_advanced_code_received' +CONF_ON_CODE_RECEIVED = "on_code_received" +CONF_ON_ADVANCED_CODE_RECEIVED = "on_advanced_code_received" -CONFIG_SCHEMA = cv.All(cv.Schema({ - cv.GenerateID(): cv.declare_id(RFBridgeComponent), - cv.Optional(CONF_ON_CODE_RECEIVED): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(RFBridgeReceivedCodeTrigger), - }), - cv.Optional(CONF_ON_ADVANCED_CODE_RECEIVED): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(RFBridgeReceivedAdvancedCodeTrigger), - }), -}).extend(uart.UART_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA)) +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(RFBridgeComponent), + cv.Optional(CONF_ON_CODE_RECEIVED): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( + RFBridgeReceivedCodeTrigger + ), + } + ), + cv.Optional(CONF_ON_ADVANCED_CODE_RECEIVED): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( + RFBridgeReceivedAdvancedCodeTrigger + ), + } + ), + } + ) + .extend(uart.UART_DEVICE_SCHEMA) + .extend(cv.COMPONENT_SCHEMA) +) def to_code(config): @@ -64,26 +86,29 @@ def to_code(config): for conf in config.get(CONF_ON_CODE_RECEIVED, []): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) - yield automation.build_automation(trigger, [(RFBridgeData, 'data')], conf) + yield automation.build_automation(trigger, [(RFBridgeData, "data")], conf) for conf in config.get(CONF_ON_ADVANCED_CODE_RECEIVED, []): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) yield automation.build_automation( - trigger, [(RFBridgeAdvancedData, 'data')], conf + trigger, [(RFBridgeAdvancedData, "data")], conf ) -RFBRIDGE_SEND_CODE_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.use_id(RFBridgeComponent), - cv.Required(CONF_SYNC): cv.templatable(cv.hex_uint16_t), - cv.Required(CONF_LOW): cv.templatable(cv.hex_uint16_t), - cv.Required(CONF_HIGH): cv.templatable(cv.hex_uint16_t), - cv.Required(CONF_CODE): cv.templatable(cv.hex_uint32_t) -}) +RFBRIDGE_SEND_CODE_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.use_id(RFBridgeComponent), + cv.Required(CONF_SYNC): cv.templatable(cv.hex_uint16_t), + cv.Required(CONF_LOW): cv.templatable(cv.hex_uint16_t), + cv.Required(CONF_HIGH): cv.templatable(cv.hex_uint16_t), + cv.Required(CONF_CODE): cv.templatable(cv.hex_uint32_t), + } +) -@automation.register_action('rf_bridge.send_code', RFBridgeSendCodeAction, - RFBRIDGE_SEND_CODE_SCHEMA) +@automation.register_action( + "rf_bridge.send_code", RFBridgeSendCodeAction, RFBRIDGE_SEND_CODE_SCHEMA +) def rf_bridge_send_code_to_code(config, action_id, template_args, args): paren = yield cg.get_variable(config[CONF_ID]) var = cg.new_Pvariable(action_id, template_args, paren) @@ -98,12 +123,10 @@ def rf_bridge_send_code_to_code(config, action_id, template_args, args): yield var -RFBRIDGE_ID_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.use_id(RFBridgeComponent) -}) +RFBRIDGE_ID_SCHEMA = cv.Schema({cv.GenerateID(): cv.use_id(RFBridgeComponent)}) -@automation.register_action('rf_bridge.learn', RFBridgeLearnAction, RFBRIDGE_ID_SCHEMA) +@automation.register_action("rf_bridge.learn", RFBridgeLearnAction, RFBRIDGE_ID_SCHEMA) def rf_bridge_learnx_to_code(config, action_id, template_args, args): paren = yield cg.get_variable(config[CONF_ID]) var = cg.new_Pvariable(action_id, template_args, paren) @@ -111,7 +134,7 @@ def rf_bridge_learnx_to_code(config, action_id, template_args, args): @automation.register_action( - 'rf_bridge.start_advanced_sniffing', + "rf_bridge.start_advanced_sniffing", RFBridgeStartAdvancedSniffingAction, RFBRIDGE_ID_SCHEMA, ) @@ -122,7 +145,7 @@ def rf_bridge_start_advanced_sniffing_to_code(config, action_id, template_args, @automation.register_action( - 'rf_bridge.stop_advanced_sniffing', + "rf_bridge.stop_advanced_sniffing", RFBridgeStopAdvancedSniffingAction, RFBRIDGE_ID_SCHEMA, ) @@ -132,18 +155,20 @@ def rf_bridge_stop_advanced_sniffing_to_code(config, action_id, template_args, a yield var -RFBRIDGE_SEND_ADVANCED_CODE_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.use_id(RFBridgeComponent), - cv.Required(CONF_LENGTH): cv.templatable(cv.hex_uint8_t), - cv.Required(CONF_PROTOCOL): cv.templatable(cv.hex_uint8_t), - cv.Required(CONF_CODE): cv.templatable(cv.string), -}) +RFBRIDGE_SEND_ADVANCED_CODE_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.use_id(RFBridgeComponent), + cv.Required(CONF_LENGTH): cv.templatable(cv.hex_uint8_t), + cv.Required(CONF_PROTOCOL): cv.templatable(cv.hex_uint8_t), + cv.Required(CONF_CODE): cv.templatable(cv.string), + } +) @automation.register_action( - 'rf_bridge.send_advanced_code', + "rf_bridge.send_advanced_code", RFBridgeSendAdvancedCodeAction, - RFBRIDGE_SEND_ADVANCED_CODE_SCHEMA + RFBRIDGE_SEND_ADVANCED_CODE_SCHEMA, ) def rf_bridge_send_advanced_code_to_code(config, action_id, template_args, args): paren = yield cg.get_variable(config[CONF_ID]) @@ -166,9 +191,7 @@ RFBRIDGE_SEND_RAW_SCHEMA = cv.Schema( @automation.register_action( - 'rf_bridge.send_raw', - RFBridgeSendRawAction, - RFBRIDGE_SEND_RAW_SCHEMA + "rf_bridge.send_raw", RFBridgeSendRawAction, RFBRIDGE_SEND_RAW_SCHEMA ) def rf_bridge_send_raw_to_code(config, action_id, template_args, args): paren = yield cg.get_variable(config[CONF_ID]) diff --git a/esphome/components/rgb/light.py b/esphome/components/rgb/light.py index 6bece17664..33e381d59e 100644 --- a/esphome/components/rgb/light.py +++ b/esphome/components/rgb/light.py @@ -3,15 +3,17 @@ import esphome.config_validation as cv from esphome.components import light, output from esphome.const import CONF_BLUE, CONF_GREEN, CONF_RED, CONF_OUTPUT_ID -rgb_ns = cg.esphome_ns.namespace('rgb') -RGBLightOutput = rgb_ns.class_('RGBLightOutput', light.LightOutput) +rgb_ns = cg.esphome_ns.namespace("rgb") +RGBLightOutput = rgb_ns.class_("RGBLightOutput", light.LightOutput) -CONFIG_SCHEMA = light.RGB_LIGHT_SCHEMA.extend({ - cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(RGBLightOutput), - cv.Required(CONF_RED): cv.use_id(output.FloatOutput), - cv.Required(CONF_GREEN): cv.use_id(output.FloatOutput), - cv.Required(CONF_BLUE): cv.use_id(output.FloatOutput), -}) +CONFIG_SCHEMA = light.RGB_LIGHT_SCHEMA.extend( + { + cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(RGBLightOutput), + cv.Required(CONF_RED): cv.use_id(output.FloatOutput), + cv.Required(CONF_GREEN): cv.use_id(output.FloatOutput), + cv.Required(CONF_BLUE): cv.use_id(output.FloatOutput), + } +) def to_code(config): diff --git a/esphome/components/rgbw/light.py b/esphome/components/rgbw/light.py index ca31a8229d..a922a7de37 100644 --- a/esphome/components/rgbw/light.py +++ b/esphome/components/rgbw/light.py @@ -3,18 +3,20 @@ import esphome.config_validation as cv from esphome.components import light, output from esphome.const import CONF_BLUE, CONF_GREEN, CONF_RED, CONF_OUTPUT_ID, CONF_WHITE -rgbw_ns = cg.esphome_ns.namespace('rgbw') -RGBWLightOutput = rgbw_ns.class_('RGBWLightOutput', light.LightOutput) -CONF_COLOR_INTERLOCK = 'color_interlock' +rgbw_ns = cg.esphome_ns.namespace("rgbw") +RGBWLightOutput = rgbw_ns.class_("RGBWLightOutput", light.LightOutput) +CONF_COLOR_INTERLOCK = "color_interlock" -CONFIG_SCHEMA = light.RGB_LIGHT_SCHEMA.extend({ - cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(RGBWLightOutput), - cv.Required(CONF_RED): cv.use_id(output.FloatOutput), - cv.Required(CONF_GREEN): cv.use_id(output.FloatOutput), - cv.Required(CONF_BLUE): cv.use_id(output.FloatOutput), - cv.Required(CONF_WHITE): cv.use_id(output.FloatOutput), - cv.Optional(CONF_COLOR_INTERLOCK, default=False): cv.boolean, -}) +CONFIG_SCHEMA = light.RGB_LIGHT_SCHEMA.extend( + { + cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(RGBWLightOutput), + cv.Required(CONF_RED): cv.use_id(output.FloatOutput), + cv.Required(CONF_GREEN): cv.use_id(output.FloatOutput), + cv.Required(CONF_BLUE): cv.use_id(output.FloatOutput), + cv.Required(CONF_WHITE): cv.use_id(output.FloatOutput), + cv.Optional(CONF_COLOR_INTERLOCK, default=False): cv.boolean, + } +) def to_code(config): diff --git a/esphome/components/rgbww/light.py b/esphome/components/rgbww/light.py index 1513a684ea..d7d0d7fb15 100644 --- a/esphome/components/rgbww/light.py +++ b/esphome/components/rgbww/light.py @@ -1,28 +1,37 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import light, output -from esphome.const import CONF_BLUE, CONF_GREEN, CONF_RED, CONF_OUTPUT_ID, CONF_COLD_WHITE, \ - CONF_WARM_WHITE, CONF_COLD_WHITE_COLOR_TEMPERATURE, \ - CONF_WARM_WHITE_COLOR_TEMPERATURE +from esphome.const import ( + CONF_BLUE, + CONF_GREEN, + CONF_RED, + CONF_OUTPUT_ID, + CONF_COLD_WHITE, + CONF_WARM_WHITE, + CONF_COLD_WHITE_COLOR_TEMPERATURE, + CONF_WARM_WHITE_COLOR_TEMPERATURE, +) -rgbww_ns = cg.esphome_ns.namespace('rgbww') -RGBWWLightOutput = rgbww_ns.class_('RGBWWLightOutput', light.LightOutput) +rgbww_ns = cg.esphome_ns.namespace("rgbww") +RGBWWLightOutput = rgbww_ns.class_("RGBWWLightOutput", light.LightOutput) -CONF_CONSTANT_BRIGHTNESS = 'constant_brightness' -CONF_COLOR_INTERLOCK = 'color_interlock' +CONF_CONSTANT_BRIGHTNESS = "constant_brightness" +CONF_COLOR_INTERLOCK = "color_interlock" -CONFIG_SCHEMA = light.RGB_LIGHT_SCHEMA.extend({ - cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(RGBWWLightOutput), - cv.Required(CONF_RED): cv.use_id(output.FloatOutput), - cv.Required(CONF_GREEN): cv.use_id(output.FloatOutput), - cv.Required(CONF_BLUE): cv.use_id(output.FloatOutput), - cv.Required(CONF_COLD_WHITE): cv.use_id(output.FloatOutput), - cv.Required(CONF_WARM_WHITE): cv.use_id(output.FloatOutput), - cv.Required(CONF_COLD_WHITE_COLOR_TEMPERATURE): cv.color_temperature, - cv.Required(CONF_WARM_WHITE_COLOR_TEMPERATURE): cv.color_temperature, - cv.Optional(CONF_CONSTANT_BRIGHTNESS, default=False): cv.boolean, - cv.Optional(CONF_COLOR_INTERLOCK, default=False): cv.boolean, -}) +CONFIG_SCHEMA = light.RGB_LIGHT_SCHEMA.extend( + { + cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(RGBWWLightOutput), + cv.Required(CONF_RED): cv.use_id(output.FloatOutput), + cv.Required(CONF_GREEN): cv.use_id(output.FloatOutput), + cv.Required(CONF_BLUE): cv.use_id(output.FloatOutput), + cv.Required(CONF_COLD_WHITE): cv.use_id(output.FloatOutput), + cv.Required(CONF_WARM_WHITE): cv.use_id(output.FloatOutput), + cv.Required(CONF_COLD_WHITE_COLOR_TEMPERATURE): cv.color_temperature, + cv.Required(CONF_WARM_WHITE_COLOR_TEMPERATURE): cv.color_temperature, + cv.Optional(CONF_CONSTANT_BRIGHTNESS, default=False): cv.boolean, + cv.Optional(CONF_COLOR_INTERLOCK, default=False): cv.boolean, + } +) def to_code(config): diff --git a/esphome/components/rotary_encoder/sensor.py b/esphome/components/rotary_encoder/sensor.py index 4064cdea81..bcc4a5c930 100644 --- a/esphome/components/rotary_encoder/sensor.py +++ b/esphome/components/rotary_encoder/sensor.py @@ -2,30 +2,45 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins, automation from esphome.components import sensor -from esphome.const import CONF_ID, CONF_RESOLUTION, CONF_MIN_VALUE, CONF_MAX_VALUE, \ - DEVICE_CLASS_EMPTY, UNIT_STEPS, ICON_ROTATE_RIGHT, CONF_VALUE, CONF_PIN_A, CONF_PIN_B, \ - CONF_TRIGGER_ID +from esphome.const import ( + CONF_ID, + CONF_RESOLUTION, + CONF_MIN_VALUE, + CONF_MAX_VALUE, + DEVICE_CLASS_EMPTY, + UNIT_STEPS, + ICON_ROTATE_RIGHT, + CONF_VALUE, + CONF_PIN_A, + CONF_PIN_B, + CONF_TRIGGER_ID, +) -rotary_encoder_ns = cg.esphome_ns.namespace('rotary_encoder') -RotaryEncoderResolution = rotary_encoder_ns.enum('RotaryEncoderResolution') +rotary_encoder_ns = cg.esphome_ns.namespace("rotary_encoder") +RotaryEncoderResolution = rotary_encoder_ns.enum("RotaryEncoderResolution") RESOLUTIONS = { 1: RotaryEncoderResolution.ROTARY_ENCODER_1_PULSE_PER_CYCLE, 2: RotaryEncoderResolution.ROTARY_ENCODER_2_PULSES_PER_CYCLE, 4: RotaryEncoderResolution.ROTARY_ENCODER_4_PULSES_PER_CYCLE, } -CONF_PIN_RESET = 'pin_reset' -CONF_ON_CLOCKWISE = 'on_clockwise' -CONF_ON_ANTICLOCKWISE = 'on_anticlockwise' +CONF_PIN_RESET = "pin_reset" +CONF_ON_CLOCKWISE = "on_clockwise" +CONF_ON_ANTICLOCKWISE = "on_anticlockwise" -RotaryEncoderSensor = rotary_encoder_ns.class_('RotaryEncoderSensor', sensor.Sensor, cg.Component) -RotaryEncoderSetValueAction = rotary_encoder_ns.class_('RotaryEncoderSetValueAction', - automation.Action) +RotaryEncoderSensor = rotary_encoder_ns.class_( + "RotaryEncoderSensor", sensor.Sensor, cg.Component +) +RotaryEncoderSetValueAction = rotary_encoder_ns.class_( + "RotaryEncoderSetValueAction", automation.Action +) -RotaryEncoderClockwiseTrigger = rotary_encoder_ns.class_('RotaryEncoderClockwiseTrigger', - automation.Trigger) -RotaryEncoderAnticlockwiseTrigger = rotary_encoder_ns.class_('RotaryEncoderAnticlockwiseTrigger', - automation.Trigger) +RotaryEncoderClockwiseTrigger = rotary_encoder_ns.class_( + "RotaryEncoderClockwiseTrigger", automation.Trigger +) +RotaryEncoderAnticlockwiseTrigger = rotary_encoder_ns.class_( + "RotaryEncoderAnticlockwiseTrigger", automation.Trigger +) def validate_min_max_value(config): @@ -33,30 +48,47 @@ def validate_min_max_value(config): min_val = config[CONF_MIN_VALUE] max_val = config[CONF_MAX_VALUE] if min_val >= max_val: - raise cv.Invalid("Max value {} must be smaller than min value {}" - "".format(max_val, min_val)) + raise cv.Invalid( + "Max value {} must be smaller than min value {}" + "".format(max_val, min_val) + ) return config -CONFIG_SCHEMA = cv.All(sensor.sensor_schema( - UNIT_STEPS, ICON_ROTATE_RIGHT, 0, DEVICE_CLASS_EMPTY -).extend({ - cv.GenerateID(): cv.declare_id(RotaryEncoderSensor), - cv.Required(CONF_PIN_A): cv.All(pins.internal_gpio_input_pin_schema, - pins.validate_has_interrupt), - cv.Required(CONF_PIN_B): cv.All(pins.internal_gpio_input_pin_schema, - pins.validate_has_interrupt), - cv.Optional(CONF_PIN_RESET): pins.internal_gpio_output_pin_schema, - cv.Optional(CONF_RESOLUTION, default=1): cv.enum(RESOLUTIONS, int=True), - cv.Optional(CONF_MIN_VALUE): cv.int_, - cv.Optional(CONF_MAX_VALUE): cv.int_, - cv.Optional(CONF_ON_CLOCKWISE): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(RotaryEncoderClockwiseTrigger), - }), - cv.Optional(CONF_ON_ANTICLOCKWISE): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(RotaryEncoderAnticlockwiseTrigger), - }), -}).extend(cv.COMPONENT_SCHEMA), validate_min_max_value) +CONFIG_SCHEMA = cv.All( + sensor.sensor_schema(UNIT_STEPS, ICON_ROTATE_RIGHT, 0, DEVICE_CLASS_EMPTY) + .extend( + { + cv.GenerateID(): cv.declare_id(RotaryEncoderSensor), + cv.Required(CONF_PIN_A): cv.All( + pins.internal_gpio_input_pin_schema, pins.validate_has_interrupt + ), + cv.Required(CONF_PIN_B): cv.All( + pins.internal_gpio_input_pin_schema, pins.validate_has_interrupt + ), + cv.Optional(CONF_PIN_RESET): pins.internal_gpio_output_pin_schema, + cv.Optional(CONF_RESOLUTION, default=1): cv.enum(RESOLUTIONS, int=True), + cv.Optional(CONF_MIN_VALUE): cv.int_, + cv.Optional(CONF_MAX_VALUE): cv.int_, + cv.Optional(CONF_ON_CLOCKWISE): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( + RotaryEncoderClockwiseTrigger + ), + } + ), + cv.Optional(CONF_ON_ANTICLOCKWISE): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( + RotaryEncoderAnticlockwiseTrigger + ), + } + ), + } + ) + .extend(cv.COMPONENT_SCHEMA), + validate_min_max_value, +) def to_code(config): @@ -85,11 +117,16 @@ def to_code(config): yield automation.build_automation(trigger, [], conf) -@automation.register_action('sensor.rotary_encoder.set_value', RotaryEncoderSetValueAction, - cv.Schema({ - cv.Required(CONF_ID): cv.use_id(sensor.Sensor), - cv.Required(CONF_VALUE): cv.templatable(cv.int_), - })) +@automation.register_action( + "sensor.rotary_encoder.set_value", + RotaryEncoderSetValueAction, + cv.Schema( + { + cv.Required(CONF_ID): cv.use_id(sensor.Sensor), + cv.Required(CONF_VALUE): cv.templatable(cv.int_), + } + ), +) def sensor_template_publish_to_code(config, action_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) var = cg.new_Pvariable(action_id, template_arg, paren) diff --git a/esphome/components/rtttl/__init__.py b/esphome/components/rtttl/__init__.py index a276f7cb86..97b7405c63 100644 --- a/esphome/components/rtttl/__init__.py +++ b/esphome/components/rtttl/__init__.py @@ -4,28 +4,33 @@ from esphome import automation from esphome.components.output import FloatOutput from esphome.const import CONF_ID, CONF_OUTPUT, CONF_TRIGGER_ID -CODEOWNERS = ['@glmnet'] -CONF_RTTTL = 'rtttl' -CONF_ON_FINISHED_PLAYBACK = 'on_finished_playback' +CODEOWNERS = ["@glmnet"] +CONF_RTTTL = "rtttl" +CONF_ON_FINISHED_PLAYBACK = "on_finished_playback" -rtttl_ns = cg.esphome_ns.namespace('rtttl') +rtttl_ns = cg.esphome_ns.namespace("rtttl") -Rtttl = rtttl_ns .class_('Rtttl', cg.Component) -PlayAction = rtttl_ns.class_('PlayAction', automation.Action) -StopAction = rtttl_ns.class_('StopAction', automation.Action) -FinishedPlaybackTrigger = rtttl_ns.class_('FinishedPlaybackTrigger', - automation.Trigger.template()) -IsPlayingCondition = rtttl_ns.class_('IsPlayingCondition', automation.Condition) +Rtttl = rtttl_ns.class_("Rtttl", cg.Component) +PlayAction = rtttl_ns.class_("PlayAction", automation.Action) +StopAction = rtttl_ns.class_("StopAction", automation.Action) +FinishedPlaybackTrigger = rtttl_ns.class_( + "FinishedPlaybackTrigger", automation.Trigger.template() +) +IsPlayingCondition = rtttl_ns.class_("IsPlayingCondition", automation.Condition) MULTI_CONF = True -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(CONF_ID): cv.declare_id(Rtttl), - cv.Required(CONF_OUTPUT): cv.use_id(FloatOutput), - cv.Optional(CONF_ON_FINISHED_PLAYBACK): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(FinishedPlaybackTrigger), - }), -}).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(CONF_ID): cv.declare_id(Rtttl), + cv.Required(CONF_OUTPUT): cv.use_id(FloatOutput), + cv.Optional(CONF_ON_FINISHED_PLAYBACK): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(FinishedPlaybackTrigger), + } + ), + } +).extend(cv.COMPONENT_SCHEMA) def to_code(config): @@ -40,10 +45,17 @@ def to_code(config): yield automation.build_automation(trigger, [], conf) -@automation.register_action('rtttl.play', PlayAction, cv.maybe_simple_value({ - cv.GenerateID(CONF_ID): cv.use_id(Rtttl), - cv.Required(CONF_RTTTL): cv.templatable(cv.string) -}, key=CONF_RTTTL)) +@automation.register_action( + "rtttl.play", + PlayAction, + cv.maybe_simple_value( + { + cv.GenerateID(CONF_ID): cv.use_id(Rtttl), + cv.Required(CONF_RTTTL): cv.templatable(cv.string), + }, + key=CONF_RTTTL, + ), +) def rtttl_play_to_code(config, action_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) var = cg.new_Pvariable(action_id, template_arg, paren) @@ -52,18 +64,30 @@ def rtttl_play_to_code(config, action_id, template_arg, args): yield var -@automation.register_action('rtttl.stop', StopAction, cv.Schema({ - cv.GenerateID(): cv.use_id(Rtttl), -})) +@automation.register_action( + "rtttl.stop", + StopAction, + cv.Schema( + { + cv.GenerateID(): cv.use_id(Rtttl), + } + ), +) def rtttl_stop_to_code(config, action_id, template_arg, args): var = cg.new_Pvariable(action_id, template_arg) yield cg.register_parented(var, config[CONF_ID]) yield var -@automation.register_condition('rtttl.is_playing', IsPlayingCondition, cv.Schema({ - cv.GenerateID(): cv.use_id(Rtttl), -})) +@automation.register_condition( + "rtttl.is_playing", + IsPlayingCondition, + cv.Schema( + { + cv.GenerateID(): cv.use_id(Rtttl), + } + ), +) def rtttl_is_playing_to_code(config, condition_id, template_arg, args): var = cg.new_Pvariable(condition_id, template_arg) yield cg.register_parented(var, config[CONF_ID]) diff --git a/esphome/components/ruuvi_ble/__init__.py b/esphome/components/ruuvi_ble/__init__.py index 05ba008dd0..74aec457bb 100644 --- a/esphome/components/ruuvi_ble/__init__.py +++ b/esphome/components/ruuvi_ble/__init__.py @@ -3,14 +3,18 @@ import esphome.config_validation as cv from esphome.components import esp32_ble_tracker from esphome.const import CONF_ID -DEPENDENCIES = ['esp32_ble_tracker'] +DEPENDENCIES = ["esp32_ble_tracker"] -ruuvi_ble_ns = cg.esphome_ns.namespace('ruuvi_ble') -RuuviListener = ruuvi_ble_ns.class_('RuuviListener', esp32_ble_tracker.ESPBTDeviceListener) +ruuvi_ble_ns = cg.esphome_ns.namespace("ruuvi_ble") +RuuviListener = ruuvi_ble_ns.class_( + "RuuviListener", esp32_ble_tracker.ESPBTDeviceListener +) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(RuuviListener), -}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA) +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(RuuviListener), + } +).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA) def to_code(config): diff --git a/esphome/components/ruuvitag/sensor.py b/esphome/components/ruuvitag/sensor.py index e992652266..2bde7b485c 100644 --- a/esphome/components/ruuvitag/sensor.py +++ b/esphome/components/ruuvitag/sensor.py @@ -1,48 +1,92 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, esp32_ble_tracker -from esphome.const import CONF_HUMIDITY, CONF_MAC_ADDRESS, CONF_TEMPERATURE, CONF_PRESSURE, \ - CONF_ACCELERATION, CONF_ACCELERATION_X, CONF_ACCELERATION_Y, CONF_ACCELERATION_Z, \ - CONF_BATTERY_VOLTAGE, CONF_TX_POWER, CONF_MEASUREMENT_SEQUENCE_NUMBER, CONF_MOVEMENT_COUNTER, \ - DEVICE_CLASS_EMPTY, DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_PRESSURE, \ - DEVICE_CLASS_SIGNAL_STRENGTH, DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_VOLTAGE, ICON_EMPTY, \ - UNIT_CELSIUS, UNIT_PERCENT, UNIT_VOLT, UNIT_HECTOPASCAL, UNIT_G, UNIT_DECIBEL_MILLIWATT, \ - UNIT_EMPTY, ICON_GAUGE, ICON_ACCELERATION, ICON_ACCELERATION_X, ICON_ACCELERATION_Y, \ - ICON_ACCELERATION_Z, CONF_ID +from esphome.const import ( + CONF_HUMIDITY, + CONF_MAC_ADDRESS, + CONF_TEMPERATURE, + CONF_PRESSURE, + CONF_ACCELERATION, + CONF_ACCELERATION_X, + CONF_ACCELERATION_Y, + CONF_ACCELERATION_Z, + CONF_BATTERY_VOLTAGE, + CONF_TX_POWER, + CONF_MEASUREMENT_SEQUENCE_NUMBER, + CONF_MOVEMENT_COUNTER, + DEVICE_CLASS_EMPTY, + DEVICE_CLASS_HUMIDITY, + DEVICE_CLASS_PRESSURE, + DEVICE_CLASS_SIGNAL_STRENGTH, + DEVICE_CLASS_TEMPERATURE, + DEVICE_CLASS_VOLTAGE, + ICON_EMPTY, + UNIT_CELSIUS, + UNIT_PERCENT, + UNIT_VOLT, + UNIT_HECTOPASCAL, + UNIT_G, + UNIT_DECIBEL_MILLIWATT, + UNIT_EMPTY, + ICON_GAUGE, + ICON_ACCELERATION, + ICON_ACCELERATION_X, + ICON_ACCELERATION_Y, + ICON_ACCELERATION_Z, + CONF_ID, +) -DEPENDENCIES = ['esp32_ble_tracker'] -AUTO_LOAD = ['ruuvi_ble'] +DEPENDENCIES = ["esp32_ble_tracker"] +AUTO_LOAD = ["ruuvi_ble"] -ruuvitag_ns = cg.esphome_ns.namespace('ruuvitag') +ruuvitag_ns = cg.esphome_ns.namespace("ruuvitag") RuuviTag = ruuvitag_ns.class_( - 'RuuviTag', esp32_ble_tracker.ESPBTDeviceListener, cg.Component) + "RuuviTag", esp32_ble_tracker.ESPBTDeviceListener, cg.Component +) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(RuuviTag), - cv.Required(CONF_MAC_ADDRESS): cv.mac_address, - cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 2, - DEVICE_CLASS_TEMPERATURE), - cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 2, - DEVICE_CLASS_HUMIDITY), - cv.Optional(CONF_PRESSURE): sensor.sensor_schema(UNIT_HECTOPASCAL, ICON_EMPTY, 2, - DEVICE_CLASS_PRESSURE), - cv.Optional(CONF_ACCELERATION): sensor.sensor_schema(UNIT_G, ICON_ACCELERATION, 3, - DEVICE_CLASS_EMPTY), - cv.Optional(CONF_ACCELERATION_X): sensor.sensor_schema(UNIT_G, ICON_ACCELERATION_X, 3, - DEVICE_CLASS_EMPTY), - cv.Optional(CONF_ACCELERATION_Y): sensor.sensor_schema(UNIT_G, ICON_ACCELERATION_Y, 3, - DEVICE_CLASS_EMPTY), - cv.Optional(CONF_ACCELERATION_Z): sensor.sensor_schema(UNIT_G, ICON_ACCELERATION_Z, 3, - DEVICE_CLASS_EMPTY), - cv.Optional(CONF_BATTERY_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_EMPTY, 3, - DEVICE_CLASS_VOLTAGE), - cv.Optional(CONF_TX_POWER): sensor.sensor_schema(UNIT_DECIBEL_MILLIWATT, ICON_EMPTY, 0, - DEVICE_CLASS_SIGNAL_STRENGTH), - cv.Optional(CONF_MOVEMENT_COUNTER): sensor.sensor_schema(UNIT_EMPTY, ICON_GAUGE, 0, - DEVICE_CLASS_EMPTY), - cv.Optional(CONF_MEASUREMENT_SEQUENCE_NUMBER): sensor.sensor_schema(UNIT_EMPTY, ICON_GAUGE, 0, - DEVICE_CLASS_EMPTY), -}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(RuuviTag), + cv.Required(CONF_MAC_ADDRESS): cv.mac_address, + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( + UNIT_CELSIUS, ICON_EMPTY, 2, DEVICE_CLASS_TEMPERATURE + ), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema( + UNIT_PERCENT, ICON_EMPTY, 2, DEVICE_CLASS_HUMIDITY + ), + cv.Optional(CONF_PRESSURE): sensor.sensor_schema( + UNIT_HECTOPASCAL, ICON_EMPTY, 2, DEVICE_CLASS_PRESSURE + ), + cv.Optional(CONF_ACCELERATION): sensor.sensor_schema( + UNIT_G, ICON_ACCELERATION, 3, DEVICE_CLASS_EMPTY + ), + cv.Optional(CONF_ACCELERATION_X): sensor.sensor_schema( + UNIT_G, ICON_ACCELERATION_X, 3, DEVICE_CLASS_EMPTY + ), + cv.Optional(CONF_ACCELERATION_Y): sensor.sensor_schema( + UNIT_G, ICON_ACCELERATION_Y, 3, DEVICE_CLASS_EMPTY + ), + cv.Optional(CONF_ACCELERATION_Z): sensor.sensor_schema( + UNIT_G, ICON_ACCELERATION_Z, 3, DEVICE_CLASS_EMPTY + ), + cv.Optional(CONF_BATTERY_VOLTAGE): sensor.sensor_schema( + UNIT_VOLT, ICON_EMPTY, 3, DEVICE_CLASS_VOLTAGE + ), + cv.Optional(CONF_TX_POWER): sensor.sensor_schema( + UNIT_DECIBEL_MILLIWATT, ICON_EMPTY, 0, DEVICE_CLASS_SIGNAL_STRENGTH + ), + cv.Optional(CONF_MOVEMENT_COUNTER): sensor.sensor_schema( + UNIT_EMPTY, ICON_GAUGE, 0, DEVICE_CLASS_EMPTY + ), + cv.Optional(CONF_MEASUREMENT_SEQUENCE_NUMBER): sensor.sensor_schema( + UNIT_EMPTY, ICON_GAUGE, 0, DEVICE_CLASS_EMPTY + ), + } + ) + .extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA) + .extend(cv.COMPONENT_SCHEMA) +) def to_code(config): diff --git a/esphome/components/scd30/sensor.py b/esphome/components/scd30/sensor.py index 5cbc55fbd6..aa3e5b65ba 100644 --- a/esphome/components/scd30/sensor.py +++ b/esphome/components/scd30/sensor.py @@ -2,40 +2,61 @@ import re import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import CONF_ID, DEVICE_CLASS_EMPTY, CONF_HUMIDITY, CONF_TEMPERATURE, CONF_CO2, \ - DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_TEMPERATURE, ICON_EMPTY, UNIT_PARTS_PER_MILLION, \ - ICON_MOLECULE_CO2, UNIT_CELSIUS, UNIT_PERCENT +from esphome.const import ( + CONF_ID, + DEVICE_CLASS_EMPTY, + CONF_HUMIDITY, + CONF_TEMPERATURE, + CONF_CO2, + DEVICE_CLASS_HUMIDITY, + DEVICE_CLASS_TEMPERATURE, + ICON_EMPTY, + UNIT_PARTS_PER_MILLION, + ICON_MOLECULE_CO2, + UNIT_CELSIUS, + UNIT_PERCENT, +) -DEPENDENCIES = ['i2c'] +DEPENDENCIES = ["i2c"] -scd30_ns = cg.esphome_ns.namespace('scd30') -SCD30Component = scd30_ns.class_('SCD30Component', cg.PollingComponent, i2c.I2CDevice) +scd30_ns = cg.esphome_ns.namespace("scd30") +SCD30Component = scd30_ns.class_("SCD30Component", cg.PollingComponent, i2c.I2CDevice) -CONF_AUTOMATIC_SELF_CALIBRATION = 'automatic_self_calibration' -CONF_ALTITUDE_COMPENSATION = 'altitude_compensation' -CONF_AMBIENT_PRESSURE_COMPENSATION = 'ambient_pressure_compensation' -CONF_TEMPERATURE_OFFSET = 'temperature_offset' +CONF_AUTOMATIC_SELF_CALIBRATION = "automatic_self_calibration" +CONF_ALTITUDE_COMPENSATION = "altitude_compensation" +CONF_AMBIENT_PRESSURE_COMPENSATION = "ambient_pressure_compensation" +CONF_TEMPERATURE_OFFSET = "temperature_offset" def remove_altitude_suffix(value): - return re.sub(r"\s*(?:m(?:\s+a\.s\.l)?)|(?:MAM?SL)$", '', value) + return re.sub(r"\s*(?:m(?:\s+a\.s\.l)?)|(?:MAM?SL)$", "", value) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(SCD30Component), - cv.Optional(CONF_CO2): sensor.sensor_schema(UNIT_PARTS_PER_MILLION, - ICON_MOLECULE_CO2, 0, DEVICE_CLASS_EMPTY), - cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, - DEVICE_CLASS_TEMPERATURE), - cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 1, - DEVICE_CLASS_HUMIDITY), - cv.Optional(CONF_AUTOMATIC_SELF_CALIBRATION, default=True): cv.boolean, - cv.Optional(CONF_ALTITUDE_COMPENSATION): cv.All(remove_altitude_suffix, - cv.int_range(min=0, max=0xFFFF, - max_included=False)), - cv.Optional(CONF_AMBIENT_PRESSURE_COMPENSATION, default=0): cv.pressure, - cv.Optional(CONF_TEMPERATURE_OFFSET): cv.temperature, -}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x61)) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(SCD30Component), + cv.Optional(CONF_CO2): sensor.sensor_schema( + UNIT_PARTS_PER_MILLION, ICON_MOLECULE_CO2, 0, DEVICE_CLASS_EMPTY + ), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( + UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE + ), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema( + UNIT_PERCENT, ICON_EMPTY, 1, DEVICE_CLASS_HUMIDITY + ), + cv.Optional(CONF_AUTOMATIC_SELF_CALIBRATION, default=True): cv.boolean, + cv.Optional(CONF_ALTITUDE_COMPENSATION): cv.All( + remove_altitude_suffix, + cv.int_range(min=0, max=0xFFFF, max_included=False), + ), + cv.Optional(CONF_AMBIENT_PRESSURE_COMPENSATION, default=0): cv.pressure, + cv.Optional(CONF_TEMPERATURE_OFFSET): cv.temperature, + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x61)) +) def to_code(config): @@ -48,7 +69,11 @@ def to_code(config): cg.add(var.set_altitude_compensation(config[CONF_ALTITUDE_COMPENSATION])) if CONF_AMBIENT_PRESSURE_COMPENSATION in config: - cg.add(var.set_ambient_pressure_compensation(config[CONF_AMBIENT_PRESSURE_COMPENSATION])) + cg.add( + var.set_ambient_pressure_compensation( + config[CONF_AMBIENT_PRESSURE_COMPENSATION] + ) + ) if CONF_TEMPERATURE_OFFSET in config: cg.add(var.set_temperature_offset(config[CONF_TEMPERATURE_OFFSET])) diff --git a/esphome/components/script/__init__.py b/esphome/components/script/__init__.py index cdb334446a..45b01778da 100644 --- a/esphome/components/script/__init__.py +++ b/esphome/components/script/__init__.py @@ -4,23 +4,23 @@ from esphome import automation from esphome.automation import maybe_simple_id from esphome.const import CONF_ID, CONF_MODE -CODEOWNERS = ['@esphome/core'] -script_ns = cg.esphome_ns.namespace('script') -Script = script_ns.class_('Script', automation.Trigger.template()) -ScriptExecuteAction = script_ns.class_('ScriptExecuteAction', automation.Action) -ScriptStopAction = script_ns.class_('ScriptStopAction', automation.Action) -ScriptWaitAction = script_ns.class_('ScriptWaitAction', automation.Action, cg.Component) -IsRunningCondition = script_ns.class_('IsRunningCondition', automation.Condition) -SingleScript = script_ns.class_('SingleScript', Script) -RestartScript = script_ns.class_('RestartScript', Script) -QueueingScript = script_ns.class_('QueueingScript', Script, cg.Component) -ParallelScript = script_ns.class_('ParallelScript', Script) +CODEOWNERS = ["@esphome/core"] +script_ns = cg.esphome_ns.namespace("script") +Script = script_ns.class_("Script", automation.Trigger.template()) +ScriptExecuteAction = script_ns.class_("ScriptExecuteAction", automation.Action) +ScriptStopAction = script_ns.class_("ScriptStopAction", automation.Action) +ScriptWaitAction = script_ns.class_("ScriptWaitAction", automation.Action, cg.Component) +IsRunningCondition = script_ns.class_("IsRunningCondition", automation.Condition) +SingleScript = script_ns.class_("SingleScript", Script) +RestartScript = script_ns.class_("RestartScript", Script) +QueueingScript = script_ns.class_("QueueingScript", Script, cg.Component) +ParallelScript = script_ns.class_("ParallelScript", Script) -CONF_SINGLE = 'single' -CONF_RESTART = 'restart' -CONF_QUEUED = 'queued' -CONF_PARALLEL = 'parallel' -CONF_MAX_RUNS = 'max_runs' +CONF_SINGLE = "single" +CONF_RESTART = "restart" +CONF_QUEUED = "queued" +CONF_PARALLEL = "parallel" +CONF_MAX_RUNS = "max_runs" SCRIPT_MODES = { CONF_SINGLE: SingleScript, @@ -34,8 +34,10 @@ def check_max_runs(value): if CONF_MAX_RUNS not in value: return value if value[CONF_MODE] not in [CONF_QUEUED, CONF_PARALLEL]: - raise cv.Invalid("The option 'max_runs' is only valid in 'queue' and 'parallel' mode.", - path=[CONF_MAX_RUNS]) + raise cv.Invalid( + "The option 'max_runs' is only valid in 'queue' and 'parallel' mode.", + path=[CONF_MAX_RUNS], + ) return value @@ -45,13 +47,18 @@ def assign_declare_id(value): return value -CONFIG_SCHEMA = automation.validate_automation({ - # Don't declare id as cv.declare_id yet, because the ID type - # dpeends on the mode. Will be checked later with assign_declare_id - cv.Required(CONF_ID): cv.string_strict, - cv.Optional(CONF_MODE, default=CONF_SINGLE): cv.one_of(*SCRIPT_MODES, lower=True), - cv.Optional(CONF_MAX_RUNS): cv.positive_int, -}, extra_validators=cv.All(check_max_runs, assign_declare_id)) +CONFIG_SCHEMA = automation.validate_automation( + { + # Don't declare id as cv.declare_id yet, because the ID type + # dpeends on the mode. Will be checked later with assign_declare_id + cv.Required(CONF_ID): cv.string_strict, + cv.Optional(CONF_MODE, default=CONF_SINGLE): cv.one_of( + *SCRIPT_MODES, lower=True + ), + cv.Optional(CONF_MAX_RUNS): cv.positive_int, + }, + extra_validators=cv.All(check_max_runs, assign_declare_id), +) def to_code(config): @@ -74,25 +81,35 @@ def to_code(config): yield automation.build_automation(trigger, [], conf) -@automation.register_action('script.execute', ScriptExecuteAction, maybe_simple_id({ - cv.Required(CONF_ID): cv.use_id(Script), -})) +@automation.register_action( + "script.execute", + ScriptExecuteAction, + maybe_simple_id( + { + cv.Required(CONF_ID): cv.use_id(Script), + } + ), +) def script_execute_action_to_code(config, action_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) yield cg.new_Pvariable(action_id, template_arg, paren) -@automation.register_action('script.stop', ScriptStopAction, maybe_simple_id({ - cv.Required(CONF_ID): cv.use_id(Script) -})) +@automation.register_action( + "script.stop", + ScriptStopAction, + maybe_simple_id({cv.Required(CONF_ID): cv.use_id(Script)}), +) def script_stop_action_to_code(config, action_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) yield cg.new_Pvariable(action_id, template_arg, paren) -@automation.register_action('script.wait', ScriptWaitAction, maybe_simple_id({ - cv.Required(CONF_ID): cv.use_id(Script) -})) +@automation.register_action( + "script.wait", + ScriptWaitAction, + maybe_simple_id({cv.Required(CONF_ID): cv.use_id(Script)}), +) def script_wait_action_to_code(config, action_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) var = yield cg.new_Pvariable(action_id, template_arg, paren) @@ -100,9 +117,11 @@ def script_wait_action_to_code(config, action_id, template_arg, args): yield var -@automation.register_condition('script.is_running', IsRunningCondition, automation.maybe_simple_id({ - cv.Required(CONF_ID): cv.use_id(Script) -})) +@automation.register_condition( + "script.is_running", + IsRunningCondition, + automation.maybe_simple_id({cv.Required(CONF_ID): cv.use_id(Script)}), +) def script_is_running_to_code(config, condition_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) yield cg.new_Pvariable(condition_id, template_arg, paren) diff --git a/esphome/components/sds011/sensor.py b/esphome/components/sds011/sensor.py index 7b16f0412f..fcee6fe7d2 100644 --- a/esphome/components/sds011/sensor.py +++ b/esphome/components/sds011/sensor.py @@ -1,13 +1,21 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, uart -from esphome.const import CONF_ID, CONF_PM_10_0, CONF_PM_2_5, CONF_RX_ONLY, CONF_UPDATE_INTERVAL, \ - DEVICE_CLASS_EMPTY, UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_CHEMICAL_WEAPON +from esphome.const import ( + CONF_ID, + CONF_PM_10_0, + CONF_PM_2_5, + CONF_RX_ONLY, + CONF_UPDATE_INTERVAL, + DEVICE_CLASS_EMPTY, + UNIT_MICROGRAMS_PER_CUBIC_METER, + ICON_CHEMICAL_WEAPON, +) -DEPENDENCIES = ['uart'] +DEPENDENCIES = ["uart"] -sds011_ns = cg.esphome_ns.namespace('sds011') -SDS011Component = sds011_ns.class_('SDS011Component', uart.UARTDevice, cg.Component) +sds011_ns = cg.esphome_ns.namespace("sds011") +SDS011Component = sds011_ns.class_("SDS011Component", uart.UARTDevice, cg.Component) def validate_sds011_rx_mode(value): @@ -18,24 +26,37 @@ def validate_sds011_rx_mode(value): elif value.get(CONF_RX_ONLY) and CONF_UPDATE_INTERVAL in value: # update_interval does not affect anything in rx-only mode, let's warn user about # that - raise cv.Invalid("update_interval has no effect in rx_only mode. Please remove it.", - path=['update_interval']) + raise cv.Invalid( + "update_interval has no effect in rx_only mode. Please remove it.", + path=["update_interval"], + ) return value -CONFIG_SCHEMA = cv.All(cv.Schema({ - cv.GenerateID(): cv.declare_id(SDS011Component), - - cv.Optional(CONF_PM_2_5): - sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_CHEMICAL_WEAPON, 1, - DEVICE_CLASS_EMPTY), - cv.Optional(CONF_PM_10_0): - sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_CHEMICAL_WEAPON, 1, - DEVICE_CLASS_EMPTY), - - cv.Optional(CONF_RX_ONLY, default=False): cv.boolean, - cv.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_minutes, -}).extend(cv.COMPONENT_SCHEMA).extend(uart.UART_DEVICE_SCHEMA), validate_sds011_rx_mode) +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(SDS011Component), + cv.Optional(CONF_PM_2_5): sensor.sensor_schema( + UNIT_MICROGRAMS_PER_CUBIC_METER, + ICON_CHEMICAL_WEAPON, + 1, + DEVICE_CLASS_EMPTY, + ), + cv.Optional(CONF_PM_10_0): sensor.sensor_schema( + UNIT_MICROGRAMS_PER_CUBIC_METER, + ICON_CHEMICAL_WEAPON, + 1, + DEVICE_CLASS_EMPTY, + ), + cv.Optional(CONF_RX_ONLY, default=False): cv.boolean, + cv.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_minutes, + } + ) + .extend(cv.COMPONENT_SCHEMA) + .extend(uart.UART_DEVICE_SCHEMA), + validate_sds011_rx_mode, +) def to_code(config): diff --git a/esphome/components/senseair/sensor.py b/esphome/components/senseair/sensor.py index 8ce06686be..8f5c7caa68 100644 --- a/esphome/components/senseair/sensor.py +++ b/esphome/components/senseair/sensor.py @@ -1,19 +1,33 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, uart -from esphome.const import CONF_CO2, CONF_ID, DEVICE_CLASS_EMPTY, ICON_MOLECULE_CO2, \ - UNIT_PARTS_PER_MILLION +from esphome.const import ( + CONF_CO2, + CONF_ID, + DEVICE_CLASS_EMPTY, + ICON_MOLECULE_CO2, + UNIT_PARTS_PER_MILLION, +) -DEPENDENCIES = ['uart'] +DEPENDENCIES = ["uart"] -senseair_ns = cg.esphome_ns.namespace('senseair') -SenseAirComponent = senseair_ns.class_('SenseAirComponent', cg.PollingComponent, uart.UARTDevice) +senseair_ns = cg.esphome_ns.namespace("senseair") +SenseAirComponent = senseair_ns.class_( + "SenseAirComponent", cg.PollingComponent, uart.UARTDevice +) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(SenseAirComponent), - cv.Required(CONF_CO2): sensor.sensor_schema(UNIT_PARTS_PER_MILLION, ICON_MOLECULE_CO2, 0, - DEVICE_CLASS_EMPTY), -}).extend(cv.polling_component_schema('60s')).extend(uart.UART_DEVICE_SCHEMA) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(SenseAirComponent), + cv.Required(CONF_CO2): sensor.sensor_schema( + UNIT_PARTS_PER_MILLION, ICON_MOLECULE_CO2, 0, DEVICE_CLASS_EMPTY + ), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(uart.UART_DEVICE_SCHEMA) +) def to_code(config): diff --git a/esphome/components/sensor/__init__.py b/esphome/components/sensor/__init__.py index a1de877d78..a10c5d7326 100644 --- a/esphome/components/sensor/__init__.py +++ b/esphome/components/sensor/__init__.py @@ -4,23 +4,64 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import automation from esphome.components import mqtt -from esphome.const import CONF_DEVICE_CLASS, CONF_ABOVE, CONF_ACCURACY_DECIMALS, CONF_ALPHA, \ - CONF_BELOW, CONF_EXPIRE_AFTER, CONF_FILTERS, CONF_FROM, CONF_ICON, CONF_ID, CONF_INTERNAL, \ - CONF_ON_RAW_VALUE, CONF_ON_VALUE, CONF_ON_VALUE_RANGE, CONF_SEND_EVERY, CONF_SEND_FIRST_AT, \ - CONF_TO, CONF_TRIGGER_ID, CONF_UNIT_OF_MEASUREMENT, CONF_WINDOW_SIZE, CONF_NAME, CONF_MQTT_ID, \ - CONF_FORCE_UPDATE, UNIT_EMPTY, ICON_EMPTY, DEVICE_CLASS_EMPTY, DEVICE_CLASS_BATTERY, \ - DEVICE_CLASS_CURRENT, DEVICE_CLASS_ENERGY, DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_ILLUMINANCE, \ - DEVICE_CLASS_SIGNAL_STRENGTH, DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_POWER, \ - DEVICE_CLASS_POWER_FACTOR, DEVICE_CLASS_PRESSURE, DEVICE_CLASS_TIMESTAMP, DEVICE_CLASS_VOLTAGE +from esphome.const import ( + CONF_DEVICE_CLASS, + CONF_ABOVE, + CONF_ACCURACY_DECIMALS, + CONF_ALPHA, + CONF_BELOW, + CONF_EXPIRE_AFTER, + CONF_FILTERS, + CONF_FROM, + CONF_ICON, + CONF_ID, + CONF_INTERNAL, + CONF_ON_RAW_VALUE, + CONF_ON_VALUE, + CONF_ON_VALUE_RANGE, + CONF_SEND_EVERY, + CONF_SEND_FIRST_AT, + CONF_TO, + CONF_TRIGGER_ID, + CONF_UNIT_OF_MEASUREMENT, + CONF_WINDOW_SIZE, + CONF_NAME, + CONF_MQTT_ID, + CONF_FORCE_UPDATE, + UNIT_EMPTY, + ICON_EMPTY, + DEVICE_CLASS_EMPTY, + DEVICE_CLASS_BATTERY, + DEVICE_CLASS_CURRENT, + DEVICE_CLASS_ENERGY, + DEVICE_CLASS_HUMIDITY, + DEVICE_CLASS_ILLUMINANCE, + DEVICE_CLASS_SIGNAL_STRENGTH, + DEVICE_CLASS_TEMPERATURE, + DEVICE_CLASS_POWER, + DEVICE_CLASS_POWER_FACTOR, + DEVICE_CLASS_PRESSURE, + DEVICE_CLASS_TIMESTAMP, + DEVICE_CLASS_VOLTAGE, +) from esphome.core import CORE, coroutine, coroutine_with_priority from esphome.util import Registry -CODEOWNERS = ['@esphome/core'] +CODEOWNERS = ["@esphome/core"] DEVICE_CLASSES = [ - DEVICE_CLASS_EMPTY, DEVICE_CLASS_BATTERY, DEVICE_CLASS_CURRENT, DEVICE_CLASS_ENERGY, - DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_ILLUMINANCE, DEVICE_CLASS_SIGNAL_STRENGTH, - DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_POWER, DEVICE_CLASS_POWER_FACTOR, DEVICE_CLASS_PRESSURE, - DEVICE_CLASS_TIMESTAMP, DEVICE_CLASS_VOLTAGE + DEVICE_CLASS_EMPTY, + DEVICE_CLASS_BATTERY, + DEVICE_CLASS_CURRENT, + DEVICE_CLASS_ENERGY, + DEVICE_CLASS_HUMIDITY, + DEVICE_CLASS_ILLUMINANCE, + DEVICE_CLASS_SIGNAL_STRENGTH, + DEVICE_CLASS_TEMPERATURE, + DEVICE_CLASS_POWER, + DEVICE_CLASS_POWER_FACTOR, + DEVICE_CLASS_PRESSURE, + DEVICE_CLASS_TIMESTAMP, + DEVICE_CLASS_VOLTAGE, ] IS_PLATFORM_COMPONENT = True @@ -30,222 +71,302 @@ def validate_send_first_at(value): send_first_at = value.get(CONF_SEND_FIRST_AT) send_every = value[CONF_SEND_EVERY] if send_first_at is not None and send_first_at > send_every: - raise cv.Invalid("send_first_at must be smaller than or equal to send_every! {} <= {}" - "".format(send_first_at, send_every)) + raise cv.Invalid( + "send_first_at must be smaller than or equal to send_every! {} <= {}" + "".format(send_first_at, send_every) + ) return value FILTER_REGISTRY = Registry() -validate_filters = cv.validate_registry('filter', FILTER_REGISTRY) +validate_filters = cv.validate_registry("filter", FILTER_REGISTRY) def validate_datapoint(value): if isinstance(value, dict): - return cv.Schema({ - cv.Required(CONF_FROM): cv.float_, - cv.Required(CONF_TO): cv.float_, - })(value) + return cv.Schema( + { + cv.Required(CONF_FROM): cv.float_, + cv.Required(CONF_TO): cv.float_, + } + )(value) value = cv.string(value) - if '->' not in value: + if "->" not in value: raise cv.Invalid("Datapoint mapping must contain '->'") - a, b = value.split('->', 1) + a, b = value.split("->", 1) a, b = a.strip(), b.strip() - return validate_datapoint({ - CONF_FROM: cv.float_(a), - CONF_TO: cv.float_(b) - }) + return validate_datapoint({CONF_FROM: cv.float_(a), CONF_TO: cv.float_(b)}) # Base -sensor_ns = cg.esphome_ns.namespace('sensor') -Sensor = sensor_ns.class_('Sensor', cg.Nameable) -SensorPtr = Sensor.operator('ptr') +sensor_ns = cg.esphome_ns.namespace("sensor") +Sensor = sensor_ns.class_("Sensor", cg.Nameable) +SensorPtr = Sensor.operator("ptr") # Triggers -SensorStateTrigger = sensor_ns.class_('SensorStateTrigger', automation.Trigger.template(cg.float_)) -SensorRawStateTrigger = sensor_ns.class_('SensorRawStateTrigger', - automation.Trigger.template(cg.float_)) -ValueRangeTrigger = sensor_ns.class_('ValueRangeTrigger', automation.Trigger.template(cg.float_), - cg.Component) -SensorPublishAction = sensor_ns.class_('SensorPublishAction', automation.Action) +SensorStateTrigger = sensor_ns.class_( + "SensorStateTrigger", automation.Trigger.template(cg.float_) +) +SensorRawStateTrigger = sensor_ns.class_( + "SensorRawStateTrigger", automation.Trigger.template(cg.float_) +) +ValueRangeTrigger = sensor_ns.class_( + "ValueRangeTrigger", automation.Trigger.template(cg.float_), cg.Component +) +SensorPublishAction = sensor_ns.class_("SensorPublishAction", automation.Action) # Filters -Filter = sensor_ns.class_('Filter') -MedianFilter = sensor_ns.class_('MedianFilter', Filter) -MinFilter = sensor_ns.class_('MinFilter', Filter) -MaxFilter = sensor_ns.class_('MaxFilter', Filter) -SlidingWindowMovingAverageFilter = sensor_ns.class_('SlidingWindowMovingAverageFilter', Filter) -ExponentialMovingAverageFilter = sensor_ns.class_('ExponentialMovingAverageFilter', Filter) -LambdaFilter = sensor_ns.class_('LambdaFilter', Filter) -OffsetFilter = sensor_ns.class_('OffsetFilter', Filter) -MultiplyFilter = sensor_ns.class_('MultiplyFilter', Filter) -FilterOutValueFilter = sensor_ns.class_('FilterOutValueFilter', Filter) -ThrottleFilter = sensor_ns.class_('ThrottleFilter', Filter) -DebounceFilter = sensor_ns.class_('DebounceFilter', Filter, cg.Component) -HeartbeatFilter = sensor_ns.class_('HeartbeatFilter', Filter, cg.Component) -DeltaFilter = sensor_ns.class_('DeltaFilter', Filter) -OrFilter = sensor_ns.class_('OrFilter', Filter) -CalibrateLinearFilter = sensor_ns.class_('CalibrateLinearFilter', Filter) -CalibratePolynomialFilter = sensor_ns.class_('CalibratePolynomialFilter', Filter) -SensorInRangeCondition = sensor_ns.class_('SensorInRangeCondition', Filter) +Filter = sensor_ns.class_("Filter") +MedianFilter = sensor_ns.class_("MedianFilter", Filter) +MinFilter = sensor_ns.class_("MinFilter", Filter) +MaxFilter = sensor_ns.class_("MaxFilter", Filter) +SlidingWindowMovingAverageFilter = sensor_ns.class_( + "SlidingWindowMovingAverageFilter", Filter +) +ExponentialMovingAverageFilter = sensor_ns.class_( + "ExponentialMovingAverageFilter", Filter +) +LambdaFilter = sensor_ns.class_("LambdaFilter", Filter) +OffsetFilter = sensor_ns.class_("OffsetFilter", Filter) +MultiplyFilter = sensor_ns.class_("MultiplyFilter", Filter) +FilterOutValueFilter = sensor_ns.class_("FilterOutValueFilter", Filter) +ThrottleFilter = sensor_ns.class_("ThrottleFilter", Filter) +DebounceFilter = sensor_ns.class_("DebounceFilter", Filter, cg.Component) +HeartbeatFilter = sensor_ns.class_("HeartbeatFilter", Filter, cg.Component) +DeltaFilter = sensor_ns.class_("DeltaFilter", Filter) +OrFilter = sensor_ns.class_("OrFilter", Filter) +CalibrateLinearFilter = sensor_ns.class_("CalibrateLinearFilter", Filter) +CalibratePolynomialFilter = sensor_ns.class_("CalibratePolynomialFilter", Filter) +SensorInRangeCondition = sensor_ns.class_("SensorInRangeCondition", Filter) unit_of_measurement = cv.string_strict accuracy_decimals = cv.int_ icon = cv.icon -device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space='_') +device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_") -SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend({ - cv.OnlyWith(CONF_MQTT_ID, 'mqtt'): cv.declare_id(mqtt.MQTTSensorComponent), - cv.GenerateID(): cv.declare_id(Sensor), - cv.Optional(CONF_UNIT_OF_MEASUREMENT): unit_of_measurement, - cv.Optional(CONF_ICON): icon, - cv.Optional(CONF_ACCURACY_DECIMALS): accuracy_decimals, - cv.Optional(CONF_DEVICE_CLASS): device_class, - cv.Optional(CONF_FORCE_UPDATE, default=False): cv.boolean, - cv.Optional(CONF_EXPIRE_AFTER): cv.All(cv.requires_component('mqtt'), - cv.Any(None, cv.positive_time_period_milliseconds)), - cv.Optional(CONF_FILTERS): validate_filters, - cv.Optional(CONF_ON_VALUE): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SensorStateTrigger), - }), - cv.Optional(CONF_ON_RAW_VALUE): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SensorRawStateTrigger), - }), - cv.Optional(CONF_ON_VALUE_RANGE): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ValueRangeTrigger), - cv.Optional(CONF_ABOVE): cv.float_, - cv.Optional(CONF_BELOW): cv.float_, - }, cv.has_at_least_one_key(CONF_ABOVE, CONF_BELOW)), -}) +SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend( + { + cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTSensorComponent), + cv.GenerateID(): cv.declare_id(Sensor), + cv.Optional(CONF_UNIT_OF_MEASUREMENT): unit_of_measurement, + cv.Optional(CONF_ICON): icon, + cv.Optional(CONF_ACCURACY_DECIMALS): accuracy_decimals, + cv.Optional(CONF_DEVICE_CLASS): device_class, + cv.Optional(CONF_FORCE_UPDATE, default=False): cv.boolean, + cv.Optional(CONF_EXPIRE_AFTER): cv.All( + cv.requires_component("mqtt"), + cv.Any(None, cv.positive_time_period_milliseconds), + ), + cv.Optional(CONF_FILTERS): validate_filters, + cv.Optional(CONF_ON_VALUE): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SensorStateTrigger), + } + ), + cv.Optional(CONF_ON_RAW_VALUE): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SensorRawStateTrigger), + } + ), + cv.Optional(CONF_ON_VALUE_RANGE): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ValueRangeTrigger), + cv.Optional(CONF_ABOVE): cv.float_, + cv.Optional(CONF_BELOW): cv.float_, + }, + cv.has_at_least_one_key(CONF_ABOVE, CONF_BELOW), + ), + } +) def sensor_schema(unit_of_measurement_, icon_, accuracy_decimals_, device_class_): # type: (str, str, int, str) -> cv.Schema schema = SENSOR_SCHEMA if unit_of_measurement_ != UNIT_EMPTY: - schema = schema.extend({ - cv.Optional(CONF_UNIT_OF_MEASUREMENT, default=unit_of_measurement_): unit_of_measurement - }) + schema = schema.extend( + { + cv.Optional( + CONF_UNIT_OF_MEASUREMENT, default=unit_of_measurement_ + ): unit_of_measurement + } + ) if icon_ != ICON_EMPTY: schema = schema.extend({cv.Optional(CONF_ICON, default=icon_): icon}) if accuracy_decimals_ != 0: - schema = schema.extend({ - cv.Optional(CONF_ACCURACY_DECIMALS, default=accuracy_decimals_): accuracy_decimals, - }) + schema = schema.extend( + { + cv.Optional( + CONF_ACCURACY_DECIMALS, default=accuracy_decimals_ + ): accuracy_decimals, + } + ) if device_class_ != DEVICE_CLASS_EMPTY: - schema = schema.extend({ - cv.Optional(CONF_DEVICE_CLASS, default=device_class_): device_class - }) + schema = schema.extend( + {cv.Optional(CONF_DEVICE_CLASS, default=device_class_): device_class} + ) return schema -@FILTER_REGISTRY.register('offset', OffsetFilter, cv.float_) +@FILTER_REGISTRY.register("offset", OffsetFilter, cv.float_) def offset_filter_to_code(config, filter_id): yield cg.new_Pvariable(filter_id, config) -@FILTER_REGISTRY.register('multiply', MultiplyFilter, cv.float_) +@FILTER_REGISTRY.register("multiply", MultiplyFilter, cv.float_) def multiply_filter_to_code(config, filter_id): yield cg.new_Pvariable(filter_id, config) -@FILTER_REGISTRY.register('filter_out', FilterOutValueFilter, cv.float_) +@FILTER_REGISTRY.register("filter_out", FilterOutValueFilter, cv.float_) def filter_out_filter_to_code(config, filter_id): yield cg.new_Pvariable(filter_id, config) -MEDIAN_SCHEMA = cv.All(cv.Schema({ - cv.Optional(CONF_WINDOW_SIZE, default=5): cv.positive_not_null_int, - cv.Optional(CONF_SEND_EVERY, default=5): cv.positive_not_null_int, - cv.Optional(CONF_SEND_FIRST_AT, default=1): cv.positive_not_null_int, -}), validate_send_first_at) +MEDIAN_SCHEMA = cv.All( + cv.Schema( + { + cv.Optional(CONF_WINDOW_SIZE, default=5): cv.positive_not_null_int, + cv.Optional(CONF_SEND_EVERY, default=5): cv.positive_not_null_int, + cv.Optional(CONF_SEND_FIRST_AT, default=1): cv.positive_not_null_int, + } + ), + validate_send_first_at, +) -@FILTER_REGISTRY.register('median', MedianFilter, MEDIAN_SCHEMA) +@FILTER_REGISTRY.register("median", MedianFilter, MEDIAN_SCHEMA) def median_filter_to_code(config, filter_id): - yield cg.new_Pvariable(filter_id, config[CONF_WINDOW_SIZE], config[CONF_SEND_EVERY], - config[CONF_SEND_FIRST_AT]) + yield cg.new_Pvariable( + filter_id, + config[CONF_WINDOW_SIZE], + config[CONF_SEND_EVERY], + config[CONF_SEND_FIRST_AT], + ) -MIN_SCHEMA = cv.All(cv.Schema({ - cv.Optional(CONF_WINDOW_SIZE, default=5): cv.positive_not_null_int, - cv.Optional(CONF_SEND_EVERY, default=5): cv.positive_not_null_int, - cv.Optional(CONF_SEND_FIRST_AT, default=1): cv.positive_not_null_int, -}), validate_send_first_at) +MIN_SCHEMA = cv.All( + cv.Schema( + { + cv.Optional(CONF_WINDOW_SIZE, default=5): cv.positive_not_null_int, + cv.Optional(CONF_SEND_EVERY, default=5): cv.positive_not_null_int, + cv.Optional(CONF_SEND_FIRST_AT, default=1): cv.positive_not_null_int, + } + ), + validate_send_first_at, +) -@FILTER_REGISTRY.register('min', MinFilter, MIN_SCHEMA) +@FILTER_REGISTRY.register("min", MinFilter, MIN_SCHEMA) def min_filter_to_code(config, filter_id): - yield cg.new_Pvariable(filter_id, config[CONF_WINDOW_SIZE], config[CONF_SEND_EVERY], - config[CONF_SEND_FIRST_AT]) + yield cg.new_Pvariable( + filter_id, + config[CONF_WINDOW_SIZE], + config[CONF_SEND_EVERY], + config[CONF_SEND_FIRST_AT], + ) -MAX_SCHEMA = cv.All(cv.Schema({ - cv.Optional(CONF_WINDOW_SIZE, default=5): cv.positive_not_null_int, - cv.Optional(CONF_SEND_EVERY, default=5): cv.positive_not_null_int, - cv.Optional(CONF_SEND_FIRST_AT, default=1): cv.positive_not_null_int, -}), validate_send_first_at) +MAX_SCHEMA = cv.All( + cv.Schema( + { + cv.Optional(CONF_WINDOW_SIZE, default=5): cv.positive_not_null_int, + cv.Optional(CONF_SEND_EVERY, default=5): cv.positive_not_null_int, + cv.Optional(CONF_SEND_FIRST_AT, default=1): cv.positive_not_null_int, + } + ), + validate_send_first_at, +) -@FILTER_REGISTRY.register('max', MaxFilter, MAX_SCHEMA) +@FILTER_REGISTRY.register("max", MaxFilter, MAX_SCHEMA) def max_filter_to_code(config, filter_id): - yield cg.new_Pvariable(filter_id, config[CONF_WINDOW_SIZE], config[CONF_SEND_EVERY], - config[CONF_SEND_FIRST_AT]) + yield cg.new_Pvariable( + filter_id, + config[CONF_WINDOW_SIZE], + config[CONF_SEND_EVERY], + config[CONF_SEND_FIRST_AT], + ) -SLIDING_AVERAGE_SCHEMA = cv.All(cv.Schema({ - cv.Optional(CONF_WINDOW_SIZE, default=15): cv.positive_not_null_int, - cv.Optional(CONF_SEND_EVERY, default=15): cv.positive_not_null_int, - cv.Optional(CONF_SEND_FIRST_AT, default=1): cv.positive_not_null_int, -}), validate_send_first_at) +SLIDING_AVERAGE_SCHEMA = cv.All( + cv.Schema( + { + cv.Optional(CONF_WINDOW_SIZE, default=15): cv.positive_not_null_int, + cv.Optional(CONF_SEND_EVERY, default=15): cv.positive_not_null_int, + cv.Optional(CONF_SEND_FIRST_AT, default=1): cv.positive_not_null_int, + } + ), + validate_send_first_at, +) -@FILTER_REGISTRY.register('sliding_window_moving_average', SlidingWindowMovingAverageFilter, - SLIDING_AVERAGE_SCHEMA) +@FILTER_REGISTRY.register( + "sliding_window_moving_average", + SlidingWindowMovingAverageFilter, + SLIDING_AVERAGE_SCHEMA, +) def sliding_window_moving_average_filter_to_code(config, filter_id): - yield cg.new_Pvariable(filter_id, config[CONF_WINDOW_SIZE], config[CONF_SEND_EVERY], - config[CONF_SEND_FIRST_AT]) + yield cg.new_Pvariable( + filter_id, + config[CONF_WINDOW_SIZE], + config[CONF_SEND_EVERY], + config[CONF_SEND_FIRST_AT], + ) -@FILTER_REGISTRY.register('exponential_moving_average', ExponentialMovingAverageFilter, cv.Schema({ - cv.Optional(CONF_ALPHA, default=0.1): cv.positive_float, - cv.Optional(CONF_SEND_EVERY, default=15): cv.positive_not_null_int, -})) +@FILTER_REGISTRY.register( + "exponential_moving_average", + ExponentialMovingAverageFilter, + cv.Schema( + { + cv.Optional(CONF_ALPHA, default=0.1): cv.positive_float, + cv.Optional(CONF_SEND_EVERY, default=15): cv.positive_not_null_int, + } + ), +) def exponential_moving_average_filter_to_code(config, filter_id): yield cg.new_Pvariable(filter_id, config[CONF_ALPHA], config[CONF_SEND_EVERY]) -@FILTER_REGISTRY.register('lambda', LambdaFilter, cv.returning_lambda) +@FILTER_REGISTRY.register("lambda", LambdaFilter, cv.returning_lambda) def lambda_filter_to_code(config, filter_id): - lambda_ = yield cg.process_lambda(config, [(float, 'x')], - return_type=cg.optional.template(float)) + lambda_ = yield cg.process_lambda( + config, [(float, "x")], return_type=cg.optional.template(float) + ) yield cg.new_Pvariable(filter_id, lambda_) -@FILTER_REGISTRY.register('delta', DeltaFilter, cv.float_) +@FILTER_REGISTRY.register("delta", DeltaFilter, cv.float_) def delta_filter_to_code(config, filter_id): yield cg.new_Pvariable(filter_id, config) -@FILTER_REGISTRY.register('or', OrFilter, validate_filters) +@FILTER_REGISTRY.register("or", OrFilter, validate_filters) def or_filter_to_code(config, filter_id): filters = yield build_filters(config) yield cg.new_Pvariable(filter_id, filters) -@FILTER_REGISTRY.register('throttle', ThrottleFilter, cv.positive_time_period_milliseconds) +@FILTER_REGISTRY.register( + "throttle", ThrottleFilter, cv.positive_time_period_milliseconds +) def throttle_filter_to_code(config, filter_id): yield cg.new_Pvariable(filter_id, config) -@FILTER_REGISTRY.register('heartbeat', HeartbeatFilter, cv.positive_time_period_milliseconds) +@FILTER_REGISTRY.register( + "heartbeat", HeartbeatFilter, cv.positive_time_period_milliseconds +) def heartbeat_filter_to_code(config, filter_id): var = cg.new_Pvariable(filter_id, config) yield cg.register_component(var, {}) yield var -@FILTER_REGISTRY.register('debounce', DebounceFilter, cv.positive_time_period_milliseconds) +@FILTER_REGISTRY.register( + "debounce", DebounceFilter, cv.positive_time_period_milliseconds +) def debounce_filter_to_code(config, filter_id): var = cg.new_Pvariable(filter_id, config) yield cg.register_component(var, {}) @@ -254,13 +375,20 @@ def debounce_filter_to_code(config, filter_id): def validate_not_all_from_same(config): if all(conf[CONF_FROM] == config[0][CONF_FROM] for conf in config): - raise cv.Invalid("The 'from' values of the calibrate_linear filter cannot all point " - "to the same value! Please add more values to the filter.") + raise cv.Invalid( + "The 'from' values of the calibrate_linear filter cannot all point " + "to the same value! Please add more values to the filter." + ) return config -@FILTER_REGISTRY.register('calibrate_linear', CalibrateLinearFilter, cv.All( - cv.ensure_list(validate_datapoint), cv.Length(min=2), validate_not_all_from_same)) +@FILTER_REGISTRY.register( + "calibrate_linear", + CalibrateLinearFilter, + cv.All( + cv.ensure_list(validate_datapoint), cv.Length(min=2), validate_not_all_from_same + ), +) def calibrate_linear_filter_to_code(config, filter_id): x = [conf[CONF_FROM] for conf in config] y = [conf[CONF_TO] for conf in config] @@ -268,26 +396,40 @@ def calibrate_linear_filter_to_code(config, filter_id): yield cg.new_Pvariable(filter_id, k, b) -CONF_DATAPOINTS = 'datapoints' -CONF_DEGREE = 'degree' +CONF_DATAPOINTS = "datapoints" +CONF_DEGREE = "degree" def validate_calibrate_polynomial(config): if config[CONF_DEGREE] >= len(config[CONF_DATAPOINTS]): - raise cv.Invalid("Degree is too high! Maximum possible degree with given datapoints is " - "{}".format(len(config[CONF_DATAPOINTS]) - 1), [CONF_DEGREE]) + raise cv.Invalid( + "Degree is too high! Maximum possible degree with given datapoints is " + "{}".format(len(config[CONF_DATAPOINTS]) - 1), + [CONF_DEGREE], + ) return config -@FILTER_REGISTRY.register('calibrate_polynomial', CalibratePolynomialFilter, cv.All(cv.Schema({ - cv.Required(CONF_DATAPOINTS): cv.All(cv.ensure_list(validate_datapoint), cv.Length(min=1)), - cv.Required(CONF_DEGREE): cv.positive_int, -}), validate_calibrate_polynomial)) +@FILTER_REGISTRY.register( + "calibrate_polynomial", + CalibratePolynomialFilter, + cv.All( + cv.Schema( + { + cv.Required(CONF_DATAPOINTS): cv.All( + cv.ensure_list(validate_datapoint), cv.Length(min=1) + ), + cv.Required(CONF_DEGREE): cv.positive_int, + } + ), + validate_calibrate_polynomial, + ), +) def calibrate_polynomial_filter_to_code(config, filter_id): x = [conf[CONF_FROM] for conf in config[CONF_DATAPOINTS]] y = [conf[CONF_TO] for conf in config[CONF_DATAPOINTS]] degree = config[CONF_DEGREE] - a = [[1] + [x_**(i+1) for i in range(degree)] for x_ in x] + a = [[1] + [x_ ** (i + 1) for i in range(degree)] for x_ in x] # Column vector b = [[v] for v in y] res = [v[0] for v in _lstsq(a, b)] @@ -319,20 +461,20 @@ def setup_sensor_core_(var, config): for conf in config.get(CONF_ON_VALUE, []): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) - yield automation.build_automation(trigger, [(float, 'x')], conf) + yield automation.build_automation(trigger, [(float, "x")], conf) for conf in config.get(CONF_ON_RAW_VALUE, []): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) - yield automation.build_automation(trigger, [(float, 'x')], conf) + yield automation.build_automation(trigger, [(float, "x")], conf) for conf in config.get(CONF_ON_VALUE_RANGE, []): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) yield cg.register_component(trigger, conf) if CONF_ABOVE in conf: - template_ = yield cg.templatable(conf[CONF_ABOVE], [(float, 'x')], float) + template_ = yield cg.templatable(conf[CONF_ABOVE], [(float, "x")], float) cg.add(trigger.set_min(template_)) if CONF_BELOW in conf: - template_ = yield cg.templatable(conf[CONF_BELOW], [(float, 'x')], float) + template_ = yield cg.templatable(conf[CONF_BELOW], [(float, "x")], float) cg.add(trigger.set_max(template_)) - yield automation.build_automation(trigger, [(float, 'x')], conf) + yield automation.build_automation(trigger, [(float, "x")], conf) if CONF_MQTT_ID in config: mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var) @@ -360,15 +502,19 @@ def new_sensor(config): yield var -SENSOR_IN_RANGE_CONDITION_SCHEMA = cv.All({ - cv.Required(CONF_ID): cv.use_id(Sensor), - cv.Optional(CONF_ABOVE): cv.float_, - cv.Optional(CONF_BELOW): cv.float_, -}, cv.has_at_least_one_key(CONF_ABOVE, CONF_BELOW)) +SENSOR_IN_RANGE_CONDITION_SCHEMA = cv.All( + { + cv.Required(CONF_ID): cv.use_id(Sensor), + cv.Optional(CONF_ABOVE): cv.float_, + cv.Optional(CONF_BELOW): cv.float_, + }, + cv.has_at_least_one_key(CONF_ABOVE, CONF_BELOW), +) -@automation.register_condition('sensor.in_range', SensorInRangeCondition, - SENSOR_IN_RANGE_CONDITION_SCHEMA) +@automation.register_condition( + "sensor.in_range", SensorInRangeCondition, SENSOR_IN_RANGE_CONDITION_SCHEMA +) def sensor_in_range_to_code(config, condition_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) var = cg.new_Pvariable(condition_id, template_arg, paren) @@ -420,7 +566,7 @@ def _mat_identity(n): def _mat_dot(a, b): b_t = _mat_transpose(b) - return [[sum(x*y for x, y in zip(row_a, col_b)) for col_b in b_t] for row_a in a] + return [[sum(x * y for x, y in zip(row_a, col_b)) for col_b in b_t] for row_a in a] def _mat_inverse(m): @@ -431,7 +577,7 @@ def _mat_inverse(m): for diag in range(n): # If diag element is 0, swap rows if m[diag][diag] == 0: - for i in range(diag+1, n): + for i in range(diag + 1, n): if m[i][diag] != 0: break else: @@ -468,5 +614,5 @@ def _lstsq(a, b): @coroutine_with_priority(40.0) def to_code(config): - cg.add_define('USE_SENSOR') + cg.add_define("USE_SENSOR") cg.add_global(sensor_ns.using) diff --git a/esphome/components/servo/__init__.py b/esphome/components/servo/__init__.py index 76690dbcf3..489c295255 100644 --- a/esphome/components/servo/__init__.py +++ b/esphome/components/servo/__init__.py @@ -3,26 +3,40 @@ import esphome.config_validation as cv from esphome import automation from esphome.automation import maybe_simple_id from esphome.components.output import FloatOutput -from esphome.const import CONF_ID, CONF_IDLE_LEVEL, CONF_MAX_LEVEL, CONF_MIN_LEVEL, CONF_OUTPUT, \ - CONF_LEVEL, CONF_RESTORE, CONF_TRANSITION_LENGTH +from esphome.const import ( + CONF_ID, + CONF_IDLE_LEVEL, + CONF_MAX_LEVEL, + CONF_MIN_LEVEL, + CONF_OUTPUT, + CONF_LEVEL, + CONF_RESTORE, + CONF_TRANSITION_LENGTH, +) -servo_ns = cg.esphome_ns.namespace('servo') -Servo = servo_ns.class_('Servo', cg.Component) -ServoWriteAction = servo_ns.class_('ServoWriteAction', automation.Action) -ServoDetachAction = servo_ns.class_('ServoDetachAction', automation.Action) +servo_ns = cg.esphome_ns.namespace("servo") +Servo = servo_ns.class_("Servo", cg.Component) +ServoWriteAction = servo_ns.class_("ServoWriteAction", automation.Action) +ServoDetachAction = servo_ns.class_("ServoDetachAction", automation.Action) -CONF_AUTO_DETACH_TIME = 'auto_detach_time' +CONF_AUTO_DETACH_TIME = "auto_detach_time" MULTI_CONF = True -CONFIG_SCHEMA = cv.Schema({ - cv.Required(CONF_ID): cv.declare_id(Servo), - cv.Required(CONF_OUTPUT): cv.use_id(FloatOutput), - cv.Optional(CONF_MIN_LEVEL, default='3%'): cv.percentage, - cv.Optional(CONF_IDLE_LEVEL, default='7.5%'): cv.percentage, - cv.Optional(CONF_MAX_LEVEL, default='12%'): cv.percentage, - cv.Optional(CONF_RESTORE, default=False): cv.boolean, - cv.Optional(CONF_AUTO_DETACH_TIME, default='0s'): cv.positive_time_period_milliseconds, - cv.Optional(CONF_TRANSITION_LENGTH, default='0s'): cv.positive_time_period_milliseconds -}).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = cv.Schema( + { + cv.Required(CONF_ID): cv.declare_id(Servo), + cv.Required(CONF_OUTPUT): cv.use_id(FloatOutput), + cv.Optional(CONF_MIN_LEVEL, default="3%"): cv.percentage, + cv.Optional(CONF_IDLE_LEVEL, default="7.5%"): cv.percentage, + cv.Optional(CONF_MAX_LEVEL, default="12%"): cv.percentage, + cv.Optional(CONF_RESTORE, default=False): cv.boolean, + cv.Optional( + CONF_AUTO_DETACH_TIME, default="0s" + ): cv.positive_time_period_milliseconds, + cv.Optional( + CONF_TRANSITION_LENGTH, default="0s" + ): cv.positive_time_period_milliseconds, + } +).extend(cv.COMPONENT_SCHEMA) def to_code(config): @@ -39,10 +53,16 @@ def to_code(config): cg.add(var.set_transition_length(config[CONF_TRANSITION_LENGTH])) -@automation.register_action('servo.write', ServoWriteAction, cv.Schema({ - cv.Required(CONF_ID): cv.use_id(Servo), - cv.Required(CONF_LEVEL): cv.templatable(cv.possibly_negative_percentage), -})) +@automation.register_action( + "servo.write", + ServoWriteAction, + cv.Schema( + { + cv.Required(CONF_ID): cv.use_id(Servo), + cv.Required(CONF_LEVEL): cv.templatable(cv.possibly_negative_percentage), + } + ), +) def servo_write_to_code(config, action_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) var = cg.new_Pvariable(action_id, template_arg, paren) @@ -51,9 +71,15 @@ def servo_write_to_code(config, action_id, template_arg, args): yield var -@automation.register_action('servo.detach', ServoDetachAction, maybe_simple_id({ - cv.Required(CONF_ID): cv.use_id(Servo), -})) +@automation.register_action( + "servo.detach", + ServoDetachAction, + maybe_simple_id( + { + cv.Required(CONF_ID): cv.use_id(Servo), + } + ), +) def servo_detach_to_code(config, action_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) yield cg.new_Pvariable(action_id, template_arg, paren) diff --git a/esphome/components/sgp30/sensor.py b/esphome/components/sgp30/sensor.py index a3ce1013e0..31b40840e3 100644 --- a/esphome/components/sgp30/sensor.py +++ b/esphome/components/sgp30/sensor.py @@ -1,38 +1,57 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import CONF_ID, DEVICE_CLASS_EMPTY, ICON_RADIATOR, UNIT_PARTS_PER_MILLION, \ - UNIT_PARTS_PER_BILLION, ICON_MOLECULE_CO2, CONF_TVOC +from esphome.const import ( + CONF_ID, + DEVICE_CLASS_EMPTY, + ICON_RADIATOR, + UNIT_PARTS_PER_MILLION, + UNIT_PARTS_PER_BILLION, + ICON_MOLECULE_CO2, + CONF_TVOC, +) -DEPENDENCIES = ['i2c'] +DEPENDENCIES = ["i2c"] -sgp30_ns = cg.esphome_ns.namespace('sgp30') -SGP30Component = sgp30_ns.class_('SGP30Component', cg.PollingComponent, i2c.I2CDevice) +sgp30_ns = cg.esphome_ns.namespace("sgp30") +SGP30Component = sgp30_ns.class_("SGP30Component", cg.PollingComponent, i2c.I2CDevice) -CONF_ECO2 = 'eco2' -CONF_BASELINE = 'baseline' -CONF_ECO2_BASELINE = 'eco2_baseline' -CONF_TVOC_BASELINE = 'tvoc_baseline' -CONF_UPTIME = 'uptime' -CONF_COMPENSATION = 'compensation' -CONF_HUMIDITY_SOURCE = 'humidity_source' -CONF_TEMPERATURE_SOURCE = 'temperature_source' +CONF_ECO2 = "eco2" +CONF_BASELINE = "baseline" +CONF_ECO2_BASELINE = "eco2_baseline" +CONF_TVOC_BASELINE = "tvoc_baseline" +CONF_UPTIME = "uptime" +CONF_COMPENSATION = "compensation" +CONF_HUMIDITY_SOURCE = "humidity_source" +CONF_TEMPERATURE_SOURCE = "temperature_source" -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(SGP30Component), - cv.Required(CONF_ECO2): sensor.sensor_schema(UNIT_PARTS_PER_MILLION, - ICON_MOLECULE_CO2, 0, DEVICE_CLASS_EMPTY), - cv.Required(CONF_TVOC): sensor.sensor_schema(UNIT_PARTS_PER_BILLION, ICON_RADIATOR, 0, - DEVICE_CLASS_EMPTY), - cv.Optional(CONF_BASELINE): cv.Schema({ - cv.Required(CONF_ECO2_BASELINE): cv.hex_uint16_t, - cv.Required(CONF_TVOC_BASELINE): cv.hex_uint16_t, - }), - cv.Optional(CONF_COMPENSATION): cv.Schema({ - cv.Required(CONF_HUMIDITY_SOURCE): cv.use_id(sensor.Sensor), - cv.Required(CONF_TEMPERATURE_SOURCE): cv.use_id(sensor.Sensor) - }), -}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x58)) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(SGP30Component), + cv.Required(CONF_ECO2): sensor.sensor_schema( + UNIT_PARTS_PER_MILLION, ICON_MOLECULE_CO2, 0, DEVICE_CLASS_EMPTY + ), + cv.Required(CONF_TVOC): sensor.sensor_schema( + UNIT_PARTS_PER_BILLION, ICON_RADIATOR, 0, DEVICE_CLASS_EMPTY + ), + cv.Optional(CONF_BASELINE): cv.Schema( + { + cv.Required(CONF_ECO2_BASELINE): cv.hex_uint16_t, + cv.Required(CONF_TVOC_BASELINE): cv.hex_uint16_t, + } + ), + cv.Optional(CONF_COMPENSATION): cv.Schema( + { + cv.Required(CONF_HUMIDITY_SOURCE): cv.use_id(sensor.Sensor), + cv.Required(CONF_TEMPERATURE_SOURCE): cv.use_id(sensor.Sensor), + } + ), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x58)) +) def to_code(config): diff --git a/esphome/components/sht3xd/sensor.py b/esphome/components/sht3xd/sensor.py index 45ede0b106..0b30d6780b 100644 --- a/esphome/components/sht3xd/sensor.py +++ b/esphome/components/sht3xd/sensor.py @@ -1,21 +1,39 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import CONF_HUMIDITY, CONF_ID, CONF_TEMPERATURE, DEVICE_CLASS_HUMIDITY, \ - DEVICE_CLASS_TEMPERATURE, ICON_EMPTY, UNIT_CELSIUS, UNIT_PERCENT +from esphome.const import ( + CONF_HUMIDITY, + CONF_ID, + CONF_TEMPERATURE, + DEVICE_CLASS_HUMIDITY, + DEVICE_CLASS_TEMPERATURE, + ICON_EMPTY, + UNIT_CELSIUS, + UNIT_PERCENT, +) -DEPENDENCIES = ['i2c'] +DEPENDENCIES = ["i2c"] -sht3xd_ns = cg.esphome_ns.namespace('sht3xd') -SHT3XDComponent = sht3xd_ns.class_('SHT3XDComponent', cg.PollingComponent, i2c.I2CDevice) +sht3xd_ns = cg.esphome_ns.namespace("sht3xd") +SHT3XDComponent = sht3xd_ns.class_( + "SHT3XDComponent", cg.PollingComponent, i2c.I2CDevice +) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(SHT3XDComponent), - cv.Required(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, - DEVICE_CLASS_TEMPERATURE), - cv.Required(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 1, - DEVICE_CLASS_HUMIDITY), -}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x44)) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(SHT3XDComponent), + cv.Required(CONF_TEMPERATURE): sensor.sensor_schema( + UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE + ), + cv.Required(CONF_HUMIDITY): sensor.sensor_schema( + UNIT_PERCENT, ICON_EMPTY, 1, DEVICE_CLASS_HUMIDITY + ), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x44)) +) def to_code(config): diff --git a/esphome/components/shtcx/sensor.py b/esphome/components/shtcx/sensor.py index 788dd0021a..4b1c6c7afe 100644 --- a/esphome/components/shtcx/sensor.py +++ b/esphome/components/shtcx/sensor.py @@ -1,23 +1,39 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import CONF_HUMIDITY, CONF_ID, CONF_TEMPERATURE, DEVICE_CLASS_HUMIDITY, \ - DEVICE_CLASS_TEMPERATURE, ICON_EMPTY, UNIT_CELSIUS, UNIT_PERCENT +from esphome.const import ( + CONF_HUMIDITY, + CONF_ID, + CONF_TEMPERATURE, + DEVICE_CLASS_HUMIDITY, + DEVICE_CLASS_TEMPERATURE, + ICON_EMPTY, + UNIT_CELSIUS, + UNIT_PERCENT, +) -DEPENDENCIES = ['i2c'] +DEPENDENCIES = ["i2c"] -shtcx_ns = cg.esphome_ns.namespace('shtcx') -SHTCXComponent = shtcx_ns.class_('SHTCXComponent', cg.PollingComponent, i2c.I2CDevice) +shtcx_ns = cg.esphome_ns.namespace("shtcx") +SHTCXComponent = shtcx_ns.class_("SHTCXComponent", cg.PollingComponent, i2c.I2CDevice) -SHTCXType = shtcx_ns.enum('SHTCXType') +SHTCXType = shtcx_ns.enum("SHTCXType") -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(SHTCXComponent), - cv.Required(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, - DEVICE_CLASS_TEMPERATURE), - cv.Required(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 1, - DEVICE_CLASS_HUMIDITY), -}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x70)) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(SHTCXComponent), + cv.Required(CONF_TEMPERATURE): sensor.sensor_schema( + UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE + ), + cv.Required(CONF_HUMIDITY): sensor.sensor_schema( + UNIT_PERCENT, ICON_EMPTY, 1, DEVICE_CLASS_HUMIDITY + ), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x70)) +) def to_code(config): diff --git a/esphome/components/shutdown/__init__.py b/esphome/components/shutdown/__init__.py index 63db7aee2e..f70ffa9520 100644 --- a/esphome/components/shutdown/__init__.py +++ b/esphome/components/shutdown/__init__.py @@ -1 +1 @@ -CODEOWNERS = ['@esphome/core'] +CODEOWNERS = ["@esphome/core"] diff --git a/esphome/components/shutdown/switch.py b/esphome/components/shutdown/switch.py index 9826f9bbe5..e35029afd7 100644 --- a/esphome/components/shutdown/switch.py +++ b/esphome/components/shutdown/switch.py @@ -3,16 +3,18 @@ import esphome.config_validation as cv from esphome.components import switch from esphome.const import CONF_ID, CONF_INVERTED, CONF_ICON, ICON_POWER -shutdown_ns = cg.esphome_ns.namespace('shutdown') -ShutdownSwitch = shutdown_ns.class_('ShutdownSwitch', switch.Switch, cg.Component) +shutdown_ns = cg.esphome_ns.namespace("shutdown") +ShutdownSwitch = shutdown_ns.class_("ShutdownSwitch", switch.Switch, cg.Component) -CONFIG_SCHEMA = switch.SWITCH_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(ShutdownSwitch), - - cv.Optional(CONF_INVERTED): cv.invalid("Shutdown switches do not support inverted mode!"), - - cv.Optional(CONF_ICON, default=ICON_POWER): switch.icon -}).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = switch.SWITCH_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(ShutdownSwitch), + cv.Optional(CONF_INVERTED): cv.invalid( + "Shutdown switches do not support inverted mode!" + ), + cv.Optional(CONF_ICON, default=ICON_POWER): switch.icon, + } +).extend(cv.COMPONENT_SCHEMA) def to_code(config): diff --git a/esphome/components/sim800l/__init__.py b/esphome/components/sim800l/__init__.py index 0c4215d8e4..3b06ca6d89 100644 --- a/esphome/components/sim800l/__init__.py +++ b/esphome/components/sim800l/__init__.py @@ -4,31 +4,42 @@ from esphome import automation from esphome.const import CONF_ID, CONF_TRIGGER_ID from esphome.components import uart -DEPENDENCIES = ['uart'] -CODEOWNERS = ['@glmnet'] +DEPENDENCIES = ["uart"] +CODEOWNERS = ["@glmnet"] MULTI_CONF = True -sim800l_ns = cg.esphome_ns.namespace('sim800l') -Sim800LComponent = sim800l_ns.class_('Sim800LComponent', cg.Component) +sim800l_ns = cg.esphome_ns.namespace("sim800l") +Sim800LComponent = sim800l_ns.class_("Sim800LComponent", cg.Component) -Sim800LReceivedMessageTrigger = sim800l_ns.class_('Sim800LReceivedMessageTrigger', - automation.Trigger.template(cg.std_string, - cg.std_string)) +Sim800LReceivedMessageTrigger = sim800l_ns.class_( + "Sim800LReceivedMessageTrigger", + automation.Trigger.template(cg.std_string, cg.std_string), +) # Actions -Sim800LSendSmsAction = sim800l_ns.class_('Sim800LSendSmsAction', automation.Action) -Sim800LDialAction = sim800l_ns.class_('Sim800LDialAction', automation.Action) +Sim800LSendSmsAction = sim800l_ns.class_("Sim800LSendSmsAction", automation.Action) +Sim800LDialAction = sim800l_ns.class_("Sim800LDialAction", automation.Action) -CONF_ON_SMS_RECEIVED = 'on_sms_received' -CONF_RECIPIENT = 'recipient' -CONF_MESSAGE = 'message' +CONF_ON_SMS_RECEIVED = "on_sms_received" +CONF_RECIPIENT = "recipient" +CONF_MESSAGE = "message" -CONFIG_SCHEMA = cv.All(cv.Schema({ - cv.GenerateID(): cv.declare_id(Sim800LComponent), - cv.Optional(CONF_ON_SMS_RECEIVED): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(Sim800LReceivedMessageTrigger), - }), -}).extend(cv.polling_component_schema('5s')).extend(uart.UART_DEVICE_SCHEMA)) +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(Sim800LComponent), + cv.Optional(CONF_ON_SMS_RECEIVED): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( + Sim800LReceivedMessageTrigger + ), + } + ), + } + ) + .extend(cv.polling_component_schema("5s")) + .extend(uart.UART_DEVICE_SCHEMA) +) def to_code(config): @@ -38,18 +49,23 @@ def to_code(config): for conf in config.get(CONF_ON_SMS_RECEIVED, []): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) - yield automation.build_automation(trigger, [(cg.std_string, 'message'), - (cg.std_string, 'sender')], conf) + yield automation.build_automation( + trigger, [(cg.std_string, "message"), (cg.std_string, "sender")], conf + ) -SIM800L_SEND_SMS_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.use_id(Sim800LComponent), - cv.Required(CONF_RECIPIENT): cv.templatable(cv.string_strict), - cv.Required(CONF_MESSAGE): cv.templatable(cv.string), -}) +SIM800L_SEND_SMS_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.use_id(Sim800LComponent), + cv.Required(CONF_RECIPIENT): cv.templatable(cv.string_strict), + cv.Required(CONF_MESSAGE): cv.templatable(cv.string), + } +) -@automation.register_action('sim800l.send_sms', Sim800LSendSmsAction, SIM800L_SEND_SMS_SCHEMA) +@automation.register_action( + "sim800l.send_sms", Sim800LSendSmsAction, SIM800L_SEND_SMS_SCHEMA +) def sim800l_send_sms_to_code(config, action_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) var = cg.new_Pvariable(action_id, template_arg, paren) @@ -60,13 +76,15 @@ def sim800l_send_sms_to_code(config, action_id, template_arg, args): yield var -SIM800L_DIAL_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.use_id(Sim800LComponent), - cv.Required(CONF_RECIPIENT): cv.templatable(cv.string_strict), -}) +SIM800L_DIAL_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.use_id(Sim800LComponent), + cv.Required(CONF_RECIPIENT): cv.templatable(cv.string_strict), + } +) -@automation.register_action('sim800l.dial', Sim800LDialAction, SIM800L_DIAL_SCHEMA) +@automation.register_action("sim800l.dial", Sim800LDialAction, SIM800L_DIAL_SCHEMA) def sim800l_dial_to_code(config, action_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) var = cg.new_Pvariable(action_id, template_arg, paren) diff --git a/esphome/components/sm16716/__init__.py b/esphome/components/sm16716/__init__.py index 4e342588f9..8030f78f41 100644 --- a/esphome/components/sm16716/__init__.py +++ b/esphome/components/sm16716/__init__.py @@ -1,21 +1,28 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins -from esphome.const import (CONF_CLOCK_PIN, CONF_DATA_PIN, CONF_ID, - CONF_NUM_CHANNELS, CONF_NUM_CHIPS) +from esphome.const import ( + CONF_CLOCK_PIN, + CONF_DATA_PIN, + CONF_ID, + CONF_NUM_CHANNELS, + CONF_NUM_CHIPS, +) -AUTO_LOAD = ['output'] -sm16716_ns = cg.esphome_ns.namespace('sm16716') -SM16716 = sm16716_ns.class_('SM16716', cg.Component) +AUTO_LOAD = ["output"] +sm16716_ns = cg.esphome_ns.namespace("sm16716") +SM16716 = sm16716_ns.class_("SM16716", cg.Component) MULTI_CONF = True -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(SM16716), - cv.Required(CONF_DATA_PIN): pins.gpio_output_pin_schema, - cv.Required(CONF_CLOCK_PIN): pins.gpio_output_pin_schema, - cv.Optional(CONF_NUM_CHANNELS, default=3): cv.int_range(min=3, max=255), - cv.Optional(CONF_NUM_CHIPS, default=1): cv.int_range(min=1, max=85), -}).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(SM16716), + cv.Required(CONF_DATA_PIN): pins.gpio_output_pin_schema, + cv.Required(CONF_CLOCK_PIN): pins.gpio_output_pin_schema, + cv.Optional(CONF_NUM_CHANNELS, default=3): cv.int_range(min=3, max=255), + cv.Optional(CONF_NUM_CHIPS, default=1): cv.int_range(min=1, max=85), + } +).extend(cv.COMPONENT_SCHEMA) def to_code(config): diff --git a/esphome/components/sm16716/output.py b/esphome/components/sm16716/output.py index 93c9ed4ce1..033bd86e9c 100644 --- a/esphome/components/sm16716/output.py +++ b/esphome/components/sm16716/output.py @@ -4,16 +4,18 @@ from esphome.components import output from esphome.const import CONF_CHANNEL, CONF_ID from . import SM16716 -DEPENDENCIES = ['sm16716'] +DEPENDENCIES = ["sm16716"] -Channel = SM16716.class_('Channel', output.FloatOutput) +Channel = SM16716.class_("Channel", output.FloatOutput) -CONF_SM16716_ID = 'sm16716_id' -CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend({ - cv.GenerateID(CONF_SM16716_ID): cv.use_id(SM16716), - cv.Required(CONF_ID): cv.declare_id(Channel), - cv.Required(CONF_CHANNEL): cv.int_range(min=0, max=65535), -}).extend(cv.COMPONENT_SCHEMA) +CONF_SM16716_ID = "sm16716_id" +CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend( + { + cv.GenerateID(CONF_SM16716_ID): cv.use_id(SM16716), + cv.Required(CONF_ID): cv.declare_id(Channel), + cv.Required(CONF_CHANNEL): cv.int_range(min=0, max=65535), + } +).extend(cv.COMPONENT_SCHEMA) def to_code(config): diff --git a/esphome/components/sm300d2/sensor.py b/esphome/components/sm300d2/sensor.py index 2191143ec2..b1df1fb1b8 100644 --- a/esphome/components/sm300d2/sensor.py +++ b/esphome/components/sm300d2/sensor.py @@ -1,36 +1,67 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, uart -from esphome.const import CONF_ID, CONF_CO2, CONF_FORMALDEHYDE, CONF_TVOC, CONF_PM_2_5, \ - CONF_PM_10_0, CONF_TEMPERATURE, CONF_HUMIDITY, DEVICE_CLASS_EMPTY, DEVICE_CLASS_TEMPERATURE, \ - DEVICE_CLASS_HUMIDITY, UNIT_PARTS_PER_MILLION, UNIT_MICROGRAMS_PER_CUBIC_METER, UNIT_CELSIUS, \ - UNIT_PERCENT, ICON_EMPTY, ICON_MOLECULE_CO2, ICON_FLASK, ICON_CHEMICAL_WEAPON, ICON_GRAIN +from esphome.const import ( + CONF_ID, + CONF_CO2, + CONF_FORMALDEHYDE, + CONF_TVOC, + CONF_PM_2_5, + CONF_PM_10_0, + CONF_TEMPERATURE, + CONF_HUMIDITY, + DEVICE_CLASS_EMPTY, + DEVICE_CLASS_TEMPERATURE, + DEVICE_CLASS_HUMIDITY, + UNIT_PARTS_PER_MILLION, + UNIT_MICROGRAMS_PER_CUBIC_METER, + UNIT_CELSIUS, + UNIT_PERCENT, + ICON_EMPTY, + ICON_MOLECULE_CO2, + ICON_FLASK, + ICON_CHEMICAL_WEAPON, + ICON_GRAIN, +) -DEPENDENCIES = ['uart'] +DEPENDENCIES = ["uart"] -sm300d2_ns = cg.esphome_ns.namespace('sm300d2') -SM300D2Sensor = sm300d2_ns.class_('SM300D2Sensor', cg.PollingComponent, uart.UARTDevice) +sm300d2_ns = cg.esphome_ns.namespace("sm300d2") +SM300D2Sensor = sm300d2_ns.class_("SM300D2Sensor", cg.PollingComponent, uart.UARTDevice) -CONFIG_SCHEMA = cv.All(cv.Schema({ - cv.GenerateID(): cv.declare_id(SM300D2Sensor), - - cv.Optional(CONF_CO2): - sensor.sensor_schema(UNIT_PARTS_PER_MILLION, ICON_MOLECULE_CO2, 0, DEVICE_CLASS_EMPTY), - cv.Optional(CONF_FORMALDEHYDE): - sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_FLASK, 0, DEVICE_CLASS_EMPTY), - cv.Optional(CONF_TVOC): - sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_CHEMICAL_WEAPON, 0, - DEVICE_CLASS_EMPTY), - cv.Optional(CONF_PM_2_5): - sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_GRAIN, 0, DEVICE_CLASS_EMPTY), - cv.Optional(CONF_PM_10_0): - sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_GRAIN, 0, DEVICE_CLASS_EMPTY), - cv.Optional(CONF_TEMPERATURE): - sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 0, DEVICE_CLASS_TEMPERATURE), - cv.Optional(CONF_HUMIDITY): - sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 0, DEVICE_CLASS_HUMIDITY), - -}).extend(cv.polling_component_schema('60s')).extend(uart.UART_DEVICE_SCHEMA)) +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(SM300D2Sensor), + cv.Optional(CONF_CO2): sensor.sensor_schema( + UNIT_PARTS_PER_MILLION, ICON_MOLECULE_CO2, 0, DEVICE_CLASS_EMPTY + ), + cv.Optional(CONF_FORMALDEHYDE): sensor.sensor_schema( + UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_FLASK, 0, DEVICE_CLASS_EMPTY + ), + cv.Optional(CONF_TVOC): sensor.sensor_schema( + UNIT_MICROGRAMS_PER_CUBIC_METER, + ICON_CHEMICAL_WEAPON, + 0, + DEVICE_CLASS_EMPTY, + ), + cv.Optional(CONF_PM_2_5): sensor.sensor_schema( + UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_GRAIN, 0, DEVICE_CLASS_EMPTY + ), + cv.Optional(CONF_PM_10_0): sensor.sensor_schema( + UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_GRAIN, 0, DEVICE_CLASS_EMPTY + ), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( + UNIT_CELSIUS, ICON_EMPTY, 0, DEVICE_CLASS_TEMPERATURE + ), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema( + UNIT_PERCENT, ICON_EMPTY, 0, DEVICE_CLASS_HUMIDITY + ), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(uart.UART_DEVICE_SCHEMA) +) def to_code(config): diff --git a/esphome/components/sn74hc595/__init__.py b/esphome/components/sn74hc595/__init__.py index 369fc41fac..152ac02106 100644 --- a/esphome/components/sn74hc595/__init__.py +++ b/esphome/components/sn74hc595/__init__.py @@ -1,28 +1,36 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins -from esphome.const import CONF_ID, CONF_NUMBER, CONF_INVERTED, CONF_DATA_PIN, CONF_CLOCK_PIN +from esphome.const import ( + CONF_ID, + CONF_NUMBER, + CONF_INVERTED, + CONF_DATA_PIN, + CONF_CLOCK_PIN, +) DEPENDENCIES = [] MULTI_CONF = True -sn74hc595_ns = cg.esphome_ns.namespace('sn74hc595') +sn74hc595_ns = cg.esphome_ns.namespace("sn74hc595") -SN74HC595Component = sn74hc595_ns.class_('SN74HC595Component', cg.Component) -SN74HC595GPIOPin = sn74hc595_ns.class_('SN74HC595GPIOPin', cg.GPIOPin) +SN74HC595Component = sn74hc595_ns.class_("SN74HC595Component", cg.Component) +SN74HC595GPIOPin = sn74hc595_ns.class_("SN74HC595GPIOPin", cg.GPIOPin) -CONF_SN74HC595 = 'sn74hc595' -CONF_LATCH_PIN = 'latch_pin' -CONF_OE_PIN = 'oe_pin' -CONF_SR_COUNT = 'sr_count' -CONFIG_SCHEMA = cv.Schema({ - cv.Required(CONF_ID): cv.declare_id(SN74HC595Component), - cv.Required(CONF_DATA_PIN): pins.gpio_output_pin_schema, - cv.Required(CONF_CLOCK_PIN): pins.gpio_output_pin_schema, - cv.Required(CONF_LATCH_PIN): pins.gpio_output_pin_schema, - cv.Optional(CONF_OE_PIN): pins.gpio_output_pin_schema, - cv.Optional(CONF_SR_COUNT, default=1): cv.int_range(1, 4) -}).extend(cv.COMPONENT_SCHEMA) +CONF_SN74HC595 = "sn74hc595" +CONF_LATCH_PIN = "latch_pin" +CONF_OE_PIN = "oe_pin" +CONF_SR_COUNT = "sr_count" +CONFIG_SCHEMA = cv.Schema( + { + cv.Required(CONF_ID): cv.declare_id(SN74HC595Component), + cv.Required(CONF_DATA_PIN): pins.gpio_output_pin_schema, + cv.Required(CONF_CLOCK_PIN): pins.gpio_output_pin_schema, + cv.Required(CONF_LATCH_PIN): pins.gpio_output_pin_schema, + cv.Optional(CONF_OE_PIN): pins.gpio_output_pin_schema, + cv.Optional(CONF_SR_COUNT, default=1): cv.int_range(1, 4), + } +).extend(cv.COMPONENT_SCHEMA) def to_code(config): @@ -40,16 +48,19 @@ def to_code(config): cg.add(var.set_sr_count(config[CONF_SR_COUNT])) -SN74HC595_OUTPUT_PIN_SCHEMA = cv.Schema({ - cv.Required(CONF_SN74HC595): cv.use_id(SN74HC595Component), - cv.Required(CONF_NUMBER): cv.int_, - cv.Optional(CONF_INVERTED, default=False): cv.boolean, -}) +SN74HC595_OUTPUT_PIN_SCHEMA = cv.Schema( + { + cv.Required(CONF_SN74HC595): cv.use_id(SN74HC595Component), + cv.Required(CONF_NUMBER): cv.int_, + cv.Optional(CONF_INVERTED, default=False): cv.boolean, + } +) SN74HC595_INPUT_PIN_SCHEMA = cv.Schema({}) -@pins.PIN_SCHEMA_REGISTRY.register(CONF_SN74HC595, - (SN74HC595_OUTPUT_PIN_SCHEMA, SN74HC595_INPUT_PIN_SCHEMA)) +@pins.PIN_SCHEMA_REGISTRY.register( + CONF_SN74HC595, (SN74HC595_OUTPUT_PIN_SCHEMA, SN74HC595_INPUT_PIN_SCHEMA) +) def sn74hc595_pin_to_code(config): parent = yield cg.get_variable(config[CONF_SN74HC595]) yield SN74HC595GPIOPin.new(parent, config[CONF_NUMBER], config[CONF_INVERTED]) diff --git a/esphome/components/sntp/time.py b/esphome/components/sntp/time.py index f6afcced0c..a142ad72d7 100644 --- a/esphome/components/sntp/time.py +++ b/esphome/components/sntp/time.py @@ -5,25 +5,28 @@ from esphome.core import CORE from esphome.const import CONF_ID, CONF_SERVERS -DEPENDENCIES = ['network'] -sntp_ns = cg.esphome_ns.namespace('sntp') -SNTPComponent = sntp_ns.class_('SNTPComponent', time_.RealTimeClock) +DEPENDENCIES = ["network"] +sntp_ns = cg.esphome_ns.namespace("sntp") +SNTPComponent = sntp_ns.class_("SNTPComponent", time_.RealTimeClock) DEFAULT_SERVERS = ["0.pool.ntp.org", "1.pool.ntp.org", "2.pool.ntp.org"] -CONFIG_SCHEMA = time_.TIME_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(SNTPComponent), - cv.Optional(CONF_SERVERS, default=DEFAULT_SERVERS): - cv.All(cv.ensure_list(cv.domain), cv.Length(min=1, max=3)), -}).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = time_.TIME_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(SNTPComponent), + cv.Optional(CONF_SERVERS, default=DEFAULT_SERVERS): cv.All( + cv.ensure_list(cv.domain), cv.Length(min=1, max=3) + ), + } +).extend(cv.COMPONENT_SCHEMA) def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) servers = config[CONF_SERVERS] - servers += [''] * (3 - len(servers)) + servers += [""] * (3 - len(servers)) cg.add(var.set_servers(*servers)) yield cg.register_component(var, config) @@ -31,4 +34,4 @@ def to_code(config): if CORE.is_esp8266 and len(servers) > 1: # We need LwIP features enabled to get 3 SNTP servers (not just one) - cg.add_build_flag('-DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY') + cg.add_build_flag("-DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY") diff --git a/esphome/components/speed/__init__.py b/esphome/components/speed/__init__.py index 7c7d64ed14..b75374863b 100644 --- a/esphome/components/speed/__init__.py +++ b/esphome/components/speed/__init__.py @@ -1,3 +1,3 @@ import esphome.codegen as cg -speed_ns = cg.esphome_ns.namespace('speed') +speed_ns = cg.esphome_ns.namespace("speed") diff --git a/esphome/components/speed/fan/__init__.py b/esphome/components/speed/fan/__init__.py index 420c957d87..a8f306d713 100644 --- a/esphome/components/speed/fan/__init__.py +++ b/esphome/components/speed/fan/__init__.py @@ -1,23 +1,35 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import fan, output -from esphome.const import CONF_OSCILLATION_OUTPUT, CONF_OUTPUT, CONF_DIRECTION_OUTPUT, \ - CONF_OUTPUT_ID, CONF_SPEED, CONF_LOW, CONF_MEDIUM, CONF_HIGH +from esphome.const import ( + CONF_OSCILLATION_OUTPUT, + CONF_OUTPUT, + CONF_DIRECTION_OUTPUT, + CONF_OUTPUT_ID, + CONF_SPEED, + CONF_LOW, + CONF_MEDIUM, + CONF_HIGH, +) from .. import speed_ns -SpeedFan = speed_ns.class_('SpeedFan', cg.Component) +SpeedFan = speed_ns.class_("SpeedFan", cg.Component) -CONFIG_SCHEMA = fan.FAN_SCHEMA.extend({ - cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(SpeedFan), - cv.Required(CONF_OUTPUT): cv.use_id(output.FloatOutput), - cv.Optional(CONF_OSCILLATION_OUTPUT): cv.use_id(output.BinaryOutput), - cv.Optional(CONF_DIRECTION_OUTPUT): cv.use_id(output.BinaryOutput), - cv.Optional(CONF_SPEED, default={}): cv.Schema({ - cv.Optional(CONF_LOW, default=0.33): cv.percentage, - cv.Optional(CONF_MEDIUM, default=0.66): cv.percentage, - cv.Optional(CONF_HIGH, default=1.0): cv.percentage, - }), -}).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = fan.FAN_SCHEMA.extend( + { + cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(SpeedFan), + cv.Required(CONF_OUTPUT): cv.use_id(output.FloatOutput), + cv.Optional(CONF_OSCILLATION_OUTPUT): cv.use_id(output.BinaryOutput), + cv.Optional(CONF_DIRECTION_OUTPUT): cv.use_id(output.BinaryOutput), + cv.Optional(CONF_SPEED, default={}): cv.Schema( + { + cv.Optional(CONF_LOW, default=0.33): cv.percentage, + cv.Optional(CONF_MEDIUM, default=0.66): cv.percentage, + cv.Optional(CONF_HIGH, default=1.0): cv.percentage, + } + ), + } +).extend(cv.COMPONENT_SCHEMA) def to_code(config): diff --git a/esphome/components/spi/__init__.py b/esphome/components/spi/__init__.py index e7f8bc378d..5a25ac254b 100644 --- a/esphome/components/spi/__init__.py +++ b/esphome/components/spi/__init__.py @@ -1,22 +1,33 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins -from esphome.const import CONF_CLK_PIN, CONF_ID, CONF_MISO_PIN, CONF_MOSI_PIN, CONF_SPI_ID, \ - CONF_CS_PIN +from esphome.const import ( + CONF_CLK_PIN, + CONF_ID, + CONF_MISO_PIN, + CONF_MOSI_PIN, + CONF_SPI_ID, + CONF_CS_PIN, +) from esphome.core import coroutine, coroutine_with_priority -CODEOWNERS = ['@esphome/core'] -spi_ns = cg.esphome_ns.namespace('spi') -SPIComponent = spi_ns.class_('SPIComponent', cg.Component) -SPIDevice = spi_ns.class_('SPIDevice') +CODEOWNERS = ["@esphome/core"] +spi_ns = cg.esphome_ns.namespace("spi") +SPIComponent = spi_ns.class_("SPIComponent", cg.Component) +SPIDevice = spi_ns.class_("SPIDevice") MULTI_CONF = True -CONFIG_SCHEMA = cv.All(cv.Schema({ - cv.GenerateID(): cv.declare_id(SPIComponent), - cv.Required(CONF_CLK_PIN): pins.gpio_output_pin_schema, - cv.Optional(CONF_MISO_PIN): pins.gpio_input_pin_schema, - cv.Optional(CONF_MOSI_PIN): pins.gpio_output_pin_schema, -}), cv.has_at_least_one_key(CONF_MISO_PIN, CONF_MOSI_PIN)) +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(SPIComponent), + cv.Required(CONF_CLK_PIN): pins.gpio_output_pin_schema, + cv.Optional(CONF_MISO_PIN): pins.gpio_input_pin_schema, + cv.Optional(CONF_MOSI_PIN): pins.gpio_output_pin_schema, + } + ), + cv.has_at_least_one_key(CONF_MISO_PIN, CONF_MOSI_PIN), +) @coroutine_with_priority(1.0) diff --git a/esphome/components/sps30/sensor.py b/esphome/components/sps30/sensor.py index 9f139aa262..5bc586ea8b 100644 --- a/esphome/components/sps30/sensor.py +++ b/esphome/components/sps30/sensor.py @@ -1,39 +1,83 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import CONF_ID, CONF_PM_1_0, CONF_PM_2_5, CONF_PM_4_0, CONF_PM_10_0, \ - CONF_PMC_0_5, CONF_PMC_1_0, CONF_PMC_2_5, CONF_PMC_4_0, CONF_PMC_10_0, CONF_PM_SIZE, \ - DEVICE_CLASS_EMPTY, UNIT_MICROGRAMS_PER_CUBIC_METER, UNIT_COUNTS_PER_CUBIC_METER, \ - UNIT_MICROMETER, ICON_CHEMICAL_WEAPON, ICON_COUNTER, ICON_RULER +from esphome.const import ( + CONF_ID, + CONF_PM_1_0, + CONF_PM_2_5, + CONF_PM_4_0, + CONF_PM_10_0, + CONF_PMC_0_5, + CONF_PMC_1_0, + CONF_PMC_2_5, + CONF_PMC_4_0, + CONF_PMC_10_0, + CONF_PM_SIZE, + DEVICE_CLASS_EMPTY, + UNIT_MICROGRAMS_PER_CUBIC_METER, + UNIT_COUNTS_PER_CUBIC_METER, + UNIT_MICROMETER, + ICON_CHEMICAL_WEAPON, + ICON_COUNTER, + ICON_RULER, +) -DEPENDENCIES = ['i2c'] +DEPENDENCIES = ["i2c"] -sps30_ns = cg.esphome_ns.namespace('sps30') -SPS30Component = sps30_ns.class_('SPS30Component', cg.PollingComponent, i2c.I2CDevice) +sps30_ns = cg.esphome_ns.namespace("sps30") +SPS30Component = sps30_ns.class_("SPS30Component", cg.PollingComponent, i2c.I2CDevice) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(SPS30Component), - cv.Optional(CONF_PM_1_0): sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, - ICON_CHEMICAL_WEAPON, 2, DEVICE_CLASS_EMPTY), - cv.Optional(CONF_PM_2_5): sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, - ICON_CHEMICAL_WEAPON, 2, DEVICE_CLASS_EMPTY), - cv.Optional(CONF_PM_4_0): sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, - ICON_CHEMICAL_WEAPON, 2, DEVICE_CLASS_EMPTY), - cv.Optional(CONF_PM_10_0): sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, - ICON_CHEMICAL_WEAPON, 2, DEVICE_CLASS_EMPTY), - cv.Optional(CONF_PMC_0_5): sensor.sensor_schema(UNIT_COUNTS_PER_CUBIC_METER, - ICON_COUNTER, 2, DEVICE_CLASS_EMPTY), - cv.Optional(CONF_PMC_1_0): sensor.sensor_schema(UNIT_COUNTS_PER_CUBIC_METER, - ICON_COUNTER, 2, DEVICE_CLASS_EMPTY), - cv.Optional(CONF_PMC_2_5): sensor.sensor_schema(UNIT_COUNTS_PER_CUBIC_METER, - ICON_COUNTER, 2, DEVICE_CLASS_EMPTY), - cv.Optional(CONF_PMC_4_0): sensor.sensor_schema(UNIT_COUNTS_PER_CUBIC_METER, - ICON_COUNTER, 2, DEVICE_CLASS_EMPTY), - cv.Optional(CONF_PMC_10_0): sensor.sensor_schema(UNIT_COUNTS_PER_CUBIC_METER, - ICON_COUNTER, 2, DEVICE_CLASS_EMPTY), - cv.Optional(CONF_PM_SIZE): sensor.sensor_schema(UNIT_MICROMETER, - ICON_RULER, 0, DEVICE_CLASS_EMPTY), -}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x69)) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(SPS30Component), + cv.Optional(CONF_PM_1_0): sensor.sensor_schema( + UNIT_MICROGRAMS_PER_CUBIC_METER, + ICON_CHEMICAL_WEAPON, + 2, + DEVICE_CLASS_EMPTY, + ), + cv.Optional(CONF_PM_2_5): sensor.sensor_schema( + UNIT_MICROGRAMS_PER_CUBIC_METER, + ICON_CHEMICAL_WEAPON, + 2, + DEVICE_CLASS_EMPTY, + ), + cv.Optional(CONF_PM_4_0): sensor.sensor_schema( + UNIT_MICROGRAMS_PER_CUBIC_METER, + ICON_CHEMICAL_WEAPON, + 2, + DEVICE_CLASS_EMPTY, + ), + cv.Optional(CONF_PM_10_0): sensor.sensor_schema( + UNIT_MICROGRAMS_PER_CUBIC_METER, + ICON_CHEMICAL_WEAPON, + 2, + DEVICE_CLASS_EMPTY, + ), + cv.Optional(CONF_PMC_0_5): sensor.sensor_schema( + UNIT_COUNTS_PER_CUBIC_METER, ICON_COUNTER, 2, DEVICE_CLASS_EMPTY + ), + cv.Optional(CONF_PMC_1_0): sensor.sensor_schema( + UNIT_COUNTS_PER_CUBIC_METER, ICON_COUNTER, 2, DEVICE_CLASS_EMPTY + ), + cv.Optional(CONF_PMC_2_5): sensor.sensor_schema( + UNIT_COUNTS_PER_CUBIC_METER, ICON_COUNTER, 2, DEVICE_CLASS_EMPTY + ), + cv.Optional(CONF_PMC_4_0): sensor.sensor_schema( + UNIT_COUNTS_PER_CUBIC_METER, ICON_COUNTER, 2, DEVICE_CLASS_EMPTY + ), + cv.Optional(CONF_PMC_10_0): sensor.sensor_schema( + UNIT_COUNTS_PER_CUBIC_METER, ICON_COUNTER, 2, DEVICE_CLASS_EMPTY + ), + cv.Optional(CONF_PM_SIZE): sensor.sensor_schema( + UNIT_MICROMETER, ICON_RULER, 0, DEVICE_CLASS_EMPTY + ), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x69)) +) def to_code(config): diff --git a/esphome/components/ssd1306_base/__init__.py b/esphome/components/ssd1306_base/__init__.py index a8b2a2a7bb..af7c1a019a 100644 --- a/esphome/components/ssd1306_base/__init__.py +++ b/esphome/components/ssd1306_base/__init__.py @@ -2,33 +2,40 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins from esphome.components import display -from esphome.const import CONF_EXTERNAL_VCC, CONF_LAMBDA, CONF_MODEL, CONF_RESET_PIN, \ - CONF_BRIGHTNESS +from esphome.const import ( + CONF_EXTERNAL_VCC, + CONF_LAMBDA, + CONF_MODEL, + CONF_RESET_PIN, + CONF_BRIGHTNESS, +) from esphome.core import coroutine -ssd1306_base_ns = cg.esphome_ns.namespace('ssd1306_base') -SSD1306 = ssd1306_base_ns.class_('SSD1306', cg.PollingComponent, display.DisplayBuffer) -SSD1306Model = ssd1306_base_ns.enum('SSD1306Model') +ssd1306_base_ns = cg.esphome_ns.namespace("ssd1306_base") +SSD1306 = ssd1306_base_ns.class_("SSD1306", cg.PollingComponent, display.DisplayBuffer) +SSD1306Model = ssd1306_base_ns.enum("SSD1306Model") MODELS = { - 'SSD1306_128X32': SSD1306Model.SSD1306_MODEL_128_32, - 'SSD1306_128X64': SSD1306Model.SSD1306_MODEL_128_64, - 'SSD1306_96X16': SSD1306Model.SSD1306_MODEL_96_16, - 'SSD1306_64X48': SSD1306Model.SSD1306_MODEL_64_48, - 'SH1106_128X32': SSD1306Model.SH1106_MODEL_128_32, - 'SH1106_128X64': SSD1306Model.SH1106_MODEL_128_64, - 'SH1106_96X16': SSD1306Model.SH1106_MODEL_96_16, - 'SH1106_64X48': SSD1306Model.SH1106_MODEL_64_48, + "SSD1306_128X32": SSD1306Model.SSD1306_MODEL_128_32, + "SSD1306_128X64": SSD1306Model.SSD1306_MODEL_128_64, + "SSD1306_96X16": SSD1306Model.SSD1306_MODEL_96_16, + "SSD1306_64X48": SSD1306Model.SSD1306_MODEL_64_48, + "SH1106_128X32": SSD1306Model.SH1106_MODEL_128_32, + "SH1106_128X64": SSD1306Model.SH1106_MODEL_128_64, + "SH1106_96X16": SSD1306Model.SH1106_MODEL_96_16, + "SH1106_64X48": SSD1306Model.SH1106_MODEL_64_48, } SSD1306_MODEL = cv.enum(MODELS, upper=True, space="_") -SSD1306_SCHEMA = display.FULL_DISPLAY_SCHEMA.extend({ - cv.Required(CONF_MODEL): SSD1306_MODEL, - cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, - cv.Optional(CONF_BRIGHTNESS, default=1.0): cv.percentage, - cv.Optional(CONF_EXTERNAL_VCC): cv.boolean, -}).extend(cv.polling_component_schema('1s')) +SSD1306_SCHEMA = display.FULL_DISPLAY_SCHEMA.extend( + { + cv.Required(CONF_MODEL): SSD1306_MODEL, + cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, + cv.Optional(CONF_BRIGHTNESS, default=1.0): cv.percentage, + cv.Optional(CONF_EXTERNAL_VCC): cv.boolean, + } +).extend(cv.polling_component_schema("1s")) @coroutine @@ -46,5 +53,6 @@ def setup_ssd1036(var, config): cg.add(var.set_external_vcc(config[CONF_EXTERNAL_VCC])) if CONF_LAMBDA in config: lambda_ = yield cg.process_lambda( - config[CONF_LAMBDA], [(display.DisplayBufferRef, 'it')], return_type=cg.void) + config[CONF_LAMBDA], [(display.DisplayBufferRef, "it")], return_type=cg.void + ) cg.add(var.set_writer(lambda_)) diff --git a/esphome/components/ssd1306_i2c/display.py b/esphome/components/ssd1306_i2c/display.py index eaa656f26b..c51ef1cfba 100644 --- a/esphome/components/ssd1306_i2c/display.py +++ b/esphome/components/ssd1306_i2c/display.py @@ -3,16 +3,22 @@ import esphome.config_validation as cv from esphome.components import ssd1306_base, i2c from esphome.const import CONF_ID, CONF_LAMBDA, CONF_PAGES -AUTO_LOAD = ['ssd1306_base'] -DEPENDENCIES = ['i2c'] +AUTO_LOAD = ["ssd1306_base"] +DEPENDENCIES = ["i2c"] -ssd1306_i2c = cg.esphome_ns.namespace('ssd1306_i2c') -I2CSSD1306 = ssd1306_i2c.class_('I2CSSD1306', ssd1306_base.SSD1306, i2c.I2CDevice) +ssd1306_i2c = cg.esphome_ns.namespace("ssd1306_i2c") +I2CSSD1306 = ssd1306_i2c.class_("I2CSSD1306", ssd1306_base.SSD1306, i2c.I2CDevice) -CONFIG_SCHEMA = cv.All(ssd1306_base.SSD1306_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(I2CSSD1306), -}).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(0x3C)), - cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA)) +CONFIG_SCHEMA = cv.All( + ssd1306_base.SSD1306_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(I2CSSD1306), + } + ) + .extend(cv.COMPONENT_SCHEMA) + .extend(i2c.i2c_device_schema(0x3C)), + cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA), +) def to_code(config): diff --git a/esphome/components/ssd1306_spi/display.py b/esphome/components/ssd1306_spi/display.py index 19882af6c4..b1558ac413 100644 --- a/esphome/components/ssd1306_spi/display.py +++ b/esphome/components/ssd1306_spi/display.py @@ -4,17 +4,23 @@ from esphome import pins from esphome.components import spi, ssd1306_base from esphome.const import CONF_DC_PIN, CONF_ID, CONF_LAMBDA, CONF_PAGES -AUTO_LOAD = ['ssd1306_base'] -DEPENDENCIES = ['spi'] +AUTO_LOAD = ["ssd1306_base"] +DEPENDENCIES = ["spi"] -ssd1306_spi = cg.esphome_ns.namespace('ssd1306_spi') -SPISSD1306 = ssd1306_spi.class_('SPISSD1306', ssd1306_base.SSD1306, spi.SPIDevice) +ssd1306_spi = cg.esphome_ns.namespace("ssd1306_spi") +SPISSD1306 = ssd1306_spi.class_("SPISSD1306", ssd1306_base.SSD1306, spi.SPIDevice) -CONFIG_SCHEMA = cv.All(ssd1306_base.SSD1306_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(SPISSD1306), - cv.Required(CONF_DC_PIN): pins.gpio_output_pin_schema, -}).extend(cv.COMPONENT_SCHEMA).extend(spi.spi_device_schema()), - cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA)) +CONFIG_SCHEMA = cv.All( + ssd1306_base.SSD1306_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(SPISSD1306), + cv.Required(CONF_DC_PIN): pins.gpio_output_pin_schema, + } + ) + .extend(cv.COMPONENT_SCHEMA) + .extend(spi.spi_device_schema()), + cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA), +) def to_code(config): diff --git a/esphome/components/ssd1322_base/__init__.py b/esphome/components/ssd1322_base/__init__.py index addfec4153..0792fd0f89 100644 --- a/esphome/components/ssd1322_base/__init__.py +++ b/esphome/components/ssd1322_base/__init__.py @@ -2,28 +2,35 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins from esphome.components import display -from esphome.const import CONF_BRIGHTNESS, CONF_EXTERNAL_VCC, CONF_LAMBDA, CONF_MODEL, \ - CONF_RESET_PIN +from esphome.const import ( + CONF_BRIGHTNESS, + CONF_EXTERNAL_VCC, + CONF_LAMBDA, + CONF_MODEL, + CONF_RESET_PIN, +) from esphome.core import coroutine -CODEOWNERS = ['@kbx81'] +CODEOWNERS = ["@kbx81"] -ssd1322_base_ns = cg.esphome_ns.namespace('ssd1322_base') -SSD1322 = ssd1322_base_ns.class_('SSD1322', cg.PollingComponent, display.DisplayBuffer) -SSD1322Model = ssd1322_base_ns.enum('SSD1322Model') +ssd1322_base_ns = cg.esphome_ns.namespace("ssd1322_base") +SSD1322 = ssd1322_base_ns.class_("SSD1322", cg.PollingComponent, display.DisplayBuffer) +SSD1322Model = ssd1322_base_ns.enum("SSD1322Model") MODELS = { - 'SSD1322_256X64': SSD1322Model.SSD1322_MODEL_256_64, + "SSD1322_256X64": SSD1322Model.SSD1322_MODEL_256_64, } SSD1322_MODEL = cv.enum(MODELS, upper=True, space="_") -SSD1322_SCHEMA = display.FULL_DISPLAY_SCHEMA.extend({ - cv.Required(CONF_MODEL): SSD1322_MODEL, - cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, - cv.Optional(CONF_BRIGHTNESS, default=1.0): cv.percentage, - cv.Optional(CONF_EXTERNAL_VCC): cv.boolean, -}).extend(cv.polling_component_schema('1s')) +SSD1322_SCHEMA = display.FULL_DISPLAY_SCHEMA.extend( + { + cv.Required(CONF_MODEL): SSD1322_MODEL, + cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, + cv.Optional(CONF_BRIGHTNESS, default=1.0): cv.percentage, + cv.Optional(CONF_EXTERNAL_VCC): cv.boolean, + } +).extend(cv.polling_component_schema("1s")) @coroutine @@ -41,5 +48,6 @@ def setup_ssd1322(var, config): cg.add(var.set_external_vcc(config[CONF_EXTERNAL_VCC])) if CONF_LAMBDA in config: lambda_ = yield cg.process_lambda( - config[CONF_LAMBDA], [(display.DisplayBufferRef, 'it')], return_type=cg.void) + config[CONF_LAMBDA], [(display.DisplayBufferRef, "it")], return_type=cg.void + ) cg.add(var.set_writer(lambda_)) diff --git a/esphome/components/ssd1322_spi/display.py b/esphome/components/ssd1322_spi/display.py index cf900e9e2d..fa094acc5f 100644 --- a/esphome/components/ssd1322_spi/display.py +++ b/esphome/components/ssd1322_spi/display.py @@ -4,19 +4,25 @@ from esphome import pins from esphome.components import spi, ssd1322_base from esphome.const import CONF_DC_PIN, CONF_ID, CONF_LAMBDA, CONF_PAGES -CODEOWNERS = ['@kbx81'] +CODEOWNERS = ["@kbx81"] -AUTO_LOAD = ['ssd1322_base'] -DEPENDENCIES = ['spi'] +AUTO_LOAD = ["ssd1322_base"] +DEPENDENCIES = ["spi"] -ssd1322_spi = cg.esphome_ns.namespace('ssd1322_spi') -SPISSD1322 = ssd1322_spi.class_('SPISSD1322', ssd1322_base.SSD1322, spi.SPIDevice) +ssd1322_spi = cg.esphome_ns.namespace("ssd1322_spi") +SPISSD1322 = ssd1322_spi.class_("SPISSD1322", ssd1322_base.SSD1322, spi.SPIDevice) -CONFIG_SCHEMA = cv.All(ssd1322_base.SSD1322_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(SPISSD1322), - cv.Required(CONF_DC_PIN): pins.gpio_output_pin_schema, -}).extend(cv.COMPONENT_SCHEMA).extend(spi.spi_device_schema(cs_pin_required=False)), - cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA)) +CONFIG_SCHEMA = cv.All( + ssd1322_base.SSD1322_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(SPISSD1322), + cv.Required(CONF_DC_PIN): pins.gpio_output_pin_schema, + } + ) + .extend(cv.COMPONENT_SCHEMA) + .extend(spi.spi_device_schema(cs_pin_required=False)), + cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA), +) def to_code(config): diff --git a/esphome/components/ssd1325_base/__init__.py b/esphome/components/ssd1325_base/__init__.py index e51f67e8b4..1e6535c7ec 100644 --- a/esphome/components/ssd1325_base/__init__.py +++ b/esphome/components/ssd1325_base/__init__.py @@ -2,32 +2,39 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins from esphome.components import display -from esphome.const import CONF_BRIGHTNESS, CONF_EXTERNAL_VCC, CONF_LAMBDA, CONF_MODEL, \ - CONF_RESET_PIN +from esphome.const import ( + CONF_BRIGHTNESS, + CONF_EXTERNAL_VCC, + CONF_LAMBDA, + CONF_MODEL, + CONF_RESET_PIN, +) from esphome.core import coroutine -CODEOWNERS = ['@kbx81'] +CODEOWNERS = ["@kbx81"] -ssd1325_base_ns = cg.esphome_ns.namespace('ssd1325_base') -SSD1325 = ssd1325_base_ns.class_('SSD1325', cg.PollingComponent, display.DisplayBuffer) -SSD1325Model = ssd1325_base_ns.enum('SSD1325Model') +ssd1325_base_ns = cg.esphome_ns.namespace("ssd1325_base") +SSD1325 = ssd1325_base_ns.class_("SSD1325", cg.PollingComponent, display.DisplayBuffer) +SSD1325Model = ssd1325_base_ns.enum("SSD1325Model") MODELS = { - 'SSD1325_128X32': SSD1325Model.SSD1325_MODEL_128_32, - 'SSD1325_128X64': SSD1325Model.SSD1325_MODEL_128_64, - 'SSD1325_96X16': SSD1325Model.SSD1325_MODEL_96_16, - 'SSD1325_64X48': SSD1325Model.SSD1325_MODEL_64_48, - 'SSD1327_128X128': SSD1325Model.SSD1327_MODEL_128_128, + "SSD1325_128X32": SSD1325Model.SSD1325_MODEL_128_32, + "SSD1325_128X64": SSD1325Model.SSD1325_MODEL_128_64, + "SSD1325_96X16": SSD1325Model.SSD1325_MODEL_96_16, + "SSD1325_64X48": SSD1325Model.SSD1325_MODEL_64_48, + "SSD1327_128X128": SSD1325Model.SSD1327_MODEL_128_128, } SSD1325_MODEL = cv.enum(MODELS, upper=True, space="_") -SSD1325_SCHEMA = display.FULL_DISPLAY_SCHEMA.extend({ - cv.Required(CONF_MODEL): SSD1325_MODEL, - cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, - cv.Optional(CONF_BRIGHTNESS, default=1.0): cv.percentage, - cv.Optional(CONF_EXTERNAL_VCC): cv.boolean, -}).extend(cv.polling_component_schema('1s')) +SSD1325_SCHEMA = display.FULL_DISPLAY_SCHEMA.extend( + { + cv.Required(CONF_MODEL): SSD1325_MODEL, + cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, + cv.Optional(CONF_BRIGHTNESS, default=1.0): cv.percentage, + cv.Optional(CONF_EXTERNAL_VCC): cv.boolean, + } +).extend(cv.polling_component_schema("1s")) @coroutine @@ -45,5 +52,6 @@ def setup_ssd1325(var, config): cg.add(var.set_external_vcc(config[CONF_EXTERNAL_VCC])) if CONF_LAMBDA in config: lambda_ = yield cg.process_lambda( - config[CONF_LAMBDA], [(display.DisplayBufferRef, 'it')], return_type=cg.void) + config[CONF_LAMBDA], [(display.DisplayBufferRef, "it")], return_type=cg.void + ) cg.add(var.set_writer(lambda_)) diff --git a/esphome/components/ssd1325_spi/display.py b/esphome/components/ssd1325_spi/display.py index 9471cf9c76..6ce133bec1 100644 --- a/esphome/components/ssd1325_spi/display.py +++ b/esphome/components/ssd1325_spi/display.py @@ -4,19 +4,25 @@ from esphome import pins from esphome.components import spi, ssd1325_base from esphome.const import CONF_DC_PIN, CONF_ID, CONF_LAMBDA, CONF_PAGES -CODEOWNERS = ['@kbx81'] +CODEOWNERS = ["@kbx81"] -AUTO_LOAD = ['ssd1325_base'] -DEPENDENCIES = ['spi'] +AUTO_LOAD = ["ssd1325_base"] +DEPENDENCIES = ["spi"] -ssd1325_spi = cg.esphome_ns.namespace('ssd1325_spi') -SPISSD1325 = ssd1325_spi.class_('SPISSD1325', ssd1325_base.SSD1325, spi.SPIDevice) +ssd1325_spi = cg.esphome_ns.namespace("ssd1325_spi") +SPISSD1325 = ssd1325_spi.class_("SPISSD1325", ssd1325_base.SSD1325, spi.SPIDevice) -CONFIG_SCHEMA = cv.All(ssd1325_base.SSD1325_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(SPISSD1325), - cv.Required(CONF_DC_PIN): pins.gpio_output_pin_schema, -}).extend(cv.COMPONENT_SCHEMA).extend(spi.spi_device_schema(cs_pin_required=False)), - cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA)) +CONFIG_SCHEMA = cv.All( + ssd1325_base.SSD1325_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(SPISSD1325), + cv.Required(CONF_DC_PIN): pins.gpio_output_pin_schema, + } + ) + .extend(cv.COMPONENT_SCHEMA) + .extend(spi.spi_device_schema(cs_pin_required=False)), + cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA), +) def to_code(config): diff --git a/esphome/components/ssd1327_base/__init__.py b/esphome/components/ssd1327_base/__init__.py index ee282f215e..74446d6f09 100644 --- a/esphome/components/ssd1327_base/__init__.py +++ b/esphome/components/ssd1327_base/__init__.py @@ -5,23 +5,25 @@ from esphome.components import display from esphome.const import CONF_BRIGHTNESS, CONF_LAMBDA, CONF_MODEL, CONF_RESET_PIN from esphome.core import coroutine -CODEOWNERS = ['@kbx81'] +CODEOWNERS = ["@kbx81"] -ssd1327_base_ns = cg.esphome_ns.namespace('ssd1327_base') -SSD1327 = ssd1327_base_ns.class_('SSD1327', cg.PollingComponent, display.DisplayBuffer) -SSD1327Model = ssd1327_base_ns.enum('SSD1327Model') +ssd1327_base_ns = cg.esphome_ns.namespace("ssd1327_base") +SSD1327 = ssd1327_base_ns.class_("SSD1327", cg.PollingComponent, display.DisplayBuffer) +SSD1327Model = ssd1327_base_ns.enum("SSD1327Model") MODELS = { - 'SSD1327_128X128': SSD1327Model.SSD1327_MODEL_128_128, + "SSD1327_128X128": SSD1327Model.SSD1327_MODEL_128_128, } SSD1327_MODEL = cv.enum(MODELS, upper=True, space="_") -SSD1327_SCHEMA = display.FULL_DISPLAY_SCHEMA.extend({ - cv.Required(CONF_MODEL): SSD1327_MODEL, - cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, - cv.Optional(CONF_BRIGHTNESS, default=1.0): cv.percentage, -}).extend(cv.polling_component_schema('1s')) +SSD1327_SCHEMA = display.FULL_DISPLAY_SCHEMA.extend( + { + cv.Required(CONF_MODEL): SSD1327_MODEL, + cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, + cv.Optional(CONF_BRIGHTNESS, default=1.0): cv.percentage, + } +).extend(cv.polling_component_schema("1s")) @coroutine @@ -37,5 +39,6 @@ def setup_ssd1327(var, config): cg.add(var.init_brightness(config[CONF_BRIGHTNESS])) if CONF_LAMBDA in config: lambda_ = yield cg.process_lambda( - config[CONF_LAMBDA], [(display.DisplayBufferRef, 'it')], return_type=cg.void) + config[CONF_LAMBDA], [(display.DisplayBufferRef, "it")], return_type=cg.void + ) cg.add(var.set_writer(lambda_)) diff --git a/esphome/components/ssd1327_i2c/display.py b/esphome/components/ssd1327_i2c/display.py index 9caa0ce031..f13ed003ad 100644 --- a/esphome/components/ssd1327_i2c/display.py +++ b/esphome/components/ssd1327_i2c/display.py @@ -3,18 +3,24 @@ import esphome.config_validation as cv from esphome.components import ssd1327_base, i2c from esphome.const import CONF_ID, CONF_LAMBDA, CONF_PAGES -CODEOWNERS = ['@kbx81'] +CODEOWNERS = ["@kbx81"] -AUTO_LOAD = ['ssd1327_base'] -DEPENDENCIES = ['i2c'] +AUTO_LOAD = ["ssd1327_base"] +DEPENDENCIES = ["i2c"] -ssd1327_i2c = cg.esphome_ns.namespace('ssd1327_i2c') -I2CSSD1327 = ssd1327_i2c.class_('I2CSSD1327', ssd1327_base.SSD1327, i2c.I2CDevice) +ssd1327_i2c = cg.esphome_ns.namespace("ssd1327_i2c") +I2CSSD1327 = ssd1327_i2c.class_("I2CSSD1327", ssd1327_base.SSD1327, i2c.I2CDevice) -CONFIG_SCHEMA = cv.All(ssd1327_base.SSD1327_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(I2CSSD1327), -}).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(0x3D)), - cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA)) +CONFIG_SCHEMA = cv.All( + ssd1327_base.SSD1327_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(I2CSSD1327), + } + ) + .extend(cv.COMPONENT_SCHEMA) + .extend(i2c.i2c_device_schema(0x3D)), + cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA), +) def to_code(config): diff --git a/esphome/components/ssd1327_spi/display.py b/esphome/components/ssd1327_spi/display.py index 5e3d21dae5..fe119836a3 100644 --- a/esphome/components/ssd1327_spi/display.py +++ b/esphome/components/ssd1327_spi/display.py @@ -4,19 +4,25 @@ from esphome import pins from esphome.components import spi, ssd1327_base from esphome.const import CONF_DC_PIN, CONF_ID, CONF_LAMBDA, CONF_PAGES -CODEOWNERS = ['@kbx81'] +CODEOWNERS = ["@kbx81"] -AUTO_LOAD = ['ssd1327_base'] -DEPENDENCIES = ['spi'] +AUTO_LOAD = ["ssd1327_base"] +DEPENDENCIES = ["spi"] -ssd1327_spi = cg.esphome_ns.namespace('ssd1327_spi') -SPISSD1327 = ssd1327_spi.class_('SPISSD1327', ssd1327_base.SSD1327, spi.SPIDevice) +ssd1327_spi = cg.esphome_ns.namespace("ssd1327_spi") +SPISSD1327 = ssd1327_spi.class_("SPISSD1327", ssd1327_base.SSD1327, spi.SPIDevice) -CONFIG_SCHEMA = cv.All(ssd1327_base.SSD1327_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(SPISSD1327), - cv.Required(CONF_DC_PIN): pins.gpio_output_pin_schema, -}).extend(cv.COMPONENT_SCHEMA).extend(spi.spi_device_schema(cs_pin_required=False)), - cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA)) +CONFIG_SCHEMA = cv.All( + ssd1327_base.SSD1327_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(SPISSD1327), + cv.Required(CONF_DC_PIN): pins.gpio_output_pin_schema, + } + ) + .extend(cv.COMPONENT_SCHEMA) + .extend(spi.spi_device_schema(cs_pin_required=False)), + cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA), +) def to_code(config): diff --git a/esphome/components/ssd1331_base/__init__.py b/esphome/components/ssd1331_base/__init__.py index f6423f4aaf..e151d3fe17 100644 --- a/esphome/components/ssd1331_base/__init__.py +++ b/esphome/components/ssd1331_base/__init__.py @@ -5,15 +5,17 @@ from esphome.components import display from esphome.const import CONF_BRIGHTNESS, CONF_LAMBDA, CONF_RESET_PIN from esphome.core import coroutine -CODEOWNERS = ['@kbx81'] +CODEOWNERS = ["@kbx81"] -ssd1331_base_ns = cg.esphome_ns.namespace('ssd1331_base') -SSD1331 = ssd1331_base_ns.class_('SSD1331', cg.PollingComponent, display.DisplayBuffer) +ssd1331_base_ns = cg.esphome_ns.namespace("ssd1331_base") +SSD1331 = ssd1331_base_ns.class_("SSD1331", cg.PollingComponent, display.DisplayBuffer) -SSD1331_SCHEMA = display.FULL_DISPLAY_SCHEMA.extend({ - cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, - cv.Optional(CONF_BRIGHTNESS, default=1.0): cv.percentage, -}).extend(cv.polling_component_schema('1s')) +SSD1331_SCHEMA = display.FULL_DISPLAY_SCHEMA.extend( + { + cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, + cv.Optional(CONF_BRIGHTNESS, default=1.0): cv.percentage, + } +).extend(cv.polling_component_schema("1s")) @coroutine @@ -28,5 +30,6 @@ def setup_ssd1331(var, config): cg.add(var.init_brightness(config[CONF_BRIGHTNESS])) if CONF_LAMBDA in config: lambda_ = yield cg.process_lambda( - config[CONF_LAMBDA], [(display.DisplayBufferRef, 'it')], return_type=cg.void) + config[CONF_LAMBDA], [(display.DisplayBufferRef, "it")], return_type=cg.void + ) cg.add(var.set_writer(lambda_)) diff --git a/esphome/components/ssd1331_spi/display.py b/esphome/components/ssd1331_spi/display.py index c10d34539e..c4e9dad8bc 100644 --- a/esphome/components/ssd1331_spi/display.py +++ b/esphome/components/ssd1331_spi/display.py @@ -4,19 +4,25 @@ from esphome import pins from esphome.components import spi, ssd1331_base from esphome.const import CONF_DC_PIN, CONF_ID, CONF_LAMBDA, CONF_PAGES -CODEOWNERS = ['@kbx81'] +CODEOWNERS = ["@kbx81"] -AUTO_LOAD = ['ssd1331_base'] -DEPENDENCIES = ['spi'] +AUTO_LOAD = ["ssd1331_base"] +DEPENDENCIES = ["spi"] -ssd1331_spi = cg.esphome_ns.namespace('ssd1331_spi') -SPISSD1331 = ssd1331_spi.class_('SPISSD1331', ssd1331_base.SSD1331, spi.SPIDevice) +ssd1331_spi = cg.esphome_ns.namespace("ssd1331_spi") +SPISSD1331 = ssd1331_spi.class_("SPISSD1331", ssd1331_base.SSD1331, spi.SPIDevice) -CONFIG_SCHEMA = cv.All(ssd1331_base.SSD1331_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(SPISSD1331), - cv.Required(CONF_DC_PIN): pins.gpio_output_pin_schema, -}).extend(cv.COMPONENT_SCHEMA).extend(spi.spi_device_schema()), - cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA)) +CONFIG_SCHEMA = cv.All( + ssd1331_base.SSD1331_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(SPISSD1331), + cv.Required(CONF_DC_PIN): pins.gpio_output_pin_schema, + } + ) + .extend(cv.COMPONENT_SCHEMA) + .extend(spi.spi_device_schema()), + cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA), +) def to_code(config): diff --git a/esphome/components/ssd1351_base/__init__.py b/esphome/components/ssd1351_base/__init__.py index 3bff245b82..5e2c14cb6f 100644 --- a/esphome/components/ssd1351_base/__init__.py +++ b/esphome/components/ssd1351_base/__init__.py @@ -5,24 +5,26 @@ from esphome.components import display from esphome.const import CONF_BRIGHTNESS, CONF_LAMBDA, CONF_MODEL, CONF_RESET_PIN from esphome.core import coroutine -CODEOWNERS = ['@kbx81'] +CODEOWNERS = ["@kbx81"] -ssd1351_base_ns = cg.esphome_ns.namespace('ssd1351_base') -SSD1351 = ssd1351_base_ns.class_('SSD1351', cg.PollingComponent, display.DisplayBuffer) -SSD1351Model = ssd1351_base_ns.enum('SSD1351Model') +ssd1351_base_ns = cg.esphome_ns.namespace("ssd1351_base") +SSD1351 = ssd1351_base_ns.class_("SSD1351", cg.PollingComponent, display.DisplayBuffer) +SSD1351Model = ssd1351_base_ns.enum("SSD1351Model") MODELS = { - 'SSD1351_128X96': SSD1351Model.SSD1351_MODEL_128_96, - 'SSD1351_128X128': SSD1351Model.SSD1351_MODEL_128_128, + "SSD1351_128X96": SSD1351Model.SSD1351_MODEL_128_96, + "SSD1351_128X128": SSD1351Model.SSD1351_MODEL_128_128, } SSD1351_MODEL = cv.enum(MODELS, upper=True, space="_") -SSD1351_SCHEMA = display.FULL_DISPLAY_SCHEMA.extend({ - cv.Required(CONF_MODEL): SSD1351_MODEL, - cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, - cv.Optional(CONF_BRIGHTNESS, default=1.0): cv.percentage, -}).extend(cv.polling_component_schema('1s')) +SSD1351_SCHEMA = display.FULL_DISPLAY_SCHEMA.extend( + { + cv.Required(CONF_MODEL): SSD1351_MODEL, + cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, + cv.Optional(CONF_BRIGHTNESS, default=1.0): cv.percentage, + } +).extend(cv.polling_component_schema("1s")) @coroutine @@ -38,5 +40,6 @@ def setup_ssd1351(var, config): cg.add(var.init_brightness(config[CONF_BRIGHTNESS])) if CONF_LAMBDA in config: lambda_ = yield cg.process_lambda( - config[CONF_LAMBDA], [(display.DisplayBufferRef, 'it')], return_type=cg.void) + config[CONF_LAMBDA], [(display.DisplayBufferRef, "it")], return_type=cg.void + ) cg.add(var.set_writer(lambda_)) diff --git a/esphome/components/ssd1351_spi/display.py b/esphome/components/ssd1351_spi/display.py index 30b4c73085..66e4fad02c 100644 --- a/esphome/components/ssd1351_spi/display.py +++ b/esphome/components/ssd1351_spi/display.py @@ -4,19 +4,25 @@ from esphome import pins from esphome.components import spi, ssd1351_base from esphome.const import CONF_DC_PIN, CONF_ID, CONF_LAMBDA, CONF_PAGES -CODEOWNERS = ['@kbx81'] +CODEOWNERS = ["@kbx81"] -AUTO_LOAD = ['ssd1351_base'] -DEPENDENCIES = ['spi'] +AUTO_LOAD = ["ssd1351_base"] +DEPENDENCIES = ["spi"] -ssd1351_spi = cg.esphome_ns.namespace('ssd1351_spi') -SPISSD1351 = ssd1351_spi.class_('SPISSD1351', ssd1351_base.SSD1351, spi.SPIDevice) +ssd1351_spi = cg.esphome_ns.namespace("ssd1351_spi") +SPISSD1351 = ssd1351_spi.class_("SPISSD1351", ssd1351_base.SSD1351, spi.SPIDevice) -CONFIG_SCHEMA = cv.All(ssd1351_base.SSD1351_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(SPISSD1351), - cv.Required(CONF_DC_PIN): pins.gpio_output_pin_schema, -}).extend(cv.COMPONENT_SCHEMA).extend(spi.spi_device_schema()), - cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA)) +CONFIG_SCHEMA = cv.All( + ssd1351_base.SSD1351_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(SPISSD1351), + cv.Required(CONF_DC_PIN): pins.gpio_output_pin_schema, + } + ) + .extend(cv.COMPONENT_SCHEMA) + .extend(spi.spi_device_schema()), + cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA), +) def to_code(config): diff --git a/esphome/components/st7735/__init__.py b/esphome/components/st7735/__init__.py index 9f1d58b671..ba854bb0ae 100644 --- a/esphome/components/st7735/__init__.py +++ b/esphome/components/st7735/__init__.py @@ -1,2 +1,3 @@ import esphome.codegen as cg -st7735_ns = cg.esphome_ns.namespace('st7735') + +st7735_ns = cg.esphome_ns.namespace("st7735") diff --git a/esphome/components/st7789v/__init__.py b/esphome/components/st7789v/__init__.py index dc85fa6b76..3e64d09c57 100644 --- a/esphome/components/st7789v/__init__.py +++ b/esphome/components/st7789v/__init__.py @@ -1,3 +1,3 @@ import esphome.codegen as cg -st7789v_ns = cg.esphome_ns.namespace('st7789v') +st7789v_ns = cg.esphome_ns.namespace("st7789v") diff --git a/esphome/components/st7789v/display.py b/esphome/components/st7789v/display.py index 5815ae599c..9b3d550374 100644 --- a/esphome/components/st7789v/display.py +++ b/esphome/components/st7789v/display.py @@ -2,26 +2,40 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins from esphome.components import display, spi -from esphome.const import CONF_BACKLIGHT_PIN, CONF_BRIGHTNESS, CONF_CS_PIN, CONF_DC_PIN, CONF_ID, \ - CONF_LAMBDA, CONF_RESET_PIN +from esphome.const import ( + CONF_BACKLIGHT_PIN, + CONF_BRIGHTNESS, + CONF_CS_PIN, + CONF_DC_PIN, + CONF_ID, + CONF_LAMBDA, + CONF_RESET_PIN, +) from . import st7789v_ns -CODEOWNERS = ['@kbx81'] +CODEOWNERS = ["@kbx81"] -DEPENDENCIES = ['spi'] +DEPENDENCIES = ["spi"] -ST7789V = st7789v_ns.class_('ST7789V', cg.PollingComponent, spi.SPIDevice, - display.DisplayBuffer) -ST7789VRef = ST7789V.operator('ref') +ST7789V = st7789v_ns.class_( + "ST7789V", cg.PollingComponent, spi.SPIDevice, display.DisplayBuffer +) +ST7789VRef = ST7789V.operator("ref") -CONFIG_SCHEMA = display.FULL_DISPLAY_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(ST7789V), - cv.Required(CONF_RESET_PIN): pins.gpio_output_pin_schema, - cv.Required(CONF_DC_PIN): pins.gpio_output_pin_schema, - cv.Required(CONF_CS_PIN): pins.gpio_output_pin_schema, - cv.Required(CONF_BACKLIGHT_PIN): pins.gpio_output_pin_schema, - cv.Optional(CONF_BRIGHTNESS, default=1.0): cv.percentage, -}).extend(cv.polling_component_schema('5s')).extend(spi.spi_device_schema()) +CONFIG_SCHEMA = ( + display.FULL_DISPLAY_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(ST7789V), + cv.Required(CONF_RESET_PIN): pins.gpio_output_pin_schema, + cv.Required(CONF_DC_PIN): pins.gpio_output_pin_schema, + cv.Required(CONF_CS_PIN): pins.gpio_output_pin_schema, + cv.Required(CONF_BACKLIGHT_PIN): pins.gpio_output_pin_schema, + cv.Optional(CONF_BRIGHTNESS, default=1.0): cv.percentage, + } + ) + .extend(cv.polling_component_schema("5s")) + .extend(spi.spi_device_schema()) +) def to_code(config): @@ -40,7 +54,8 @@ def to_code(config): if CONF_LAMBDA in config: lambda_ = yield cg.process_lambda( - config[CONF_LAMBDA], [(display.DisplayBufferRef, 'it')], return_type=cg.void) + config[CONF_LAMBDA], [(display.DisplayBufferRef, "it")], return_type=cg.void + ) cg.add(var.set_writer(lambda_)) yield display.register_display(var, config) diff --git a/esphome/components/status/binary_sensor.py b/esphome/components/status/binary_sensor.py index 9963461cef..3a52b0b617 100644 --- a/esphome/components/status/binary_sensor.py +++ b/esphome/components/status/binary_sensor.py @@ -3,15 +3,19 @@ import esphome.config_validation as cv from esphome.components import binary_sensor from esphome.const import CONF_ID, CONF_DEVICE_CLASS, DEVICE_CLASS_CONNECTIVITY -status_ns = cg.esphome_ns.namespace('status') -StatusBinarySensor = status_ns.class_('StatusBinarySensor', binary_sensor.BinarySensor, - cg.Component) +status_ns = cg.esphome_ns.namespace("status") +StatusBinarySensor = status_ns.class_( + "StatusBinarySensor", binary_sensor.BinarySensor, cg.Component +) -CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(StatusBinarySensor), - - cv.Optional(CONF_DEVICE_CLASS, default=DEVICE_CLASS_CONNECTIVITY): binary_sensor.device_class, -}).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(StatusBinarySensor), + cv.Optional( + CONF_DEVICE_CLASS, default=DEVICE_CLASS_CONNECTIVITY + ): binary_sensor.device_class, + } +).extend(cv.COMPONENT_SCHEMA) def to_code(config): diff --git a/esphome/components/status_led/__init__.py b/esphome/components/status_led/__init__.py index 26105b5e92..76fbc01f39 100644 --- a/esphome/components/status_led/__init__.py +++ b/esphome/components/status_led/__init__.py @@ -4,13 +4,15 @@ import esphome.codegen as cg from esphome.const import CONF_ID, CONF_PIN from esphome.core import coroutine_with_priority -status_led_ns = cg.esphome_ns.namespace('status_led') -StatusLED = status_led_ns.class_('StatusLED', cg.Component) +status_led_ns = cg.esphome_ns.namespace("status_led") +StatusLED = status_led_ns.class_("StatusLED", cg.Component) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(StatusLED), - cv.Required(CONF_PIN): pins.gpio_output_pin_schema, -}).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(StatusLED), + cv.Required(CONF_PIN): pins.gpio_output_pin_schema, + } +).extend(cv.COMPONENT_SCHEMA) @coroutine_with_priority(80.0) @@ -20,4 +22,4 @@ def to_code(config): var = cg.Pvariable(config[CONF_ID], rhs) yield cg.register_component(var, config) cg.add(var.pre_setup()) - cg.add_define('USE_STATUS_LED') + cg.add_define("USE_STATUS_LED") diff --git a/esphome/components/stepper/__init__.py b/esphome/components/stepper/__init__.py index c61aaa7fc9..53ddc82e09 100644 --- a/esphome/components/stepper/__init__.py +++ b/esphome/components/stepper/__init__.py @@ -1,28 +1,35 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import automation -from esphome.const import CONF_ACCELERATION, CONF_DECELERATION, CONF_ID, CONF_MAX_SPEED, \ - CONF_POSITION, CONF_TARGET, CONF_SPEED +from esphome.const import ( + CONF_ACCELERATION, + CONF_DECELERATION, + CONF_ID, + CONF_MAX_SPEED, + CONF_POSITION, + CONF_TARGET, + CONF_SPEED, +) from esphome.core import CORE, coroutine, coroutine_with_priority IS_PLATFORM_COMPONENT = True # pylint: disable=invalid-name -stepper_ns = cg.esphome_ns.namespace('stepper') -Stepper = stepper_ns.class_('Stepper') +stepper_ns = cg.esphome_ns.namespace("stepper") +Stepper = stepper_ns.class_("Stepper") -SetTargetAction = stepper_ns.class_('SetTargetAction', automation.Action) -ReportPositionAction = stepper_ns.class_('ReportPositionAction', automation.Action) -SetSpeedAction = stepper_ns.class_('SetSpeedAction', automation.Action) +SetTargetAction = stepper_ns.class_("SetTargetAction", automation.Action) +ReportPositionAction = stepper_ns.class_("ReportPositionAction", automation.Action) +SetSpeedAction = stepper_ns.class_("SetSpeedAction", automation.Action) def validate_acceleration(value): value = cv.string(value) - for suffix in ('steps/s^2', 'steps/s*s', 'steps/s/s', 'steps/ss', 'steps/(s*s)'): + for suffix in ("steps/s^2", "steps/s*s", "steps/s/s", "steps/ss", "steps/(s*s)"): if value.endswith(suffix): - value = value[:-len(suffix)] + value = value[: -len(suffix)] - if value == 'inf': + if value == "inf": return 1e6 try: @@ -39,11 +46,11 @@ def validate_acceleration(value): def validate_speed(value): value = cv.string(value) - for suffix in ('steps/s', 'steps/s'): + for suffix in ("steps/s", "steps/s"): if value.endswith(suffix): - value = value[:-len(suffix)] + value = value[: -len(suffix)] - if value == 'inf': + if value == "inf": return 1e6 try: @@ -58,11 +65,13 @@ def validate_speed(value): return value -STEPPER_SCHEMA = cv.Schema({ - cv.Required(CONF_MAX_SPEED): validate_speed, - cv.Optional(CONF_ACCELERATION, default='inf'): validate_acceleration, - cv.Optional(CONF_DECELERATION, default='inf'): validate_acceleration, -}) +STEPPER_SCHEMA = cv.Schema( + { + cv.Required(CONF_MAX_SPEED): validate_speed, + cv.Optional(CONF_ACCELERATION, default="inf"): validate_acceleration, + cv.Optional(CONF_DECELERATION, default="inf"): validate_acceleration, + } +) @coroutine @@ -82,10 +91,16 @@ def register_stepper(var, config): yield setup_stepper_core_(var, config) -@automation.register_action('stepper.set_target', SetTargetAction, cv.Schema({ - cv.Required(CONF_ID): cv.use_id(Stepper), - cv.Required(CONF_TARGET): cv.templatable(cv.int_), -})) +@automation.register_action( + "stepper.set_target", + SetTargetAction, + cv.Schema( + { + cv.Required(CONF_ID): cv.use_id(Stepper), + cv.Required(CONF_TARGET): cv.templatable(cv.int_), + } + ), +) def stepper_set_target_to_code(config, action_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) var = cg.new_Pvariable(action_id, template_arg, paren) @@ -94,10 +109,16 @@ def stepper_set_target_to_code(config, action_id, template_arg, args): yield var -@automation.register_action('stepper.report_position', ReportPositionAction, cv.Schema({ - cv.Required(CONF_ID): cv.use_id(Stepper), - cv.Required(CONF_POSITION): cv.templatable(cv.int_), -})) +@automation.register_action( + "stepper.report_position", + ReportPositionAction, + cv.Schema( + { + cv.Required(CONF_ID): cv.use_id(Stepper), + cv.Required(CONF_POSITION): cv.templatable(cv.int_), + } + ), +) def stepper_report_position_to_code(config, action_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) var = cg.new_Pvariable(action_id, template_arg, paren) @@ -106,10 +127,16 @@ def stepper_report_position_to_code(config, action_id, template_arg, args): yield var -@automation.register_action('stepper.set_speed', SetSpeedAction, cv.Schema({ - cv.Required(CONF_ID): cv.use_id(Stepper), - cv.Required(CONF_SPEED): cv.templatable(validate_speed), -})) +@automation.register_action( + "stepper.set_speed", + SetSpeedAction, + cv.Schema( + { + cv.Required(CONF_ID): cv.use_id(Stepper), + cv.Required(CONF_SPEED): cv.templatable(validate_speed), + } + ), +) def stepper_set_speed_to_code(config, action_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) var = cg.new_Pvariable(action_id, template_arg, paren) diff --git a/esphome/components/sts3x/sensor.py b/esphome/components/sts3x/sensor.py index f13ddf3978..5e44c3ca3c 100644 --- a/esphome/components/sts3x/sensor.py +++ b/esphome/components/sts3x/sensor.py @@ -3,16 +3,24 @@ import esphome.config_validation as cv from esphome.components import i2c, sensor from esphome.const import CONF_ID, DEVICE_CLASS_TEMPERATURE, ICON_EMPTY, UNIT_CELSIUS -DEPENDENCIES = ['i2c'] +DEPENDENCIES = ["i2c"] -sts3x_ns = cg.esphome_ns.namespace('sts3x') +sts3x_ns = cg.esphome_ns.namespace("sts3x") -STS3XComponent = sts3x_ns.class_('STS3XComponent', sensor.Sensor, - cg.PollingComponent, i2c.I2CDevice) +STS3XComponent = sts3x_ns.class_( + "STS3XComponent", sensor.Sensor, cg.PollingComponent, i2c.I2CDevice +) -CONFIG_SCHEMA = sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE).extend({ - cv.GenerateID(): cv.declare_id(STS3XComponent), -}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x4A)) +CONFIG_SCHEMA = ( + sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE) + .extend( + { + cv.GenerateID(): cv.declare_id(STS3XComponent), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x4A)) +) def to_code(config): diff --git a/esphome/components/substitutions/__init__.py b/esphome/components/substitutions/__init__.py index 1cc5b50f47..61ad4a698a 100644 --- a/esphome/components/substitutions/__init__.py +++ b/esphome/components/substitutions/__init__.py @@ -6,18 +6,19 @@ from esphome import core from esphome.const import CONF_SUBSTITUTIONS from esphome.yaml_util import ESPHomeDataBase, make_data_base -CODEOWNERS = ['@esphome/core'] +CODEOWNERS = ["@esphome/core"] _LOGGER = logging.getLogger(__name__) -VALID_SUBSTITUTIONS_CHARACTERS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' \ - '0123456789_' +VALID_SUBSTITUTIONS_CHARACTERS = ( + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_" +) def validate_substitution_key(value): value = cv.string(value) if not value: raise cv.Invalid("Substitution key must not be empty") - if value[0] == '$': + if value[0] == "$": value = value[1:] if value[0].isdigit(): raise cv.Invalid("First character in substitutions cannot be a digit.") @@ -25,24 +26,29 @@ def validate_substitution_key(value): if char not in VALID_SUBSTITUTIONS_CHARACTERS: raise cv.Invalid( "Substitution must only consist of upper/lowercase characters, the underscore " - "and numbers. The character '{}' cannot be used".format(char)) + "and numbers. The character '{}' cannot be used".format(char) + ) return value -CONFIG_SCHEMA = cv.Schema({ - validate_substitution_key: cv.string_strict, -}) +CONFIG_SCHEMA = cv.Schema( + { + validate_substitution_key: cv.string_strict, + } +) def to_code(config): pass -VARIABLE_PROG = re.compile('\\$([{0}]+|\\{{[{0}]*\\}})'.format(VALID_SUBSTITUTIONS_CHARACTERS)) +VARIABLE_PROG = re.compile( + "\\$([{0}]+|\\{{[{0}]*\\}})".format(VALID_SUBSTITUTIONS_CHARACTERS) +) def _expand_substitutions(substitutions, value, path): - if '$' not in value: + if "$" not in value: return value orig_value = value @@ -56,11 +62,16 @@ def _expand_substitutions(substitutions, value, path): i, j = m.span(0) name = m.group(1) - if name.startswith('{') and name.endswith('}'): + if name.startswith("{") and name.endswith("}"): name = name[1:-1] if name not in substitutions: - _LOGGER.warning("Found '%s' (see %s) which looks like a substitution, but '%s' was " - "not declared", orig_value, '->'.join(str(x) for x in path), name) + _LOGGER.warning( + "Found '%s' (see %s) which looks like a substitution, but '%s' was " + "not declared", + orig_value, + "->".join(str(x) for x in path), + name, + ) i = j continue @@ -119,10 +130,12 @@ def do_substitution_pass(config, command_line_substitutions): substitutions = command_line_substitutions elif command_line_substitutions: substitutions = {**substitutions, **command_line_substitutions} - with cv.prepend_path('substitutions'): + with cv.prepend_path("substitutions"): if not isinstance(substitutions, dict): - raise cv.Invalid("Substitutions must be a key to value mapping, got {}" - "".format(type(substitutions))) + raise cv.Invalid( + "Substitutions must be a key to value mapping, got {}" + "".format(type(substitutions)) + ) replace_keys = [] for key, value in substitutions.items(): diff --git a/esphome/components/sun/__init__.py b/esphome/components/sun/__init__.py index a92442ea56..5241f1bb55 100644 --- a/esphome/components/sun/__init__.py +++ b/esphome/components/sun/__init__.py @@ -4,58 +4,67 @@ from esphome import automation from esphome.components import time from esphome.const import CONF_TIME_ID, CONF_ID, CONF_TRIGGER_ID -CODEOWNERS = ['@OttoWinter'] -sun_ns = cg.esphome_ns.namespace('sun') +CODEOWNERS = ["@OttoWinter"] +sun_ns = cg.esphome_ns.namespace("sun") -Sun = sun_ns.class_('Sun') -SunTrigger = sun_ns.class_('SunTrigger', cg.PollingComponent, automation.Trigger.template()) -SunCondition = sun_ns.class_('SunCondition', automation.Condition) +Sun = sun_ns.class_("Sun") +SunTrigger = sun_ns.class_( + "SunTrigger", cg.PollingComponent, automation.Trigger.template() +) +SunCondition = sun_ns.class_("SunCondition", automation.Condition) -CONF_SUN_ID = 'sun_id' -CONF_LATITUDE = 'latitude' -CONF_LONGITUDE = 'longitude' -CONF_ELEVATION = 'elevation' -CONF_ON_SUNRISE = 'on_sunrise' -CONF_ON_SUNSET = 'on_sunset' +CONF_SUN_ID = "sun_id" +CONF_LATITUDE = "latitude" +CONF_LONGITUDE = "longitude" +CONF_ELEVATION = "elevation" +CONF_ON_SUNRISE = "on_sunrise" +CONF_ON_SUNSET = "on_sunset" # Default sun elevation is a bit below horizon because sunset # means time when the entire sun disk is below the horizon DEFAULT_ELEVATION = -0.883 ELEVATION_MAP = { - 'sunrise': 0.0, - 'sunset': 0.0, - 'civil': -6.0, - 'nautical': -12.0, - 'astronomical': -18.0, + "sunrise": 0.0, + "sunset": 0.0, + "civil": -6.0, + "nautical": -12.0, + "astronomical": -18.0, } def elevation(value): if isinstance(value, str): try: - value = ELEVATION_MAP[cv.one_of(*ELEVATION_MAP, lower=True, space='_')(value)] + value = ELEVATION_MAP[ + cv.one_of(*ELEVATION_MAP, lower=True, space="_")(value) + ] except cv.Invalid: pass value = cv.angle(value) return cv.float_range(min=-180, max=180)(value) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(Sun), - cv.GenerateID(CONF_TIME_ID): cv.use_id(time.RealTimeClock), - cv.Required(CONF_LATITUDE): cv.float_range(min=-90, max=90), - cv.Required(CONF_LONGITUDE): cv.float_range(min=-180, max=180), - - cv.Optional(CONF_ON_SUNRISE): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SunTrigger), - cv.Optional(CONF_ELEVATION, default=DEFAULT_ELEVATION): elevation, - }), - cv.Optional(CONF_ON_SUNSET): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SunTrigger), - cv.Optional(CONF_ELEVATION, default=DEFAULT_ELEVATION): elevation, - }), -}) +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(Sun), + cv.GenerateID(CONF_TIME_ID): cv.use_id(time.RealTimeClock), + cv.Required(CONF_LATITUDE): cv.float_range(min=-90, max=90), + cv.Required(CONF_LONGITUDE): cv.float_range(min=-180, max=180), + cv.Optional(CONF_ON_SUNRISE): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SunTrigger), + cv.Optional(CONF_ELEVATION, default=DEFAULT_ELEVATION): elevation, + } + ), + cv.Optional(CONF_ON_SUNSET): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SunTrigger), + cv.Optional(CONF_ELEVATION, default=DEFAULT_ELEVATION): elevation, + } + ), + } +) def to_code(config): @@ -82,10 +91,18 @@ def to_code(config): yield automation.build_automation(trigger, [], conf) -@automation.register_condition('sun.is_above_horizon', SunCondition, cv.Schema({ - cv.GenerateID(): cv.use_id(Sun), - cv.Optional(CONF_ELEVATION, default=DEFAULT_ELEVATION): cv.templatable(elevation), -})) +@automation.register_condition( + "sun.is_above_horizon", + SunCondition, + cv.Schema( + { + cv.GenerateID(): cv.use_id(Sun), + cv.Optional(CONF_ELEVATION, default=DEFAULT_ELEVATION): cv.templatable( + elevation + ), + } + ), +) def sun_above_horizon_to_code(config, condition_id, template_arg, args): var = cg.new_Pvariable(condition_id, template_arg) yield cg.register_parented(var, config[CONF_ID]) @@ -95,10 +112,18 @@ def sun_above_horizon_to_code(config, condition_id, template_arg, args): yield var -@automation.register_condition('sun.is_below_horizon', SunCondition, cv.Schema({ - cv.GenerateID(): cv.use_id(Sun), - cv.Optional(CONF_ELEVATION, default=DEFAULT_ELEVATION): cv.templatable(elevation), -})) +@automation.register_condition( + "sun.is_below_horizon", + SunCondition, + cv.Schema( + { + cv.GenerateID(): cv.use_id(Sun), + cv.Optional(CONF_ELEVATION, default=DEFAULT_ELEVATION): cv.templatable( + elevation + ), + } + ), +) def sun_below_horizon_to_code(config, condition_id, template_arg, args): var = cg.new_Pvariable(condition_id, template_arg) yield cg.register_parented(var, config[CONF_ID]) diff --git a/esphome/components/sun/sensor/__init__.py b/esphome/components/sun/sensor/__init__.py index 880e271fc5..02e1ef28eb 100644 --- a/esphome/components/sun/sensor/__init__.py +++ b/esphome/components/sun/sensor/__init__.py @@ -1,25 +1,35 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor -from esphome.const import DEVICE_CLASS_EMPTY, UNIT_DEGREES, ICON_WEATHER_SUNSET, CONF_ID, CONF_TYPE +from esphome.const import ( + DEVICE_CLASS_EMPTY, + UNIT_DEGREES, + ICON_WEATHER_SUNSET, + CONF_ID, + CONF_TYPE, +) from .. import sun_ns, CONF_SUN_ID, Sun -DEPENDENCIES = ['sun'] +DEPENDENCIES = ["sun"] -SunSensor = sun_ns.class_('SunSensor', sensor.Sensor, cg.PollingComponent) -SensorType = sun_ns.enum('SensorType') +SunSensor = sun_ns.class_("SunSensor", sensor.Sensor, cg.PollingComponent) +SensorType = sun_ns.enum("SensorType") TYPES = { - 'elevation': SensorType.SUN_SENSOR_ELEVATION, - 'azimuth': SensorType.SUN_SENSOR_AZIMUTH, + "elevation": SensorType.SUN_SENSOR_ELEVATION, + "azimuth": SensorType.SUN_SENSOR_AZIMUTH, } -CONFIG_SCHEMA = sensor.sensor_schema( - UNIT_DEGREES, ICON_WEATHER_SUNSET, 1, DEVICE_CLASS_EMPTY -).extend({ - cv.GenerateID(): cv.declare_id(SunSensor), - cv.GenerateID(CONF_SUN_ID): cv.use_id(Sun), - cv.Required(CONF_TYPE): cv.enum(TYPES, lower=True), -}).extend(cv.polling_component_schema('60s')) +CONFIG_SCHEMA = ( + sensor.sensor_schema(UNIT_DEGREES, ICON_WEATHER_SUNSET, 1, DEVICE_CLASS_EMPTY) + .extend( + { + cv.GenerateID(): cv.declare_id(SunSensor), + cv.GenerateID(CONF_SUN_ID): cv.use_id(Sun), + cv.Required(CONF_TYPE): cv.enum(TYPES, lower=True), + } + ) + .extend(cv.polling_component_schema("60s")) +) def to_code(config): diff --git a/esphome/components/sun/text_sensor/__init__.py b/esphome/components/sun/text_sensor/__init__.py index 984250f9f7..b527da699b 100644 --- a/esphome/components/sun/text_sensor/__init__.py +++ b/esphome/components/sun/text_sensor/__init__.py @@ -1,16 +1,24 @@ from esphome.components import text_sensor import esphome.config_validation as cv import esphome.codegen as cg -from esphome.const import CONF_ICON, ICON_WEATHER_SUNSET_DOWN, ICON_WEATHER_SUNSET_UP, CONF_TYPE, \ - CONF_ID, CONF_FORMAT +from esphome.const import ( + CONF_ICON, + ICON_WEATHER_SUNSET_DOWN, + ICON_WEATHER_SUNSET_UP, + CONF_TYPE, + CONF_ID, + CONF_FORMAT, +) from .. import sun_ns, CONF_SUN_ID, Sun, CONF_ELEVATION, elevation, DEFAULT_ELEVATION -DEPENDENCIES = ['sun'] +DEPENDENCIES = ["sun"] -SunTextSensor = sun_ns.class_('SunTextSensor', text_sensor.TextSensor, cg.PollingComponent) +SunTextSensor = sun_ns.class_( + "SunTextSensor", text_sensor.TextSensor, cg.PollingComponent +) SUN_TYPES = { - 'sunset': False, - 'sunrise': True, + "sunset": False, + "sunrise": True, } @@ -18,19 +26,24 @@ def validate_optional_icon(config): if CONF_ICON not in config: config = config.copy() config[CONF_ICON] = { - 'sunset': ICON_WEATHER_SUNSET_DOWN, - 'sunrise': ICON_WEATHER_SUNSET_UP, + "sunset": ICON_WEATHER_SUNSET_DOWN, + "sunrise": ICON_WEATHER_SUNSET_UP, }[config[CONF_TYPE]] return config -CONFIG_SCHEMA = cv.All(text_sensor.TEXT_SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(SunTextSensor), - cv.GenerateID(CONF_SUN_ID): cv.use_id(Sun), - cv.Required(CONF_TYPE): cv.one_of(*SUN_TYPES, lower=True), - cv.Optional(CONF_ELEVATION, default=DEFAULT_ELEVATION): elevation, - cv.Optional(CONF_FORMAT, default='%X'): cv.string_strict, -}).extend(cv.polling_component_schema('60s')), validate_optional_icon) +CONFIG_SCHEMA = cv.All( + text_sensor.TEXT_SENSOR_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(SunTextSensor), + cv.GenerateID(CONF_SUN_ID): cv.use_id(Sun), + cv.Required(CONF_TYPE): cv.one_of(*SUN_TYPES, lower=True), + cv.Optional(CONF_ELEVATION, default=DEFAULT_ELEVATION): elevation, + cv.Optional(CONF_FORMAT, default="%X"): cv.string_strict, + } + ).extend(cv.polling_component_schema("60s")), + validate_optional_icon, +) def to_code(config): diff --git a/esphome/components/switch/__init__.py b/esphome/components/switch/__init__.py index 7378b2a140..cc47b059cb 100644 --- a/esphome/components/switch/__init__.py +++ b/esphome/components/switch/__init__.py @@ -3,40 +3,58 @@ import esphome.config_validation as cv from esphome import automation from esphome.automation import Condition, maybe_simple_id from esphome.components import mqtt -from esphome.const import CONF_ICON, CONF_ID, CONF_INTERNAL, CONF_INVERTED, CONF_ON_TURN_OFF, \ - CONF_ON_TURN_ON, CONF_TRIGGER_ID, CONF_MQTT_ID, CONF_NAME +from esphome.const import ( + CONF_ICON, + CONF_ID, + CONF_INTERNAL, + CONF_INVERTED, + CONF_ON_TURN_OFF, + CONF_ON_TURN_ON, + CONF_TRIGGER_ID, + CONF_MQTT_ID, + CONF_NAME, +) from esphome.core import CORE, coroutine, coroutine_with_priority -CODEOWNERS = ['@esphome/core'] +CODEOWNERS = ["@esphome/core"] IS_PLATFORM_COMPONENT = True -switch_ns = cg.esphome_ns.namespace('switch_') -Switch = switch_ns.class_('Switch', cg.Nameable) -SwitchPtr = Switch.operator('ptr') +switch_ns = cg.esphome_ns.namespace("switch_") +Switch = switch_ns.class_("Switch", cg.Nameable) +SwitchPtr = Switch.operator("ptr") -ToggleAction = switch_ns.class_('ToggleAction', automation.Action) -TurnOffAction = switch_ns.class_('TurnOffAction', automation.Action) -TurnOnAction = switch_ns.class_('TurnOnAction', automation.Action) -SwitchPublishAction = switch_ns.class_('SwitchPublishAction', automation.Action) +ToggleAction = switch_ns.class_("ToggleAction", automation.Action) +TurnOffAction = switch_ns.class_("TurnOffAction", automation.Action) +TurnOnAction = switch_ns.class_("TurnOnAction", automation.Action) +SwitchPublishAction = switch_ns.class_("SwitchPublishAction", automation.Action) -SwitchCondition = switch_ns.class_('SwitchCondition', Condition) -SwitchTurnOnTrigger = switch_ns.class_('SwitchTurnOnTrigger', automation.Trigger.template()) -SwitchTurnOffTrigger = switch_ns.class_('SwitchTurnOffTrigger', automation.Trigger.template()) +SwitchCondition = switch_ns.class_("SwitchCondition", Condition) +SwitchTurnOnTrigger = switch_ns.class_( + "SwitchTurnOnTrigger", automation.Trigger.template() +) +SwitchTurnOffTrigger = switch_ns.class_( + "SwitchTurnOffTrigger", automation.Trigger.template() +) icon = cv.icon -SWITCH_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend({ - cv.OnlyWith(CONF_MQTT_ID, 'mqtt'): cv.declare_id(mqtt.MQTTSwitchComponent), - - cv.Optional(CONF_ICON): icon, - cv.Optional(CONF_INVERTED): cv.boolean, - cv.Optional(CONF_ON_TURN_ON): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SwitchTurnOnTrigger), - }), - cv.Optional(CONF_ON_TURN_OFF): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SwitchTurnOffTrigger), - }), -}) +SWITCH_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend( + { + cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTSwitchComponent), + cv.Optional(CONF_ICON): icon, + cv.Optional(CONF_INVERTED): cv.boolean, + cv.Optional(CONF_ON_TURN_ON): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SwitchTurnOnTrigger), + } + ), + cv.Optional(CONF_ON_TURN_OFF): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SwitchTurnOffTrigger), + } + ), + } +) @coroutine @@ -68,26 +86,28 @@ def register_switch(var, config): yield setup_switch_core_(var, config) -SWITCH_ACTION_SCHEMA = maybe_simple_id({ - cv.Required(CONF_ID): cv.use_id(Switch), -}) +SWITCH_ACTION_SCHEMA = maybe_simple_id( + { + cv.Required(CONF_ID): cv.use_id(Switch), + } +) -@automation.register_action('switch.toggle', ToggleAction, SWITCH_ACTION_SCHEMA) -@automation.register_action('switch.turn_off', TurnOffAction, SWITCH_ACTION_SCHEMA) -@automation.register_action('switch.turn_on', TurnOnAction, SWITCH_ACTION_SCHEMA) +@automation.register_action("switch.toggle", ToggleAction, SWITCH_ACTION_SCHEMA) +@automation.register_action("switch.turn_off", TurnOffAction, SWITCH_ACTION_SCHEMA) +@automation.register_action("switch.turn_on", TurnOnAction, SWITCH_ACTION_SCHEMA) def switch_toggle_to_code(config, action_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) yield cg.new_Pvariable(action_id, template_arg, paren) -@automation.register_condition('switch.is_on', SwitchCondition, SWITCH_ACTION_SCHEMA) +@automation.register_condition("switch.is_on", SwitchCondition, SWITCH_ACTION_SCHEMA) def switch_is_on_to_code(config, condition_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) yield cg.new_Pvariable(condition_id, template_arg, paren, True) -@automation.register_condition('switch.is_off', SwitchCondition, SWITCH_ACTION_SCHEMA) +@automation.register_condition("switch.is_off", SwitchCondition, SWITCH_ACTION_SCHEMA) def switch_is_off_to_code(config, condition_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) yield cg.new_Pvariable(condition_id, template_arg, paren, False) @@ -96,4 +116,4 @@ def switch_is_off_to_code(config, condition_id, template_arg, args): @coroutine_with_priority(100.0) def to_code(config): cg.add_global(switch_ns.using) - cg.add_define('USE_SWITCH') + cg.add_define("USE_SWITCH") diff --git a/esphome/components/sx1509/__init__.py b/esphome/components/sx1509/__init__.py index 11fcfe3955..06ee364e1b 100644 --- a/esphome/components/sx1509/__init__.py +++ b/esphome/components/sx1509/__init__.py @@ -4,39 +4,47 @@ from esphome import pins from esphome.components import i2c from esphome.const import CONF_ID, CONF_NUMBER, CONF_MODE, CONF_INVERTED -CONF_KEYPAD = 'keypad' -CONF_KEY_ROWS = 'key_rows' -CONF_KEY_COLUMNS = 'key_columns' -CONF_SLEEP_TIME = 'sleep_time' -CONF_SCAN_TIME = 'scan_time' -CONF_DEBOUNCE_TIME = 'debounce_time' +CONF_KEYPAD = "keypad" +CONF_KEY_ROWS = "key_rows" +CONF_KEY_COLUMNS = "key_columns" +CONF_SLEEP_TIME = "sleep_time" +CONF_SCAN_TIME = "scan_time" +CONF_DEBOUNCE_TIME = "debounce_time" -DEPENDENCIES = ['i2c'] +DEPENDENCIES = ["i2c"] MULTI_CONF = True -sx1509_ns = cg.esphome_ns.namespace('sx1509') -SX1509GPIOMode = sx1509_ns.enum('SX1509GPIOMode') +sx1509_ns = cg.esphome_ns.namespace("sx1509") +SX1509GPIOMode = sx1509_ns.enum("SX1509GPIOMode") SX1509_GPIO_MODES = { - 'INPUT': SX1509GPIOMode.SX1509_INPUT, - 'INPUT_PULLUP': SX1509GPIOMode.SX1509_INPUT_PULLUP, - 'OUTPUT': SX1509GPIOMode.SX1509_OUTPUT + "INPUT": SX1509GPIOMode.SX1509_INPUT, + "INPUT_PULLUP": SX1509GPIOMode.SX1509_INPUT_PULLUP, + "OUTPUT": SX1509GPIOMode.SX1509_OUTPUT, } -SX1509Component = sx1509_ns.class_('SX1509Component', cg.Component, i2c.I2CDevice) -SX1509GPIOPin = sx1509_ns.class_('SX1509GPIOPin', cg.GPIOPin) +SX1509Component = sx1509_ns.class_("SX1509Component", cg.Component, i2c.I2CDevice) +SX1509GPIOPin = sx1509_ns.class_("SX1509GPIOPin", cg.GPIOPin) -KEYPAD_SCHEMA = cv.Schema({ - cv.Required(CONF_KEY_ROWS): cv.int_range(min=1, max=8), - cv.Required(CONF_KEY_COLUMNS): cv.int_range(min=1, max=8), - cv.Optional(CONF_SLEEP_TIME): cv.int_range(min=128, max=8192), - cv.Optional(CONF_SCAN_TIME): cv.int_range(min=1, max=128), - cv.Optional(CONF_DEBOUNCE_TIME): cv.int_range(min=1, max=64), -}) +KEYPAD_SCHEMA = cv.Schema( + { + cv.Required(CONF_KEY_ROWS): cv.int_range(min=1, max=8), + cv.Required(CONF_KEY_COLUMNS): cv.int_range(min=1, max=8), + cv.Optional(CONF_SLEEP_TIME): cv.int_range(min=128, max=8192), + cv.Optional(CONF_SCAN_TIME): cv.int_range(min=1, max=128), + cv.Optional(CONF_DEBOUNCE_TIME): cv.int_range(min=1, max=64), + } +) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(SX1509Component), - cv.Optional(CONF_KEYPAD): cv.Schema(KEYPAD_SCHEMA), -}).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(0x3E)) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(SX1509Component), + cv.Optional(CONF_KEYPAD): cv.Schema(KEYPAD_SCHEMA), + } + ) + .extend(cv.COMPONENT_SCHEMA) + .extend(i2c.i2c_device_schema(0x3E)) +) def to_code(config): @@ -46,32 +54,44 @@ def to_code(config): if CONF_KEYPAD in config: keypad = config[CONF_KEYPAD] cg.add(var.set_rows_cols(keypad[CONF_KEY_ROWS], keypad[CONF_KEY_COLUMNS])) - if CONF_SLEEP_TIME in keypad and CONF_SCAN_TIME in keypad and CONF_DEBOUNCE_TIME in keypad: + if ( + CONF_SLEEP_TIME in keypad + and CONF_SCAN_TIME in keypad + and CONF_DEBOUNCE_TIME in keypad + ): cg.add(var.set_sleep_time(keypad[CONF_SLEEP_TIME])) cg.add(var.set_scan_time(keypad[CONF_SCAN_TIME])) cg.add(var.set_debounce_time(keypad[CONF_DEBOUNCE_TIME])) -CONF_SX1509 = 'sx1509' -CONF_SX1509_ID = 'sx1509_id' +CONF_SX1509 = "sx1509" +CONF_SX1509_ID = "sx1509_id" -SX1509_OUTPUT_PIN_SCHEMA = cv.Schema({ - cv.Required(CONF_SX1509): cv.use_id(SX1509Component), - cv.Required(CONF_NUMBER): cv.int_, - cv.Optional(CONF_MODE, default="OUTPUT"): cv.enum(SX1509_GPIO_MODES, upper=True), - cv.Optional(CONF_INVERTED, default=False): cv.boolean, -}) -SX1509_INPUT_PIN_SCHEMA = cv.Schema({ - cv.Required(CONF_SX1509): cv.use_id(SX1509Component), - cv.Required(CONF_NUMBER): cv.int_, - cv.Optional(CONF_MODE, default="INPUT"): cv.enum(SX1509_GPIO_MODES, upper=True), - cv.Optional(CONF_INVERTED, default=False): cv.boolean, -}) +SX1509_OUTPUT_PIN_SCHEMA = cv.Schema( + { + cv.Required(CONF_SX1509): cv.use_id(SX1509Component), + cv.Required(CONF_NUMBER): cv.int_, + cv.Optional(CONF_MODE, default="OUTPUT"): cv.enum( + SX1509_GPIO_MODES, upper=True + ), + cv.Optional(CONF_INVERTED, default=False): cv.boolean, + } +) +SX1509_INPUT_PIN_SCHEMA = cv.Schema( + { + cv.Required(CONF_SX1509): cv.use_id(SX1509Component), + cv.Required(CONF_NUMBER): cv.int_, + cv.Optional(CONF_MODE, default="INPUT"): cv.enum(SX1509_GPIO_MODES, upper=True), + cv.Optional(CONF_INVERTED, default=False): cv.boolean, + } +) -@pins.PIN_SCHEMA_REGISTRY.register(CONF_SX1509, - (SX1509_OUTPUT_PIN_SCHEMA, SX1509_INPUT_PIN_SCHEMA)) +@pins.PIN_SCHEMA_REGISTRY.register( + CONF_SX1509, (SX1509_OUTPUT_PIN_SCHEMA, SX1509_INPUT_PIN_SCHEMA) +) def sx1509_pin_to_code(config): parent = yield cg.get_variable(config[CONF_SX1509]) - yield SX1509GPIOPin.new(parent, config[CONF_NUMBER], config[CONF_MODE], - config[CONF_INVERTED]) + yield SX1509GPIOPin.new( + parent, config[CONF_NUMBER], config[CONF_MODE], config[CONF_INVERTED] + ) diff --git a/esphome/components/sx1509/binary_sensor/__init__.py b/esphome/components/sx1509/binary_sensor/__init__.py index 9a65524383..e2344cd4af 100644 --- a/esphome/components/sx1509/binary_sensor/__init__.py +++ b/esphome/components/sx1509/binary_sensor/__init__.py @@ -4,19 +4,21 @@ from esphome.components import binary_sensor from esphome.const import CONF_ID from .. import SX1509Component, sx1509_ns, CONF_SX1509_ID -CONF_ROW = 'row' -CONF_COL = 'col' +CONF_ROW = "row" +CONF_COL = "col" -DEPENDENCIES = ['sx1509'] +DEPENDENCIES = ["sx1509"] -SX1509BinarySensor = sx1509_ns.class_('SX1509BinarySensor', binary_sensor.BinarySensor) +SX1509BinarySensor = sx1509_ns.class_("SX1509BinarySensor", binary_sensor.BinarySensor) -CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(SX1509BinarySensor), - cv.GenerateID(CONF_SX1509_ID): cv.use_id(SX1509Component), - cv.Required(CONF_ROW): cv.int_range(min=0, max=4), - cv.Required(CONF_COL): cv.int_range(min=0, max=4), -}) +CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(SX1509BinarySensor), + cv.GenerateID(CONF_SX1509_ID): cv.use_id(SX1509Component), + cv.Required(CONF_ROW): cv.int_range(min=0, max=4), + cv.Required(CONF_COL): cv.int_range(min=0, max=4), + } +) def to_code(config): diff --git a/esphome/components/sx1509/output/__init__.py b/esphome/components/sx1509/output/__init__.py index 80aec0afd4..878534e829 100644 --- a/esphome/components/sx1509/output/__init__.py +++ b/esphome/components/sx1509/output/__init__.py @@ -4,16 +4,19 @@ from esphome.components import output from esphome.const import CONF_PIN, CONF_ID from .. import SX1509Component, sx1509_ns, CONF_SX1509_ID -DEPENDENCIES = ['sx1509'] +DEPENDENCIES = ["sx1509"] -SX1509FloatOutputChannel = sx1509_ns.class_('SX1509FloatOutputChannel', - output.FloatOutput, cg.Component) +SX1509FloatOutputChannel = sx1509_ns.class_( + "SX1509FloatOutputChannel", output.FloatOutput, cg.Component +) -CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend({ - cv.Required(CONF_ID): cv.declare_id(SX1509FloatOutputChannel), - cv.GenerateID(CONF_SX1509_ID): cv.use_id(SX1509Component), - cv.Required(CONF_PIN): cv.int_range(min=0, max=15), -}).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend( + { + cv.Required(CONF_ID): cv.declare_id(SX1509FloatOutputChannel), + cv.GenerateID(CONF_SX1509_ID): cv.use_id(SX1509Component), + cv.Required(CONF_PIN): cv.int_range(min=0, max=15), + } +).extend(cv.COMPONENT_SCHEMA) def to_code(config): diff --git a/esphome/components/tcl112/climate.py b/esphome/components/tcl112/climate.py index 11ebdc7be8..587bdcfbe9 100644 --- a/esphome/components/tcl112/climate.py +++ b/esphome/components/tcl112/climate.py @@ -3,15 +3,17 @@ import esphome.config_validation as cv from esphome.components import climate_ir from esphome.const import CONF_ID -AUTO_LOAD = ['climate_ir'] -CODEOWNERS = ['@glmnet'] +AUTO_LOAD = ["climate_ir"] +CODEOWNERS = ["@glmnet"] -tcl112_ns = cg.esphome_ns.namespace('tcl112') -Tcl112Climate = tcl112_ns.class_('Tcl112Climate', climate_ir.ClimateIR) +tcl112_ns = cg.esphome_ns.namespace("tcl112") +Tcl112Climate = tcl112_ns.class_("Tcl112Climate", climate_ir.ClimateIR) -CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(Tcl112Climate), -}) +CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(Tcl112Climate), + } +) def to_code(config): diff --git a/esphome/components/tcs34725/sensor.py b/esphome/components/tcs34725/sensor.py index acd484db3c..84e3c290bf 100644 --- a/esphome/components/tcs34725/sensor.py +++ b/esphome/components/tcs34725/sensor.py @@ -1,55 +1,81 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import CONF_COLOR_TEMPERATURE, CONF_GAIN, CONF_ID, CONF_ILLUMINANCE, \ - CONF_INTEGRATION_TIME, DEVICE_CLASS_EMPTY, DEVICE_CLASS_ILLUMINANCE, ICON_EMPTY, \ - ICON_LIGHTBULB, UNIT_PERCENT, ICON_THERMOMETER, UNIT_KELVIN, UNIT_LUX +from esphome.const import ( + CONF_COLOR_TEMPERATURE, + CONF_GAIN, + CONF_ID, + CONF_ILLUMINANCE, + CONF_INTEGRATION_TIME, + DEVICE_CLASS_EMPTY, + DEVICE_CLASS_ILLUMINANCE, + ICON_EMPTY, + ICON_LIGHTBULB, + UNIT_PERCENT, + ICON_THERMOMETER, + UNIT_KELVIN, + UNIT_LUX, +) -DEPENDENCIES = ['i2c'] +DEPENDENCIES = ["i2c"] -CONF_RED_CHANNEL = 'red_channel' -CONF_GREEN_CHANNEL = 'green_channel' -CONF_BLUE_CHANNEL = 'blue_channel' -CONF_CLEAR_CHANNEL = 'clear_channel' +CONF_RED_CHANNEL = "red_channel" +CONF_GREEN_CHANNEL = "green_channel" +CONF_BLUE_CHANNEL = "blue_channel" +CONF_CLEAR_CHANNEL = "clear_channel" -tcs34725_ns = cg.esphome_ns.namespace('tcs34725') -TCS34725Component = tcs34725_ns.class_('TCS34725Component', cg.PollingComponent, i2c.I2CDevice) +tcs34725_ns = cg.esphome_ns.namespace("tcs34725") +TCS34725Component = tcs34725_ns.class_( + "TCS34725Component", cg.PollingComponent, i2c.I2CDevice +) -TCS34725IntegrationTime = tcs34725_ns.enum('TCS34725IntegrationTime') +TCS34725IntegrationTime = tcs34725_ns.enum("TCS34725IntegrationTime") TCS34725_INTEGRATION_TIMES = { - '2.4ms': TCS34725IntegrationTime.TCS34725_INTEGRATION_TIME_2_4MS, - '24ms': TCS34725IntegrationTime.TCS34725_INTEGRATION_TIME_24MS, - '50ms': TCS34725IntegrationTime.TCS34725_INTEGRATION_TIME_50MS, - '101ms': TCS34725IntegrationTime.TCS34725_INTEGRATION_TIME_101MS, - '154ms': TCS34725IntegrationTime.TCS34725_INTEGRATION_TIME_154MS, - '700ms': TCS34725IntegrationTime.TCS34725_INTEGRATION_TIME_700MS, + "2.4ms": TCS34725IntegrationTime.TCS34725_INTEGRATION_TIME_2_4MS, + "24ms": TCS34725IntegrationTime.TCS34725_INTEGRATION_TIME_24MS, + "50ms": TCS34725IntegrationTime.TCS34725_INTEGRATION_TIME_50MS, + "101ms": TCS34725IntegrationTime.TCS34725_INTEGRATION_TIME_101MS, + "154ms": TCS34725IntegrationTime.TCS34725_INTEGRATION_TIME_154MS, + "700ms": TCS34725IntegrationTime.TCS34725_INTEGRATION_TIME_700MS, } -TCS34725Gain = tcs34725_ns.enum('TCS34725Gain') +TCS34725Gain = tcs34725_ns.enum("TCS34725Gain") TCS34725_GAINS = { - '1X': TCS34725Gain.TCS34725_GAIN_1X, - '4X': TCS34725Gain.TCS34725_GAIN_4X, - '16X': TCS34725Gain.TCS34725_GAIN_16X, - '60X': TCS34725Gain.TCS34725_GAIN_60X, + "1X": TCS34725Gain.TCS34725_GAIN_1X, + "4X": TCS34725Gain.TCS34725_GAIN_4X, + "16X": TCS34725Gain.TCS34725_GAIN_16X, + "60X": TCS34725Gain.TCS34725_GAIN_60X, } -color_channel_schema = sensor.sensor_schema(UNIT_PERCENT, ICON_LIGHTBULB, 1, DEVICE_CLASS_EMPTY) -color_temperature_schema = sensor.sensor_schema(UNIT_KELVIN, ICON_THERMOMETER, 1, - DEVICE_CLASS_EMPTY) -illuminance_schema = sensor.sensor_schema(UNIT_LUX, ICON_EMPTY, 1, DEVICE_CLASS_ILLUMINANCE) +color_channel_schema = sensor.sensor_schema( + UNIT_PERCENT, ICON_LIGHTBULB, 1, DEVICE_CLASS_EMPTY +) +color_temperature_schema = sensor.sensor_schema( + UNIT_KELVIN, ICON_THERMOMETER, 1, DEVICE_CLASS_EMPTY +) +illuminance_schema = sensor.sensor_schema( + UNIT_LUX, ICON_EMPTY, 1, DEVICE_CLASS_ILLUMINANCE +) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(TCS34725Component), - cv.Optional(CONF_RED_CHANNEL): color_channel_schema, - cv.Optional(CONF_GREEN_CHANNEL): color_channel_schema, - cv.Optional(CONF_BLUE_CHANNEL): color_channel_schema, - cv.Optional(CONF_CLEAR_CHANNEL): color_channel_schema, - cv.Optional(CONF_ILLUMINANCE): illuminance_schema, - cv.Optional(CONF_COLOR_TEMPERATURE): color_temperature_schema, - cv.Optional(CONF_INTEGRATION_TIME, default='2.4ms'): - cv.enum(TCS34725_INTEGRATION_TIMES, lower=True), - cv.Optional(CONF_GAIN, default='1X'): cv.enum(TCS34725_GAINS, upper=True), -}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x29)) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(TCS34725Component), + cv.Optional(CONF_RED_CHANNEL): color_channel_schema, + cv.Optional(CONF_GREEN_CHANNEL): color_channel_schema, + cv.Optional(CONF_BLUE_CHANNEL): color_channel_schema, + cv.Optional(CONF_CLEAR_CHANNEL): color_channel_schema, + cv.Optional(CONF_ILLUMINANCE): illuminance_schema, + cv.Optional(CONF_COLOR_TEMPERATURE): color_temperature_schema, + cv.Optional(CONF_INTEGRATION_TIME, default="2.4ms"): cv.enum( + TCS34725_INTEGRATION_TIMES, lower=True + ), + cv.Optional(CONF_GAIN, default="1X"): cv.enum(TCS34725_GAINS, upper=True), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x29)) +) def to_code(config): diff --git a/esphome/components/teleinfo/__init__.py b/esphome/components/teleinfo/__init__.py index 00ca592272..2e279e892e 100644 --- a/esphome/components/teleinfo/__init__.py +++ b/esphome/components/teleinfo/__init__.py @@ -1 +1 @@ -CODEOWNERS = ['@0hax'] +CODEOWNERS = ["@0hax"] diff --git a/esphome/components/teleinfo/sensor.py b/esphome/components/teleinfo/sensor.py index 600464aa2b..9f6c2c8e89 100644 --- a/esphome/components/teleinfo/sensor.py +++ b/esphome/components/teleinfo/sensor.py @@ -1,27 +1,42 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, uart -from esphome.const import CONF_ID, CONF_SENSOR, DEVICE_CLASS_POWER, ICON_EMPTY, UNIT_WATT_HOURS +from esphome.const import ( + CONF_ID, + CONF_SENSOR, + DEVICE_CLASS_POWER, + ICON_EMPTY, + UNIT_WATT_HOURS, +) -DEPENDENCIES = ['uart'] +DEPENDENCIES = ["uart"] -teleinfo_ns = cg.esphome_ns.namespace('teleinfo') -TeleInfo = teleinfo_ns.class_('TeleInfo', cg.PollingComponent, uart.UARTDevice) +teleinfo_ns = cg.esphome_ns.namespace("teleinfo") +TeleInfo = teleinfo_ns.class_("TeleInfo", cg.PollingComponent, uart.UARTDevice) CONF_TAG_NAME = "tag_name" -TELEINFO_TAG_SCHEMA = cv.Schema({ - cv.Required(CONF_TAG_NAME): cv.string, - cv.Required(CONF_SENSOR): sensor.sensor_schema(UNIT_WATT_HOURS, ICON_EMPTY, 0, - DEVICE_CLASS_POWER) -}) +TELEINFO_TAG_SCHEMA = cv.Schema( + { + cv.Required(CONF_TAG_NAME): cv.string, + cv.Required(CONF_SENSOR): sensor.sensor_schema( + UNIT_WATT_HOURS, ICON_EMPTY, 0, DEVICE_CLASS_POWER + ), + } +) CONF_TAGS = "tags" CONF_HISTORICAL_MODE = "historical_mode" -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(TeleInfo), - cv.Optional(CONF_HISTORICAL_MODE, default=False): cv.boolean, - cv.Optional(CONF_TAGS): cv.ensure_list(TELEINFO_TAG_SCHEMA), -}).extend(cv.polling_component_schema('60s')).extend(uart.UART_DEVICE_SCHEMA) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(TeleInfo), + cv.Optional(CONF_HISTORICAL_MODE, default=False): cv.boolean, + cv.Optional(CONF_TAGS): cv.ensure_list(TELEINFO_TAG_SCHEMA), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(uart.UART_DEVICE_SCHEMA) +) def to_code(config): diff --git a/esphome/components/template/__init__.py b/esphome/components/template/__init__.py index 44c59260d3..6253af9090 100644 --- a/esphome/components/template/__init__.py +++ b/esphome/components/template/__init__.py @@ -1,3 +1,3 @@ import esphome.codegen as cg -template_ns = cg.esphome_ns.namespace('template_') +template_ns = cg.esphome_ns.namespace("template_") diff --git a/esphome/components/template/binary_sensor/__init__.py b/esphome/components/template/binary_sensor/__init__.py index 14f9f23ec2..d23e6423a1 100644 --- a/esphome/components/template/binary_sensor/__init__.py +++ b/esphome/components/template/binary_sensor/__init__.py @@ -5,13 +5,16 @@ from esphome.components import binary_sensor from esphome.const import CONF_ID, CONF_LAMBDA, CONF_STATE from .. import template_ns -TemplateBinarySensor = template_ns.class_('TemplateBinarySensor', binary_sensor.BinarySensor, - cg.Component) +TemplateBinarySensor = template_ns.class_( + "TemplateBinarySensor", binary_sensor.BinarySensor, cg.Component +) -CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(TemplateBinarySensor), - cv.Optional(CONF_LAMBDA): cv.returning_lambda, -}).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(TemplateBinarySensor), + cv.Optional(CONF_LAMBDA): cv.returning_lambda, + } +).extend(cv.COMPONENT_SCHEMA) def to_code(config): @@ -20,17 +23,22 @@ def to_code(config): yield binary_sensor.register_binary_sensor(var, config) if CONF_LAMBDA in config: - template_ = yield cg.process_lambda(config[CONF_LAMBDA], [], - return_type=cg.optional.template(bool)) + template_ = yield cg.process_lambda( + config[CONF_LAMBDA], [], return_type=cg.optional.template(bool) + ) cg.add(var.set_template(template_)) -@automation.register_action('binary_sensor.template.publish', - binary_sensor.BinarySensorPublishAction, - cv.Schema({ - cv.Required(CONF_ID): cv.use_id(binary_sensor.BinarySensor), - cv.Required(CONF_STATE): cv.templatable(cv.boolean), - })) +@automation.register_action( + "binary_sensor.template.publish", + binary_sensor.BinarySensorPublishAction, + cv.Schema( + { + cv.Required(CONF_ID): cv.use_id(binary_sensor.BinarySensor), + cv.Required(CONF_STATE): cv.templatable(cv.boolean), + } + ), +) def binary_sensor_template_publish_to_code(config, action_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) var = cg.new_Pvariable(action_id, template_arg, paren) diff --git a/esphome/components/template/cover/__init__.py b/esphome/components/template/cover/__init__.py index 607d9d0064..0c81e095be 100644 --- a/esphome/components/template/cover/__init__.py +++ b/esphome/components/template/cover/__init__.py @@ -2,37 +2,54 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import automation from esphome.components import cover -from esphome.const import CONF_ASSUMED_STATE, CONF_CLOSE_ACTION, CONF_CURRENT_OPERATION, CONF_ID, \ - CONF_LAMBDA, CONF_OPEN_ACTION, CONF_OPTIMISTIC, CONF_POSITION, CONF_RESTORE_MODE, \ - CONF_STATE, CONF_STOP_ACTION, CONF_TILT, CONF_TILT_ACTION, CONF_TILT_LAMBDA, \ - CONF_POSITION_ACTION +from esphome.const import ( + CONF_ASSUMED_STATE, + CONF_CLOSE_ACTION, + CONF_CURRENT_OPERATION, + CONF_ID, + CONF_LAMBDA, + CONF_OPEN_ACTION, + CONF_OPTIMISTIC, + CONF_POSITION, + CONF_RESTORE_MODE, + CONF_STATE, + CONF_STOP_ACTION, + CONF_TILT, + CONF_TILT_ACTION, + CONF_TILT_LAMBDA, + CONF_POSITION_ACTION, +) from .. import template_ns -TemplateCover = template_ns.class_('TemplateCover', cover.Cover, cg.Component) +TemplateCover = template_ns.class_("TemplateCover", cover.Cover, cg.Component) -TemplateCoverRestoreMode = template_ns.enum('TemplateCoverRestoreMode') +TemplateCoverRestoreMode = template_ns.enum("TemplateCoverRestoreMode") RESTORE_MODES = { - 'NO_RESTORE': TemplateCoverRestoreMode.COVER_NO_RESTORE, - 'RESTORE': TemplateCoverRestoreMode.COVER_RESTORE, - 'RESTORE_AND_CALL': TemplateCoverRestoreMode.COVER_RESTORE_AND_CALL, + "NO_RESTORE": TemplateCoverRestoreMode.COVER_NO_RESTORE, + "RESTORE": TemplateCoverRestoreMode.COVER_RESTORE, + "RESTORE_AND_CALL": TemplateCoverRestoreMode.COVER_RESTORE_AND_CALL, } -CONF_HAS_POSITION = 'has_position' +CONF_HAS_POSITION = "has_position" -CONFIG_SCHEMA = cover.COVER_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(TemplateCover), - cv.Optional(CONF_LAMBDA): cv.returning_lambda, - cv.Optional(CONF_OPTIMISTIC, default=False): cv.boolean, - cv.Optional(CONF_ASSUMED_STATE, default=False): cv.boolean, - cv.Optional(CONF_HAS_POSITION, default=False): cv.boolean, - cv.Optional(CONF_OPEN_ACTION): automation.validate_automation(single=True), - cv.Optional(CONF_CLOSE_ACTION): automation.validate_automation(single=True), - cv.Optional(CONF_STOP_ACTION): automation.validate_automation(single=True), - cv.Optional(CONF_TILT_ACTION): automation.validate_automation(single=True), - cv.Optional(CONF_TILT_LAMBDA): cv.returning_lambda, - cv.Optional(CONF_POSITION_ACTION): automation.validate_automation(single=True), - cv.Optional(CONF_RESTORE_MODE, default='RESTORE'): cv.enum(RESTORE_MODES, upper=True), -}).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = cover.COVER_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(TemplateCover), + cv.Optional(CONF_LAMBDA): cv.returning_lambda, + cv.Optional(CONF_OPTIMISTIC, default=False): cv.boolean, + cv.Optional(CONF_ASSUMED_STATE, default=False): cv.boolean, + cv.Optional(CONF_HAS_POSITION, default=False): cv.boolean, + cv.Optional(CONF_OPEN_ACTION): automation.validate_automation(single=True), + cv.Optional(CONF_CLOSE_ACTION): automation.validate_automation(single=True), + cv.Optional(CONF_STOP_ACTION): automation.validate_automation(single=True), + cv.Optional(CONF_TILT_ACTION): automation.validate_automation(single=True), + cv.Optional(CONF_TILT_LAMBDA): cv.returning_lambda, + cv.Optional(CONF_POSITION_ACTION): automation.validate_automation(single=True), + cv.Optional(CONF_RESTORE_MODE, default="RESTORE"): cv.enum( + RESTORE_MODES, upper=True + ), + } +).extend(cv.COMPONENT_SCHEMA) def to_code(config): @@ -40,26 +57,36 @@ def to_code(config): yield cg.register_component(var, config) yield cover.register_cover(var, config) if CONF_LAMBDA in config: - template_ = yield cg.process_lambda(config[CONF_LAMBDA], [], - return_type=cg.optional.template(float)) + template_ = yield cg.process_lambda( + config[CONF_LAMBDA], [], return_type=cg.optional.template(float) + ) cg.add(var.set_state_lambda(template_)) if CONF_OPEN_ACTION in config: - yield automation.build_automation(var.get_open_trigger(), [], config[CONF_OPEN_ACTION]) + yield automation.build_automation( + var.get_open_trigger(), [], config[CONF_OPEN_ACTION] + ) if CONF_CLOSE_ACTION in config: - yield automation.build_automation(var.get_close_trigger(), [], config[CONF_CLOSE_ACTION]) + yield automation.build_automation( + var.get_close_trigger(), [], config[CONF_CLOSE_ACTION] + ) if CONF_STOP_ACTION in config: - yield automation.build_automation(var.get_stop_trigger(), [], config[CONF_STOP_ACTION]) + yield automation.build_automation( + var.get_stop_trigger(), [], config[CONF_STOP_ACTION] + ) if CONF_TILT_ACTION in config: - yield automation.build_automation(var.get_tilt_trigger(), [(float, 'tilt')], - config[CONF_TILT_ACTION]) + yield automation.build_automation( + var.get_tilt_trigger(), [(float, "tilt")], config[CONF_TILT_ACTION] + ) cg.add(var.set_has_tilt(True)) if CONF_TILT_LAMBDA in config: - tilt_template_ = yield cg.process_lambda(config[CONF_TILT_LAMBDA], [], - return_type=cg.optional.template(float)) + tilt_template_ = yield cg.process_lambda( + config[CONF_TILT_LAMBDA], [], return_type=cg.optional.template(float) + ) cg.add(var.set_tilt_lambda(tilt_template_)) if CONF_POSITION_ACTION in config: - yield automation.build_automation(var.get_position_trigger(), [(float, 'pos')], - config[CONF_POSITION_ACTION]) + yield automation.build_automation( + var.get_position_trigger(), [(float, "pos")], config[CONF_POSITION_ACTION] + ) cg.add(var.set_has_position(True)) else: cg.add(var.set_has_position(config[CONF_HAS_POSITION])) @@ -68,13 +95,21 @@ def to_code(config): cg.add(var.set_restore_mode(config[CONF_RESTORE_MODE])) -@automation.register_action('cover.template.publish', cover.CoverPublishAction, cv.Schema({ - cv.Required(CONF_ID): cv.use_id(cover.Cover), - cv.Exclusive(CONF_STATE, 'pos'): cv.templatable(cover.validate_cover_state), - cv.Exclusive(CONF_POSITION, 'pos'): cv.templatable(cv.zero_to_one_float), - cv.Optional(CONF_CURRENT_OPERATION): cv.templatable(cover.validate_cover_operation), - cv.Optional(CONF_TILT): cv.templatable(cv.zero_to_one_float), -})) +@automation.register_action( + "cover.template.publish", + cover.CoverPublishAction, + cv.Schema( + { + cv.Required(CONF_ID): cv.use_id(cover.Cover), + cv.Exclusive(CONF_STATE, "pos"): cv.templatable(cover.validate_cover_state), + cv.Exclusive(CONF_POSITION, "pos"): cv.templatable(cv.zero_to_one_float), + cv.Optional(CONF_CURRENT_OPERATION): cv.templatable( + cover.validate_cover_operation + ), + cv.Optional(CONF_TILT): cv.templatable(cv.zero_to_one_float), + } + ), +) def cover_template_publish_to_code(config, action_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) var = cg.new_Pvariable(action_id, template_arg, paren) @@ -88,6 +123,8 @@ def cover_template_publish_to_code(config, action_id, template_arg, args): template_ = yield cg.templatable(config[CONF_TILT], args, float) cg.add(var.set_tilt(template_)) if CONF_CURRENT_OPERATION in config: - template_ = yield cg.templatable(config[CONF_CURRENT_OPERATION], args, cover.CoverOperation) + template_ = yield cg.templatable( + config[CONF_CURRENT_OPERATION], args, cover.CoverOperation + ) cg.add(var.set_current_operation(template_)) yield var diff --git a/esphome/components/template/output/__init__.py b/esphome/components/template/output/__init__.py index cc85a9da68..61286772d2 100644 --- a/esphome/components/template/output/__init__.py +++ b/esphome/components/template/output/__init__.py @@ -5,30 +5,43 @@ from esphome.components import output from esphome.const import CONF_ID, CONF_TYPE, CONF_BINARY from .. import template_ns -TemplateBinaryOutput = template_ns.class_('TemplateBinaryOutput', output.BinaryOutput) -TemplateFloatOutput = template_ns.class_('TemplateFloatOutput', output.FloatOutput) +TemplateBinaryOutput = template_ns.class_("TemplateBinaryOutput", output.BinaryOutput) +TemplateFloatOutput = template_ns.class_("TemplateFloatOutput", output.FloatOutput) -CONF_FLOAT = 'float' -CONF_WRITE_ACTION = 'write_action' +CONF_FLOAT = "float" +CONF_WRITE_ACTION = "write_action" -CONFIG_SCHEMA = cv.typed_schema({ - CONF_BINARY: output.BINARY_OUTPUT_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(TemplateBinaryOutput), - cv.Required(CONF_WRITE_ACTION): automation.validate_automation(single=True), - }), - CONF_FLOAT: output.FLOAT_OUTPUT_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(TemplateFloatOutput), - cv.Required(CONF_WRITE_ACTION): automation.validate_automation(single=True), - }), -}, lower=True) +CONFIG_SCHEMA = cv.typed_schema( + { + CONF_BINARY: output.BINARY_OUTPUT_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(TemplateBinaryOutput), + cv.Required(CONF_WRITE_ACTION): automation.validate_automation( + single=True + ), + } + ), + CONF_FLOAT: output.FLOAT_OUTPUT_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(TemplateFloatOutput), + cv.Required(CONF_WRITE_ACTION): automation.validate_automation( + single=True + ), + } + ), + }, + lower=True, +) def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) if config[CONF_TYPE] == CONF_BINARY: - yield automation.build_automation(var.get_trigger(), [(bool, 'state')], - config[CONF_WRITE_ACTION]) + yield automation.build_automation( + var.get_trigger(), [(bool, "state")], config[CONF_WRITE_ACTION] + ) else: - yield automation.build_automation(var.get_trigger(), [(float, 'state')], - config[CONF_WRITE_ACTION]) + yield automation.build_automation( + var.get_trigger(), [(float, "state")], config[CONF_WRITE_ACTION] + ) yield output.register_output(var, config) diff --git a/esphome/components/template/sensor/__init__.py b/esphome/components/template/sensor/__init__.py index 7ebfc1047a..21ef5db32d 100644 --- a/esphome/components/template/sensor/__init__.py +++ b/esphome/components/template/sensor/__init__.py @@ -2,16 +2,30 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import automation from esphome.components import sensor -from esphome.const import CONF_ID, CONF_LAMBDA, CONF_STATE, DEVICE_CLASS_EMPTY, UNIT_EMPTY, \ - ICON_EMPTY +from esphome.const import ( + CONF_ID, + CONF_LAMBDA, + CONF_STATE, + DEVICE_CLASS_EMPTY, + UNIT_EMPTY, + ICON_EMPTY, +) from .. import template_ns -TemplateSensor = template_ns.class_('TemplateSensor', sensor.Sensor, cg.PollingComponent) +TemplateSensor = template_ns.class_( + "TemplateSensor", sensor.Sensor, cg.PollingComponent +) -CONFIG_SCHEMA = sensor.sensor_schema(UNIT_EMPTY, ICON_EMPTY, 1, DEVICE_CLASS_EMPTY).extend({ - cv.GenerateID(): cv.declare_id(TemplateSensor), - cv.Optional(CONF_LAMBDA): cv.returning_lambda, -}).extend(cv.polling_component_schema('60s')) +CONFIG_SCHEMA = ( + sensor.sensor_schema(UNIT_EMPTY, ICON_EMPTY, 1, DEVICE_CLASS_EMPTY) + .extend( + { + cv.GenerateID(): cv.declare_id(TemplateSensor), + cv.Optional(CONF_LAMBDA): cv.returning_lambda, + } + ) + .extend(cv.polling_component_schema("60s")) +) def to_code(config): @@ -20,16 +34,22 @@ def to_code(config): yield sensor.register_sensor(var, config) if CONF_LAMBDA in config: - template_ = yield cg.process_lambda(config[CONF_LAMBDA], [], - return_type=cg.optional.template(float)) + template_ = yield cg.process_lambda( + config[CONF_LAMBDA], [], return_type=cg.optional.template(float) + ) cg.add(var.set_template(template_)) -@automation.register_action('sensor.template.publish', sensor.SensorPublishAction, - cv.Schema({ - cv.Required(CONF_ID): cv.use_id(sensor.Sensor), - cv.Required(CONF_STATE): cv.templatable(cv.float_), - })) +@automation.register_action( + "sensor.template.publish", + sensor.SensorPublishAction, + cv.Schema( + { + cv.Required(CONF_ID): cv.use_id(sensor.Sensor), + cv.Required(CONF_STATE): cv.templatable(cv.float_), + } + ), +) def sensor_template_publish_to_code(config, action_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) var = cg.new_Pvariable(action_id, template_arg, paren) diff --git a/esphome/components/template/switch/__init__.py b/esphome/components/template/switch/__init__.py index 783f5a1922..7698e4f2a9 100644 --- a/esphome/components/template/switch/__init__.py +++ b/esphome/components/template/switch/__init__.py @@ -2,21 +2,31 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import automation from esphome.components import switch -from esphome.const import CONF_ASSUMED_STATE, CONF_ID, CONF_LAMBDA, CONF_OPTIMISTIC, \ - CONF_RESTORE_STATE, CONF_STATE, CONF_TURN_OFF_ACTION, CONF_TURN_ON_ACTION +from esphome.const import ( + CONF_ASSUMED_STATE, + CONF_ID, + CONF_LAMBDA, + CONF_OPTIMISTIC, + CONF_RESTORE_STATE, + CONF_STATE, + CONF_TURN_OFF_ACTION, + CONF_TURN_ON_ACTION, +) from .. import template_ns -TemplateSwitch = template_ns.class_('TemplateSwitch', switch.Switch, cg.Component) +TemplateSwitch = template_ns.class_("TemplateSwitch", switch.Switch, cg.Component) -CONFIG_SCHEMA = switch.SWITCH_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(TemplateSwitch), - cv.Optional(CONF_LAMBDA): cv.returning_lambda, - cv.Optional(CONF_OPTIMISTIC, default=False): cv.boolean, - cv.Optional(CONF_ASSUMED_STATE, default=False): cv.boolean, - cv.Optional(CONF_TURN_OFF_ACTION): automation.validate_automation(single=True), - cv.Optional(CONF_TURN_ON_ACTION): automation.validate_automation(single=True), - cv.Optional(CONF_RESTORE_STATE, default=False): cv.boolean, -}).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = switch.SWITCH_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(TemplateSwitch), + cv.Optional(CONF_LAMBDA): cv.returning_lambda, + cv.Optional(CONF_OPTIMISTIC, default=False): cv.boolean, + cv.Optional(CONF_ASSUMED_STATE, default=False): cv.boolean, + cv.Optional(CONF_TURN_OFF_ACTION): automation.validate_automation(single=True), + cv.Optional(CONF_TURN_ON_ACTION): automation.validate_automation(single=True), + cv.Optional(CONF_RESTORE_STATE, default=False): cv.boolean, + } +).extend(cv.COMPONENT_SCHEMA) def to_code(config): @@ -25,24 +35,33 @@ def to_code(config): yield switch.register_switch(var, config) if CONF_LAMBDA in config: - template_ = yield cg.process_lambda(config[CONF_LAMBDA], [], - return_type=cg.optional.template(bool)) + template_ = yield cg.process_lambda( + config[CONF_LAMBDA], [], return_type=cg.optional.template(bool) + ) cg.add(var.set_state_lambda(template_)) if CONF_TURN_OFF_ACTION in config: - yield automation.build_automation(var.get_turn_off_trigger(), [], - config[CONF_TURN_OFF_ACTION]) + yield automation.build_automation( + var.get_turn_off_trigger(), [], config[CONF_TURN_OFF_ACTION] + ) if CONF_TURN_ON_ACTION in config: - yield automation.build_automation(var.get_turn_on_trigger(), [], - config[CONF_TURN_ON_ACTION]) + yield automation.build_automation( + var.get_turn_on_trigger(), [], config[CONF_TURN_ON_ACTION] + ) cg.add(var.set_optimistic(config[CONF_OPTIMISTIC])) cg.add(var.set_assumed_state(config[CONF_ASSUMED_STATE])) cg.add(var.set_restore_state(config[CONF_RESTORE_STATE])) -@automation.register_action('switch.template.publish', switch.SwitchPublishAction, cv.Schema({ - cv.Required(CONF_ID): cv.use_id(switch.Switch), - cv.Required(CONF_STATE): cv.templatable(cv.boolean), -})) +@automation.register_action( + "switch.template.publish", + switch.SwitchPublishAction, + cv.Schema( + { + cv.Required(CONF_ID): cv.use_id(switch.Switch), + cv.Required(CONF_STATE): cv.templatable(cv.boolean), + } + ), +) def switch_template_publish_to_code(config, action_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) var = cg.new_Pvariable(action_id, template_arg, paren) diff --git a/esphome/components/template/text_sensor/__init__.py b/esphome/components/template/text_sensor/__init__.py index dae99dc9bc..cae00e3932 100644 --- a/esphome/components/template/text_sensor/__init__.py +++ b/esphome/components/template/text_sensor/__init__.py @@ -6,13 +6,16 @@ from esphome.components.text_sensor import TextSensorPublishAction from esphome.const import CONF_ID, CONF_LAMBDA, CONF_STATE from .. import template_ns -TemplateTextSensor = template_ns.class_('TemplateTextSensor', text_sensor.TextSensor, - cg.PollingComponent) +TemplateTextSensor = template_ns.class_( + "TemplateTextSensor", text_sensor.TextSensor, cg.PollingComponent +) -CONFIG_SCHEMA = text_sensor.TEXT_SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(TemplateTextSensor), - cv.Optional(CONF_LAMBDA): cv.returning_lambda, -}).extend(cv.polling_component_schema('60s')) +CONFIG_SCHEMA = text_sensor.TEXT_SENSOR_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(TemplateTextSensor), + cv.Optional(CONF_LAMBDA): cv.returning_lambda, + } +).extend(cv.polling_component_schema("60s")) def to_code(config): @@ -21,15 +24,22 @@ def to_code(config): yield text_sensor.register_text_sensor(var, config) if CONF_LAMBDA in config: - template_ = yield cg.process_lambda(config[CONF_LAMBDA], [], - return_type=cg.optional.template(cg.std_string)) + template_ = yield cg.process_lambda( + config[CONF_LAMBDA], [], return_type=cg.optional.template(cg.std_string) + ) cg.add(var.set_template(template_)) -@automation.register_action('text_sensor.template.publish', TextSensorPublishAction, cv.Schema({ - cv.Required(CONF_ID): cv.use_id(text_sensor.TextSensor), - cv.Required(CONF_STATE): cv.templatable(cv.string_strict), -})) +@automation.register_action( + "text_sensor.template.publish", + TextSensorPublishAction, + cv.Schema( + { + cv.Required(CONF_ID): cv.use_id(text_sensor.TextSensor), + cv.Required(CONF_STATE): cv.templatable(cv.string_strict), + } + ), +) def text_sensor_template_publish_to_code(config, action_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) var = cg.new_Pvariable(action_id, template_arg, paren) diff --git a/esphome/components/text_sensor/__init__.py b/esphome/components/text_sensor/__init__.py index f138f38d2f..ff73889d61 100644 --- a/esphome/components/text_sensor/__init__.py +++ b/esphome/components/text_sensor/__init__.py @@ -2,31 +2,48 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import automation from esphome.components import mqtt -from esphome.const import CONF_ICON, CONF_ID, CONF_INTERNAL, CONF_ON_VALUE, \ - CONF_TRIGGER_ID, CONF_MQTT_ID, CONF_NAME, CONF_STATE +from esphome.const import ( + CONF_ICON, + CONF_ID, + CONF_INTERNAL, + CONF_ON_VALUE, + CONF_TRIGGER_ID, + CONF_MQTT_ID, + CONF_NAME, + CONF_STATE, +) from esphome.core import CORE, coroutine, coroutine_with_priority IS_PLATFORM_COMPONENT = True # pylint: disable=invalid-name -text_sensor_ns = cg.esphome_ns.namespace('text_sensor') -TextSensor = text_sensor_ns.class_('TextSensor', cg.Nameable) -TextSensorPtr = TextSensor.operator('ptr') +text_sensor_ns = cg.esphome_ns.namespace("text_sensor") +TextSensor = text_sensor_ns.class_("TextSensor", cg.Nameable) +TextSensorPtr = TextSensor.operator("ptr") -TextSensorStateTrigger = text_sensor_ns.class_('TextSensorStateTrigger', - automation.Trigger.template(cg.std_string)) -TextSensorPublishAction = text_sensor_ns.class_('TextSensorPublishAction', automation.Action) -TextSensorStateCondition = text_sensor_ns.class_('TextSensorStateCondition', automation.Condition) +TextSensorStateTrigger = text_sensor_ns.class_( + "TextSensorStateTrigger", automation.Trigger.template(cg.std_string) +) +TextSensorPublishAction = text_sensor_ns.class_( + "TextSensorPublishAction", automation.Action +) +TextSensorStateCondition = text_sensor_ns.class_( + "TextSensorStateCondition", automation.Condition +) icon = cv.icon -TEXT_SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend({ - cv.OnlyWith(CONF_MQTT_ID, 'mqtt'): cv.declare_id(mqtt.MQTTTextSensor), - cv.Optional(CONF_ICON): icon, - cv.Optional(CONF_ON_VALUE): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(TextSensorStateTrigger), - }), -}) +TEXT_SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend( + { + cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTTextSensor), + cv.Optional(CONF_ICON): icon, + cv.Optional(CONF_ON_VALUE): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(TextSensorStateTrigger), + } + ), + } +) @coroutine @@ -39,7 +56,7 @@ def setup_text_sensor_core_(var, config): for conf in config.get(CONF_ON_VALUE, []): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) - yield automation.build_automation(trigger, [(cg.std_string, 'x')], conf) + yield automation.build_automation(trigger, [(cg.std_string, "x")], conf) if CONF_MQTT_ID in config: mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var) @@ -56,14 +73,20 @@ def register_text_sensor(var, config): @coroutine_with_priority(100.0) def to_code(config): - cg.add_define('USE_TEXT_SENSOR') + cg.add_define("USE_TEXT_SENSOR") cg.add_global(text_sensor_ns.using) -@automation.register_condition('text_sensor.state', TextSensorStateCondition, cv.Schema({ - cv.Required(CONF_ID): cv.use_id(TextSensor), - cv.Required(CONF_STATE): cv.templatable(cv.string_strict), -})) +@automation.register_condition( + "text_sensor.state", + TextSensorStateCondition, + cv.Schema( + { + cv.Required(CONF_ID): cv.use_id(TextSensor), + cv.Required(CONF_STATE): cv.templatable(cv.string_strict), + } + ), +) def text_sensor_state_to_code(config, condition_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) var = cg.new_Pvariable(condition_id, template_arg, paren) diff --git a/esphome/components/thermostat/climate.py b/esphome/components/thermostat/climate.py index bd8633ee1c..04584583af 100644 --- a/esphome/components/thermostat/climate.py +++ b/esphome/components/thermostat/climate.py @@ -2,109 +2,224 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import automation from esphome.components import climate, sensor -from esphome.const import CONF_AUTO_MODE, CONF_AWAY_CONFIG, CONF_COOL_ACTION, CONF_COOL_MODE, \ - CONF_DEFAULT_TARGET_TEMPERATURE_HIGH, CONF_DEFAULT_TARGET_TEMPERATURE_LOW, CONF_DRY_ACTION, \ - CONF_DRY_MODE, CONF_FAN_MODE_ON_ACTION, CONF_FAN_MODE_OFF_ACTION, CONF_FAN_MODE_AUTO_ACTION, \ - CONF_FAN_MODE_LOW_ACTION, CONF_FAN_MODE_MEDIUM_ACTION, CONF_FAN_MODE_HIGH_ACTION, \ - CONF_FAN_MODE_MIDDLE_ACTION, CONF_FAN_MODE_FOCUS_ACTION, CONF_FAN_MODE_DIFFUSE_ACTION, \ - CONF_FAN_ONLY_ACTION, CONF_FAN_ONLY_MODE, CONF_HEAT_ACTION, CONF_HEAT_MODE, CONF_HYSTERESIS, \ - CONF_ID, CONF_IDLE_ACTION, CONF_OFF_MODE, CONF_SENSOR, CONF_SWING_BOTH_ACTION, \ - CONF_SWING_HORIZONTAL_ACTION, CONF_SWING_OFF_ACTION, CONF_SWING_VERTICAL_ACTION +from esphome.const import ( + CONF_AUTO_MODE, + CONF_AWAY_CONFIG, + CONF_COOL_ACTION, + CONF_COOL_MODE, + CONF_DEFAULT_TARGET_TEMPERATURE_HIGH, + CONF_DEFAULT_TARGET_TEMPERATURE_LOW, + CONF_DRY_ACTION, + CONF_DRY_MODE, + CONF_FAN_MODE_ON_ACTION, + CONF_FAN_MODE_OFF_ACTION, + CONF_FAN_MODE_AUTO_ACTION, + CONF_FAN_MODE_LOW_ACTION, + CONF_FAN_MODE_MEDIUM_ACTION, + CONF_FAN_MODE_HIGH_ACTION, + CONF_FAN_MODE_MIDDLE_ACTION, + CONF_FAN_MODE_FOCUS_ACTION, + CONF_FAN_MODE_DIFFUSE_ACTION, + CONF_FAN_ONLY_ACTION, + CONF_FAN_ONLY_MODE, + CONF_HEAT_ACTION, + CONF_HEAT_MODE, + CONF_HYSTERESIS, + CONF_ID, + CONF_IDLE_ACTION, + CONF_OFF_MODE, + CONF_SENSOR, + CONF_SWING_BOTH_ACTION, + CONF_SWING_HORIZONTAL_ACTION, + CONF_SWING_OFF_ACTION, + CONF_SWING_VERTICAL_ACTION, +) -CODEOWNERS = ['@kbx81'] +CODEOWNERS = ["@kbx81"] -thermostat_ns = cg.esphome_ns.namespace('thermostat') -ThermostatClimate = thermostat_ns.class_('ThermostatClimate', climate.Climate, cg.Component) -ThermostatClimateTargetTempConfig = thermostat_ns.struct('ThermostatClimateTargetTempConfig') +thermostat_ns = cg.esphome_ns.namespace("thermostat") +ThermostatClimate = thermostat_ns.class_( + "ThermostatClimate", climate.Climate, cg.Component +) +ThermostatClimateTargetTempConfig = thermostat_ns.struct( + "ThermostatClimateTargetTempConfig" +) def validate_thermostat(config): # verify corresponding climate action action exists for any defined climate mode action if CONF_COOL_MODE in config and CONF_COOL_ACTION not in config: - raise cv.Invalid("{} must be defined to use {}".format(CONF_COOL_ACTION, CONF_COOL_MODE)) + raise cv.Invalid( + "{} must be defined to use {}".format(CONF_COOL_ACTION, CONF_COOL_MODE) + ) if CONF_DRY_MODE in config and CONF_DRY_ACTION not in config: - raise cv.Invalid("{} must be defined to use {}".format(CONF_DRY_ACTION, CONF_DRY_MODE)) + raise cv.Invalid( + "{} must be defined to use {}".format(CONF_DRY_ACTION, CONF_DRY_MODE) + ) if CONF_FAN_ONLY_MODE in config and CONF_FAN_ONLY_ACTION not in config: - raise cv.Invalid("{} must be defined to use {}".format(CONF_FAN_ONLY_ACTION, - CONF_FAN_ONLY_MODE)) + raise cv.Invalid( + "{} must be defined to use {}".format( + CONF_FAN_ONLY_ACTION, CONF_FAN_ONLY_MODE + ) + ) if CONF_HEAT_MODE in config and CONF_HEAT_ACTION not in config: - raise cv.Invalid("{} must be defined to use {}".format(CONF_HEAT_ACTION, CONF_HEAT_MODE)) + raise cv.Invalid( + "{} must be defined to use {}".format(CONF_HEAT_ACTION, CONF_HEAT_MODE) + ) # verify corresponding default target temperature exists when a given climate action exists - if CONF_DEFAULT_TARGET_TEMPERATURE_HIGH not in config and (CONF_COOL_ACTION in config - or CONF_FAN_ONLY_ACTION in config): - raise cv.Invalid("{} must be defined when using {} or {}".format( - CONF_DEFAULT_TARGET_TEMPERATURE_HIGH, CONF_COOL_ACTION, CONF_FAN_ONLY_ACTION)) + if CONF_DEFAULT_TARGET_TEMPERATURE_HIGH not in config and ( + CONF_COOL_ACTION in config or CONF_FAN_ONLY_ACTION in config + ): + raise cv.Invalid( + "{} must be defined when using {} or {}".format( + CONF_DEFAULT_TARGET_TEMPERATURE_HIGH, + CONF_COOL_ACTION, + CONF_FAN_ONLY_ACTION, + ) + ) if CONF_DEFAULT_TARGET_TEMPERATURE_LOW not in config and CONF_HEAT_ACTION in config: - raise cv.Invalid("{} must be defined when using {}".format( - CONF_DEFAULT_TARGET_TEMPERATURE_LOW, CONF_HEAT_ACTION)) + raise cv.Invalid( + "{} must be defined when using {}".format( + CONF_DEFAULT_TARGET_TEMPERATURE_LOW, CONF_HEAT_ACTION + ) + ) # if a given climate action is NOT defined, it should not have a default target temperature - if CONF_DEFAULT_TARGET_TEMPERATURE_HIGH in config and (CONF_COOL_ACTION not in config - and CONF_FAN_ONLY_ACTION not in config): - raise cv.Invalid("{} is defined with no {}".format( - CONF_DEFAULT_TARGET_TEMPERATURE_HIGH, CONF_COOL_ACTION)) + if CONF_DEFAULT_TARGET_TEMPERATURE_HIGH in config and ( + CONF_COOL_ACTION not in config and CONF_FAN_ONLY_ACTION not in config + ): + raise cv.Invalid( + "{} is defined with no {}".format( + CONF_DEFAULT_TARGET_TEMPERATURE_HIGH, CONF_COOL_ACTION + ) + ) if CONF_DEFAULT_TARGET_TEMPERATURE_LOW in config and CONF_HEAT_ACTION not in config: - raise cv.Invalid("{} is defined with no {}".format( - CONF_DEFAULT_TARGET_TEMPERATURE_LOW, CONF_HEAT_ACTION)) + raise cv.Invalid( + "{} is defined with no {}".format( + CONF_DEFAULT_TARGET_TEMPERATURE_LOW, CONF_HEAT_ACTION + ) + ) if CONF_AWAY_CONFIG in config: away = config[CONF_AWAY_CONFIG] # verify corresponding default target temperature exists when a given climate action exists - if CONF_DEFAULT_TARGET_TEMPERATURE_HIGH not in away and (CONF_COOL_ACTION in config or - CONF_FAN_ONLY_ACTION in config): - raise cv.Invalid("{} must be defined in away configuration when using {} or {}".format( - CONF_DEFAULT_TARGET_TEMPERATURE_HIGH, CONF_COOL_ACTION, CONF_FAN_ONLY_ACTION)) - if CONF_DEFAULT_TARGET_TEMPERATURE_LOW not in away and CONF_HEAT_ACTION in config: - raise cv.Invalid("{} must be defined in away configuration when using {}".format( - CONF_DEFAULT_TARGET_TEMPERATURE_LOW, CONF_HEAT_ACTION)) + if CONF_DEFAULT_TARGET_TEMPERATURE_HIGH not in away and ( + CONF_COOL_ACTION in config or CONF_FAN_ONLY_ACTION in config + ): + raise cv.Invalid( + "{} must be defined in away configuration when using {} or {}".format( + CONF_DEFAULT_TARGET_TEMPERATURE_HIGH, + CONF_COOL_ACTION, + CONF_FAN_ONLY_ACTION, + ) + ) + if ( + CONF_DEFAULT_TARGET_TEMPERATURE_LOW not in away + and CONF_HEAT_ACTION in config + ): + raise cv.Invalid( + "{} must be defined in away configuration when using {}".format( + CONF_DEFAULT_TARGET_TEMPERATURE_LOW, CONF_HEAT_ACTION + ) + ) # if a given climate action is NOT defined, it should not have a default target temperature - if CONF_DEFAULT_TARGET_TEMPERATURE_HIGH in away and (CONF_COOL_ACTION not in config and - CONF_FAN_ONLY_ACTION not in config): - raise cv.Invalid("{} is defined in away configuration with no {} or {}".format( - CONF_DEFAULT_TARGET_TEMPERATURE_HIGH, CONF_COOL_ACTION, CONF_FAN_ONLY_ACTION)) - if CONF_DEFAULT_TARGET_TEMPERATURE_LOW in away and CONF_HEAT_ACTION not in config: - raise cv.Invalid("{} is defined in away configuration with no {}".format( - CONF_DEFAULT_TARGET_TEMPERATURE_LOW, CONF_HEAT_ACTION)) + if CONF_DEFAULT_TARGET_TEMPERATURE_HIGH in away and ( + CONF_COOL_ACTION not in config and CONF_FAN_ONLY_ACTION not in config + ): + raise cv.Invalid( + "{} is defined in away configuration with no {} or {}".format( + CONF_DEFAULT_TARGET_TEMPERATURE_HIGH, + CONF_COOL_ACTION, + CONF_FAN_ONLY_ACTION, + ) + ) + if ( + CONF_DEFAULT_TARGET_TEMPERATURE_LOW in away + and CONF_HEAT_ACTION not in config + ): + raise cv.Invalid( + "{} is defined in away configuration with no {}".format( + CONF_DEFAULT_TARGET_TEMPERATURE_LOW, CONF_HEAT_ACTION + ) + ) return config -CONFIG_SCHEMA = cv.All(climate.CLIMATE_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(ThermostatClimate), - cv.Required(CONF_SENSOR): cv.use_id(sensor.Sensor), - cv.Required(CONF_IDLE_ACTION): automation.validate_automation(single=True), - cv.Optional(CONF_COOL_ACTION): automation.validate_automation(single=True), - cv.Optional(CONF_DRY_ACTION): automation.validate_automation(single=True), - cv.Optional(CONF_FAN_ONLY_ACTION): automation.validate_automation(single=True), - cv.Optional(CONF_HEAT_ACTION): automation.validate_automation(single=True), - cv.Optional(CONF_AUTO_MODE): automation.validate_automation(single=True), - cv.Optional(CONF_COOL_MODE): automation.validate_automation(single=True), - cv.Optional(CONF_DRY_MODE): automation.validate_automation(single=True), - cv.Optional(CONF_FAN_ONLY_MODE): automation.validate_automation(single=True), - cv.Optional(CONF_HEAT_MODE): automation.validate_automation(single=True), - cv.Optional(CONF_OFF_MODE): automation.validate_automation(single=True), - cv.Optional(CONF_FAN_MODE_ON_ACTION): automation.validate_automation(single=True), - cv.Optional(CONF_FAN_MODE_OFF_ACTION): automation.validate_automation(single=True), - cv.Optional(CONF_FAN_MODE_AUTO_ACTION): automation.validate_automation(single=True), - cv.Optional(CONF_FAN_MODE_LOW_ACTION): automation.validate_automation(single=True), - cv.Optional(CONF_FAN_MODE_MEDIUM_ACTION): automation.validate_automation(single=True), - cv.Optional(CONF_FAN_MODE_HIGH_ACTION): automation.validate_automation(single=True), - cv.Optional(CONF_FAN_MODE_MIDDLE_ACTION): automation.validate_automation(single=True), - cv.Optional(CONF_FAN_MODE_FOCUS_ACTION): automation.validate_automation(single=True), - cv.Optional(CONF_FAN_MODE_DIFFUSE_ACTION): automation.validate_automation(single=True), - cv.Optional(CONF_SWING_BOTH_ACTION): automation.validate_automation(single=True), - cv.Optional(CONF_SWING_HORIZONTAL_ACTION): automation.validate_automation(single=True), - cv.Optional(CONF_SWING_OFF_ACTION): automation.validate_automation(single=True), - cv.Optional(CONF_SWING_VERTICAL_ACTION): automation.validate_automation(single=True), - cv.Optional(CONF_DEFAULT_TARGET_TEMPERATURE_HIGH): cv.temperature, - cv.Optional(CONF_DEFAULT_TARGET_TEMPERATURE_LOW): cv.temperature, - cv.Optional(CONF_HYSTERESIS, default=0.5): cv.temperature, - cv.Optional(CONF_AWAY_CONFIG): cv.Schema({ - cv.Optional(CONF_DEFAULT_TARGET_TEMPERATURE_HIGH): cv.temperature, - cv.Optional(CONF_DEFAULT_TARGET_TEMPERATURE_LOW): cv.temperature, - }), -}).extend(cv.COMPONENT_SCHEMA), cv.has_at_least_one_key(CONF_COOL_ACTION, CONF_DRY_ACTION, - CONF_FAN_ONLY_ACTION, CONF_HEAT_ACTION), - validate_thermostat) +CONFIG_SCHEMA = cv.All( + climate.CLIMATE_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(ThermostatClimate), + cv.Required(CONF_SENSOR): cv.use_id(sensor.Sensor), + cv.Required(CONF_IDLE_ACTION): automation.validate_automation(single=True), + cv.Optional(CONF_COOL_ACTION): automation.validate_automation(single=True), + cv.Optional(CONF_DRY_ACTION): automation.validate_automation(single=True), + cv.Optional(CONF_FAN_ONLY_ACTION): automation.validate_automation( + single=True + ), + cv.Optional(CONF_HEAT_ACTION): automation.validate_automation(single=True), + cv.Optional(CONF_AUTO_MODE): automation.validate_automation(single=True), + cv.Optional(CONF_COOL_MODE): automation.validate_automation(single=True), + cv.Optional(CONF_DRY_MODE): automation.validate_automation(single=True), + cv.Optional(CONF_FAN_ONLY_MODE): automation.validate_automation( + single=True + ), + cv.Optional(CONF_HEAT_MODE): automation.validate_automation(single=True), + cv.Optional(CONF_OFF_MODE): automation.validate_automation(single=True), + cv.Optional(CONF_FAN_MODE_ON_ACTION): automation.validate_automation( + single=True + ), + cv.Optional(CONF_FAN_MODE_OFF_ACTION): automation.validate_automation( + single=True + ), + cv.Optional(CONF_FAN_MODE_AUTO_ACTION): automation.validate_automation( + single=True + ), + cv.Optional(CONF_FAN_MODE_LOW_ACTION): automation.validate_automation( + single=True + ), + cv.Optional(CONF_FAN_MODE_MEDIUM_ACTION): automation.validate_automation( + single=True + ), + cv.Optional(CONF_FAN_MODE_HIGH_ACTION): automation.validate_automation( + single=True + ), + cv.Optional(CONF_FAN_MODE_MIDDLE_ACTION): automation.validate_automation( + single=True + ), + cv.Optional(CONF_FAN_MODE_FOCUS_ACTION): automation.validate_automation( + single=True + ), + cv.Optional(CONF_FAN_MODE_DIFFUSE_ACTION): automation.validate_automation( + single=True + ), + cv.Optional(CONF_SWING_BOTH_ACTION): automation.validate_automation( + single=True + ), + cv.Optional(CONF_SWING_HORIZONTAL_ACTION): automation.validate_automation( + single=True + ), + cv.Optional(CONF_SWING_OFF_ACTION): automation.validate_automation( + single=True + ), + cv.Optional(CONF_SWING_VERTICAL_ACTION): automation.validate_automation( + single=True + ), + cv.Optional(CONF_DEFAULT_TARGET_TEMPERATURE_HIGH): cv.temperature, + cv.Optional(CONF_DEFAULT_TARGET_TEMPERATURE_LOW): cv.temperature, + cv.Optional(CONF_HYSTERESIS, default=0.5): cv.temperature, + cv.Optional(CONF_AWAY_CONFIG): cv.Schema( + { + cv.Optional(CONF_DEFAULT_TARGET_TEMPERATURE_HIGH): cv.temperature, + cv.Optional(CONF_DEFAULT_TARGET_TEMPERATURE_LOW): cv.temperature, + } + ), + } + ).extend(cv.COMPONENT_SCHEMA), + cv.has_at_least_one_key( + CONF_COOL_ACTION, CONF_DRY_ACTION, CONF_FAN_ONLY_ACTION, CONF_HEAT_ACTION + ), + validate_thermostat, +) def to_code(config): @@ -113,8 +228,9 @@ def to_code(config): yield climate.register_climate(var, config) auto_mode_available = CONF_HEAT_ACTION in config and CONF_COOL_ACTION in config - two_points_available = CONF_HEAT_ACTION in config and (CONF_COOL_ACTION in config or - CONF_FAN_ONLY_ACTION in config) + two_points_available = CONF_HEAT_ACTION in config and ( + CONF_COOL_ACTION in config or CONF_FAN_ONLY_ACTION in config + ) sens = yield cg.get_variable(config[CONF_SENSOR]) cg.add(var.set_sensor(sens)) @@ -124,7 +240,7 @@ def to_code(config): cg.add(var.set_supports_two_points(True)) normal_config = ThermostatClimateTargetTempConfig( config[CONF_DEFAULT_TARGET_TEMPERATURE_LOW], - config[CONF_DEFAULT_TARGET_TEMPERATURE_HIGH] + config[CONF_DEFAULT_TARGET_TEMPERATURE_HIGH], ) elif CONF_DEFAULT_TARGET_TEMPERATURE_HIGH in config: cg.add(var.set_supports_two_points(False)) @@ -138,8 +254,9 @@ def to_code(config): ) cg.add(var.set_normal_config(normal_config)) - yield automation.build_automation(var.get_idle_action_trigger(), [], - config[CONF_IDLE_ACTION]) + yield automation.build_automation( + var.get_idle_action_trigger(), [], config[CONF_IDLE_ACTION] + ) if auto_mode_available is True: cg.add(var.set_supports_auto(True)) @@ -147,94 +264,121 @@ def to_code(config): cg.add(var.set_supports_auto(False)) if CONF_COOL_ACTION in config: - yield automation.build_automation(var.get_cool_action_trigger(), [], - config[CONF_COOL_ACTION]) + yield automation.build_automation( + var.get_cool_action_trigger(), [], config[CONF_COOL_ACTION] + ) cg.add(var.set_supports_cool(True)) if CONF_DRY_ACTION in config: - yield automation.build_automation(var.get_dry_action_trigger(), [], - config[CONF_DRY_ACTION]) + yield automation.build_automation( + var.get_dry_action_trigger(), [], config[CONF_DRY_ACTION] + ) cg.add(var.set_supports_dry(True)) if CONF_FAN_ONLY_ACTION in config: - yield automation.build_automation(var.get_fan_only_action_trigger(), [], - config[CONF_FAN_ONLY_ACTION]) + yield automation.build_automation( + var.get_fan_only_action_trigger(), [], config[CONF_FAN_ONLY_ACTION] + ) cg.add(var.set_supports_fan_only(True)) if CONF_HEAT_ACTION in config: - yield automation.build_automation(var.get_heat_action_trigger(), [], - config[CONF_HEAT_ACTION]) + yield automation.build_automation( + var.get_heat_action_trigger(), [], config[CONF_HEAT_ACTION] + ) cg.add(var.set_supports_heat(True)) if CONF_AUTO_MODE in config: - yield automation.build_automation(var.get_auto_mode_trigger(), [], - config[CONF_AUTO_MODE]) + yield automation.build_automation( + var.get_auto_mode_trigger(), [], config[CONF_AUTO_MODE] + ) if CONF_COOL_MODE in config: - yield automation.build_automation(var.get_cool_mode_trigger(), [], - config[CONF_COOL_MODE]) + yield automation.build_automation( + var.get_cool_mode_trigger(), [], config[CONF_COOL_MODE] + ) cg.add(var.set_supports_cool(True)) if CONF_DRY_MODE in config: - yield automation.build_automation(var.get_dry_mode_trigger(), [], - config[CONF_DRY_MODE]) + yield automation.build_automation( + var.get_dry_mode_trigger(), [], config[CONF_DRY_MODE] + ) cg.add(var.set_supports_dry(True)) if CONF_FAN_ONLY_MODE in config: - yield automation.build_automation(var.get_fan_only_mode_trigger(), [], - config[CONF_FAN_ONLY_MODE]) + yield automation.build_automation( + var.get_fan_only_mode_trigger(), [], config[CONF_FAN_ONLY_MODE] + ) cg.add(var.set_supports_fan_only(True)) if CONF_HEAT_MODE in config: - yield automation.build_automation(var.get_heat_mode_trigger(), [], - config[CONF_HEAT_MODE]) + yield automation.build_automation( + var.get_heat_mode_trigger(), [], config[CONF_HEAT_MODE] + ) cg.add(var.set_supports_heat(True)) if CONF_OFF_MODE in config: - yield automation.build_automation(var.get_off_mode_trigger(), [], - config[CONF_OFF_MODE]) + yield automation.build_automation( + var.get_off_mode_trigger(), [], config[CONF_OFF_MODE] + ) if CONF_FAN_MODE_ON_ACTION in config: - yield automation.build_automation(var.get_fan_mode_on_trigger(), [], - config[CONF_FAN_MODE_ON_ACTION]) + yield automation.build_automation( + var.get_fan_mode_on_trigger(), [], config[CONF_FAN_MODE_ON_ACTION] + ) cg.add(var.set_supports_fan_mode_on(True)) if CONF_FAN_MODE_OFF_ACTION in config: - yield automation.build_automation(var.get_fan_mode_off_trigger(), [], - config[CONF_FAN_MODE_OFF_ACTION]) + yield automation.build_automation( + var.get_fan_mode_off_trigger(), [], config[CONF_FAN_MODE_OFF_ACTION] + ) cg.add(var.set_supports_fan_mode_off(True)) if CONF_FAN_MODE_AUTO_ACTION in config: - yield automation.build_automation(var.get_fan_mode_auto_trigger(), [], - config[CONF_FAN_MODE_AUTO_ACTION]) + yield automation.build_automation( + var.get_fan_mode_auto_trigger(), [], config[CONF_FAN_MODE_AUTO_ACTION] + ) cg.add(var.set_supports_fan_mode_auto(True)) if CONF_FAN_MODE_LOW_ACTION in config: - yield automation.build_automation(var.get_fan_mode_low_trigger(), [], - config[CONF_FAN_MODE_LOW_ACTION]) + yield automation.build_automation( + var.get_fan_mode_low_trigger(), [], config[CONF_FAN_MODE_LOW_ACTION] + ) cg.add(var.set_supports_fan_mode_low(True)) if CONF_FAN_MODE_MEDIUM_ACTION in config: - yield automation.build_automation(var.get_fan_mode_medium_trigger(), [], - config[CONF_FAN_MODE_MEDIUM_ACTION]) + yield automation.build_automation( + var.get_fan_mode_medium_trigger(), [], config[CONF_FAN_MODE_MEDIUM_ACTION] + ) cg.add(var.set_supports_fan_mode_medium(True)) if CONF_FAN_MODE_HIGH_ACTION in config: - yield automation.build_automation(var.get_fan_mode_high_trigger(), [], - config[CONF_FAN_MODE_HIGH_ACTION]) + yield automation.build_automation( + var.get_fan_mode_high_trigger(), [], config[CONF_FAN_MODE_HIGH_ACTION] + ) cg.add(var.set_supports_fan_mode_high(True)) if CONF_FAN_MODE_MIDDLE_ACTION in config: - yield automation.build_automation(var.get_fan_mode_middle_trigger(), [], - config[CONF_FAN_MODE_MIDDLE_ACTION]) + yield automation.build_automation( + var.get_fan_mode_middle_trigger(), [], config[CONF_FAN_MODE_MIDDLE_ACTION] + ) cg.add(var.set_supports_fan_mode_middle(True)) if CONF_FAN_MODE_FOCUS_ACTION in config: - yield automation.build_automation(var.get_fan_mode_focus_trigger(), [], - config[CONF_FAN_MODE_FOCUS_ACTION]) + yield automation.build_automation( + var.get_fan_mode_focus_trigger(), [], config[CONF_FAN_MODE_FOCUS_ACTION] + ) cg.add(var.set_supports_fan_mode_focus(True)) if CONF_FAN_MODE_DIFFUSE_ACTION in config: - yield automation.build_automation(var.get_fan_mode_diffuse_trigger(), [], - config[CONF_FAN_MODE_DIFFUSE_ACTION]) + yield automation.build_automation( + var.get_fan_mode_diffuse_trigger(), [], config[CONF_FAN_MODE_DIFFUSE_ACTION] + ) cg.add(var.set_supports_fan_mode_diffuse(True)) if CONF_SWING_BOTH_ACTION in config: - yield automation.build_automation(var.get_swing_mode_both_trigger(), [], - config[CONF_SWING_BOTH_ACTION]) + yield automation.build_automation( + var.get_swing_mode_both_trigger(), [], config[CONF_SWING_BOTH_ACTION] + ) cg.add(var.set_supports_swing_mode_both(True)) if CONF_SWING_HORIZONTAL_ACTION in config: - yield automation.build_automation(var.get_swing_mode_horizontal_trigger(), [], - config[CONF_SWING_HORIZONTAL_ACTION]) + yield automation.build_automation( + var.get_swing_mode_horizontal_trigger(), + [], + config[CONF_SWING_HORIZONTAL_ACTION], + ) cg.add(var.set_supports_swing_mode_horizontal(True)) if CONF_SWING_OFF_ACTION in config: - yield automation.build_automation(var.get_swing_mode_off_trigger(), [], - config[CONF_SWING_OFF_ACTION]) + yield automation.build_automation( + var.get_swing_mode_off_trigger(), [], config[CONF_SWING_OFF_ACTION] + ) cg.add(var.set_supports_swing_mode_off(True)) if CONF_SWING_VERTICAL_ACTION in config: - yield automation.build_automation(var.get_swing_mode_vertical_trigger(), [], - config[CONF_SWING_VERTICAL_ACTION]) + yield automation.build_automation( + var.get_swing_mode_vertical_trigger(), + [], + config[CONF_SWING_VERTICAL_ACTION], + ) cg.add(var.set_supports_swing_mode_vertical(True)) if CONF_AWAY_CONFIG in config: @@ -243,7 +387,7 @@ def to_code(config): if two_points_available is True: away_config = ThermostatClimateTargetTempConfig( away[CONF_DEFAULT_TARGET_TEMPERATURE_LOW], - away[CONF_DEFAULT_TARGET_TEMPERATURE_HIGH] + away[CONF_DEFAULT_TARGET_TEMPERATURE_HIGH], ) elif CONF_DEFAULT_TARGET_TEMPERATURE_HIGH in away: away_config = ThermostatClimateTargetTempConfig( diff --git a/esphome/components/time/__init__.py b/esphome/components/time/__init__.py index 5f30a8f2ee..b1c938c18e 100644 --- a/esphome/components/time/__init__.py +++ b/esphome/components/time/__init__.py @@ -10,23 +10,38 @@ import tzlocal import esphome.codegen as cg import esphome.config_validation as cv from esphome import automation -from esphome.const import CONF_ID, CONF_CRON, CONF_DAYS_OF_MONTH, CONF_DAYS_OF_WEEK, CONF_HOURS, \ - CONF_MINUTES, CONF_MONTHS, CONF_ON_TIME, CONF_ON_TIME_SYNC, CONF_SECONDS, CONF_TIMEZONE, \ - CONF_TRIGGER_ID, CONF_AT, CONF_SECOND, CONF_HOUR, CONF_MINUTE +from esphome.const import ( + CONF_ID, + CONF_CRON, + CONF_DAYS_OF_MONTH, + CONF_DAYS_OF_WEEK, + CONF_HOURS, + CONF_MINUTES, + CONF_MONTHS, + CONF_ON_TIME, + CONF_ON_TIME_SYNC, + CONF_SECONDS, + CONF_TIMEZONE, + CONF_TRIGGER_ID, + CONF_AT, + CONF_SECOND, + CONF_HOUR, + CONF_MINUTE, +) from esphome.core import coroutine, coroutine_with_priority from esphome.automation import Condition _LOGGER = logging.getLogger(__name__) -CODEOWNERS = ['@OttoWinter'] +CODEOWNERS = ["@OttoWinter"] IS_PLATFORM_COMPONENT = True -time_ns = cg.esphome_ns.namespace('time') -RealTimeClock = time_ns.class_('RealTimeClock', cg.PollingComponent) -CronTrigger = time_ns.class_('CronTrigger', automation.Trigger.template(), cg.Component) -SyncTrigger = time_ns.class_('SyncTrigger', automation.Trigger.template(), cg.Component) -ESPTime = time_ns.struct('ESPTime') -TimeHasTimeCondition = time_ns.class_('TimeHasTimeCondition', Condition) +time_ns = cg.esphome_ns.namespace("time") +RealTimeClock = time_ns.class_("RealTimeClock", cg.PollingComponent) +CronTrigger = time_ns.class_("CronTrigger", automation.Trigger.template(), cg.Component) +SyncTrigger = time_ns.class_("SyncTrigger", automation.Trigger.template(), cg.Component) +ESPTime = time_ns.struct("ESPTime") +TimeHasTimeCondition = time_ns.class_("TimeHasTimeCondition", Condition) def _tz_timedelta(td): @@ -34,12 +49,12 @@ def _tz_timedelta(td): offset_minute = int(abs(td.total_seconds() / 60)) % 60 offset_second = int(abs(td.total_seconds())) % 60 if offset_hour == 0 and offset_minute == 0 and offset_second == 0: - return '0' + return "0" if offset_minute == 0 and offset_second == 0: - return f'{offset_hour}' + return f"{offset_hour}" if offset_second == 0: - return f'{offset_hour}:{offset_minute}' - return f'{offset_hour}:{offset_minute}:{offset_second}' + return f"{offset_hour}:{offset_minute}" + return f"{offset_hour}:{offset_minute}:{offset_second}" # https://stackoverflow.com/a/16804556/8924614 @@ -52,8 +67,9 @@ def _week_of_month(dt): def _tz_dst_str(dt): td = datetime.timedelta(hours=dt.hour, minutes=dt.minute, seconds=dt.second) - return 'M{}.{}.{}/{}'.format(dt.month, _week_of_month(dt), dt.isoweekday() % 7, - _tz_timedelta(td)) + return "M{}.{}.{}/{}".format( + dt.month, _week_of_month(dt), dt.isoweekday() % 7, _tz_timedelta(td) + ) def _safe_tzname(tz, dt): @@ -62,16 +78,17 @@ def _safe_tzname(tz, dt): # For example: 'Europe/Saratov' returns '+04' # Work around it by using a generic name for the timezone if not all(c in string.ascii_letters for c in tzname): - return 'TZ' + return "TZ" return tzname def _non_dst_tz(tz, dt): tzname = _safe_tzname(tz, dt) utcoffset = tz.utcoffset(dt) - _LOGGER.info("Detected timezone '%s' with UTC offset %s", - tzname, _tz_timedelta(utcoffset)) - tzbase = '{}{}'.format(tzname, _tz_timedelta(-1 * utcoffset)) + _LOGGER.info( + "Detected timezone '%s' with UTC offset %s", tzname, _tz_timedelta(utcoffset) + ) + tzbase = "{}{}".format(tzname, _tz_timedelta(-1 * utcoffset)) return tzbase @@ -112,15 +129,22 @@ def convert_tz(pytz_obj): dst_ends_utc = transition_times[idx2] dst_ends_local = dst_ends_utc + utcoffset_on - tzbase = '{}{}'.format(tzname_off, _tz_timedelta(-1 * utcoffset_off)) + tzbase = "{}{}".format(tzname_off, _tz_timedelta(-1 * utcoffset_off)) - tzext = '{}{},{},{}'.format(tzname_on, _tz_timedelta(-1 * utcoffset_on), - _tz_dst_str(dst_begins_local), _tz_dst_str(dst_ends_local)) - _LOGGER.info("Detected timezone '%s' with UTC offset %s and daylight savings time from " - "%s to %s", - tzname_off, _tz_timedelta(utcoffset_off), - dst_begins_local.strftime("%d %B %X"), - dst_ends_local.strftime("%d %B %X")) + tzext = "{}{},{},{}".format( + tzname_on, + _tz_timedelta(-1 * utcoffset_on), + _tz_dst_str(dst_begins_local), + _tz_dst_str(dst_ends_local), + ) + _LOGGER.info( + "Detected timezone '%s' with UTC offset %s and daylight savings time from " + "%s to %s", + tzname_off, + _tz_timedelta(utcoffset_off), + dst_begins_local.strftime("%d %B %X"), + dst_ends_local.strftime("%d %B %X"), + ) return tzbase + tzext @@ -129,7 +153,7 @@ def detect_tz(): tz = tzlocal.get_localzone() except pytz.exceptions.UnknownTimeZoneError: _LOGGER.warning("Could not auto-detect timezone. Using UTC...") - return 'UTC' + return "UTC" return convert_tz(tz) @@ -146,42 +170,61 @@ def _parse_cron_int(value, special_mapping, message): def _parse_cron_part(part, min_value, max_value, special_mapping): - if part in ('*', '?'): + if part in ("*", "?"): return set(range(min_value, max_value + 1)) - if '/' in part: - data = part.split('/') + if "/" in part: + data = part.split("/") if len(data) > 2: - raise cv.Invalid("Can't have more than two '/' in one time expression, got {}" - .format(part)) + raise cv.Invalid( + "Can't have more than two '/' in one time expression, got {}".format( + part + ) + ) offset, repeat = data offset_n = 0 if offset: - offset_n = _parse_cron_int(offset, special_mapping, - "Offset for '/' time expression must be an integer, got {}") + offset_n = _parse_cron_int( + offset, + special_mapping, + "Offset for '/' time expression must be an integer, got {}", + ) try: repeat_n = int(repeat) except ValueError: # pylint: disable=raise-missing-from - raise cv.Invalid("Repeat for '/' time expression must be an integer, got {}" - .format(repeat)) + raise cv.Invalid( + "Repeat for '/' time expression must be an integer, got {}".format( + repeat + ) + ) return set(range(offset_n, max_value + 1, repeat_n)) - if '-' in part: - data = part.split('-') + if "-" in part: + data = part.split("-") if len(data) > 2: - raise cv.Invalid("Can't have more than two '-' in range time expression '{}'" - .format(part)) + raise cv.Invalid( + "Can't have more than two '-' in range time expression '{}'".format( + part + ) + ) begin, end = data - begin_n = _parse_cron_int(begin, special_mapping, "Number for time range must be integer, " - "got {}") - end_n = _parse_cron_int(end, special_mapping, "Number for time range must be integer, " - "got {}") + begin_n = _parse_cron_int( + begin, special_mapping, "Number for time range must be integer, " "got {}" + ) + end_n = _parse_cron_int( + end, special_mapping, "Number for time range must be integer, " "got {}" + ) if end_n < begin_n: return set(range(end_n, max_value + 1)) | set(range(min_value, begin_n + 1)) return set(range(begin_n, end_n + 1)) - return {_parse_cron_int(part, special_mapping, "Number for time expression must be an " - "integer, got {}")} + return { + _parse_cron_int( + part, + special_mapping, + "Number for time expression must be an " "integer, got {}", + ) + } def cron_expression_validator(name, min_value, max_value, special_mapping=None): @@ -190,42 +233,71 @@ def cron_expression_validator(name, min_value, max_value, special_mapping=None): for v in value: if not isinstance(v, int): raise cv.Invalid( - "Expected integer for {} '{}', got {}".format(v, name, type(v))) + "Expected integer for {} '{}', got {}".format(v, name, type(v)) + ) if v < min_value or v > max_value: raise cv.Invalid( - "{} {} is out of range (min={} max={}).".format(name, v, min_value, - max_value)) + "{} {} is out of range (min={} max={}).".format( + name, v, min_value, max_value + ) + ) return list(sorted(value)) value = cv.string(value) values = set() - for part in value.split(','): + for part in value.split(","): values |= _parse_cron_part(part, min_value, max_value, special_mapping) return validator(list(values)) return validator -validate_cron_seconds = cron_expression_validator('seconds', 0, 60) -validate_cron_minutes = cron_expression_validator('minutes', 0, 59) -validate_cron_hours = cron_expression_validator('hours', 0, 23) -validate_cron_days_of_month = cron_expression_validator('days of month', 1, 31) -validate_cron_months = cron_expression_validator('months', 1, 12, { - 'JAN': 1, 'FEB': 2, 'MAR': 3, 'APR': 4, 'MAY': 5, 'JUN': 6, 'JUL': 7, 'AUG': 8, - 'SEP': 9, 'OCT': 10, 'NOV': 11, 'DEC': 12 -}) -validate_cron_days_of_week = cron_expression_validator('days of week', 1, 7, { - 'SUN': 1, 'MON': 2, 'TUE': 3, 'WED': 4, 'THU': 5, 'FRI': 6, 'SAT': 7 -}) -CRON_KEYS = [CONF_SECONDS, CONF_MINUTES, CONF_HOURS, CONF_DAYS_OF_MONTH, CONF_MONTHS, - CONF_DAYS_OF_WEEK] +validate_cron_seconds = cron_expression_validator("seconds", 0, 60) +validate_cron_minutes = cron_expression_validator("minutes", 0, 59) +validate_cron_hours = cron_expression_validator("hours", 0, 23) +validate_cron_days_of_month = cron_expression_validator("days of month", 1, 31) +validate_cron_months = cron_expression_validator( + "months", + 1, + 12, + { + "JAN": 1, + "FEB": 2, + "MAR": 3, + "APR": 4, + "MAY": 5, + "JUN": 6, + "JUL": 7, + "AUG": 8, + "SEP": 9, + "OCT": 10, + "NOV": 11, + "DEC": 12, + }, +) +validate_cron_days_of_week = cron_expression_validator( + "days of week", + 1, + 7, + {"SUN": 1, "MON": 2, "TUE": 3, "WED": 4, "THU": 5, "FRI": 6, "SAT": 7}, +) +CRON_KEYS = [ + CONF_SECONDS, + CONF_MINUTES, + CONF_HOURS, + CONF_DAYS_OF_MONTH, + CONF_MONTHS, + CONF_DAYS_OF_WEEK, +] def validate_cron_raw(value): value = cv.string(value) - value = value.split(' ') + value = value.split(" ") if len(value) != 6: - raise cv.Invalid("Cron expression must consist of exactly 6 space-separated parts, " - "not {}".format(len(value))) + raise cv.Invalid( + "Cron expression must consist of exactly 6 space-separated parts, " + "not {}".format(len(value)) + ) seconds, minutes, hours, days_of_month, months, days_of_week = value return { CONF_SECONDS: validate_cron_seconds(seconds), @@ -243,9 +315,9 @@ def validate_time_at(value): CONF_HOURS: [value[CONF_HOUR]], CONF_MINUTES: [value[CONF_MINUTE]], CONF_SECONDS: [value[CONF_SECOND]], - CONF_DAYS_OF_MONTH: validate_cron_days_of_month('*'), - CONF_MONTHS: validate_cron_months('*'), - CONF_DAYS_OF_WEEK: validate_cron_days_of_week('*'), + CONF_DAYS_OF_MONTH: validate_cron_days_of_month("*"), + CONF_MONTHS: validate_cron_months("*"), + CONF_DAYS_OF_WEEK: validate_cron_days_of_week("*"), } @@ -282,23 +354,30 @@ def validate_tz(value): return convert_tz(pytz_obj) -TIME_SCHEMA = cv.Schema({ - cv.Optional(CONF_TIMEZONE, default=detect_tz): validate_tz, - cv.Optional(CONF_ON_TIME): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(CronTrigger), - cv.Optional(CONF_SECONDS): validate_cron_seconds, - cv.Optional(CONF_MINUTES): validate_cron_minutes, - cv.Optional(CONF_HOURS): validate_cron_hours, - cv.Optional(CONF_DAYS_OF_MONTH): validate_cron_days_of_month, - cv.Optional(CONF_MONTHS): validate_cron_months, - cv.Optional(CONF_DAYS_OF_WEEK): validate_cron_days_of_week, - cv.Optional(CONF_CRON): validate_cron_raw, - cv.Optional(CONF_AT): validate_time_at, - }, validate_cron_keys), - cv.Optional(CONF_ON_TIME_SYNC): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SyncTrigger), - }), -}).extend(cv.polling_component_schema('15min')) +TIME_SCHEMA = cv.Schema( + { + cv.Optional(CONF_TIMEZONE, default=detect_tz): validate_tz, + cv.Optional(CONF_ON_TIME): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(CronTrigger), + cv.Optional(CONF_SECONDS): validate_cron_seconds, + cv.Optional(CONF_MINUTES): validate_cron_minutes, + cv.Optional(CONF_HOURS): validate_cron_hours, + cv.Optional(CONF_DAYS_OF_MONTH): validate_cron_days_of_month, + cv.Optional(CONF_MONTHS): validate_cron_months, + cv.Optional(CONF_DAYS_OF_WEEK): validate_cron_days_of_week, + cv.Optional(CONF_CRON): validate_cron_raw, + cv.Optional(CONF_AT): validate_time_at, + }, + validate_cron_keys, + ), + cv.Optional(CONF_ON_TIME_SYNC): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SyncTrigger), + } + ), + } +).extend(cv.polling_component_schema("15min")) @coroutine @@ -338,13 +417,19 @@ def register_time(time_var, config): @coroutine_with_priority(100.0) def to_code(config): - cg.add_define('USE_TIME') + cg.add_define("USE_TIME") cg.add_global(time_ns.using) -@automation.register_condition('time.has_time', TimeHasTimeCondition, cv.Schema({ - cv.GenerateID(): cv.use_id(RealTimeClock), -})) +@automation.register_condition( + "time.has_time", + TimeHasTimeCondition, + cv.Schema( + { + cv.GenerateID(): cv.use_id(RealTimeClock), + } + ), +) def time_has_time_to_code(config, condition_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) yield cg.new_Pvariable(condition_id, template_arg, paren) diff --git a/esphome/components/time_based/cover.py b/esphome/components/time_based/cover.py index dcb8d9505b..9246f78884 100644 --- a/esphome/components/time_based/cover.py +++ b/esphome/components/time_based/cover.py @@ -2,27 +2,33 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import automation from esphome.components import cover -from esphome.const import CONF_CLOSE_ACTION, CONF_CLOSE_DURATION, CONF_ID, CONF_OPEN_ACTION, \ - CONF_OPEN_DURATION, CONF_STOP_ACTION, CONF_ASSUMED_STATE +from esphome.const import ( + CONF_CLOSE_ACTION, + CONF_CLOSE_DURATION, + CONF_ID, + CONF_OPEN_ACTION, + CONF_OPEN_DURATION, + CONF_STOP_ACTION, + CONF_ASSUMED_STATE, +) -time_based_ns = cg.esphome_ns.namespace('time_based') -TimeBasedCover = time_based_ns.class_('TimeBasedCover', cover.Cover, cg.Component) +time_based_ns = cg.esphome_ns.namespace("time_based") +TimeBasedCover = time_based_ns.class_("TimeBasedCover", cover.Cover, cg.Component) -CONF_HAS_BUILT_IN_ENDSTOP = 'has_built_in_endstop' +CONF_HAS_BUILT_IN_ENDSTOP = "has_built_in_endstop" -CONFIG_SCHEMA = cover.COVER_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(TimeBasedCover), - cv.Required(CONF_STOP_ACTION): automation.validate_automation(single=True), - - cv.Required(CONF_OPEN_ACTION): automation.validate_automation(single=True), - cv.Required(CONF_OPEN_DURATION): cv.positive_time_period_milliseconds, - - cv.Required(CONF_CLOSE_ACTION): automation.validate_automation(single=True), - cv.Required(CONF_CLOSE_DURATION): cv.positive_time_period_milliseconds, - - cv.Optional(CONF_HAS_BUILT_IN_ENDSTOP, default=False): cv.boolean, - cv.Optional(CONF_ASSUMED_STATE, default=True): cv.boolean, -}).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = cover.COVER_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(TimeBasedCover), + cv.Required(CONF_STOP_ACTION): automation.validate_automation(single=True), + cv.Required(CONF_OPEN_ACTION): automation.validate_automation(single=True), + cv.Required(CONF_OPEN_DURATION): cv.positive_time_period_milliseconds, + cv.Required(CONF_CLOSE_ACTION): automation.validate_automation(single=True), + cv.Required(CONF_CLOSE_DURATION): cv.positive_time_period_milliseconds, + cv.Optional(CONF_HAS_BUILT_IN_ENDSTOP, default=False): cv.boolean, + cv.Optional(CONF_ASSUMED_STATE, default=True): cv.boolean, + } +).extend(cv.COMPONENT_SCHEMA) def to_code(config): @@ -30,13 +36,19 @@ def to_code(config): yield cg.register_component(var, config) yield cover.register_cover(var, config) - yield automation.build_automation(var.get_stop_trigger(), [], config[CONF_STOP_ACTION]) + yield automation.build_automation( + var.get_stop_trigger(), [], config[CONF_STOP_ACTION] + ) cg.add(var.set_open_duration(config[CONF_OPEN_DURATION])) - yield automation.build_automation(var.get_open_trigger(), [], config[CONF_OPEN_ACTION]) + yield automation.build_automation( + var.get_open_trigger(), [], config[CONF_OPEN_ACTION] + ) cg.add(var.set_close_duration(config[CONF_CLOSE_DURATION])) - yield automation.build_automation(var.get_close_trigger(), [], config[CONF_CLOSE_ACTION]) + yield automation.build_automation( + var.get_close_trigger(), [], config[CONF_CLOSE_ACTION] + ) cg.add(var.set_has_built_in_endstop(config[CONF_HAS_BUILT_IN_ENDSTOP])) cg.add(var.set_assumed_state(config[CONF_ASSUMED_STATE])) diff --git a/esphome/components/tlc59208f/__init__.py b/esphome/components/tlc59208f/__init__.py index 4666b63b46..ff5b75954b 100644 --- a/esphome/components/tlc59208f/__init__.py +++ b/esphome/components/tlc59208f/__init__.py @@ -3,15 +3,21 @@ import esphome.config_validation as cv from esphome.components import i2c from esphome.const import CONF_ID -DEPENDENCIES = ['i2c'] +DEPENDENCIES = ["i2c"] MULTI_CONF = True -tlc59208f_ns = cg.esphome_ns.namespace('tlc59208f') -TLC59208FOutput = tlc59208f_ns.class_('TLC59208FOutput', cg.Component, i2c.I2CDevice) +tlc59208f_ns = cg.esphome_ns.namespace("tlc59208f") +TLC59208FOutput = tlc59208f_ns.class_("TLC59208FOutput", cg.Component, i2c.I2CDevice) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(TLC59208FOutput), -}).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(0x20)) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(TLC59208FOutput), + } + ) + .extend(cv.COMPONENT_SCHEMA) + .extend(i2c.i2c_device_schema(0x20)) +) def to_code(config): diff --git a/esphome/components/tlc59208f/output.py b/esphome/components/tlc59208f/output.py index f61f7729e7..94cf529a75 100644 --- a/esphome/components/tlc59208f/output.py +++ b/esphome/components/tlc59208f/output.py @@ -4,17 +4,18 @@ from esphome.components import output from esphome.const import CONF_CHANNEL, CONF_ID from . import TLC59208FOutput, tlc59208f_ns -DEPENDENCIES = ['tlc59208f'] +DEPENDENCIES = ["tlc59208f"] -TLC59208FChannel = tlc59208f_ns.class_('TLC59208FChannel', output.FloatOutput) -CONF_TLC59208F_ID = 'tlc59208f_id' +TLC59208FChannel = tlc59208f_ns.class_("TLC59208FChannel", output.FloatOutput) +CONF_TLC59208F_ID = "tlc59208f_id" -CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend({ - cv.Required(CONF_ID): cv.declare_id(TLC59208FChannel), - cv.GenerateID(CONF_TLC59208F_ID): cv.use_id(TLC59208FOutput), - - cv.Required(CONF_CHANNEL): cv.int_range(min=0, max=7), -}) +CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend( + { + cv.Required(CONF_ID): cv.declare_id(TLC59208FChannel), + cv.GenerateID(CONF_TLC59208F_ID): cv.use_id(TLC59208FOutput), + cv.Required(CONF_CHANNEL): cv.int_range(min=0, max=7), + } +) def to_code(config): diff --git a/esphome/components/tm1637/display.py b/esphome/components/tm1637/display.py index c2692e30de..06a9716e59 100644 --- a/esphome/components/tm1637/display.py +++ b/esphome/components/tm1637/display.py @@ -2,21 +2,30 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins from esphome.components import display -from esphome.const import CONF_CLK_PIN, CONF_DIO_PIN, CONF_ID, CONF_LAMBDA, CONF_INTENSITY +from esphome.const import ( + CONF_CLK_PIN, + CONF_DIO_PIN, + CONF_ID, + CONF_LAMBDA, + CONF_INTENSITY, +) -CODEOWNERS = ['@glmnet'] +CODEOWNERS = ["@glmnet"] -tm1637_ns = cg.esphome_ns.namespace('tm1637') -TM1637Display = tm1637_ns.class_('TM1637Display', cg.PollingComponent) -TM1637DisplayRef = TM1637Display.operator('ref') +tm1637_ns = cg.esphome_ns.namespace("tm1637") +TM1637Display = tm1637_ns.class_("TM1637Display", cg.PollingComponent) +TM1637DisplayRef = TM1637Display.operator("ref") -CONFIG_SCHEMA = display.BASIC_DISPLAY_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(TM1637Display), - - cv.Optional(CONF_INTENSITY, default=7): cv.All(cv.uint8_t, cv.Range(min=0, max=7)), - cv.Required(CONF_CLK_PIN): pins.gpio_output_pin_schema, - cv.Required(CONF_DIO_PIN): pins.gpio_output_pin_schema, -}).extend(cv.polling_component_schema('1s')) +CONFIG_SCHEMA = display.BASIC_DISPLAY_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(TM1637Display), + cv.Optional(CONF_INTENSITY, default=7): cv.All( + cv.uint8_t, cv.Range(min=0, max=7) + ), + cv.Required(CONF_CLK_PIN): pins.gpio_output_pin_schema, + cv.Required(CONF_DIO_PIN): pins.gpio_output_pin_schema, + } +).extend(cv.polling_component_schema("1s")) def to_code(config): @@ -32,6 +41,7 @@ def to_code(config): cg.add(var.set_intensity(config[CONF_INTENSITY])) if CONF_LAMBDA in config: - lambda_ = yield cg.process_lambda(config[CONF_LAMBDA], [(TM1637DisplayRef, 'it')], - return_type=cg.void) + lambda_ = yield cg.process_lambda( + config[CONF_LAMBDA], [(TM1637DisplayRef, "it")], return_type=cg.void + ) cg.add(var.set_writer(lambda_)) diff --git a/esphome/components/tm1651/__init__.py b/esphome/components/tm1651/__init__.py index aa972552f4..97c1e472e9 100644 --- a/esphome/components/tm1651/__init__.py +++ b/esphome/components/tm1651/__init__.py @@ -2,30 +2,38 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins, automation from esphome.automation import maybe_simple_id -from esphome.const import CONF_ID, CONF_CLK_PIN, CONF_DIO_PIN, CONF_LEVEL, CONF_BRIGHTNESS +from esphome.const import ( + CONF_ID, + CONF_CLK_PIN, + CONF_DIO_PIN, + CONF_LEVEL, + CONF_BRIGHTNESS, +) -tm1651_ns = cg.esphome_ns.namespace('tm1651') -TM1651Display = tm1651_ns.class_('TM1651Display', cg.Component) +tm1651_ns = cg.esphome_ns.namespace("tm1651") +TM1651Display = tm1651_ns.class_("TM1651Display", cg.Component) -SetLevelPercentAction = tm1651_ns.class_('SetLevelPercentAction', automation.Action) -SetLevelAction = tm1651_ns.class_('SetLevelAction', automation.Action) -SetBrightnessAction = tm1651_ns.class_('SetBrightnessAction', automation.Action) -TurnOnAction = tm1651_ns.class_('SetLevelPercentAction', automation.Action) -TurnOffAction = tm1651_ns.class_('SetLevelPercentAction', automation.Action) +SetLevelPercentAction = tm1651_ns.class_("SetLevelPercentAction", automation.Action) +SetLevelAction = tm1651_ns.class_("SetLevelAction", automation.Action) +SetBrightnessAction = tm1651_ns.class_("SetBrightnessAction", automation.Action) +TurnOnAction = tm1651_ns.class_("SetLevelPercentAction", automation.Action) +TurnOffAction = tm1651_ns.class_("SetLevelPercentAction", automation.Action) -CONF_LEVEL_PERCENT = 'level_percent' +CONF_LEVEL_PERCENT = "level_percent" TM1651_BRIGHTNESS_OPTIONS = { 1: TM1651Display.TM1651_BRIGHTNESS_LOW, 2: TM1651Display.TM1651_BRIGHTNESS_MEDIUM, - 3: TM1651Display.TM1651_BRIGHTNESS_HIGH + 3: TM1651Display.TM1651_BRIGHTNESS_HIGH, } -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(TM1651Display), - cv.Required(CONF_CLK_PIN): pins.internal_gpio_output_pin_schema, - cv.Required(CONF_DIO_PIN): pins.internal_gpio_output_pin_schema, -}) +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(TM1651Display), + cv.Required(CONF_CLK_PIN): pins.internal_gpio_output_pin_schema, + cv.Required(CONF_DIO_PIN): pins.internal_gpio_output_pin_schema, + } +) validate_level_percent = cv.All(cv.int_range(min=0, max=100)) validate_level = cv.All(cv.int_range(min=0, max=7)) @@ -42,22 +50,26 @@ def to_code(config): cg.add(var.set_dio_pin(dio_pin)) # https://platformio.org/lib/show/6865/TM1651 - cg.add_library('6865', '1.0.1') + cg.add_library("6865", "1.0.1") -BINARY_OUTPUT_ACTION_SCHEMA = maybe_simple_id({ - cv.Required(CONF_ID): cv.use_id(TM1651Display), -}) +BINARY_OUTPUT_ACTION_SCHEMA = maybe_simple_id( + { + cv.Required(CONF_ID): cv.use_id(TM1651Display), + } +) -@automation.register_action('tm1651.turn_on', TurnOnAction, BINARY_OUTPUT_ACTION_SCHEMA) +@automation.register_action("tm1651.turn_on", TurnOnAction, BINARY_OUTPUT_ACTION_SCHEMA) def output_turn_on_to_code(config, action_id, template_arg, args): var = cg.new_Pvariable(action_id, template_arg) yield cg.register_parented(var, config[CONF_ID]) yield var -@automation.register_action('tm1651.turn_off', TurnOffAction, BINARY_OUTPUT_ACTION_SCHEMA) +@automation.register_action( + "tm1651.turn_off", TurnOffAction, BINARY_OUTPUT_ACTION_SCHEMA +) def output_turn_off_to_code(config, action_id, template_arg, args): var = cg.new_Pvariable(action_id, template_arg) yield cg.register_parented(var, config[CONF_ID]) @@ -65,12 +77,16 @@ def output_turn_off_to_code(config, action_id, template_arg, args): @automation.register_action( - 'tm1651.set_level_percent', + "tm1651.set_level_percent", SetLevelPercentAction, - cv.maybe_simple_value({ - cv.GenerateID(): cv.use_id(TM1651Display), - cv.Required(CONF_LEVEL_PERCENT): cv.templatable(validate_level_percent), - }, key=CONF_LEVEL_PERCENT)) + cv.maybe_simple_value( + { + cv.GenerateID(): cv.use_id(TM1651Display), + cv.Required(CONF_LEVEL_PERCENT): cv.templatable(validate_level_percent), + }, + key=CONF_LEVEL_PERCENT, + ), +) def tm1651_set_level_percent_to_code(config, action_id, template_arg, args): var = cg.new_Pvariable(action_id, template_arg) yield cg.register_parented(var, config[CONF_ID]) @@ -80,12 +96,16 @@ def tm1651_set_level_percent_to_code(config, action_id, template_arg, args): @automation.register_action( - 'tm1651.set_level', + "tm1651.set_level", SetLevelAction, - cv.maybe_simple_value({ - cv.GenerateID(): cv.use_id(TM1651Display), - cv.Required(CONF_LEVEL): cv.templatable(validate_level), - }, key=CONF_LEVEL)) + cv.maybe_simple_value( + { + cv.GenerateID(): cv.use_id(TM1651Display), + cv.Required(CONF_LEVEL): cv.templatable(validate_level), + }, + key=CONF_LEVEL, + ), +) def tm1651_set_level_to_code(config, action_id, template_arg, args): var = cg.new_Pvariable(action_id, template_arg) yield cg.register_parented(var, config[CONF_ID]) @@ -95,12 +115,16 @@ def tm1651_set_level_to_code(config, action_id, template_arg, args): @automation.register_action( - 'tm1651.set_brightness', + "tm1651.set_brightness", SetBrightnessAction, - cv.maybe_simple_value({ - cv.GenerateID(): cv.use_id(TM1651Display), - cv.Required(CONF_BRIGHTNESS): cv.templatable(validate_brightness), - }, key=CONF_BRIGHTNESS)) + cv.maybe_simple_value( + { + cv.GenerateID(): cv.use_id(TM1651Display), + cv.Required(CONF_BRIGHTNESS): cv.templatable(validate_brightness), + }, + key=CONF_BRIGHTNESS, + ), +) def tm1651_set_brightness_to_code(config, action_id, template_arg, args): var = cg.new_Pvariable(action_id, template_arg) yield cg.register_parented(var, config[CONF_ID]) diff --git a/esphome/components/tmp102/sensor.py b/esphome/components/tmp102/sensor.py index 05f33c43ab..4c04271d96 100644 --- a/esphome/components/tmp102/sensor.py +++ b/esphome/components/tmp102/sensor.py @@ -12,16 +12,24 @@ import esphome.config_validation as cv from esphome.components import i2c, sensor from esphome.const import CONF_ID, DEVICE_CLASS_TEMPERATURE, ICON_EMPTY, UNIT_CELSIUS -CODEOWNERS = ['@timsavage'] -DEPENDENCIES = ['i2c'] +CODEOWNERS = ["@timsavage"] +DEPENDENCIES = ["i2c"] -tmp102_ns = cg.esphome_ns.namespace('tmp102') -TMP102Component = tmp102_ns.class_("TMP102Component", cg.PollingComponent, i2c.I2CDevice, - sensor.Sensor) +tmp102_ns = cg.esphome_ns.namespace("tmp102") +TMP102Component = tmp102_ns.class_( + "TMP102Component", cg.PollingComponent, i2c.I2CDevice, sensor.Sensor +) -CONFIG_SCHEMA = sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE).extend({ - cv.GenerateID(): cv.declare_id(TMP102Component), -}).extend(cv.polling_component_schema("60s")).extend(i2c.i2c_device_schema(0x48)) +CONFIG_SCHEMA = ( + sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE) + .extend( + { + cv.GenerateID(): cv.declare_id(TMP102Component), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x48)) +) def to_code(config): diff --git a/esphome/components/tmp117/sensor.py b/esphome/components/tmp117/sensor.py index 4d2e662b53..33c13e3f3b 100644 --- a/esphome/components/tmp117/sensor.py +++ b/esphome/components/tmp117/sensor.py @@ -1,20 +1,31 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import CONF_ID, CONF_UPDATE_INTERVAL, DEVICE_CLASS_TEMPERATURE, ICON_EMPTY, \ - UNIT_CELSIUS +from esphome.const import ( + CONF_ID, + CONF_UPDATE_INTERVAL, + DEVICE_CLASS_TEMPERATURE, + ICON_EMPTY, + UNIT_CELSIUS, +) -DEPENDENCIES = ['i2c'] +DEPENDENCIES = ["i2c"] -tmp117_ns = cg.esphome_ns.namespace('tmp117') -TMP117Component = tmp117_ns.class_('TMP117Component', - cg.PollingComponent, i2c.I2CDevice, sensor.Sensor) +tmp117_ns = cg.esphome_ns.namespace("tmp117") +TMP117Component = tmp117_ns.class_( + "TMP117Component", cg.PollingComponent, i2c.I2CDevice, sensor.Sensor +) -CONFIG_SCHEMA = cv.All(sensor.sensor_schema( - UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE -).extend({ - cv.GenerateID(): cv.declare_id(TMP117Component), -}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x48))) +CONFIG_SCHEMA = cv.All( + sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE) + .extend( + { + cv.GenerateID(): cv.declare_id(TMP117Component), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x48)) +) def determine_config_register(polling_period): diff --git a/esphome/components/toshiba/climate.py b/esphome/components/toshiba/climate.py index ea7efbf2f5..3021697f21 100644 --- a/esphome/components/toshiba/climate.py +++ b/esphome/components/toshiba/climate.py @@ -3,14 +3,16 @@ import esphome.config_validation as cv from esphome.components import climate_ir from esphome.const import CONF_ID -AUTO_LOAD = ['climate_ir'] +AUTO_LOAD = ["climate_ir"] -toshiba_ns = cg.esphome_ns.namespace('toshiba') -ToshibaClimate = toshiba_ns.class_('ToshibaClimate', climate_ir.ClimateIR) +toshiba_ns = cg.esphome_ns.namespace("toshiba") +ToshibaClimate = toshiba_ns.class_("ToshibaClimate", climate_ir.ClimateIR) -CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(ToshibaClimate), -}) +CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(ToshibaClimate), + } +) def to_code(config): diff --git a/esphome/components/total_daily_energy/sensor.py b/esphome/components/total_daily_energy/sensor.py index 7fde9f5582..150cab77b4 100644 --- a/esphome/components/total_daily_energy/sensor.py +++ b/esphome/components/total_daily_energy/sensor.py @@ -3,17 +3,21 @@ import esphome.config_validation as cv from esphome.components import sensor, time from esphome.const import CONF_ID, CONF_TIME_ID -DEPENDENCIES = ['time'] +DEPENDENCIES = ["time"] -CONF_POWER_ID = 'power_id' -total_daily_energy_ns = cg.esphome_ns.namespace('total_daily_energy') -TotalDailyEnergy = total_daily_energy_ns.class_('TotalDailyEnergy', sensor.Sensor, cg.Component) +CONF_POWER_ID = "power_id" +total_daily_energy_ns = cg.esphome_ns.namespace("total_daily_energy") +TotalDailyEnergy = total_daily_energy_ns.class_( + "TotalDailyEnergy", sensor.Sensor, cg.Component +) -CONFIG_SCHEMA = sensor.SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(TotalDailyEnergy), - cv.GenerateID(CONF_TIME_ID): cv.use_id(time.RealTimeClock), - cv.Required(CONF_POWER_ID): cv.use_id(sensor.Sensor), -}).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = sensor.SENSOR_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(TotalDailyEnergy), + cv.GenerateID(CONF_TIME_ID): cv.use_id(time.RealTimeClock), + cv.Required(CONF_POWER_ID): cv.use_id(sensor.Sensor), + } +).extend(cv.COMPONENT_SCHEMA) def to_code(config): diff --git a/esphome/components/tsl2561/sensor.py b/esphome/components/tsl2561/sensor.py index 92ad64e2ee..d0219fc078 100644 --- a/esphome/components/tsl2561/sensor.py +++ b/esphome/components/tsl2561/sensor.py @@ -1,26 +1,32 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import CONF_GAIN, CONF_ID, CONF_INTEGRATION_TIME, DEVICE_CLASS_ILLUMINANCE, \ - ICON_EMPTY, UNIT_LUX +from esphome.const import ( + CONF_GAIN, + CONF_ID, + CONF_INTEGRATION_TIME, + DEVICE_CLASS_ILLUMINANCE, + ICON_EMPTY, + UNIT_LUX, +) -DEPENDENCIES = ['i2c'] +DEPENDENCIES = ["i2c"] -tsl2561_ns = cg.esphome_ns.namespace('tsl2561') -TSL2561IntegrationTime = tsl2561_ns.enum('TSL2561IntegrationTime') +tsl2561_ns = cg.esphome_ns.namespace("tsl2561") +TSL2561IntegrationTime = tsl2561_ns.enum("TSL2561IntegrationTime") INTEGRATION_TIMES = { 14: TSL2561IntegrationTime.TSL2561_INTEGRATION_14MS, 101: TSL2561IntegrationTime.TSL2561_INTEGRATION_101MS, 402: TSL2561IntegrationTime.TSL2561_INTEGRATION_402MS, } -TSL2561Gain = tsl2561_ns.enum('TSL2561Gain') +TSL2561Gain = tsl2561_ns.enum("TSL2561Gain") GAINS = { - '1X': TSL2561Gain.TSL2561_GAIN_1X, - '16X': TSL2561Gain.TSL2561_GAIN_16X, + "1X": TSL2561Gain.TSL2561_GAIN_1X, + "16X": TSL2561Gain.TSL2561_GAIN_16X, } -CONF_IS_CS_PACKAGE = 'is_cs_package' +CONF_IS_CS_PACKAGE = "is_cs_package" def validate_integration_time(value): @@ -28,15 +34,25 @@ def validate_integration_time(value): return cv.enum(INTEGRATION_TIMES, int=True)(value) -TSL2561Sensor = tsl2561_ns.class_('TSL2561Sensor', sensor.Sensor, cg.PollingComponent, - i2c.I2CDevice) +TSL2561Sensor = tsl2561_ns.class_( + "TSL2561Sensor", sensor.Sensor, cg.PollingComponent, i2c.I2CDevice +) -CONFIG_SCHEMA = sensor.sensor_schema(UNIT_LUX, ICON_EMPTY, 1, DEVICE_CLASS_ILLUMINANCE).extend({ - cv.GenerateID(): cv.declare_id(TSL2561Sensor), - cv.Optional(CONF_INTEGRATION_TIME, default='402ms'): validate_integration_time, - cv.Optional(CONF_GAIN, default='1X'): cv.enum(GAINS, upper=True), - cv.Optional(CONF_IS_CS_PACKAGE, default=False): cv.boolean, -}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x39)) +CONFIG_SCHEMA = ( + sensor.sensor_schema(UNIT_LUX, ICON_EMPTY, 1, DEVICE_CLASS_ILLUMINANCE) + .extend( + { + cv.GenerateID(): cv.declare_id(TSL2561Sensor), + cv.Optional( + CONF_INTEGRATION_TIME, default="402ms" + ): validate_integration_time, + cv.Optional(CONF_GAIN, default="1X"): cv.enum(GAINS, upper=True), + cv.Optional(CONF_IS_CS_PACKAGE, default=False): cv.boolean, + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x39)) +) def to_code(config): diff --git a/esphome/components/ttp229_bsf/__init__.py b/esphome/components/ttp229_bsf/__init__.py index 5ec182d46b..8707c9efe3 100644 --- a/esphome/components/ttp229_bsf/__init__.py +++ b/esphome/components/ttp229_bsf/__init__.py @@ -3,20 +3,22 @@ import esphome.config_validation as cv from esphome import pins from esphome.const import CONF_ID, CONF_SDO_PIN, CONF_SCL_PIN -DEPENDENCIES = ['i2c'] -AUTO_LOAD = ['binary_sensor'] +DEPENDENCIES = ["i2c"] +AUTO_LOAD = ["binary_sensor"] -CONF_TTP229_ID = 'ttp229_id' -ttp229_bsf_ns = cg.esphome_ns.namespace('ttp229_bsf') +CONF_TTP229_ID = "ttp229_id" +ttp229_bsf_ns = cg.esphome_ns.namespace("ttp229_bsf") -TTP229BSFComponent = ttp229_bsf_ns.class_('TTP229BSFComponent', cg.Component) +TTP229BSFComponent = ttp229_bsf_ns.class_("TTP229BSFComponent", cg.Component) MULTI_CONF = True -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(TTP229BSFComponent), - cv.Required(CONF_SDO_PIN): pins.gpio_input_pullup_pin_schema, - cv.Required(CONF_SCL_PIN): pins.gpio_output_pin_schema, -}).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(TTP229BSFComponent), + cv.Required(CONF_SDO_PIN): pins.gpio_input_pullup_pin_schema, + cv.Required(CONF_SCL_PIN): pins.gpio_output_pin_schema, + } +).extend(cv.COMPONENT_SCHEMA) def to_code(config): diff --git a/esphome/components/ttp229_bsf/binary_sensor.py b/esphome/components/ttp229_bsf/binary_sensor.py index 7a1ab3dc9f..7351e73d80 100644 --- a/esphome/components/ttp229_bsf/binary_sensor.py +++ b/esphome/components/ttp229_bsf/binary_sensor.py @@ -4,14 +4,16 @@ from esphome.components import binary_sensor from esphome.const import CONF_CHANNEL, CONF_ID from . import ttp229_bsf_ns, TTP229BSFComponent, CONF_TTP229_ID -DEPENDENCIES = ['ttp229_bsf'] -TTP229BSFChannel = ttp229_bsf_ns.class_('TTP229BSFChannel', binary_sensor.BinarySensor) +DEPENDENCIES = ["ttp229_bsf"] +TTP229BSFChannel = ttp229_bsf_ns.class_("TTP229BSFChannel", binary_sensor.BinarySensor) -CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(TTP229BSFChannel), - cv.GenerateID(CONF_TTP229_ID): cv.use_id(TTP229BSFComponent), - cv.Required(CONF_CHANNEL): cv.int_range(min=0, max=15), -}) +CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(TTP229BSFChannel), + cv.GenerateID(CONF_TTP229_ID): cv.use_id(TTP229BSFComponent), + cv.Required(CONF_CHANNEL): cv.int_range(min=0, max=15), + } +) def to_code(config): diff --git a/esphome/components/ttp229_lsf/__init__.py b/esphome/components/ttp229_lsf/__init__.py index 6faca970f0..1d7efea205 100644 --- a/esphome/components/ttp229_lsf/__init__.py +++ b/esphome/components/ttp229_lsf/__init__.py @@ -3,18 +3,26 @@ import esphome.config_validation as cv from esphome.components import i2c from esphome.const import CONF_ID -DEPENDENCIES = ['i2c'] -AUTO_LOAD = ['binary_sensor'] +DEPENDENCIES = ["i2c"] +AUTO_LOAD = ["binary_sensor"] -CONF_TTP229_ID = 'ttp229_id' -ttp229_lsf_ns = cg.esphome_ns.namespace('ttp229_lsf') +CONF_TTP229_ID = "ttp229_id" +ttp229_lsf_ns = cg.esphome_ns.namespace("ttp229_lsf") -TTP229LSFComponent = ttp229_lsf_ns.class_('TTP229LSFComponent', cg.Component, i2c.I2CDevice) +TTP229LSFComponent = ttp229_lsf_ns.class_( + "TTP229LSFComponent", cg.Component, i2c.I2CDevice +) MULTI_CONF = True -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(TTP229LSFComponent), -}).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(0x57)) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(TTP229LSFComponent), + } + ) + .extend(cv.COMPONENT_SCHEMA) + .extend(i2c.i2c_device_schema(0x57)) +) def to_code(config): diff --git a/esphome/components/ttp229_lsf/binary_sensor.py b/esphome/components/ttp229_lsf/binary_sensor.py index 870bf16287..09a8a1e207 100644 --- a/esphome/components/ttp229_lsf/binary_sensor.py +++ b/esphome/components/ttp229_lsf/binary_sensor.py @@ -4,14 +4,16 @@ from esphome.components import binary_sensor from esphome.const import CONF_CHANNEL, CONF_ID from . import ttp229_lsf_ns, TTP229LSFComponent, CONF_TTP229_ID -DEPENDENCIES = ['ttp229_lsf'] -TTP229Channel = ttp229_lsf_ns.class_('TTP229Channel', binary_sensor.BinarySensor) +DEPENDENCIES = ["ttp229_lsf"] +TTP229Channel = ttp229_lsf_ns.class_("TTP229Channel", binary_sensor.BinarySensor) -CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(TTP229Channel), - cv.GenerateID(CONF_TTP229_ID): cv.use_id(TTP229LSFComponent), - cv.Required(CONF_CHANNEL): cv.int_range(min=0, max=15), -}) +CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(TTP229Channel), + cv.GenerateID(CONF_TTP229_ID): cv.use_id(TTP229LSFComponent), + cv.Required(CONF_CHANNEL): cv.int_range(min=0, max=15), + } +) def to_code(config): diff --git a/esphome/components/tuya/__init__.py b/esphome/components/tuya/__init__.py index 83a4f733ca..58dad13257 100644 --- a/esphome/components/tuya/__init__.py +++ b/esphome/components/tuya/__init__.py @@ -4,19 +4,27 @@ import esphome.config_validation as cv from esphome.components import uart from esphome.const import CONF_ID, CONF_TIME_ID -DEPENDENCIES = ['uart'] +DEPENDENCIES = ["uart"] CONF_IGNORE_MCU_UPDATE_ON_DATAPOINTS = "ignore_mcu_update_on_datapoints" -tuya_ns = cg.esphome_ns.namespace('tuya') -Tuya = tuya_ns.class_('Tuya', cg.Component, uart.UARTDevice) +tuya_ns = cg.esphome_ns.namespace("tuya") +Tuya = tuya_ns.class_("Tuya", cg.Component, uart.UARTDevice) -CONF_TUYA_ID = 'tuya_id' -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(Tuya), - cv.Optional(CONF_TIME_ID): cv.use_id(time.RealTimeClock), - cv.Optional(CONF_IGNORE_MCU_UPDATE_ON_DATAPOINTS): cv.ensure_list(cv.uint8_t), -}).extend(cv.COMPONENT_SCHEMA).extend(uart.UART_DEVICE_SCHEMA) +CONF_TUYA_ID = "tuya_id" +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(Tuya), + cv.Optional(CONF_TIME_ID): cv.use_id(time.RealTimeClock), + cv.Optional(CONF_IGNORE_MCU_UPDATE_ON_DATAPOINTS): cv.ensure_list( + cv.uint8_t + ), + } + ) + .extend(cv.COMPONENT_SCHEMA) + .extend(uart.UART_DEVICE_SCHEMA) +) def to_code(config): diff --git a/esphome/components/tuya/binary_sensor/__init__.py b/esphome/components/tuya/binary_sensor/__init__.py index b63638b4cc..45f918ff24 100644 --- a/esphome/components/tuya/binary_sensor/__init__.py +++ b/esphome/components/tuya/binary_sensor/__init__.py @@ -4,18 +4,22 @@ import esphome.codegen as cg from esphome.const import CONF_ID from .. import tuya_ns, CONF_TUYA_ID, Tuya -DEPENDENCIES = ['tuya'] -CODEOWNERS = ['@jesserockz'] +DEPENDENCIES = ["tuya"] +CODEOWNERS = ["@jesserockz"] CONF_SENSOR_DATAPOINT = "sensor_datapoint" -TuyaBinarySensor = tuya_ns.class_('TuyaBinarySensor', binary_sensor.BinarySensor, cg.Component) +TuyaBinarySensor = tuya_ns.class_( + "TuyaBinarySensor", binary_sensor.BinarySensor, cg.Component +) -CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(TuyaBinarySensor), - cv.GenerateID(CONF_TUYA_ID): cv.use_id(Tuya), - cv.Required(CONF_SENSOR_DATAPOINT): cv.uint8_t, -}).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(TuyaBinarySensor), + cv.GenerateID(CONF_TUYA_ID): cv.use_id(Tuya), + cv.Required(CONF_SENSOR_DATAPOINT): cv.uint8_t, + } +).extend(cv.COMPONENT_SCHEMA) def to_code(config): diff --git a/esphome/components/tuya/climate/__init__.py b/esphome/components/tuya/climate/__init__.py index f0219de97b..8ac42f7c8f 100644 --- a/esphome/components/tuya/climate/__init__.py +++ b/esphome/components/tuya/climate/__init__.py @@ -4,60 +4,77 @@ import esphome.codegen as cg from esphome.const import CONF_ID, CONF_SWITCH_DATAPOINT from .. import tuya_ns, CONF_TUYA_ID, Tuya -DEPENDENCIES = ['tuya'] -CODEOWNERS = ['@jesserockz'] +DEPENDENCIES = ["tuya"] +CODEOWNERS = ["@jesserockz"] -CONF_TARGET_TEMPERATURE_DATAPOINT = 'target_temperature_datapoint' -CONF_CURRENT_TEMPERATURE_DATAPOINT = 'current_temperature_datapoint' -CONF_TEMPERATURE_MULTIPLIER = 'temperature_multiplier' -CONF_CURRENT_TEMPERATURE_MULTIPLIER = 'current_temperature_multiplier' -CONF_TARGET_TEMPERATURE_MULTIPLIER = 'target_temperature_multiplier' +CONF_TARGET_TEMPERATURE_DATAPOINT = "target_temperature_datapoint" +CONF_CURRENT_TEMPERATURE_DATAPOINT = "current_temperature_datapoint" +CONF_TEMPERATURE_MULTIPLIER = "temperature_multiplier" +CONF_CURRENT_TEMPERATURE_MULTIPLIER = "current_temperature_multiplier" +CONF_TARGET_TEMPERATURE_MULTIPLIER = "target_temperature_multiplier" -TuyaClimate = tuya_ns.class_('TuyaClimate', climate.Climate, cg.Component) +TuyaClimate = tuya_ns.class_("TuyaClimate", climate.Climate, cg.Component) def validate_temperature_multipliers(value): if CONF_TEMPERATURE_MULTIPLIER in value: if ( - CONF_CURRENT_TEMPERATURE_MULTIPLIER in value - or CONF_TARGET_TEMPERATURE_MULTIPLIER in value - ): - raise cv.Invalid((f"Cannot have {CONF_TEMPERATURE_MULTIPLIER} at the same time as " - f"{CONF_CURRENT_TEMPERATURE_MULTIPLIER} and " - f"{CONF_TARGET_TEMPERATURE_MULTIPLIER}")) - if ( CONF_CURRENT_TEMPERATURE_MULTIPLIER in value - and CONF_TARGET_TEMPERATURE_MULTIPLIER not in value - ): - raise cv.Invalid((f"{CONF_TARGET_TEMPERATURE_MULTIPLIER} required if using " - f"{CONF_CURRENT_TEMPERATURE_MULTIPLIER}")) + or CONF_TARGET_TEMPERATURE_MULTIPLIER in value + ): + raise cv.Invalid( + ( + f"Cannot have {CONF_TEMPERATURE_MULTIPLIER} at the same time as " + f"{CONF_CURRENT_TEMPERATURE_MULTIPLIER} and " + f"{CONF_TARGET_TEMPERATURE_MULTIPLIER}" + ) + ) if ( - CONF_TARGET_TEMPERATURE_MULTIPLIER in value - and CONF_CURRENT_TEMPERATURE_MULTIPLIER not in value + CONF_CURRENT_TEMPERATURE_MULTIPLIER in value + and CONF_TARGET_TEMPERATURE_MULTIPLIER not in value ): - raise cv.Invalid((f"{CONF_CURRENT_TEMPERATURE_MULTIPLIER} required if using " - f"{CONF_TARGET_TEMPERATURE_MULTIPLIER}")) + raise cv.Invalid( + ( + f"{CONF_TARGET_TEMPERATURE_MULTIPLIER} required if using " + f"{CONF_CURRENT_TEMPERATURE_MULTIPLIER}" + ) + ) + if ( + CONF_TARGET_TEMPERATURE_MULTIPLIER in value + and CONF_CURRENT_TEMPERATURE_MULTIPLIER not in value + ): + raise cv.Invalid( + ( + f"{CONF_CURRENT_TEMPERATURE_MULTIPLIER} required if using " + f"{CONF_TARGET_TEMPERATURE_MULTIPLIER}" + ) + ) keys = ( CONF_TEMPERATURE_MULTIPLIER, CONF_CURRENT_TEMPERATURE_MULTIPLIER, - CONF_TARGET_TEMPERATURE_MULTIPLIER + CONF_TARGET_TEMPERATURE_MULTIPLIER, ) if all(multiplier not in value for multiplier in keys): value[CONF_TEMPERATURE_MULTIPLIER] = 1.0 return value -CONFIG_SCHEMA = cv.All(climate.CLIMATE_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(TuyaClimate), - cv.GenerateID(CONF_TUYA_ID): cv.use_id(Tuya), - cv.Optional(CONF_SWITCH_DATAPOINT): cv.uint8_t, - cv.Optional(CONF_TARGET_TEMPERATURE_DATAPOINT): cv.uint8_t, - cv.Optional(CONF_CURRENT_TEMPERATURE_DATAPOINT): cv.uint8_t, - cv.Optional(CONF_TEMPERATURE_MULTIPLIER): cv.positive_float, - cv.Optional(CONF_CURRENT_TEMPERATURE_MULTIPLIER): cv.positive_float, - cv.Optional(CONF_TARGET_TEMPERATURE_MULTIPLIER): cv.positive_float, -}).extend(cv.COMPONENT_SCHEMA), cv.has_at_least_one_key( - CONF_TARGET_TEMPERATURE_DATAPOINT, CONF_SWITCH_DATAPOINT), validate_temperature_multipliers) +CONFIG_SCHEMA = cv.All( + climate.CLIMATE_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(TuyaClimate), + cv.GenerateID(CONF_TUYA_ID): cv.use_id(Tuya), + cv.Optional(CONF_SWITCH_DATAPOINT): cv.uint8_t, + cv.Optional(CONF_TARGET_TEMPERATURE_DATAPOINT): cv.uint8_t, + cv.Optional(CONF_CURRENT_TEMPERATURE_DATAPOINT): cv.uint8_t, + cv.Optional(CONF_TEMPERATURE_MULTIPLIER): cv.positive_float, + cv.Optional(CONF_CURRENT_TEMPERATURE_MULTIPLIER): cv.positive_float, + cv.Optional(CONF_TARGET_TEMPERATURE_MULTIPLIER): cv.positive_float, + } + ).extend(cv.COMPONENT_SCHEMA), + cv.has_at_least_one_key(CONF_TARGET_TEMPERATURE_DATAPOINT, CONF_SWITCH_DATAPOINT), + validate_temperature_multipliers, +) def to_code(config): @@ -73,10 +90,24 @@ def to_code(config): if CONF_TARGET_TEMPERATURE_DATAPOINT in config: cg.add(var.set_target_temperature_id(config[CONF_TARGET_TEMPERATURE_DATAPOINT])) if CONF_CURRENT_TEMPERATURE_DATAPOINT in config: - cg.add(var.set_current_temperature_id(config[CONF_CURRENT_TEMPERATURE_DATAPOINT])) + cg.add( + var.set_current_temperature_id(config[CONF_CURRENT_TEMPERATURE_DATAPOINT]) + ) if CONF_TEMPERATURE_MULTIPLIER in config: - cg.add(var.set_target_temperature_multiplier(config[CONF_TEMPERATURE_MULTIPLIER])) - cg.add(var.set_current_temperature_multiplier(config[CONF_TEMPERATURE_MULTIPLIER])) + cg.add( + var.set_target_temperature_multiplier(config[CONF_TEMPERATURE_MULTIPLIER]) + ) + cg.add( + var.set_current_temperature_multiplier(config[CONF_TEMPERATURE_MULTIPLIER]) + ) else: - cg.add(var.set_current_temperature_multiplier(config[CONF_CURRENT_TEMPERATURE_MULTIPLIER])) - cg.add(var.set_target_temperature_multiplier(config[CONF_TARGET_TEMPERATURE_MULTIPLIER])) + cg.add( + var.set_current_temperature_multiplier( + config[CONF_CURRENT_TEMPERATURE_MULTIPLIER] + ) + ) + cg.add( + var.set_target_temperature_multiplier( + config[CONF_TARGET_TEMPERATURE_MULTIPLIER] + ) + ) diff --git a/esphome/components/tuya/fan/__init__.py b/esphome/components/tuya/fan/__init__.py index e8492fd71b..8615f3ae85 100644 --- a/esphome/components/tuya/fan/__init__.py +++ b/esphome/components/tuya/fan/__init__.py @@ -4,21 +4,25 @@ import esphome.codegen as cg from esphome.const import CONF_OUTPUT_ID, CONF_SWITCH_DATAPOINT from .. import tuya_ns, CONF_TUYA_ID, Tuya -DEPENDENCIES = ['tuya'] +DEPENDENCIES = ["tuya"] CONF_SPEED_DATAPOINT = "speed_datapoint" CONF_OSCILLATION_DATAPOINT = "oscillation_datapoint" -TuyaFan = tuya_ns.class_('TuyaFan', cg.Component) +TuyaFan = tuya_ns.class_("TuyaFan", cg.Component) -CONFIG_SCHEMA = cv.All(fan.FAN_SCHEMA.extend({ - cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(TuyaFan), - cv.GenerateID(CONF_TUYA_ID): cv.use_id(Tuya), - cv.Optional(CONF_OSCILLATION_DATAPOINT): cv.uint8_t, - cv.Optional(CONF_SPEED_DATAPOINT): cv.uint8_t, - cv.Optional(CONF_SWITCH_DATAPOINT): cv.uint8_t, -}).extend(cv.COMPONENT_SCHEMA), cv.has_at_least_one_key( - CONF_SPEED_DATAPOINT, CONF_SWITCH_DATAPOINT)) +CONFIG_SCHEMA = cv.All( + fan.FAN_SCHEMA.extend( + { + cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(TuyaFan), + cv.GenerateID(CONF_TUYA_ID): cv.use_id(Tuya), + cv.Optional(CONF_OSCILLATION_DATAPOINT): cv.uint8_t, + cv.Optional(CONF_SPEED_DATAPOINT): cv.uint8_t, + cv.Optional(CONF_SWITCH_DATAPOINT): cv.uint8_t, + } + ).extend(cv.COMPONENT_SCHEMA), + cv.has_at_least_one_key(CONF_SPEED_DATAPOINT, CONF_SWITCH_DATAPOINT), +) def to_code(config): diff --git a/esphome/components/tuya/light/__init__.py b/esphome/components/tuya/light/__init__.py index 05605822cb..f8026e47e8 100644 --- a/esphome/components/tuya/light/__init__.py +++ b/esphome/components/tuya/light/__init__.py @@ -1,32 +1,43 @@ from esphome.components import light import esphome.config_validation as cv import esphome.codegen as cg -from esphome.const import CONF_OUTPUT_ID, CONF_MIN_VALUE, CONF_MAX_VALUE, CONF_GAMMA_CORRECT, \ - CONF_DEFAULT_TRANSITION_LENGTH, CONF_SWITCH_DATAPOINT +from esphome.const import ( + CONF_OUTPUT_ID, + CONF_MIN_VALUE, + CONF_MAX_VALUE, + CONF_GAMMA_CORRECT, + CONF_DEFAULT_TRANSITION_LENGTH, + CONF_SWITCH_DATAPOINT, +) from .. import tuya_ns, CONF_TUYA_ID, Tuya -DEPENDENCIES = ['tuya'] +DEPENDENCIES = ["tuya"] CONF_DIMMER_DATAPOINT = "dimmer_datapoint" CONF_MIN_VALUE_DATAPOINT = "min_value_datapoint" -TuyaLight = tuya_ns.class_('TuyaLight', light.LightOutput, cg.Component) +TuyaLight = tuya_ns.class_("TuyaLight", light.LightOutput, cg.Component) -CONFIG_SCHEMA = cv.All(light.BRIGHTNESS_ONLY_LIGHT_SCHEMA.extend({ - cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(TuyaLight), - cv.GenerateID(CONF_TUYA_ID): cv.use_id(Tuya), - cv.Optional(CONF_DIMMER_DATAPOINT): cv.uint8_t, - cv.Optional(CONF_MIN_VALUE_DATAPOINT): cv.uint8_t, - cv.Optional(CONF_SWITCH_DATAPOINT): cv.uint8_t, - cv.Optional(CONF_MIN_VALUE): cv.int_, - cv.Optional(CONF_MAX_VALUE): cv.int_, - - # Change the default gamma_correct and default transition length settings. - # The Tuya MCU handles transitions and gamma correction on its own. - cv.Optional(CONF_GAMMA_CORRECT, default=1.0): cv.positive_float, - cv.Optional(CONF_DEFAULT_TRANSITION_LENGTH, default='0s'): cv.positive_time_period_milliseconds, -}).extend(cv.COMPONENT_SCHEMA), cv.has_at_least_one_key(CONF_DIMMER_DATAPOINT, - CONF_SWITCH_DATAPOINT)) +CONFIG_SCHEMA = cv.All( + light.BRIGHTNESS_ONLY_LIGHT_SCHEMA.extend( + { + cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(TuyaLight), + cv.GenerateID(CONF_TUYA_ID): cv.use_id(Tuya), + cv.Optional(CONF_DIMMER_DATAPOINT): cv.uint8_t, + cv.Optional(CONF_MIN_VALUE_DATAPOINT): cv.uint8_t, + cv.Optional(CONF_SWITCH_DATAPOINT): cv.uint8_t, + cv.Optional(CONF_MIN_VALUE): cv.int_, + cv.Optional(CONF_MAX_VALUE): cv.int_, + # Change the default gamma_correct and default transition length settings. + # The Tuya MCU handles transitions and gamma correction on its own. + cv.Optional(CONF_GAMMA_CORRECT, default=1.0): cv.positive_float, + cv.Optional( + CONF_DEFAULT_TRANSITION_LENGTH, default="0s" + ): cv.positive_time_period_milliseconds, + } + ).extend(cv.COMPONENT_SCHEMA), + cv.has_at_least_one_key(CONF_DIMMER_DATAPOINT, CONF_SWITCH_DATAPOINT), +) def to_code(config): diff --git a/esphome/components/tuya/sensor/__init__.py b/esphome/components/tuya/sensor/__init__.py index b3260bfe0b..0a02fb77a1 100644 --- a/esphome/components/tuya/sensor/__init__.py +++ b/esphome/components/tuya/sensor/__init__.py @@ -4,18 +4,20 @@ import esphome.codegen as cg from esphome.const import CONF_ID from .. import tuya_ns, CONF_TUYA_ID, Tuya -DEPENDENCIES = ['tuya'] -CODEOWNERS = ['@jesserockz'] +DEPENDENCIES = ["tuya"] +CODEOWNERS = ["@jesserockz"] CONF_SENSOR_DATAPOINT = "sensor_datapoint" -TuyaSensor = tuya_ns.class_('TuyaSensor', sensor.Sensor, cg.Component) +TuyaSensor = tuya_ns.class_("TuyaSensor", sensor.Sensor, cg.Component) -CONFIG_SCHEMA = sensor.SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(TuyaSensor), - cv.GenerateID(CONF_TUYA_ID): cv.use_id(Tuya), - cv.Required(CONF_SENSOR_DATAPOINT): cv.uint8_t, -}).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = sensor.SENSOR_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(TuyaSensor), + cv.GenerateID(CONF_TUYA_ID): cv.use_id(Tuya), + cv.Required(CONF_SENSOR_DATAPOINT): cv.uint8_t, + } +).extend(cv.COMPONENT_SCHEMA) def to_code(config): diff --git a/esphome/components/tuya/switch/__init__.py b/esphome/components/tuya/switch/__init__.py index f68bbbcdb6..4c4ccbf814 100644 --- a/esphome/components/tuya/switch/__init__.py +++ b/esphome/components/tuya/switch/__init__.py @@ -4,16 +4,18 @@ import esphome.codegen as cg from esphome.const import CONF_ID, CONF_SWITCH_DATAPOINT from .. import tuya_ns, CONF_TUYA_ID, Tuya -DEPENDENCIES = ['tuya'] -CODEOWNERS = ['@jesserockz'] +DEPENDENCIES = ["tuya"] +CODEOWNERS = ["@jesserockz"] -TuyaSwitch = tuya_ns.class_('TuyaSwitch', switch.Switch, cg.Component) +TuyaSwitch = tuya_ns.class_("TuyaSwitch", switch.Switch, cg.Component) -CONFIG_SCHEMA = switch.SWITCH_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(TuyaSwitch), - cv.GenerateID(CONF_TUYA_ID): cv.use_id(Tuya), - cv.Required(CONF_SWITCH_DATAPOINT): cv.uint8_t, -}).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = switch.SWITCH_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(TuyaSwitch), + cv.GenerateID(CONF_TUYA_ID): cv.use_id(Tuya), + cv.Required(CONF_SWITCH_DATAPOINT): cv.uint8_t, + } +).extend(cv.COMPONENT_SCHEMA) def to_code(config): diff --git a/esphome/components/tx20/sensor.py b/esphome/components/tx20/sensor.py index 060bd233be..434257470b 100644 --- a/esphome/components/tx20/sensor.py +++ b/esphome/components/tx20/sensor.py @@ -2,22 +2,35 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins from esphome.components import sensor -from esphome.const import CONF_ID, CONF_WIND_SPEED, CONF_PIN, \ - CONF_WIND_DIRECTION_DEGREES, DEVICE_CLASS_EMPTY, UNIT_KILOMETER_PER_HOUR, \ - ICON_WEATHER_WINDY, ICON_SIGN_DIRECTION, UNIT_DEGREES +from esphome.const import ( + CONF_ID, + CONF_WIND_SPEED, + CONF_PIN, + CONF_WIND_DIRECTION_DEGREES, + DEVICE_CLASS_EMPTY, + UNIT_KILOMETER_PER_HOUR, + ICON_WEATHER_WINDY, + ICON_SIGN_DIRECTION, + UNIT_DEGREES, +) -tx20_ns = cg.esphome_ns.namespace('tx20') -Tx20Component = tx20_ns.class_('Tx20Component', cg.Component) +tx20_ns = cg.esphome_ns.namespace("tx20") +Tx20Component = tx20_ns.class_("Tx20Component", cg.Component) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(Tx20Component), - cv.Optional(CONF_WIND_SPEED): - sensor.sensor_schema(UNIT_KILOMETER_PER_HOUR, ICON_WEATHER_WINDY, 1, DEVICE_CLASS_EMPTY), - cv.Optional(CONF_WIND_DIRECTION_DEGREES): - sensor.sensor_schema(UNIT_DEGREES, ICON_SIGN_DIRECTION, 1, DEVICE_CLASS_EMPTY), - cv.Required(CONF_PIN): cv.All(pins.internal_gpio_input_pin_schema, - pins.validate_has_interrupt), -}).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(Tx20Component), + cv.Optional(CONF_WIND_SPEED): sensor.sensor_schema( + UNIT_KILOMETER_PER_HOUR, ICON_WEATHER_WINDY, 1, DEVICE_CLASS_EMPTY + ), + cv.Optional(CONF_WIND_DIRECTION_DEGREES): sensor.sensor_schema( + UNIT_DEGREES, ICON_SIGN_DIRECTION, 1, DEVICE_CLASS_EMPTY + ), + cv.Required(CONF_PIN): cv.All( + pins.internal_gpio_input_pin_schema, pins.validate_has_interrupt + ), + } +).extend(cv.COMPONENT_SCHEMA) def to_code(config): diff --git a/esphome/components/uart/__init__.py b/esphome/components/uart/__init__.py index b3f94a4718..a02ea58def 100644 --- a/esphome/components/uart/__init__.py +++ b/esphome/components/uart/__init__.py @@ -1,26 +1,36 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins, automation -from esphome.const import CONF_BAUD_RATE, CONF_ID, CONF_RX_PIN, CONF_TX_PIN, CONF_UART_ID, \ - CONF_DATA, CONF_RX_BUFFER_SIZE, CONF_INVERT +from esphome.const import ( + CONF_BAUD_RATE, + CONF_ID, + CONF_RX_PIN, + CONF_TX_PIN, + CONF_UART_ID, + CONF_DATA, + CONF_RX_BUFFER_SIZE, + CONF_INVERT, +) from esphome.core import CORE, coroutine -CODEOWNERS = ['@esphome/core'] -uart_ns = cg.esphome_ns.namespace('uart') -UARTComponent = uart_ns.class_('UARTComponent', cg.Component) -UARTDevice = uart_ns.class_('UARTDevice') -UARTWriteAction = uart_ns.class_('UARTWriteAction', automation.Action) +CODEOWNERS = ["@esphome/core"] +uart_ns = cg.esphome_ns.namespace("uart") +UARTComponent = uart_ns.class_("UARTComponent", cg.Component) +UARTDevice = uart_ns.class_("UARTDevice") +UARTWriteAction = uart_ns.class_("UARTWriteAction", automation.Action) MULTI_CONF = True def validate_raw_data(value): if isinstance(value, str): - return value.encode('utf-8') + return value.encode("utf-8") if isinstance(value, str): return value if isinstance(value, list): return cv.Schema([cv.hex_uint8_t])(value) - raise cv.Invalid("data must either be a string wrapped in quotes or a list of bytes") + raise cv.Invalid( + "data must either be a string wrapped in quotes or a list of bytes" + ) def validate_rx_pin(value): @@ -30,29 +40,37 @@ def validate_rx_pin(value): return value -UARTParityOptions = uart_ns.enum('UARTParityOptions') +UARTParityOptions = uart_ns.enum("UARTParityOptions") UART_PARITY_OPTIONS = { - 'NONE': UARTParityOptions.UART_CONFIG_PARITY_NONE, - 'EVEN': UARTParityOptions.UART_CONFIG_PARITY_EVEN, - 'ODD': UARTParityOptions.UART_CONFIG_PARITY_ODD, + "NONE": UARTParityOptions.UART_CONFIG_PARITY_NONE, + "EVEN": UARTParityOptions.UART_CONFIG_PARITY_EVEN, + "ODD": UARTParityOptions.UART_CONFIG_PARITY_ODD, } -CONF_STOP_BITS = 'stop_bits' -CONF_DATA_BITS = 'data_bits' -CONF_PARITY = 'parity' +CONF_STOP_BITS = "stop_bits" +CONF_DATA_BITS = "data_bits" +CONF_PARITY = "parity" -CONFIG_SCHEMA = cv.All(cv.Schema({ - cv.GenerateID(): cv.declare_id(UARTComponent), - cv.Required(CONF_BAUD_RATE): cv.int_range(min=1), - cv.Optional(CONF_TX_PIN): pins.output_pin, - cv.Optional(CONF_RX_PIN): validate_rx_pin, - cv.Optional(CONF_RX_BUFFER_SIZE, default=256): cv.validate_bytes, - cv.SplitDefault(CONF_INVERT, esp32=False): cv.All(cv.only_on_esp32, - cv.boolean), - cv.Optional(CONF_STOP_BITS, default=1): cv.one_of(1, 2, int=True), - cv.Optional(CONF_DATA_BITS, default=8): cv.int_range(min=5, max=8), - cv.Optional(CONF_PARITY, default="NONE"): cv.enum(UART_PARITY_OPTIONS, upper=True) -}).extend(cv.COMPONENT_SCHEMA), cv.has_at_least_one_key(CONF_TX_PIN, CONF_RX_PIN)) +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(UARTComponent), + cv.Required(CONF_BAUD_RATE): cv.int_range(min=1), + cv.Optional(CONF_TX_PIN): pins.output_pin, + cv.Optional(CONF_RX_PIN): validate_rx_pin, + cv.Optional(CONF_RX_BUFFER_SIZE, default=256): cv.validate_bytes, + cv.SplitDefault(CONF_INVERT, esp32=False): cv.All( + cv.only_on_esp32, cv.boolean + ), + cv.Optional(CONF_STOP_BITS, default=1): cv.one_of(1, 2, int=True), + cv.Optional(CONF_DATA_BITS, default=8): cv.int_range(min=5, max=8), + cv.Optional(CONF_PARITY, default="NONE"): cv.enum( + UART_PARITY_OPTIONS, upper=True + ), + } + ).extend(cv.COMPONENT_SCHEMA), + cv.has_at_least_one_key(CONF_TX_PIN, CONF_RX_PIN), +) def to_code(config): @@ -75,9 +93,11 @@ def to_code(config): # A schema to use for all UART devices, all UART integrations must extend this! -UART_DEVICE_SCHEMA = cv.Schema({ - cv.GenerateID(CONF_UART_ID): cv.use_id(UARTComponent), -}) +UART_DEVICE_SCHEMA = cv.Schema( + { + cv.GenerateID(CONF_UART_ID): cv.use_id(UARTComponent), + } +) @coroutine @@ -90,10 +110,17 @@ def register_uart_device(var, config): cg.add(var.set_uart_parent(parent)) -@automation.register_action('uart.write', UARTWriteAction, cv.maybe_simple_value({ - cv.GenerateID(): cv.use_id(UARTComponent), - cv.Required(CONF_DATA): cv.templatable(validate_raw_data), -}, key=CONF_DATA)) +@automation.register_action( + "uart.write", + UARTWriteAction, + cv.maybe_simple_value( + { + cv.GenerateID(): cv.use_id(UARTComponent), + cv.Required(CONF_DATA): cv.templatable(validate_raw_data), + }, + key=CONF_DATA, + ), +) def uart_write_to_code(config, action_id, template_arg, args): var = cg.new_Pvariable(action_id, template_arg) yield cg.register_parented(var, config[CONF_ID]) diff --git a/esphome/components/uart/switch/__init__.py b/esphome/components/uart/switch/__init__.py index 0c9ebe56f7..e84035aa3e 100644 --- a/esphome/components/uart/switch/__init__.py +++ b/esphome/components/uart/switch/__init__.py @@ -5,17 +5,25 @@ from esphome.const import CONF_DATA, CONF_ID, CONF_INVERTED, CONF_SEND_EVERY from esphome.core import HexInt from .. import uart_ns, validate_raw_data -DEPENDENCIES = ['uart'] +DEPENDENCIES = ["uart"] -UARTSwitch = uart_ns.class_('UARTSwitch', switch.Switch, uart.UARTDevice, cg.Component) +UARTSwitch = uart_ns.class_("UARTSwitch", switch.Switch, uart.UARTDevice, cg.Component) -CONFIG_SCHEMA = switch.SWITCH_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(UARTSwitch), - cv.Required(CONF_DATA): validate_raw_data, - cv.Optional(CONF_INVERTED): cv.invalid("UART switches do not support inverted mode!"), - cv.Optional(CONF_SEND_EVERY): cv.positive_time_period_milliseconds, -}).extend(uart.UART_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = ( + switch.SWITCH_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(UARTSwitch), + cv.Required(CONF_DATA): validate_raw_data, + cv.Optional(CONF_INVERTED): cv.invalid( + "UART switches do not support inverted mode!" + ), + cv.Optional(CONF_SEND_EVERY): cv.positive_time_period_milliseconds, + } + ) + .extend(uart.UART_DEVICE_SCHEMA) + .extend(cv.COMPONENT_SCHEMA) +) def to_code(config): diff --git a/esphome/components/uln2003/stepper.py b/esphome/components/uln2003/stepper.py index 278fcf67eb..4d2e5ab518 100644 --- a/esphome/components/uln2003/stepper.py +++ b/esphome/components/uln2003/stepper.py @@ -2,29 +2,40 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins from esphome.components import stepper -from esphome.const import CONF_ID, CONF_PIN_A, CONF_PIN_B, CONF_PIN_C, CONF_PIN_D, \ - CONF_SLEEP_WHEN_DONE, CONF_STEP_MODE +from esphome.const import ( + CONF_ID, + CONF_PIN_A, + CONF_PIN_B, + CONF_PIN_C, + CONF_PIN_D, + CONF_SLEEP_WHEN_DONE, + CONF_STEP_MODE, +) -uln2003_ns = cg.esphome_ns.namespace('uln2003') -ULN2003StepMode = uln2003_ns.enum('ULN2003StepMode') +uln2003_ns = cg.esphome_ns.namespace("uln2003") +ULN2003StepMode = uln2003_ns.enum("ULN2003StepMode") STEP_MODES = { - 'FULL_STEP': ULN2003StepMode.ULN2003_STEP_MODE_FULL_STEP, - 'HALF_STEP': ULN2003StepMode.ULN2003_STEP_MODE_HALF_STEP, - 'WAVE_DRIVE': ULN2003StepMode.ULN2003_STEP_MODE_WAVE_DRIVE, + "FULL_STEP": ULN2003StepMode.ULN2003_STEP_MODE_FULL_STEP, + "HALF_STEP": ULN2003StepMode.ULN2003_STEP_MODE_HALF_STEP, + "WAVE_DRIVE": ULN2003StepMode.ULN2003_STEP_MODE_WAVE_DRIVE, } -ULN2003 = uln2003_ns.class_('ULN2003', stepper.Stepper, cg.Component) +ULN2003 = uln2003_ns.class_("ULN2003", stepper.Stepper, cg.Component) -CONFIG_SCHEMA = stepper.STEPPER_SCHEMA.extend({ - cv.Required(CONF_ID): cv.declare_id(ULN2003), - cv.Required(CONF_PIN_A): pins.gpio_output_pin_schema, - cv.Required(CONF_PIN_B): pins.gpio_output_pin_schema, - cv.Required(CONF_PIN_C): pins.gpio_output_pin_schema, - cv.Required(CONF_PIN_D): pins.gpio_output_pin_schema, - cv.Optional(CONF_SLEEP_WHEN_DONE, default=False): cv.boolean, - cv.Optional(CONF_STEP_MODE, default='FULL_STEP'): cv.enum(STEP_MODES, upper=True, space='_') -}).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = stepper.STEPPER_SCHEMA.extend( + { + cv.Required(CONF_ID): cv.declare_id(ULN2003), + cv.Required(CONF_PIN_A): pins.gpio_output_pin_schema, + cv.Required(CONF_PIN_B): pins.gpio_output_pin_schema, + cv.Required(CONF_PIN_C): pins.gpio_output_pin_schema, + cv.Required(CONF_PIN_D): pins.gpio_output_pin_schema, + cv.Optional(CONF_SLEEP_WHEN_DONE, default=False): cv.boolean, + cv.Optional(CONF_STEP_MODE, default="FULL_STEP"): cv.enum( + STEP_MODES, upper=True, space="_" + ), + } +).extend(cv.COMPONENT_SCHEMA) def to_code(config): diff --git a/esphome/components/ultrasonic/__init__.py b/esphome/components/ultrasonic/__init__.py index 6f14e10033..71a87b6ae5 100644 --- a/esphome/components/ultrasonic/__init__.py +++ b/esphome/components/ultrasonic/__init__.py @@ -1 +1 @@ -CODEOWNERS = ['@OttoWinter'] +CODEOWNERS = ["@OttoWinter"] diff --git a/esphome/components/ultrasonic/sensor.py b/esphome/components/ultrasonic/sensor.py index 889044fc7a..d5d8dec6f4 100644 --- a/esphome/components/ultrasonic/sensor.py +++ b/esphome/components/ultrasonic/sensor.py @@ -2,30 +2,45 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins from esphome.components import sensor -from esphome.const import CONF_ECHO_PIN, CONF_ID, CONF_TRIGGER_PIN, \ - CONF_TIMEOUT, DEVICE_CLASS_EMPTY, UNIT_METER, ICON_ARROW_EXPAND_VERTICAL +from esphome.const import ( + CONF_ECHO_PIN, + CONF_ID, + CONF_TRIGGER_PIN, + CONF_TIMEOUT, + DEVICE_CLASS_EMPTY, + UNIT_METER, + ICON_ARROW_EXPAND_VERTICAL, +) -CONF_PULSE_TIME = 'pulse_time' +CONF_PULSE_TIME = "pulse_time" -ultrasonic_ns = cg.esphome_ns.namespace('ultrasonic') -UltrasonicSensorComponent = ultrasonic_ns.class_('UltrasonicSensorComponent', - sensor.Sensor, cg.PollingComponent) +ultrasonic_ns = cg.esphome_ns.namespace("ultrasonic") +UltrasonicSensorComponent = ultrasonic_ns.class_( + "UltrasonicSensorComponent", sensor.Sensor, cg.PollingComponent +) -CONFIG_SCHEMA = sensor.sensor_schema( - UNIT_METER, ICON_ARROW_EXPAND_VERTICAL, 2, DEVICE_CLASS_EMPTY -).extend({ - cv.GenerateID(): cv.declare_id(UltrasonicSensorComponent), - cv.Required(CONF_TRIGGER_PIN): pins.gpio_output_pin_schema, - cv.Required(CONF_ECHO_PIN): pins.internal_gpio_input_pin_schema, - - cv.Optional(CONF_TIMEOUT, default='2m'): cv.distance, - cv.Optional(CONF_PULSE_TIME, default='10us'): cv.positive_time_period_microseconds, - - cv.Optional('timeout_meter'): cv.invalid("The timeout_meter option has been renamed " - "to 'timeout' in 1.12."), - cv.Optional('timeout_time'): cv.invalid("The timeout_time option has been removed. Please " - "use 'timeout' in 1.12."), -}).extend(cv.polling_component_schema('60s')) +CONFIG_SCHEMA = ( + sensor.sensor_schema(UNIT_METER, ICON_ARROW_EXPAND_VERTICAL, 2, DEVICE_CLASS_EMPTY) + .extend( + { + cv.GenerateID(): cv.declare_id(UltrasonicSensorComponent), + cv.Required(CONF_TRIGGER_PIN): pins.gpio_output_pin_schema, + cv.Required(CONF_ECHO_PIN): pins.internal_gpio_input_pin_schema, + cv.Optional(CONF_TIMEOUT, default="2m"): cv.distance, + cv.Optional( + CONF_PULSE_TIME, default="10us" + ): cv.positive_time_period_microseconds, + cv.Optional("timeout_meter"): cv.invalid( + "The timeout_meter option has been renamed " "to 'timeout' in 1.12." + ), + cv.Optional("timeout_time"): cv.invalid( + "The timeout_time option has been removed. Please " + "use 'timeout' in 1.12." + ), + } + ) + .extend(cv.polling_component_schema("60s")) +) def to_code(config): diff --git a/esphome/components/uptime/sensor.py b/esphome/components/uptime/sensor.py index 94d259d4b0..c2e35ddfef 100644 --- a/esphome/components/uptime/sensor.py +++ b/esphome/components/uptime/sensor.py @@ -3,12 +3,18 @@ import esphome.config_validation as cv from esphome.components import sensor from esphome.const import CONF_ID, DEVICE_CLASS_EMPTY, UNIT_SECOND, ICON_TIMER -uptime_ns = cg.esphome_ns.namespace('uptime') -UptimeSensor = uptime_ns.class_('UptimeSensor', sensor.Sensor, cg.PollingComponent) +uptime_ns = cg.esphome_ns.namespace("uptime") +UptimeSensor = uptime_ns.class_("UptimeSensor", sensor.Sensor, cg.PollingComponent) -CONFIG_SCHEMA = sensor.sensor_schema(UNIT_SECOND, ICON_TIMER, 0, DEVICE_CLASS_EMPTY).extend({ - cv.GenerateID(): cv.declare_id(UptimeSensor), -}).extend(cv.polling_component_schema('60s')) +CONFIG_SCHEMA = ( + sensor.sensor_schema(UNIT_SECOND, ICON_TIMER, 0, DEVICE_CLASS_EMPTY) + .extend( + { + cv.GenerateID(): cv.declare_id(UptimeSensor), + } + ) + .extend(cv.polling_component_schema("60s")) +) def to_code(config): diff --git a/esphome/components/version/__init__.py b/esphome/components/version/__init__.py index 63db7aee2e..f70ffa9520 100644 --- a/esphome/components/version/__init__.py +++ b/esphome/components/version/__init__.py @@ -1 +1 @@ -CODEOWNERS = ['@esphome/core'] +CODEOWNERS = ["@esphome/core"] diff --git a/esphome/components/version/text_sensor.py b/esphome/components/version/text_sensor.py index 01cf8ba30b..711800136c 100644 --- a/esphome/components/version/text_sensor.py +++ b/esphome/components/version/text_sensor.py @@ -3,14 +3,18 @@ import esphome.config_validation as cv from esphome.components import text_sensor from esphome.const import CONF_ID, CONF_ICON, ICON_NEW_BOX, CONF_HIDE_TIMESTAMP -version_ns = cg.esphome_ns.namespace('version') -VersionTextSensor = version_ns.class_('VersionTextSensor', text_sensor.TextSensor, cg.Component) +version_ns = cg.esphome_ns.namespace("version") +VersionTextSensor = version_ns.class_( + "VersionTextSensor", text_sensor.TextSensor, cg.Component +) -CONFIG_SCHEMA = text_sensor.TEXT_SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(VersionTextSensor), - cv.Optional(CONF_ICON, default=ICON_NEW_BOX): text_sensor.icon, - cv.Optional(CONF_HIDE_TIMESTAMP, default=False): cv.boolean -}).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = text_sensor.TEXT_SENSOR_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(VersionTextSensor), + cv.Optional(CONF_ICON, default=ICON_NEW_BOX): text_sensor.icon, + cv.Optional(CONF_HIDE_TIMESTAMP, default=False): cv.boolean, + } +).extend(cv.COMPONENT_SCHEMA) def to_code(config): diff --git a/esphome/components/vl53l0x/sensor.py b/esphome/components/vl53l0x/sensor.py index 8fc6a0d88d..309d4cf8b3 100644 --- a/esphome/components/vl53l0x/sensor.py +++ b/esphome/components/vl53l0x/sensor.py @@ -1,18 +1,26 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor -from esphome.const import (CONF_ID, DEVICE_CLASS_EMPTY, UNIT_METER, ICON_ARROW_EXPAND_VERTICAL, - CONF_ADDRESS, CONF_TIMEOUT, CONF_ENABLE_PIN) +from esphome.const import ( + CONF_ID, + DEVICE_CLASS_EMPTY, + UNIT_METER, + ICON_ARROW_EXPAND_VERTICAL, + CONF_ADDRESS, + CONF_TIMEOUT, + CONF_ENABLE_PIN, +) from esphome import pins -DEPENDENCIES = ['i2c'] +DEPENDENCIES = ["i2c"] -vl53l0x_ns = cg.esphome_ns.namespace('vl53l0x') -VL53L0XSensor = vl53l0x_ns.class_('VL53L0XSensor', sensor.Sensor, cg.PollingComponent, - i2c.I2CDevice) +vl53l0x_ns = cg.esphome_ns.namespace("vl53l0x") +VL53L0XSensor = vl53l0x_ns.class_( + "VL53L0XSensor", sensor.Sensor, cg.PollingComponent, i2c.I2CDevice +) -CONF_SIGNAL_RATE_LIMIT = 'signal_rate_limit' -CONF_LONG_RANGE = 'long_range' +CONF_SIGNAL_RATE_LIMIT = "signal_rate_limit" +CONF_LONG_RANGE = "long_range" def check_keys(obj): @@ -32,14 +40,22 @@ def check_timeout(value): CONFIG_SCHEMA = cv.All( - sensor.sensor_schema(UNIT_METER, ICON_ARROW_EXPAND_VERTICAL, 2, DEVICE_CLASS_EMPTY).extend({ - cv.GenerateID(): cv.declare_id(VL53L0XSensor), - cv.Optional(CONF_SIGNAL_RATE_LIMIT, default=0.25): cv.float_range( - min=0.0, max=512.0, min_included=False, max_included=False), - cv.Optional(CONF_LONG_RANGE, default=False): cv.boolean, - cv.Optional(CONF_TIMEOUT, default='10ms'): check_timeout, - cv.Optional(CONF_ENABLE_PIN): pins.gpio_output_pin_schema, - }).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x29)), check_keys) + sensor.sensor_schema(UNIT_METER, ICON_ARROW_EXPAND_VERTICAL, 2, DEVICE_CLASS_EMPTY) + .extend( + { + cv.GenerateID(): cv.declare_id(VL53L0XSensor), + cv.Optional(CONF_SIGNAL_RATE_LIMIT, default=0.25): cv.float_range( + min=0.0, max=512.0, min_included=False, max_included=False + ), + cv.Optional(CONF_LONG_RANGE, default=False): cv.boolean, + cv.Optional(CONF_TIMEOUT, default="10ms"): check_timeout, + cv.Optional(CONF_ENABLE_PIN): pins.gpio_output_pin_schema, + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x29)), + check_keys, +) def to_code(config): diff --git a/esphome/components/voltage_sampler/__init__.py b/esphome/components/voltage_sampler/__init__.py index 64161205d8..e60918096e 100644 --- a/esphome/components/voltage_sampler/__init__.py +++ b/esphome/components/voltage_sampler/__init__.py @@ -1,4 +1,4 @@ import esphome.codegen as cg -voltage_sampler_ns = cg.esphome_ns.namespace('voltage_sampler') -VoltageSampler = voltage_sampler_ns.class_('VoltageSampler') +voltage_sampler_ns = cg.esphome_ns.namespace("voltage_sampler") +VoltageSampler = voltage_sampler_ns.class_("VoltageSampler") diff --git a/esphome/components/waveshare_epaper/display.py b/esphome/components/waveshare_epaper/display.py index fcbbc0a500..430022e542 100644 --- a/esphome/components/waveshare_epaper/display.py +++ b/esphome/components/waveshare_epaper/display.py @@ -2,68 +2,99 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins from esphome.components import display, spi -from esphome.const import CONF_BUSY_PIN, CONF_DC_PIN, CONF_FULL_UPDATE_EVERY, \ - CONF_ID, CONF_LAMBDA, CONF_MODEL, CONF_PAGES, CONF_RESET_PIN +from esphome.const import ( + CONF_BUSY_PIN, + CONF_DC_PIN, + CONF_FULL_UPDATE_EVERY, + CONF_ID, + CONF_LAMBDA, + CONF_MODEL, + CONF_PAGES, + CONF_RESET_PIN, +) -DEPENDENCIES = ['spi'] +DEPENDENCIES = ["spi"] -waveshare_epaper_ns = cg.esphome_ns.namespace('waveshare_epaper') -WaveshareEPaper = waveshare_epaper_ns.class_('WaveshareEPaper', cg.PollingComponent, spi.SPIDevice, - display.DisplayBuffer) -WaveshareEPaperTypeA = waveshare_epaper_ns.class_('WaveshareEPaperTypeA', WaveshareEPaper) -WaveshareEPaper2P7In = waveshare_epaper_ns.class_('WaveshareEPaper2P7In', WaveshareEPaper) -WaveshareEPaper2P9InB = waveshare_epaper_ns.class_('WaveshareEPaper2P9InB', WaveshareEPaper) -WaveshareEPaper4P2In = waveshare_epaper_ns.class_('WaveshareEPaper4P2In', WaveshareEPaper) -WaveshareEPaper5P8In = waveshare_epaper_ns.class_('WaveshareEPaper5P8In', WaveshareEPaper) -WaveshareEPaper7P5In = waveshare_epaper_ns.class_('WaveshareEPaper7P5In', WaveshareEPaper) -WaveshareEPaper7P5InV2 = waveshare_epaper_ns.class_('WaveshareEPaper7P5InV2', WaveshareEPaper) +waveshare_epaper_ns = cg.esphome_ns.namespace("waveshare_epaper") +WaveshareEPaper = waveshare_epaper_ns.class_( + "WaveshareEPaper", cg.PollingComponent, spi.SPIDevice, display.DisplayBuffer +) +WaveshareEPaperTypeA = waveshare_epaper_ns.class_( + "WaveshareEPaperTypeA", WaveshareEPaper +) +WaveshareEPaper2P7In = waveshare_epaper_ns.class_( + "WaveshareEPaper2P7In", WaveshareEPaper +) +WaveshareEPaper2P9InB = waveshare_epaper_ns.class_( + "WaveshareEPaper2P9InB", WaveshareEPaper +) +WaveshareEPaper4P2In = waveshare_epaper_ns.class_( + "WaveshareEPaper4P2In", WaveshareEPaper +) +WaveshareEPaper5P8In = waveshare_epaper_ns.class_( + "WaveshareEPaper5P8In", WaveshareEPaper +) +WaveshareEPaper7P5In = waveshare_epaper_ns.class_( + "WaveshareEPaper7P5In", WaveshareEPaper +) +WaveshareEPaper7P5InV2 = waveshare_epaper_ns.class_( + "WaveshareEPaper7P5InV2", WaveshareEPaper +) -WaveshareEPaperTypeAModel = waveshare_epaper_ns.enum('WaveshareEPaperTypeAModel') -WaveshareEPaperTypeBModel = waveshare_epaper_ns.enum('WaveshareEPaperTypeBModel') +WaveshareEPaperTypeAModel = waveshare_epaper_ns.enum("WaveshareEPaperTypeAModel") +WaveshareEPaperTypeBModel = waveshare_epaper_ns.enum("WaveshareEPaperTypeBModel") MODELS = { - '1.54in': ('a', WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_1_54_IN), - '2.13in': ('a', WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_2_13_IN), - '2.13in-ttgo': ('a', WaveshareEPaperTypeAModel.TTGO_EPAPER_2_13_IN), - '2.13in-ttgo-b73': ('a', WaveshareEPaperTypeAModel.TTGO_EPAPER_2_13_IN_B73), - '2.90in': ('a', WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_2_9_IN), - '2.90inv2': ('a', WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_2_9_IN_V2), - '2.70in': ('b', WaveshareEPaper2P7In), - '2.90in-b': ('b', WaveshareEPaper2P9InB), - '4.20in': ('b', WaveshareEPaper4P2In), - '5.83in': ('b', WaveshareEPaper5P8In), - '7.50in': ('b', WaveshareEPaper7P5In), - '7.50inv2': ('b', WaveshareEPaper7P5InV2), + "1.54in": ("a", WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_1_54_IN), + "2.13in": ("a", WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_2_13_IN), + "2.13in-ttgo": ("a", WaveshareEPaperTypeAModel.TTGO_EPAPER_2_13_IN), + "2.13in-ttgo-b73": ("a", WaveshareEPaperTypeAModel.TTGO_EPAPER_2_13_IN_B73), + "2.90in": ("a", WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_2_9_IN), + "2.90inv2": ("a", WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_2_9_IN_V2), + "2.70in": ("b", WaveshareEPaper2P7In), + "2.90in-b": ("b", WaveshareEPaper2P9InB), + "4.20in": ("b", WaveshareEPaper4P2In), + "5.83in": ("b", WaveshareEPaper5P8In), + "7.50in": ("b", WaveshareEPaper7P5In), + "7.50inv2": ("b", WaveshareEPaper7P5InV2), } def validate_full_update_every_only_type_a(value): if CONF_FULL_UPDATE_EVERY not in value: return value - if MODELS[value[CONF_MODEL]][0] != 'a': - raise cv.Invalid("The 'full_update_every' option is only available for models " - "'1.54in', '2.13in', '2.90in', and '2.90inV2'.") + if MODELS[value[CONF_MODEL]][0] != "a": + raise cv.Invalid( + "The 'full_update_every' option is only available for models " + "'1.54in', '2.13in', '2.90in', and '2.90inV2'." + ) return value -CONFIG_SCHEMA = cv.All(display.FULL_DISPLAY_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(WaveshareEPaper), - cv.Required(CONF_DC_PIN): pins.gpio_output_pin_schema, - cv.Required(CONF_MODEL): cv.one_of(*MODELS, lower=True), - cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, - cv.Optional(CONF_BUSY_PIN): pins.gpio_input_pin_schema, - cv.Optional(CONF_FULL_UPDATE_EVERY): cv.uint32_t, -}).extend(cv.polling_component_schema('1s')).extend(spi.spi_device_schema()), - validate_full_update_every_only_type_a, - cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA)) +CONFIG_SCHEMA = cv.All( + display.FULL_DISPLAY_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(WaveshareEPaper), + cv.Required(CONF_DC_PIN): pins.gpio_output_pin_schema, + cv.Required(CONF_MODEL): cv.one_of(*MODELS, lower=True), + cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, + cv.Optional(CONF_BUSY_PIN): pins.gpio_input_pin_schema, + cv.Optional(CONF_FULL_UPDATE_EVERY): cv.uint32_t, + } + ) + .extend(cv.polling_component_schema("1s")) + .extend(spi.spi_device_schema()), + validate_full_update_every_only_type_a, + cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA), +) def to_code(config): model_type, model = MODELS[config[CONF_MODEL]] - if model_type == 'a': + if model_type == "a": rhs = WaveshareEPaperTypeA.new(model) var = cg.Pvariable(config[CONF_ID], rhs, WaveshareEPaperTypeA) - elif model_type == 'b': + elif model_type == "b": rhs = model.new() var = cg.Pvariable(config[CONF_ID], rhs, model) else: @@ -77,8 +108,9 @@ def to_code(config): cg.add(var.set_dc_pin(dc)) if CONF_LAMBDA in config: - lambda_ = yield cg.process_lambda(config[CONF_LAMBDA], [(display.DisplayBufferRef, 'it')], - return_type=cg.void) + lambda_ = yield cg.process_lambda( + config[CONF_LAMBDA], [(display.DisplayBufferRef, "it")], return_type=cg.void + ) cg.add(var.set_writer(lambda_)) if CONF_RESET_PIN in config: reset = yield cg.gpio_pin_expression(config[CONF_RESET_PIN]) diff --git a/esphome/components/web_server/__init__.py b/esphome/components/web_server/__init__.py index 069b0a3895..d04f2077f4 100644 --- a/esphome/components/web_server/__init__.py +++ b/esphome/components/web_server/__init__.py @@ -3,29 +3,46 @@ import esphome.config_validation as cv from esphome.components import web_server_base from esphome.components.web_server_base import CONF_WEB_SERVER_BASE_ID from esphome.const import ( - CONF_CSS_INCLUDE, CONF_CSS_URL, CONF_ID, CONF_JS_INCLUDE, CONF_JS_URL, CONF_PORT, - CONF_AUTH, CONF_USERNAME, CONF_PASSWORD) + CONF_CSS_INCLUDE, + CONF_CSS_URL, + CONF_ID, + CONF_JS_INCLUDE, + CONF_JS_URL, + CONF_PORT, + CONF_AUTH, + CONF_USERNAME, + CONF_PASSWORD, +) from esphome.core import coroutine_with_priority -AUTO_LOAD = ['json', 'web_server_base'] +AUTO_LOAD = ["json", "web_server_base"] -web_server_ns = cg.esphome_ns.namespace('web_server') -WebServer = web_server_ns.class_('WebServer', cg.Component, cg.Controller) +web_server_ns = cg.esphome_ns.namespace("web_server") +WebServer = web_server_ns.class_("WebServer", cg.Component, cg.Controller) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(WebServer), - cv.Optional(CONF_PORT, default=80): cv.port, - cv.Optional(CONF_CSS_URL, default="https://esphome.io/_static/webserver-v1.min.css"): cv.string, - cv.Optional(CONF_CSS_INCLUDE): cv.file_, - cv.Optional(CONF_JS_URL, default="https://esphome.io/_static/webserver-v1.min.js"): cv.string, - cv.Optional(CONF_JS_INCLUDE): cv.file_, - cv.Optional(CONF_AUTH): cv.Schema({ - cv.Required(CONF_USERNAME): cv.string_strict, - cv.Required(CONF_PASSWORD): cv.string_strict, - }), - - cv.GenerateID(CONF_WEB_SERVER_BASE_ID): cv.use_id(web_server_base.WebServerBase), -}).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(WebServer), + cv.Optional(CONF_PORT, default=80): cv.port, + cv.Optional( + CONF_CSS_URL, default="https://esphome.io/_static/webserver-v1.min.css" + ): cv.string, + cv.Optional(CONF_CSS_INCLUDE): cv.file_, + cv.Optional( + CONF_JS_URL, default="https://esphome.io/_static/webserver-v1.min.js" + ): cv.string, + cv.Optional(CONF_JS_INCLUDE): cv.file_, + cv.Optional(CONF_AUTH): cv.Schema( + { + cv.Required(CONF_USERNAME): cv.string_strict, + cv.Required(CONF_PASSWORD): cv.string_strict, + } + ), + cv.GenerateID(CONF_WEB_SERVER_BASE_ID): cv.use_id( + web_server_base.WebServerBase + ), + } +).extend(cv.COMPONENT_SCHEMA) @coroutine_with_priority(40.0) @@ -36,17 +53,17 @@ def to_code(config): yield cg.register_component(var, config) cg.add(paren.set_port(config[CONF_PORT])) - cg.add_define('WEBSERVER_PORT', config[CONF_PORT]) + cg.add_define("WEBSERVER_PORT", config[CONF_PORT]) cg.add(var.set_css_url(config[CONF_CSS_URL])) cg.add(var.set_js_url(config[CONF_JS_URL])) if CONF_AUTH in config: cg.add(var.set_username(config[CONF_AUTH][CONF_USERNAME])) cg.add(var.set_password(config[CONF_AUTH][CONF_PASSWORD])) if CONF_CSS_INCLUDE in config: - cg.add_define('WEBSERVER_CSS_INCLUDE') + cg.add_define("WEBSERVER_CSS_INCLUDE") with open(config[CONF_CSS_INCLUDE], "r") as myfile: cg.add(var.set_css_include(myfile.read())) if CONF_JS_INCLUDE in config: - cg.add_define('WEBSERVER_JS_INCLUDE') + cg.add_define("WEBSERVER_JS_INCLUDE") with open(config[CONF_JS_INCLUDE], "r") as myfile: cg.add(var.set_js_include(myfile.read())) diff --git a/esphome/components/web_server_base/__init__.py b/esphome/components/web_server_base/__init__.py index 05f4a4a4c6..09f5dacd7c 100644 --- a/esphome/components/web_server_base/__init__.py +++ b/esphome/components/web_server_base/__init__.py @@ -3,17 +3,19 @@ import esphome.codegen as cg from esphome.const import CONF_ID from esphome.core import coroutine_with_priority, CORE -CODEOWNERS = ['@OttoWinter'] -DEPENDENCIES = ['network'] -AUTO_LOAD = ['async_tcp'] +CODEOWNERS = ["@OttoWinter"] +DEPENDENCIES = ["network"] +AUTO_LOAD = ["async_tcp"] -web_server_base_ns = cg.esphome_ns.namespace('web_server_base') -WebServerBase = web_server_base_ns.class_('WebServerBase', cg.Component) +web_server_base_ns = cg.esphome_ns.namespace("web_server_base") +WebServerBase = web_server_base_ns.class_("WebServerBase", cg.Component) -CONF_WEB_SERVER_BASE_ID = 'web_server_base_id' -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(WebServerBase), -}) +CONF_WEB_SERVER_BASE_ID = "web_server_base_id" +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(WebServerBase), + } +) @coroutine_with_priority(65.0) @@ -22,6 +24,6 @@ def to_code(config): yield cg.register_component(var, config) if CORE.is_esp32: - cg.add_library('FS', None) + cg.add_library("FS", None) # https://github.com/OttoWinter/ESPAsyncWebServer/blob/master/library.json - cg.add_library('ESPAsyncWebServer-esphome', '1.2.7') + cg.add_library("ESPAsyncWebServer-esphome", "1.2.7") diff --git a/esphome/components/whirlpool/climate.py b/esphome/components/whirlpool/climate.py index 1fd62b411a..d6d9f3e111 100644 --- a/esphome/components/whirlpool/climate.py +++ b/esphome/components/whirlpool/climate.py @@ -3,22 +3,24 @@ import esphome.config_validation as cv from esphome.components import climate_ir from esphome.const import CONF_ID, CONF_MODEL -AUTO_LOAD = ['climate_ir'] -CODEOWNERS = ['@glmnet'] +AUTO_LOAD = ["climate_ir"] +CODEOWNERS = ["@glmnet"] -whirlpool_ns = cg.esphome_ns.namespace('whirlpool') -WhirlpoolClimate = whirlpool_ns.class_('WhirlpoolClimate', climate_ir.ClimateIR) +whirlpool_ns = cg.esphome_ns.namespace("whirlpool") +WhirlpoolClimate = whirlpool_ns.class_("WhirlpoolClimate", climate_ir.ClimateIR) -Model = whirlpool_ns.enum('Model') +Model = whirlpool_ns.enum("Model") MODELS = { - 'DG11J1-3A': Model.MODEL_DG11J1_3A, - 'DG11J1-91': Model.MODEL_DG11J1_91, + "DG11J1-3A": Model.MODEL_DG11J1_3A, + "DG11J1-91": Model.MODEL_DG11J1_91, } -CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(WhirlpoolClimate), - cv.Optional(CONF_MODEL, default='DG11J1-3A'): cv.enum(MODELS, upper=True) -}) +CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(WhirlpoolClimate), + cv.Optional(CONF_MODEL, default="DG11J1-3A"): cv.enum(MODELS, upper=True), + } +) def to_code(config): diff --git a/esphome/components/wifi/__init__.py b/esphome/components/wifi/__init__.py index 4fe6929d75..f5b7340ad6 100644 --- a/esphome/components/wifi/__init__.py +++ b/esphome/components/wifi/__init__.py @@ -2,31 +2,54 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import automation from esphome.automation import Condition -from esphome.const import CONF_AP, CONF_BSSID, CONF_CHANNEL, CONF_DNS1, CONF_DNS2, CONF_DOMAIN, \ - CONF_FAST_CONNECT, CONF_GATEWAY, CONF_HIDDEN, CONF_ID, CONF_MANUAL_IP, CONF_NETWORKS, \ - CONF_PASSWORD, CONF_POWER_SAVE_MODE, CONF_REBOOT_TIMEOUT, CONF_SSID, CONF_STATIC_IP, \ - CONF_SUBNET, CONF_USE_ADDRESS, CONF_PRIORITY, CONF_IDENTITY, CONF_CERTIFICATE_AUTHORITY, \ - CONF_CERTIFICATE, CONF_KEY, CONF_USERNAME, CONF_EAP +from esphome.const import ( + CONF_AP, + CONF_BSSID, + CONF_CHANNEL, + CONF_DNS1, + CONF_DNS2, + CONF_DOMAIN, + CONF_FAST_CONNECT, + CONF_GATEWAY, + CONF_HIDDEN, + CONF_ID, + CONF_MANUAL_IP, + CONF_NETWORKS, + CONF_PASSWORD, + CONF_POWER_SAVE_MODE, + CONF_REBOOT_TIMEOUT, + CONF_SSID, + CONF_STATIC_IP, + CONF_SUBNET, + CONF_USE_ADDRESS, + CONF_PRIORITY, + CONF_IDENTITY, + CONF_CERTIFICATE_AUTHORITY, + CONF_CERTIFICATE, + CONF_KEY, + CONF_USERNAME, + CONF_EAP, +) from esphome.core import CORE, HexInt, coroutine_with_priority from . import wpa2_eap -AUTO_LOAD = ['network'] +AUTO_LOAD = ["network"] -wifi_ns = cg.esphome_ns.namespace('wifi') -EAPAuth = wifi_ns.struct('EAPAuth') -IPAddress = cg.global_ns.class_('IPAddress') -ManualIP = wifi_ns.struct('ManualIP') -WiFiComponent = wifi_ns.class_('WiFiComponent', cg.Component) -WiFiAP = wifi_ns.struct('WiFiAP') +wifi_ns = cg.esphome_ns.namespace("wifi") +EAPAuth = wifi_ns.struct("EAPAuth") +IPAddress = cg.global_ns.class_("IPAddress") +ManualIP = wifi_ns.struct("ManualIP") +WiFiComponent = wifi_ns.class_("WiFiComponent", cg.Component) +WiFiAP = wifi_ns.struct("WiFiAP") -WiFiPowerSaveMode = wifi_ns.enum('WiFiPowerSaveMode') +WiFiPowerSaveMode = wifi_ns.enum("WiFiPowerSaveMode") WIFI_POWER_SAVE_MODES = { - 'NONE': WiFiPowerSaveMode.WIFI_POWER_SAVE_NONE, - 'LIGHT': WiFiPowerSaveMode.WIFI_POWER_SAVE_LIGHT, - 'HIGH': WiFiPowerSaveMode.WIFI_POWER_SAVE_HIGH, + "NONE": WiFiPowerSaveMode.WIFI_POWER_SAVE_NONE, + "LIGHT": WiFiPowerSaveMode.WIFI_POWER_SAVE_LIGHT, + "HIGH": WiFiPowerSaveMode.WIFI_POWER_SAVE_HIGH, } -WiFiConnectedCondition = wifi_ns.class_('WiFiConnectedCondition', Condition) +WiFiConnectedCondition = wifi_ns.class_("WiFiConnectedCondition", Condition) def validate_password(value): @@ -49,47 +72,67 @@ def validate_channel(value): return value -AP_MANUAL_IP_SCHEMA = cv.Schema({ - cv.Required(CONF_STATIC_IP): cv.ipv4, - cv.Required(CONF_GATEWAY): cv.ipv4, - cv.Required(CONF_SUBNET): cv.ipv4, -}) +AP_MANUAL_IP_SCHEMA = cv.Schema( + { + cv.Required(CONF_STATIC_IP): cv.ipv4, + cv.Required(CONF_GATEWAY): cv.ipv4, + cv.Required(CONF_SUBNET): cv.ipv4, + } +) -STA_MANUAL_IP_SCHEMA = AP_MANUAL_IP_SCHEMA.extend({ - cv.Optional(CONF_DNS1, default="0.0.0.0"): cv.ipv4, - cv.Optional(CONF_DNS2, default="0.0.0.0"): cv.ipv4, -}) +STA_MANUAL_IP_SCHEMA = AP_MANUAL_IP_SCHEMA.extend( + { + cv.Optional(CONF_DNS1, default="0.0.0.0"): cv.ipv4, + cv.Optional(CONF_DNS2, default="0.0.0.0"): cv.ipv4, + } +) -EAP_AUTH_SCHEMA = cv.All(cv.Schema({ - cv.Optional(CONF_IDENTITY): cv.string_strict, - cv.Optional(CONF_USERNAME): cv.string_strict, - cv.Optional(CONF_PASSWORD): cv.string_strict, - cv.Optional(CONF_CERTIFICATE_AUTHORITY): wpa2_eap.validate_certificate, - cv.Inclusive(CONF_CERTIFICATE, 'certificate_and_key'): wpa2_eap.validate_certificate, - # Only validate as file first because we need the password to load it - # Actual validation happens in validate_eap. - cv.Inclusive(CONF_KEY, 'certificate_and_key'): cv.file_, -}), wpa2_eap.validate_eap, cv.has_at_least_one_key(CONF_IDENTITY, CONF_CERTIFICATE)) +EAP_AUTH_SCHEMA = cv.All( + cv.Schema( + { + cv.Optional(CONF_IDENTITY): cv.string_strict, + cv.Optional(CONF_USERNAME): cv.string_strict, + cv.Optional(CONF_PASSWORD): cv.string_strict, + cv.Optional(CONF_CERTIFICATE_AUTHORITY): wpa2_eap.validate_certificate, + cv.Inclusive( + CONF_CERTIFICATE, "certificate_and_key" + ): wpa2_eap.validate_certificate, + # Only validate as file first because we need the password to load it + # Actual validation happens in validate_eap. + cv.Inclusive(CONF_KEY, "certificate_and_key"): cv.file_, + } + ), + wpa2_eap.validate_eap, + cv.has_at_least_one_key(CONF_IDENTITY, CONF_CERTIFICATE), +) -WIFI_NETWORK_BASE = cv.Schema({ - cv.GenerateID(): cv.declare_id(WiFiAP), - cv.Optional(CONF_SSID): cv.ssid, - cv.Optional(CONF_PASSWORD): validate_password, - cv.Optional(CONF_CHANNEL): validate_channel, - cv.Optional(CONF_MANUAL_IP): STA_MANUAL_IP_SCHEMA, -}) +WIFI_NETWORK_BASE = cv.Schema( + { + cv.GenerateID(): cv.declare_id(WiFiAP), + cv.Optional(CONF_SSID): cv.ssid, + cv.Optional(CONF_PASSWORD): validate_password, + cv.Optional(CONF_CHANNEL): validate_channel, + cv.Optional(CONF_MANUAL_IP): STA_MANUAL_IP_SCHEMA, + } +) -CONF_AP_TIMEOUT = 'ap_timeout' -WIFI_NETWORK_AP = WIFI_NETWORK_BASE.extend({ - cv.Optional(CONF_AP_TIMEOUT, default='1min'): cv.positive_time_period_milliseconds, -}) +CONF_AP_TIMEOUT = "ap_timeout" +WIFI_NETWORK_AP = WIFI_NETWORK_BASE.extend( + { + cv.Optional( + CONF_AP_TIMEOUT, default="1min" + ): cv.positive_time_period_milliseconds, + } +) -WIFI_NETWORK_STA = WIFI_NETWORK_BASE.extend({ - cv.Optional(CONF_BSSID): cv.mac_address, - cv.Optional(CONF_HIDDEN): cv.boolean, - cv.Optional(CONF_PRIORITY, default=0.0): cv.float_, - cv.Optional(CONF_EAP): EAP_AUTH_SCHEMA, -}) +WIFI_NETWORK_STA = WIFI_NETWORK_BASE.extend( + { + cv.Optional(CONF_BSSID): cv.mac_address, + cv.Optional(CONF_HIDDEN): cv.boolean, + cv.Optional(CONF_PRIORITY, default=0.0): cv.float_, + cv.Optional(CONF_EAP): EAP_AUTH_SCHEMA, + } +) def validate(config): @@ -105,13 +148,16 @@ def validate(config): if CONF_EAP in config: network[CONF_EAP] = config.pop(CONF_EAP) if CONF_NETWORKS in config: - raise cv.Invalid("You cannot use the 'ssid:' option together with 'networks:'. Please " - "copy your network into the 'networks:' key") + raise cv.Invalid( + "You cannot use the 'ssid:' option together with 'networks:'. Please " + "copy your network into the 'networks:' key" + ) config[CONF_NETWORKS] = cv.ensure_list(WIFI_NETWORK_STA)(network) if (CONF_NETWORKS not in config) and (CONF_AP not in config): - raise cv.Invalid("Please specify at least an SSID or an Access Point " - "to create.") + raise cv.Invalid( + "Please specify at least an SSID or an Access Point " "to create." + ) if config.get(CONF_FAST_CONNECT, False): networks = config.get(CONF_NETWORKS, []) @@ -130,28 +176,36 @@ def validate(config): return config -CONF_OUTPUT_POWER = 'output_power' -CONFIG_SCHEMA = cv.All(cv.Schema({ - cv.GenerateID(): cv.declare_id(WiFiComponent), - cv.Optional(CONF_NETWORKS): cv.ensure_list(WIFI_NETWORK_STA), - - cv.Optional(CONF_SSID): cv.ssid, - cv.Optional(CONF_PASSWORD): validate_password, - cv.Optional(CONF_MANUAL_IP): STA_MANUAL_IP_SCHEMA, - cv.Optional(CONF_EAP): EAP_AUTH_SCHEMA, - - cv.Optional(CONF_AP): WIFI_NETWORK_AP, - cv.Optional(CONF_DOMAIN, default='.local'): cv.domain_name, - cv.Optional(CONF_REBOOT_TIMEOUT, default='15min'): cv.positive_time_period_milliseconds, - cv.SplitDefault(CONF_POWER_SAVE_MODE, esp8266='none', esp32='light'): - cv.enum(WIFI_POWER_SAVE_MODES, upper=True), - cv.Optional(CONF_FAST_CONNECT, default=False): cv.boolean, - cv.Optional(CONF_USE_ADDRESS): cv.string_strict, - cv.SplitDefault(CONF_OUTPUT_POWER, esp8266=20.0): cv.All( - cv.decibel, cv.float_range(min=10.0, max=20.5)), - - cv.Optional('hostname'): cv.invalid("The hostname option has been removed in 1.11.0"), -}), validate) +CONF_OUTPUT_POWER = "output_power" +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(WiFiComponent), + cv.Optional(CONF_NETWORKS): cv.ensure_list(WIFI_NETWORK_STA), + cv.Optional(CONF_SSID): cv.ssid, + cv.Optional(CONF_PASSWORD): validate_password, + cv.Optional(CONF_MANUAL_IP): STA_MANUAL_IP_SCHEMA, + cv.Optional(CONF_EAP): EAP_AUTH_SCHEMA, + cv.Optional(CONF_AP): WIFI_NETWORK_AP, + cv.Optional(CONF_DOMAIN, default=".local"): cv.domain_name, + cv.Optional( + CONF_REBOOT_TIMEOUT, default="15min" + ): cv.positive_time_period_milliseconds, + cv.SplitDefault( + CONF_POWER_SAVE_MODE, esp8266="none", esp32="light" + ): cv.enum(WIFI_POWER_SAVE_MODES, upper=True), + cv.Optional(CONF_FAST_CONNECT, default=False): cv.boolean, + cv.Optional(CONF_USE_ADDRESS): cv.string_strict, + cv.SplitDefault(CONF_OUTPUT_POWER, esp8266=20.0): cv.All( + cv.decibel, cv.float_range(min=10.0, max=20.5) + ), + cv.Optional("hostname"): cv.invalid( + "The hostname option has been removed in 1.11.0" + ), + } + ), + validate, +) def eap_auth(config): @@ -168,12 +222,12 @@ def eap_auth(config): key = wpa2_eap.read_relative_config_path(config[CONF_KEY]) return cg.StructInitializer( EAPAuth, - ('identity', config.get(CONF_IDENTITY, "")), - ('username', config.get(CONF_USERNAME, "")), - ('password', config.get(CONF_PASSWORD, "")), - ('ca_cert', ca_cert), - ('client_cert', client_cert), - ('client_key', key), + ("identity", config.get(CONF_IDENTITY, "")), + ("username", config.get(CONF_USERNAME, "")), + ("password", config.get(CONF_PASSWORD, "")), + ("ca_cert", ca_cert), + ("client_cert", client_cert), + ("client_key", key), ) @@ -188,11 +242,11 @@ def manual_ip(config): return None return cg.StructInitializer( ManualIP, - ('static_ip', safe_ip(config[CONF_STATIC_IP])), - ('gateway', safe_ip(config[CONF_GATEWAY])), - ('subnet', safe_ip(config[CONF_SUBNET])), - ('dns1', safe_ip(config.get(CONF_DNS1))), - ('dns2', safe_ip(config.get(CONF_DNS2))), + ("static_ip", safe_ip(config[CONF_STATIC_IP])), + ("gateway", safe_ip(config[CONF_GATEWAY])), + ("subnet", safe_ip(config[CONF_SUBNET])), + ("dns1", safe_ip(config.get(CONF_DNS1))), + ("dns2", safe_ip(config.get(CONF_DNS2))), ) @@ -204,7 +258,7 @@ def wifi_network(config, static_ip): cg.add(ap.set_password(config[CONF_PASSWORD])) if CONF_EAP in config: cg.add(ap.set_eap(eap_auth(config[CONF_EAP]))) - cg.add_define('ESPHOME_WIFI_WPA2_EAP') + cg.add_define("ESPHOME_WIFI_WPA2_EAP") if CONF_BSSID in config: cg.add(ap.set_bssid([HexInt(i) for i in config[CONF_BSSID].parts])) if CONF_HIDDEN in config: @@ -240,14 +294,14 @@ def to_code(config): cg.add(var.set_output_power(config[CONF_OUTPUT_POWER])) if CORE.is_esp8266: - cg.add_library('ESP8266WiFi', None) + cg.add_library("ESP8266WiFi", None) - cg.add_define('USE_WIFI') + cg.add_define("USE_WIFI") # Register at end for OTA safe mode yield cg.register_component(var, config) -@automation.register_condition('wifi.connected', WiFiConnectedCondition, cv.Schema({})) +@automation.register_condition("wifi.connected", WiFiConnectedCondition, cv.Schema({})) def wifi_connected_to_code(config, condition_id, template_arg, args): yield cg.new_Pvariable(condition_id, template_arg) diff --git a/esphome/components/wifi/wpa2_eap.py b/esphome/components/wifi/wpa2_eap.py index 54195c852b..071737ccd7 100644 --- a/esphome/components/wifi/wpa2_eap.py +++ b/esphome/components/wifi/wpa2_eap.py @@ -8,8 +8,13 @@ from pathlib import Path from esphome.core import CORE import esphome.config_validation as cv -from esphome.const import CONF_USERNAME, CONF_IDENTITY, CONF_PASSWORD, CONF_CERTIFICATE, \ - CONF_KEY +from esphome.const import ( + CONF_USERNAME, + CONF_IDENTITY, + CONF_PASSWORD, + CONF_CERTIFICATE, + CONF_KEY, +) _LOGGER = logging.getLogger(__name__) @@ -19,12 +24,16 @@ def validate_cryptography_installed(): try: import cryptography except ImportError as err: - raise cv.Invalid("This settings requires the cryptography python package. " - "Please install it with `pip install cryptography`") from err + raise cv.Invalid( + "This settings requires the cryptography python package. " + "Please install it with `pip install cryptography`" + ) from err - if cryptography.__version__[0] < '2': - raise cv.Invalid("Please update your python cryptography installation to least 2.x " - "(pip install -U cryptography)") + if cryptography.__version__[0] < "2": + raise cv.Invalid( + "Please update your python cryptography installation to least 2.x " + "(pip install -U cryptography)" + ) def wrapped_load_pem_x509_certificate(value): @@ -33,7 +42,7 @@ def wrapped_load_pem_x509_certificate(value): from cryptography import x509 from cryptography.hazmat.backends import default_backend - return x509.load_pem_x509_certificate(value.encode('UTF-8'), default_backend()) + return x509.load_pem_x509_certificate(value.encode("UTF-8"), default_backend()) def wrapped_load_pem_private_key(value, password): @@ -44,7 +53,7 @@ def wrapped_load_pem_private_key(value, password): if password: password = password.encode("UTF-8") - return load_pem_private_key(value.encode('UTF-8'), password, default_backend()) + return load_pem_private_key(value.encode("UTF-8"), password, default_backend()) def read_relative_config_path(value): @@ -72,7 +81,9 @@ def _validate_load_private_key(key, cert_pw): contents = read_relative_config_path(key) return wrapped_load_pem_private_key(contents, cert_pw) except ValueError as e: - raise cv.Invalid(f"There was an error with the EAP 'password:' provided for 'key' {e}") + raise cv.Invalid( + f"There was an error with the EAP 'password:' provided for 'key' {e}" + ) except TypeError as e: raise cv.Invalid(f"There was an error with the EAP 'key:' provided: {e}") @@ -95,10 +106,12 @@ def _check_private_key_cert_match(key, cert): # pylint: disable=no-name-in-module from cryptography.hazmat.primitives.asymmetric import ed448, ed25519 - private_key_types.update({ - ed448.Ed448PrivateKey: check_match_b, - ed25519.Ed25519PrivateKey: check_match_b, - }) + private_key_types.update( + { + ed448.Ed448PrivateKey: check_match_b, + ed25519.Ed25519PrivateKey: check_match_b, + } + ) except ImportError: # ed448, ed25519 not supported pass @@ -107,7 +120,7 @@ def _check_private_key_cert_match(key, cert): if key_type is None: _LOGGER.warning( "Unrecognised EAP 'certificate:' 'key:' pair format: %s. Proceed with caution!", - type(key) + type(key), ) elif not private_key_types[key_type](): raise cv.Invalid("The provided EAP 'key' is not valid for the 'certificate'.") @@ -120,8 +133,10 @@ def validate_eap(value): value = value.copy() value[CONF_IDENTITY] = value[CONF_USERNAME] if CONF_PASSWORD not in value: - raise cv.Invalid("You cannot use the EAP 'username:' option without a 'password:'. " - "Please provide the 'password:' key") + raise cv.Invalid( + "You cannot use the EAP 'username:' option without a 'password:'. " + "Please provide the 'password:' key" + ) if CONF_CERTIFICATE in value or CONF_KEY in value: # Check the key is valid and for this certificate, just to check the user hasn't pasted diff --git a/esphome/components/wifi_info/text_sensor.py b/esphome/components/wifi_info/text_sensor.py index 56670b4173..07af63524c 100644 --- a/esphome/components/wifi_info/text_sensor.py +++ b/esphome/components/wifi_info/text_sensor.py @@ -1,31 +1,53 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import text_sensor -from esphome.const import CONF_BSSID, CONF_ID, CONF_IP_ADDRESS, CONF_SSID, CONF_MAC_ADDRESS +from esphome.const import ( + CONF_BSSID, + CONF_ID, + CONF_IP_ADDRESS, + CONF_SSID, + CONF_MAC_ADDRESS, +) from esphome.core import coroutine -DEPENDENCIES = ['wifi'] +DEPENDENCIES = ["wifi"] -wifi_info_ns = cg.esphome_ns.namespace('wifi_info') -IPAddressWiFiInfo = wifi_info_ns.class_('IPAddressWiFiInfo', text_sensor.TextSensor, cg.Component) -SSIDWiFiInfo = wifi_info_ns.class_('SSIDWiFiInfo', text_sensor.TextSensor, cg.Component) -BSSIDWiFiInfo = wifi_info_ns.class_('BSSIDWiFiInfo', text_sensor.TextSensor, cg.Component) -MacAddressWifiInfo = wifi_info_ns.class_('MacAddressWifiInfo', text_sensor.TextSensor, cg.Component) +wifi_info_ns = cg.esphome_ns.namespace("wifi_info") +IPAddressWiFiInfo = wifi_info_ns.class_( + "IPAddressWiFiInfo", text_sensor.TextSensor, cg.Component +) +SSIDWiFiInfo = wifi_info_ns.class_("SSIDWiFiInfo", text_sensor.TextSensor, cg.Component) +BSSIDWiFiInfo = wifi_info_ns.class_( + "BSSIDWiFiInfo", text_sensor.TextSensor, cg.Component +) +MacAddressWifiInfo = wifi_info_ns.class_( + "MacAddressWifiInfo", text_sensor.TextSensor, cg.Component +) -CONFIG_SCHEMA = cv.Schema({ - cv.Optional(CONF_IP_ADDRESS): text_sensor.TEXT_SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(IPAddressWiFiInfo), - }), - cv.Optional(CONF_SSID): text_sensor.TEXT_SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(SSIDWiFiInfo), - }), - cv.Optional(CONF_BSSID): text_sensor.TEXT_SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(BSSIDWiFiInfo), - }), - cv.Optional(CONF_MAC_ADDRESS): text_sensor.TEXT_SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(MacAddressWifiInfo), - }) -}) +CONFIG_SCHEMA = cv.Schema( + { + cv.Optional(CONF_IP_ADDRESS): text_sensor.TEXT_SENSOR_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(IPAddressWiFiInfo), + } + ), + cv.Optional(CONF_SSID): text_sensor.TEXT_SENSOR_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(SSIDWiFiInfo), + } + ), + cv.Optional(CONF_BSSID): text_sensor.TEXT_SENSOR_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(BSSIDWiFiInfo), + } + ), + cv.Optional(CONF_MAC_ADDRESS): text_sensor.TEXT_SENSOR_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(MacAddressWifiInfo), + } + ), + } +) @coroutine diff --git a/esphome/components/wifi_signal/sensor.py b/esphome/components/wifi_signal/sensor.py index c1174fdecd..f2a9f5408c 100644 --- a/esphome/components/wifi_signal/sensor.py +++ b/esphome/components/wifi_signal/sensor.py @@ -1,17 +1,28 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor -from esphome.const import CONF_ID, DEVICE_CLASS_SIGNAL_STRENGTH, ICON_EMPTY, UNIT_DECIBEL +from esphome.const import ( + CONF_ID, + DEVICE_CLASS_SIGNAL_STRENGTH, + ICON_EMPTY, + UNIT_DECIBEL, +) -DEPENDENCIES = ['wifi'] -wifi_signal_ns = cg.esphome_ns.namespace('wifi_signal') -WiFiSignalSensor = wifi_signal_ns.class_('WiFiSignalSensor', sensor.Sensor, cg.PollingComponent) +DEPENDENCIES = ["wifi"] +wifi_signal_ns = cg.esphome_ns.namespace("wifi_signal") +WiFiSignalSensor = wifi_signal_ns.class_( + "WiFiSignalSensor", sensor.Sensor, cg.PollingComponent +) -CONFIG_SCHEMA = sensor.sensor_schema( - UNIT_DECIBEL, ICON_EMPTY, 0, DEVICE_CLASS_SIGNAL_STRENGTH -).extend({ - cv.GenerateID(): cv.declare_id(WiFiSignalSensor), -}).extend(cv.polling_component_schema('60s')) +CONFIG_SCHEMA = ( + sensor.sensor_schema(UNIT_DECIBEL, ICON_EMPTY, 0, DEVICE_CLASS_SIGNAL_STRENGTH) + .extend( + { + cv.GenerateID(): cv.declare_id(WiFiSignalSensor), + } + ) + .extend(cv.polling_component_schema("60s")) +) def to_code(config): diff --git a/esphome/components/wled/__init__.py b/esphome/components/wled/__init__.py index 1a248e530f..31ec318281 100644 --- a/esphome/components/wled/__init__.py +++ b/esphome/components/wled/__init__.py @@ -4,15 +4,20 @@ from esphome.components.light.types import AddressableLightEffect from esphome.components.light.effects import register_addressable_effect from esphome.const import CONF_NAME, CONF_PORT -wled_ns = cg.esphome_ns.namespace('wled') -WLEDLightEffect = wled_ns.class_('WLEDLightEffect', AddressableLightEffect) +wled_ns = cg.esphome_ns.namespace("wled") +WLEDLightEffect = wled_ns.class_("WLEDLightEffect", AddressableLightEffect) CONFIG_SCHEMA = cv.Schema({}) -@register_addressable_effect('wled', WLEDLightEffect, "WLED", { - cv.Optional(CONF_PORT, default=21324): cv.port, -}) +@register_addressable_effect( + "wled", + WLEDLightEffect, + "WLED", + { + cv.Optional(CONF_PORT, default=21324): cv.port, + }, +) def wled_light_effect_to_code(config, effect_id): effect = cg.new_Pvariable(effect_id, config[CONF_NAME]) cg.add(effect.set_port(config[CONF_PORT])) diff --git a/esphome/components/xiaomi_ble/__init__.py b/esphome/components/xiaomi_ble/__init__.py index 2b36090293..3d11ea8125 100644 --- a/esphome/components/xiaomi_ble/__init__.py +++ b/esphome/components/xiaomi_ble/__init__.py @@ -3,14 +3,18 @@ import esphome.config_validation as cv from esphome.components import esp32_ble_tracker from esphome.const import CONF_ID -DEPENDENCIES = ['esp32_ble_tracker'] +DEPENDENCIES = ["esp32_ble_tracker"] -xiaomi_ble_ns = cg.esphome_ns.namespace('xiaomi_ble') -XiaomiListener = xiaomi_ble_ns.class_('XiaomiListener', esp32_ble_tracker.ESPBTDeviceListener) +xiaomi_ble_ns = cg.esphome_ns.namespace("xiaomi_ble") +XiaomiListener = xiaomi_ble_ns.class_( + "XiaomiListener", esp32_ble_tracker.ESPBTDeviceListener +) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(XiaomiListener), -}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA) +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(XiaomiListener), + } +).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA) def to_code(config): diff --git a/esphome/components/xiaomi_cgd1/sensor.py b/esphome/components/xiaomi_cgd1/sensor.py index c84c996504..25d1f93674 100644 --- a/esphome/components/xiaomi_cgd1/sensor.py +++ b/esphome/components/xiaomi_cgd1/sensor.py @@ -1,28 +1,49 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, esp32_ble_tracker -from esphome.const import CONF_BATTERY_LEVEL, CONF_HUMIDITY, CONF_MAC_ADDRESS, CONF_TEMPERATURE, \ - CONF_ID, DEVICE_CLASS_BATTERY, DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_TEMPERATURE, ICON_EMPTY, \ - UNIT_CELSIUS, UNIT_PERCENT, CONF_BINDKEY +from esphome.const import ( + CONF_BATTERY_LEVEL, + CONF_HUMIDITY, + CONF_MAC_ADDRESS, + CONF_TEMPERATURE, + CONF_ID, + DEVICE_CLASS_BATTERY, + DEVICE_CLASS_HUMIDITY, + DEVICE_CLASS_TEMPERATURE, + ICON_EMPTY, + UNIT_CELSIUS, + UNIT_PERCENT, + CONF_BINDKEY, +) -DEPENDENCIES = ['esp32_ble_tracker'] -AUTO_LOAD = ['xiaomi_ble'] +DEPENDENCIES = ["esp32_ble_tracker"] +AUTO_LOAD = ["xiaomi_ble"] -xiaomi_cgd1_ns = cg.esphome_ns.namespace('xiaomi_cgd1') -XiaomiCGD1 = xiaomi_cgd1_ns.class_('XiaomiCGD1', esp32_ble_tracker.ESPBTDeviceListener, - cg.Component) +xiaomi_cgd1_ns = cg.esphome_ns.namespace("xiaomi_cgd1") +XiaomiCGD1 = xiaomi_cgd1_ns.class_( + "XiaomiCGD1", esp32_ble_tracker.ESPBTDeviceListener, cg.Component +) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(XiaomiCGD1), - cv.Required(CONF_BINDKEY): cv.bind_key, - cv.Required(CONF_MAC_ADDRESS): cv.mac_address, - cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, - DEVICE_CLASS_TEMPERATURE), - cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 1, - DEVICE_CLASS_HUMIDITY), - cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 0, - DEVICE_CLASS_BATTERY), -}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(XiaomiCGD1), + cv.Required(CONF_BINDKEY): cv.bind_key, + cv.Required(CONF_MAC_ADDRESS): cv.mac_address, + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( + UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE + ), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema( + UNIT_PERCENT, ICON_EMPTY, 1, DEVICE_CLASS_HUMIDITY + ), + cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema( + UNIT_PERCENT, ICON_EMPTY, 0, DEVICE_CLASS_BATTERY + ), + } + ) + .extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA) + .extend(cv.COMPONENT_SCHEMA) +) def to_code(config): diff --git a/esphome/components/xiaomi_cgg1/sensor.py b/esphome/components/xiaomi_cgg1/sensor.py index e78c503c98..6201df61b8 100644 --- a/esphome/components/xiaomi_cgg1/sensor.py +++ b/esphome/components/xiaomi_cgg1/sensor.py @@ -1,27 +1,47 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, esp32_ble_tracker -from esphome.const import CONF_BATTERY_LEVEL, CONF_HUMIDITY, CONF_MAC_ADDRESS, CONF_TEMPERATURE, \ - CONF_ID, DEVICE_CLASS_BATTERY, DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_TEMPERATURE, ICON_EMPTY, \ - UNIT_CELSIUS, UNIT_PERCENT +from esphome.const import ( + CONF_BATTERY_LEVEL, + CONF_HUMIDITY, + CONF_MAC_ADDRESS, + CONF_TEMPERATURE, + CONF_ID, + DEVICE_CLASS_BATTERY, + DEVICE_CLASS_HUMIDITY, + DEVICE_CLASS_TEMPERATURE, + ICON_EMPTY, + UNIT_CELSIUS, + UNIT_PERCENT, +) -DEPENDENCIES = ['esp32_ble_tracker'] -AUTO_LOAD = ['xiaomi_ble'] +DEPENDENCIES = ["esp32_ble_tracker"] +AUTO_LOAD = ["xiaomi_ble"] -xiaomi_cgg1_ns = cg.esphome_ns.namespace('xiaomi_cgg1') +xiaomi_cgg1_ns = cg.esphome_ns.namespace("xiaomi_cgg1") XiaomiCGG1 = xiaomi_cgg1_ns.class_( - 'XiaomiCGG1', esp32_ble_tracker.ESPBTDeviceListener, cg.Component) + "XiaomiCGG1", esp32_ble_tracker.ESPBTDeviceListener, cg.Component +) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(XiaomiCGG1), - cv.Required(CONF_MAC_ADDRESS): cv.mac_address, - cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, - DEVICE_CLASS_TEMPERATURE), - cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 1, - DEVICE_CLASS_HUMIDITY), - cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 0, - DEVICE_CLASS_BATTERY), -}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(XiaomiCGG1), + cv.Required(CONF_MAC_ADDRESS): cv.mac_address, + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( + UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE + ), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema( + UNIT_PERCENT, ICON_EMPTY, 1, DEVICE_CLASS_HUMIDITY + ), + cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema( + UNIT_PERCENT, ICON_EMPTY, 0, DEVICE_CLASS_BATTERY + ), + } + ) + .extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA) + .extend(cv.COMPONENT_SCHEMA) +) def to_code(config): diff --git a/esphome/components/xiaomi_gcls002/sensor.py b/esphome/components/xiaomi_gcls002/sensor.py index 8f434b6a3b..b838371155 100644 --- a/esphome/components/xiaomi_gcls002/sensor.py +++ b/esphome/components/xiaomi_gcls002/sensor.py @@ -1,30 +1,55 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, esp32_ble_tracker -from esphome.const import CONF_MAC_ADDRESS, CONF_TEMPERATURE, DEVICE_CLASS_EMPTY, \ - DEVICE_CLASS_ILLUMINANCE, DEVICE_CLASS_TEMPERATURE, ICON_EMPTY, ICON_WATER_PERCENT, \ - UNIT_CELSIUS, UNIT_PERCENT, CONF_ID, CONF_MOISTURE, CONF_ILLUMINANCE, UNIT_LUX, \ - CONF_CONDUCTIVITY, UNIT_MICROSIEMENS_PER_CENTIMETER, ICON_FLOWER +from esphome.const import ( + CONF_MAC_ADDRESS, + CONF_TEMPERATURE, + DEVICE_CLASS_EMPTY, + DEVICE_CLASS_ILLUMINANCE, + DEVICE_CLASS_TEMPERATURE, + ICON_EMPTY, + ICON_WATER_PERCENT, + UNIT_CELSIUS, + UNIT_PERCENT, + CONF_ID, + CONF_MOISTURE, + CONF_ILLUMINANCE, + UNIT_LUX, + CONF_CONDUCTIVITY, + UNIT_MICROSIEMENS_PER_CENTIMETER, + ICON_FLOWER, +) -DEPENDENCIES = ['esp32_ble_tracker'] -AUTO_LOAD = ['xiaomi_ble'] +DEPENDENCIES = ["esp32_ble_tracker"] +AUTO_LOAD = ["xiaomi_ble"] -xiaomi_gcls002_ns = cg.esphome_ns.namespace('xiaomi_gcls002') -XiaomiGCLS002 = xiaomi_gcls002_ns.class_('XiaomiGCLS002', - esp32_ble_tracker.ESPBTDeviceListener, cg.Component) +xiaomi_gcls002_ns = cg.esphome_ns.namespace("xiaomi_gcls002") +XiaomiGCLS002 = xiaomi_gcls002_ns.class_( + "XiaomiGCLS002", esp32_ble_tracker.ESPBTDeviceListener, cg.Component +) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(XiaomiGCLS002), - cv.Required(CONF_MAC_ADDRESS): cv.mac_address, - cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, - DEVICE_CLASS_TEMPERATURE), - cv.Optional(CONF_MOISTURE): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 0, - DEVICE_CLASS_EMPTY), - cv.Optional(CONF_ILLUMINANCE): sensor.sensor_schema(UNIT_LUX, ICON_EMPTY, 0, - DEVICE_CLASS_ILLUMINANCE), - cv.Optional(CONF_CONDUCTIVITY): - sensor.sensor_schema(UNIT_MICROSIEMENS_PER_CENTIMETER, ICON_FLOWER, 0, DEVICE_CLASS_EMPTY), -}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(XiaomiGCLS002), + cv.Required(CONF_MAC_ADDRESS): cv.mac_address, + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( + UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE + ), + cv.Optional(CONF_MOISTURE): sensor.sensor_schema( + UNIT_PERCENT, ICON_WATER_PERCENT, 0, DEVICE_CLASS_EMPTY + ), + cv.Optional(CONF_ILLUMINANCE): sensor.sensor_schema( + UNIT_LUX, ICON_EMPTY, 0, DEVICE_CLASS_ILLUMINANCE + ), + cv.Optional(CONF_CONDUCTIVITY): sensor.sensor_schema( + UNIT_MICROSIEMENS_PER_CENTIMETER, ICON_FLOWER, 0, DEVICE_CLASS_EMPTY + ), + } + ) + .extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA) + .extend(cv.COMPONENT_SCHEMA) +) def to_code(config): diff --git a/esphome/components/xiaomi_hhccjcy01/sensor.py b/esphome/components/xiaomi_hhccjcy01/sensor.py index 960f652bba..f657ec9373 100644 --- a/esphome/components/xiaomi_hhccjcy01/sensor.py +++ b/esphome/components/xiaomi_hhccjcy01/sensor.py @@ -1,33 +1,60 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, esp32_ble_tracker -from esphome.const import CONF_MAC_ADDRESS, CONF_TEMPERATURE, DEVICE_CLASS_EMPTY, \ - DEVICE_CLASS_ILLUMINANCE, DEVICE_CLASS_TEMPERATURE, ICON_EMPTY, ICON_WATER_PERCENT, \ - UNIT_CELSIUS, UNIT_PERCENT, CONF_ID, CONF_MOISTURE, CONF_ILLUMINANCE, UNIT_LUX, \ - CONF_CONDUCTIVITY, UNIT_MICROSIEMENS_PER_CENTIMETER, ICON_FLOWER, DEVICE_CLASS_BATTERY, \ - CONF_BATTERY_LEVEL +from esphome.const import ( + CONF_MAC_ADDRESS, + CONF_TEMPERATURE, + DEVICE_CLASS_EMPTY, + DEVICE_CLASS_ILLUMINANCE, + DEVICE_CLASS_TEMPERATURE, + ICON_EMPTY, + ICON_WATER_PERCENT, + UNIT_CELSIUS, + UNIT_PERCENT, + CONF_ID, + CONF_MOISTURE, + CONF_ILLUMINANCE, + UNIT_LUX, + CONF_CONDUCTIVITY, + UNIT_MICROSIEMENS_PER_CENTIMETER, + ICON_FLOWER, + DEVICE_CLASS_BATTERY, + CONF_BATTERY_LEVEL, +) -DEPENDENCIES = ['esp32_ble_tracker'] -AUTO_LOAD = ['xiaomi_ble'] +DEPENDENCIES = ["esp32_ble_tracker"] +AUTO_LOAD = ["xiaomi_ble"] -xiaomi_hhccjcy01_ns = cg.esphome_ns.namespace('xiaomi_hhccjcy01') -XiaomiHHCCJCY01 = xiaomi_hhccjcy01_ns.class_('XiaomiHHCCJCY01', - esp32_ble_tracker.ESPBTDeviceListener, cg.Component) +xiaomi_hhccjcy01_ns = cg.esphome_ns.namespace("xiaomi_hhccjcy01") +XiaomiHHCCJCY01 = xiaomi_hhccjcy01_ns.class_( + "XiaomiHHCCJCY01", esp32_ble_tracker.ESPBTDeviceListener, cg.Component +) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(XiaomiHHCCJCY01), - cv.Required(CONF_MAC_ADDRESS): cv.mac_address, - cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, - DEVICE_CLASS_TEMPERATURE), - cv.Optional(CONF_MOISTURE): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 0, - DEVICE_CLASS_EMPTY), - cv.Optional(CONF_ILLUMINANCE): sensor.sensor_schema(UNIT_LUX, ICON_EMPTY, 0, - DEVICE_CLASS_ILLUMINANCE), - cv.Optional(CONF_CONDUCTIVITY): - sensor.sensor_schema(UNIT_MICROSIEMENS_PER_CENTIMETER, ICON_FLOWER, 0, DEVICE_CLASS_EMPTY), - cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 0, - DEVICE_CLASS_BATTERY), -}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(XiaomiHHCCJCY01), + cv.Required(CONF_MAC_ADDRESS): cv.mac_address, + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( + UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE + ), + cv.Optional(CONF_MOISTURE): sensor.sensor_schema( + UNIT_PERCENT, ICON_WATER_PERCENT, 0, DEVICE_CLASS_EMPTY + ), + cv.Optional(CONF_ILLUMINANCE): sensor.sensor_schema( + UNIT_LUX, ICON_EMPTY, 0, DEVICE_CLASS_ILLUMINANCE + ), + cv.Optional(CONF_CONDUCTIVITY): sensor.sensor_schema( + UNIT_MICROSIEMENS_PER_CENTIMETER, ICON_FLOWER, 0, DEVICE_CLASS_EMPTY + ), + cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema( + UNIT_PERCENT, ICON_EMPTY, 0, DEVICE_CLASS_BATTERY + ), + } + ) + .extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA) + .extend(cv.COMPONENT_SCHEMA) +) def to_code(config): diff --git a/esphome/components/xiaomi_hhccpot002/sensor.py b/esphome/components/xiaomi_hhccpot002/sensor.py index ad8f2184b3..820cda173d 100644 --- a/esphome/components/xiaomi_hhccpot002/sensor.py +++ b/esphome/components/xiaomi_hhccpot002/sensor.py @@ -1,24 +1,42 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, esp32_ble_tracker -from esphome.const import CONF_MAC_ADDRESS, DEVICE_CLASS_EMPTY, UNIT_PERCENT, ICON_WATER_PERCENT, \ - CONF_ID, CONF_MOISTURE, CONF_CONDUCTIVITY, UNIT_MICROSIEMENS_PER_CENTIMETER, ICON_FLOWER +from esphome.const import ( + CONF_MAC_ADDRESS, + DEVICE_CLASS_EMPTY, + UNIT_PERCENT, + ICON_WATER_PERCENT, + CONF_ID, + CONF_MOISTURE, + CONF_CONDUCTIVITY, + UNIT_MICROSIEMENS_PER_CENTIMETER, + ICON_FLOWER, +) -DEPENDENCIES = ['esp32_ble_tracker'] -AUTO_LOAD = ['xiaomi_ble'] +DEPENDENCIES = ["esp32_ble_tracker"] +AUTO_LOAD = ["xiaomi_ble"] -xiaomi_hhccpot002_ns = cg.esphome_ns.namespace('xiaomi_hhccpot002') -XiaomiHHCCPOT002 = xiaomi_hhccpot002_ns.class_('XiaomiHHCCPOT002', - esp32_ble_tracker.ESPBTDeviceListener, cg.Component) +xiaomi_hhccpot002_ns = cg.esphome_ns.namespace("xiaomi_hhccpot002") +XiaomiHHCCPOT002 = xiaomi_hhccpot002_ns.class_( + "XiaomiHHCCPOT002", esp32_ble_tracker.ESPBTDeviceListener, cg.Component +) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(XiaomiHHCCPOT002), - cv.Required(CONF_MAC_ADDRESS): cv.mac_address, - cv.Optional(CONF_MOISTURE): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 0, - DEVICE_CLASS_EMPTY), - cv.Optional(CONF_CONDUCTIVITY): - sensor.sensor_schema(UNIT_MICROSIEMENS_PER_CENTIMETER, ICON_FLOWER, 0, DEVICE_CLASS_EMPTY), -}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(XiaomiHHCCPOT002), + cv.Required(CONF_MAC_ADDRESS): cv.mac_address, + cv.Optional(CONF_MOISTURE): sensor.sensor_schema( + UNIT_PERCENT, ICON_WATER_PERCENT, 0, DEVICE_CLASS_EMPTY + ), + cv.Optional(CONF_CONDUCTIVITY): sensor.sensor_schema( + UNIT_MICROSIEMENS_PER_CENTIMETER, ICON_FLOWER, 0, DEVICE_CLASS_EMPTY + ), + } + ) + .extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA) + .extend(cv.COMPONENT_SCHEMA) +) def to_code(config): diff --git a/esphome/components/xiaomi_jqjcy01ym/sensor.py b/esphome/components/xiaomi_jqjcy01ym/sensor.py index c7451af5c4..ce5e8e2b37 100644 --- a/esphome/components/xiaomi_jqjcy01ym/sensor.py +++ b/esphome/components/xiaomi_jqjcy01ym/sensor.py @@ -1,31 +1,57 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, esp32_ble_tracker -from esphome.const import CONF_BATTERY_LEVEL, CONF_MAC_ADDRESS, CONF_TEMPERATURE, CONF_ID, \ - DEVICE_CLASS_BATTERY, DEVICE_CLASS_EMPTY, DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_TEMPERATURE, \ - ICON_EMPTY, UNIT_CELSIUS, UNIT_PERCENT, CONF_HUMIDITY, UNIT_MILLIGRAMS_PER_CUBIC_METER, \ - ICON_FLASK_OUTLINE, CONF_FORMALDEHYDE +from esphome.const import ( + CONF_BATTERY_LEVEL, + CONF_MAC_ADDRESS, + CONF_TEMPERATURE, + CONF_ID, + DEVICE_CLASS_BATTERY, + DEVICE_CLASS_EMPTY, + DEVICE_CLASS_HUMIDITY, + DEVICE_CLASS_TEMPERATURE, + ICON_EMPTY, + UNIT_CELSIUS, + UNIT_PERCENT, + CONF_HUMIDITY, + UNIT_MILLIGRAMS_PER_CUBIC_METER, + ICON_FLASK_OUTLINE, + CONF_FORMALDEHYDE, +) -DEPENDENCIES = ['esp32_ble_tracker'] -AUTO_LOAD = ['xiaomi_ble'] +DEPENDENCIES = ["esp32_ble_tracker"] +AUTO_LOAD = ["xiaomi_ble"] -xiaomi_jqjcy01ym_ns = cg.esphome_ns.namespace('xiaomi_jqjcy01ym') -XiaomiJQJCY01YM = xiaomi_jqjcy01ym_ns.class_('XiaomiJQJCY01YM', - esp32_ble_tracker.ESPBTDeviceListener, cg.Component) +xiaomi_jqjcy01ym_ns = cg.esphome_ns.namespace("xiaomi_jqjcy01ym") +XiaomiJQJCY01YM = xiaomi_jqjcy01ym_ns.class_( + "XiaomiJQJCY01YM", esp32_ble_tracker.ESPBTDeviceListener, cg.Component +) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(XiaomiJQJCY01YM), - cv.Required(CONF_MAC_ADDRESS): cv.mac_address, - cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, - DEVICE_CLASS_TEMPERATURE), - cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 0, - DEVICE_CLASS_HUMIDITY), - cv.Optional(CONF_FORMALDEHYDE): - sensor.sensor_schema(UNIT_MILLIGRAMS_PER_CUBIC_METER, ICON_FLASK_OUTLINE, 2, - DEVICE_CLASS_EMPTY), - cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 0, - DEVICE_CLASS_BATTERY), -}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(XiaomiJQJCY01YM), + cv.Required(CONF_MAC_ADDRESS): cv.mac_address, + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( + UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE + ), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema( + UNIT_PERCENT, ICON_EMPTY, 0, DEVICE_CLASS_HUMIDITY + ), + cv.Optional(CONF_FORMALDEHYDE): sensor.sensor_schema( + UNIT_MILLIGRAMS_PER_CUBIC_METER, + ICON_FLASK_OUTLINE, + 2, + DEVICE_CLASS_EMPTY, + ), + cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema( + UNIT_PERCENT, ICON_EMPTY, 0, DEVICE_CLASS_BATTERY + ), + } + ) + .extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA) + .extend(cv.COMPONENT_SCHEMA) +) def to_code(config): diff --git a/esphome/components/xiaomi_lywsd02/sensor.py b/esphome/components/xiaomi_lywsd02/sensor.py index ced9875b55..c17eb17a5f 100644 --- a/esphome/components/xiaomi_lywsd02/sensor.py +++ b/esphome/components/xiaomi_lywsd02/sensor.py @@ -1,27 +1,47 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, esp32_ble_tracker -from esphome.const import CONF_BATTERY_LEVEL, CONF_HUMIDITY, CONF_MAC_ADDRESS, CONF_TEMPERATURE, \ - DEVICE_CLASS_TEMPERATURE, UNIT_CELSIUS, ICON_EMPTY, UNIT_PERCENT, DEVICE_CLASS_HUMIDITY, \ - DEVICE_CLASS_BATTERY, CONF_ID +from esphome.const import ( + CONF_BATTERY_LEVEL, + CONF_HUMIDITY, + CONF_MAC_ADDRESS, + CONF_TEMPERATURE, + DEVICE_CLASS_TEMPERATURE, + UNIT_CELSIUS, + ICON_EMPTY, + UNIT_PERCENT, + DEVICE_CLASS_HUMIDITY, + DEVICE_CLASS_BATTERY, + CONF_ID, +) -DEPENDENCIES = ['esp32_ble_tracker'] -AUTO_LOAD = ['xiaomi_ble'] +DEPENDENCIES = ["esp32_ble_tracker"] +AUTO_LOAD = ["xiaomi_ble"] -xiaomi_lywsd02_ns = cg.esphome_ns.namespace('xiaomi_lywsd02') -XiaomiLYWSD02 = xiaomi_lywsd02_ns.class_('XiaomiLYWSD02', esp32_ble_tracker.ESPBTDeviceListener, - cg.Component) +xiaomi_lywsd02_ns = cg.esphome_ns.namespace("xiaomi_lywsd02") +XiaomiLYWSD02 = xiaomi_lywsd02_ns.class_( + "XiaomiLYWSD02", esp32_ble_tracker.ESPBTDeviceListener, cg.Component +) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(XiaomiLYWSD02), - cv.Required(CONF_MAC_ADDRESS): cv.mac_address, - cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, - DEVICE_CLASS_TEMPERATURE), - cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 1, - DEVICE_CLASS_HUMIDITY), - cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 0, - DEVICE_CLASS_BATTERY), -}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(XiaomiLYWSD02), + cv.Required(CONF_MAC_ADDRESS): cv.mac_address, + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( + UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE + ), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema( + UNIT_PERCENT, ICON_EMPTY, 1, DEVICE_CLASS_HUMIDITY + ), + cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema( + UNIT_PERCENT, ICON_EMPTY, 0, DEVICE_CLASS_BATTERY + ), + } + ) + .extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA) + .extend(cv.COMPONENT_SCHEMA) +) def to_code(config): diff --git a/esphome/components/xiaomi_lywsd03mmc/sensor.py b/esphome/components/xiaomi_lywsd03mmc/sensor.py index de3b4a4f9c..b9de3f0bcc 100644 --- a/esphome/components/xiaomi_lywsd03mmc/sensor.py +++ b/esphome/components/xiaomi_lywsd03mmc/sensor.py @@ -1,31 +1,51 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, esp32_ble_tracker -from esphome.const import CONF_BATTERY_LEVEL, CONF_HUMIDITY, CONF_MAC_ADDRESS, CONF_TEMPERATURE, \ - UNIT_CELSIUS, ICON_EMPTY, UNIT_PERCENT, DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_HUMIDITY, \ - CONF_ID, CONF_BINDKEY, DEVICE_CLASS_BATTERY +from esphome.const import ( + CONF_BATTERY_LEVEL, + CONF_HUMIDITY, + CONF_MAC_ADDRESS, + CONF_TEMPERATURE, + UNIT_CELSIUS, + ICON_EMPTY, + UNIT_PERCENT, + DEVICE_CLASS_TEMPERATURE, + DEVICE_CLASS_HUMIDITY, + CONF_ID, + CONF_BINDKEY, + DEVICE_CLASS_BATTERY, +) -CODEOWNERS = ['@ahpohl'] +CODEOWNERS = ["@ahpohl"] -DEPENDENCIES = ['esp32_ble_tracker'] -AUTO_LOAD = ['xiaomi_ble'] +DEPENDENCIES = ["esp32_ble_tracker"] +AUTO_LOAD = ["xiaomi_ble"] -xiaomi_lywsd03mmc_ns = cg.esphome_ns.namespace('xiaomi_lywsd03mmc') -XiaomiLYWSD03MMC = xiaomi_lywsd03mmc_ns.class_('XiaomiLYWSD03MMC', - esp32_ble_tracker.ESPBTDeviceListener, - cg.Component) +xiaomi_lywsd03mmc_ns = cg.esphome_ns.namespace("xiaomi_lywsd03mmc") +XiaomiLYWSD03MMC = xiaomi_lywsd03mmc_ns.class_( + "XiaomiLYWSD03MMC", esp32_ble_tracker.ESPBTDeviceListener, cg.Component +) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(XiaomiLYWSD03MMC), - cv.Required(CONF_BINDKEY): cv.bind_key, - cv.Required(CONF_MAC_ADDRESS): cv.mac_address, - cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, - DEVICE_CLASS_TEMPERATURE), - cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 0, - DEVICE_CLASS_HUMIDITY), - cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 0, - DEVICE_CLASS_BATTERY), -}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(XiaomiLYWSD03MMC), + cv.Required(CONF_BINDKEY): cv.bind_key, + cv.Required(CONF_MAC_ADDRESS): cv.mac_address, + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( + UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE + ), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema( + UNIT_PERCENT, ICON_EMPTY, 0, DEVICE_CLASS_HUMIDITY + ), + cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema( + UNIT_PERCENT, ICON_EMPTY, 0, DEVICE_CLASS_BATTERY + ), + } + ) + .extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA) + .extend(cv.COMPONENT_SCHEMA) +) def to_code(config): diff --git a/esphome/components/xiaomi_lywsdcgq/sensor.py b/esphome/components/xiaomi_lywsdcgq/sensor.py index 3bff2fca78..a4a03a3fb0 100644 --- a/esphome/components/xiaomi_lywsdcgq/sensor.py +++ b/esphome/components/xiaomi_lywsdcgq/sensor.py @@ -1,27 +1,47 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, esp32_ble_tracker -from esphome.const import CONF_BATTERY_LEVEL, CONF_HUMIDITY, CONF_MAC_ADDRESS, CONF_TEMPERATURE, \ - UNIT_CELSIUS, ICON_EMPTY, UNIT_PERCENT, DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_HUMIDITY, \ - DEVICE_CLASS_BATTERY, CONF_ID +from esphome.const import ( + CONF_BATTERY_LEVEL, + CONF_HUMIDITY, + CONF_MAC_ADDRESS, + CONF_TEMPERATURE, + UNIT_CELSIUS, + ICON_EMPTY, + UNIT_PERCENT, + DEVICE_CLASS_TEMPERATURE, + DEVICE_CLASS_HUMIDITY, + DEVICE_CLASS_BATTERY, + CONF_ID, +) -DEPENDENCIES = ['esp32_ble_tracker'] -AUTO_LOAD = ['xiaomi_ble'] +DEPENDENCIES = ["esp32_ble_tracker"] +AUTO_LOAD = ["xiaomi_ble"] -xiaomi_lywsdcgq_ns = cg.esphome_ns.namespace('xiaomi_lywsdcgq') -XiaomiLYWSDCGQ = xiaomi_lywsdcgq_ns.class_('XiaomiLYWSDCGQ', esp32_ble_tracker.ESPBTDeviceListener, - cg.Component) +xiaomi_lywsdcgq_ns = cg.esphome_ns.namespace("xiaomi_lywsdcgq") +XiaomiLYWSDCGQ = xiaomi_lywsdcgq_ns.class_( + "XiaomiLYWSDCGQ", esp32_ble_tracker.ESPBTDeviceListener, cg.Component +) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(XiaomiLYWSDCGQ), - cv.Required(CONF_MAC_ADDRESS): cv.mac_address, - cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, - DEVICE_CLASS_TEMPERATURE), - cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 1, - DEVICE_CLASS_HUMIDITY), - cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 0, - DEVICE_CLASS_BATTERY), -}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(XiaomiLYWSDCGQ), + cv.Required(CONF_MAC_ADDRESS): cv.mac_address, + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( + UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE + ), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema( + UNIT_PERCENT, ICON_EMPTY, 1, DEVICE_CLASS_HUMIDITY + ), + cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema( + UNIT_PERCENT, ICON_EMPTY, 0, DEVICE_CLASS_BATTERY + ), + } + ) + .extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA) + .extend(cv.COMPONENT_SCHEMA) +) def to_code(config): diff --git a/esphome/components/xiaomi_mhoc401/sensor.py b/esphome/components/xiaomi_mhoc401/sensor.py index 301c8914a1..ee0e06b3a8 100644 --- a/esphome/components/xiaomi_mhoc401/sensor.py +++ b/esphome/components/xiaomi_mhoc401/sensor.py @@ -1,30 +1,50 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, esp32_ble_tracker -from esphome.const import CONF_BATTERY_LEVEL, CONF_HUMIDITY, CONF_MAC_ADDRESS, CONF_TEMPERATURE, \ - UNIT_CELSIUS, ICON_EMPTY, UNIT_PERCENT, DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_HUMIDITY, \ - CONF_ID, CONF_BINDKEY, DEVICE_CLASS_BATTERY +from esphome.const import ( + CONF_BATTERY_LEVEL, + CONF_HUMIDITY, + CONF_MAC_ADDRESS, + CONF_TEMPERATURE, + UNIT_CELSIUS, + ICON_EMPTY, + UNIT_PERCENT, + DEVICE_CLASS_TEMPERATURE, + DEVICE_CLASS_HUMIDITY, + CONF_ID, + CONF_BINDKEY, + DEVICE_CLASS_BATTERY, +) -CODEOWNERS = ['@vevsvevs'] -DEPENDENCIES = ['esp32_ble_tracker'] -AUTO_LOAD = ['xiaomi_ble'] +CODEOWNERS = ["@vevsvevs"] +DEPENDENCIES = ["esp32_ble_tracker"] +AUTO_LOAD = ["xiaomi_ble"] -xiaomi_mhoc401_ns = cg.esphome_ns.namespace('xiaomi_mhoc401') -XiaomiMHOC401 = xiaomi_mhoc401_ns.class_('XiaomiMHOC401', - esp32_ble_tracker.ESPBTDeviceListener, - cg.Component) +xiaomi_mhoc401_ns = cg.esphome_ns.namespace("xiaomi_mhoc401") +XiaomiMHOC401 = xiaomi_mhoc401_ns.class_( + "XiaomiMHOC401", esp32_ble_tracker.ESPBTDeviceListener, cg.Component +) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(XiaomiMHOC401), - cv.Required(CONF_BINDKEY): cv.bind_key, - cv.Required(CONF_MAC_ADDRESS): cv.mac_address, - cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, - DEVICE_CLASS_TEMPERATURE), - cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 0, - DEVICE_CLASS_HUMIDITY), - cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 0, - DEVICE_CLASS_BATTERY), -}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(XiaomiMHOC401), + cv.Required(CONF_BINDKEY): cv.bind_key, + cv.Required(CONF_MAC_ADDRESS): cv.mac_address, + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( + UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE + ), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema( + UNIT_PERCENT, ICON_EMPTY, 0, DEVICE_CLASS_HUMIDITY + ), + cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema( + UNIT_PERCENT, ICON_EMPTY, 0, DEVICE_CLASS_BATTERY + ), + } + ) + .extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA) + .extend(cv.COMPONENT_SCHEMA) +) def to_code(config): diff --git a/esphome/components/xiaomi_miscale/sensor.py b/esphome/components/xiaomi_miscale/sensor.py index 8fcdad96af..cd225e4853 100644 --- a/esphome/components/xiaomi_miscale/sensor.py +++ b/esphome/components/xiaomi_miscale/sensor.py @@ -1,22 +1,35 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, esp32_ble_tracker -from esphome.const import CONF_MAC_ADDRESS, CONF_ID, CONF_WEIGHT, UNIT_KILOGRAM, \ - ICON_SCALE_BATHROOM, DEVICE_CLASS_EMPTY +from esphome.const import ( + CONF_MAC_ADDRESS, + CONF_ID, + CONF_WEIGHT, + UNIT_KILOGRAM, + ICON_SCALE_BATHROOM, + DEVICE_CLASS_EMPTY, +) -DEPENDENCIES = ['esp32_ble_tracker'] +DEPENDENCIES = ["esp32_ble_tracker"] -xiaomi_miscale_ns = cg.esphome_ns.namespace('xiaomi_miscale') -XiaomiMiscale = xiaomi_miscale_ns.class_('XiaomiMiscale', - esp32_ble_tracker.ESPBTDeviceListener, - cg.Component) +xiaomi_miscale_ns = cg.esphome_ns.namespace("xiaomi_miscale") +XiaomiMiscale = xiaomi_miscale_ns.class_( + "XiaomiMiscale", esp32_ble_tracker.ESPBTDeviceListener, cg.Component +) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(XiaomiMiscale), - cv.Required(CONF_MAC_ADDRESS): cv.mac_address, - cv.Optional(CONF_WEIGHT): sensor.sensor_schema( - UNIT_KILOGRAM, ICON_SCALE_BATHROOM, 2, DEVICE_CLASS_EMPTY), -}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(XiaomiMiscale), + cv.Required(CONF_MAC_ADDRESS): cv.mac_address, + cv.Optional(CONF_WEIGHT): sensor.sensor_schema( + UNIT_KILOGRAM, ICON_SCALE_BATHROOM, 2, DEVICE_CLASS_EMPTY + ), + } + ) + .extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA) + .extend(cv.COMPONENT_SCHEMA) +) def to_code(config): diff --git a/esphome/components/xiaomi_miscale2/sensor.py b/esphome/components/xiaomi_miscale2/sensor.py index 84244cfc58..fa124e8860 100644 --- a/esphome/components/xiaomi_miscale2/sensor.py +++ b/esphome/components/xiaomi_miscale2/sensor.py @@ -1,24 +1,41 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, esp32_ble_tracker -from esphome.const import CONF_MAC_ADDRESS, CONF_ID, CONF_WEIGHT, UNIT_KILOGRAM, \ - ICON_SCALE_BATHROOM, UNIT_OHM, CONF_IMPEDANCE, ICON_OMEGA, DEVICE_CLASS_EMPTY +from esphome.const import ( + CONF_MAC_ADDRESS, + CONF_ID, + CONF_WEIGHT, + UNIT_KILOGRAM, + ICON_SCALE_BATHROOM, + UNIT_OHM, + CONF_IMPEDANCE, + ICON_OMEGA, + DEVICE_CLASS_EMPTY, +) -DEPENDENCIES = ['esp32_ble_tracker'] +DEPENDENCIES = ["esp32_ble_tracker"] -xiaomi_miscale2_ns = cg.esphome_ns.namespace('xiaomi_miscale2') -XiaomiMiscale2 = xiaomi_miscale2_ns.class_('XiaomiMiscale2', - esp32_ble_tracker.ESPBTDeviceListener, - cg.Component) +xiaomi_miscale2_ns = cg.esphome_ns.namespace("xiaomi_miscale2") +XiaomiMiscale2 = xiaomi_miscale2_ns.class_( + "XiaomiMiscale2", esp32_ble_tracker.ESPBTDeviceListener, cg.Component +) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(XiaomiMiscale2), - cv.Required(CONF_MAC_ADDRESS): cv.mac_address, - cv.Optional(CONF_WEIGHT): sensor.sensor_schema( - UNIT_KILOGRAM, ICON_SCALE_BATHROOM, 2, DEVICE_CLASS_EMPTY), - cv.Optional(CONF_IMPEDANCE): sensor.sensor_schema( - UNIT_OHM, ICON_OMEGA, 0, DEVICE_CLASS_EMPTY), -}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(XiaomiMiscale2), + cv.Required(CONF_MAC_ADDRESS): cv.mac_address, + cv.Optional(CONF_WEIGHT): sensor.sensor_schema( + UNIT_KILOGRAM, ICON_SCALE_BATHROOM, 2, DEVICE_CLASS_EMPTY + ), + cv.Optional(CONF_IMPEDANCE): sensor.sensor_schema( + UNIT_OHM, ICON_OMEGA, 0, DEVICE_CLASS_EMPTY + ), + } + ) + .extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA) + .extend(cv.COMPONENT_SCHEMA) +) def to_code(config): diff --git a/esphome/components/xiaomi_mjyd02yla/binary_sensor.py b/esphome/components/xiaomi_mjyd02yla/binary_sensor.py index a50f507b49..1b0ad03f1a 100644 --- a/esphome/components/xiaomi_mjyd02yla/binary_sensor.py +++ b/esphome/components/xiaomi_mjyd02yla/binary_sensor.py @@ -1,33 +1,66 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, binary_sensor, esp32_ble_tracker -from esphome.const import CONF_MAC_ADDRESS, CONF_ID, CONF_BINDKEY, CONF_DEVICE_CLASS, CONF_LIGHT, \ - CONF_BATTERY_LEVEL, DEVICE_CLASS_BATTERY, DEVICE_CLASS_EMPTY, DEVICE_CLASS_ILLUMINANCE, \ - ICON_EMPTY, UNIT_PERCENT, CONF_IDLE_TIME, CONF_ILLUMINANCE, UNIT_MINUTE, UNIT_LUX, \ - ICON_TIMELAPSE +from esphome.const import ( + CONF_MAC_ADDRESS, + CONF_ID, + CONF_BINDKEY, + CONF_DEVICE_CLASS, + CONF_LIGHT, + CONF_BATTERY_LEVEL, + DEVICE_CLASS_BATTERY, + DEVICE_CLASS_EMPTY, + DEVICE_CLASS_ILLUMINANCE, + ICON_EMPTY, + UNIT_PERCENT, + CONF_IDLE_TIME, + CONF_ILLUMINANCE, + UNIT_MINUTE, + UNIT_LUX, + ICON_TIMELAPSE, +) -DEPENDENCIES = ['esp32_ble_tracker'] -AUTO_LOAD = ['xiaomi_ble'] +DEPENDENCIES = ["esp32_ble_tracker"] +AUTO_LOAD = ["xiaomi_ble"] -xiaomi_mjyd02yla_ns = cg.esphome_ns.namespace('xiaomi_mjyd02yla') -XiaomiMJYD02YLA = xiaomi_mjyd02yla_ns.class_('XiaomiMJYD02YLA', binary_sensor.BinarySensor, - cg.Component, esp32_ble_tracker.ESPBTDeviceListener) +xiaomi_mjyd02yla_ns = cg.esphome_ns.namespace("xiaomi_mjyd02yla") +XiaomiMJYD02YLA = xiaomi_mjyd02yla_ns.class_( + "XiaomiMJYD02YLA", + binary_sensor.BinarySensor, + cg.Component, + esp32_ble_tracker.ESPBTDeviceListener, +) -CONFIG_SCHEMA = cv.All(binary_sensor.BINARY_SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(XiaomiMJYD02YLA), - cv.Required(CONF_MAC_ADDRESS): cv.mac_address, - cv.Required(CONF_BINDKEY): cv.bind_key, - cv.Optional(CONF_DEVICE_CLASS, default='motion'): binary_sensor.device_class, - cv.Optional(CONF_IDLE_TIME): sensor.sensor_schema(UNIT_MINUTE, ICON_TIMELAPSE, 0, - DEVICE_CLASS_EMPTY), - cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 0, - DEVICE_CLASS_BATTERY), - cv.Optional(CONF_ILLUMINANCE): sensor.sensor_schema(UNIT_LUX, ICON_EMPTY, 0, - DEVICE_CLASS_ILLUMINANCE), - cv.Optional(CONF_LIGHT): binary_sensor.BINARY_SENSOR_SCHEMA.extend({ - cv.Optional(CONF_DEVICE_CLASS, default='light'): binary_sensor.device_class, - }), -}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA)) +CONFIG_SCHEMA = cv.All( + binary_sensor.BINARY_SENSOR_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(XiaomiMJYD02YLA), + cv.Required(CONF_MAC_ADDRESS): cv.mac_address, + cv.Required(CONF_BINDKEY): cv.bind_key, + cv.Optional( + CONF_DEVICE_CLASS, default="motion" + ): binary_sensor.device_class, + cv.Optional(CONF_IDLE_TIME): sensor.sensor_schema( + UNIT_MINUTE, ICON_TIMELAPSE, 0, DEVICE_CLASS_EMPTY + ), + cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema( + UNIT_PERCENT, ICON_EMPTY, 0, DEVICE_CLASS_BATTERY + ), + cv.Optional(CONF_ILLUMINANCE): sensor.sensor_schema( + UNIT_LUX, ICON_EMPTY, 0, DEVICE_CLASS_ILLUMINANCE + ), + cv.Optional(CONF_LIGHT): binary_sensor.BINARY_SENSOR_SCHEMA.extend( + { + cv.Optional( + CONF_DEVICE_CLASS, default="light" + ): binary_sensor.device_class, + } + ), + } + ) + .extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA) + .extend(cv.COMPONENT_SCHEMA) +) def to_code(config): diff --git a/esphome/components/xiaomi_mue4094rt/binary_sensor.py b/esphome/components/xiaomi_mue4094rt/binary_sensor.py index 946b1694c4..353e9aa3a6 100644 --- a/esphome/components/xiaomi_mue4094rt/binary_sensor.py +++ b/esphome/components/xiaomi_mue4094rt/binary_sensor.py @@ -4,19 +4,33 @@ from esphome.components import binary_sensor, esp32_ble_tracker from esphome.const import CONF_MAC_ADDRESS, CONF_DEVICE_CLASS, CONF_TIMEOUT, CONF_ID -DEPENDENCIES = ['esp32_ble_tracker'] -AUTO_LOAD = ['xiaomi_ble'] +DEPENDENCIES = ["esp32_ble_tracker"] +AUTO_LOAD = ["xiaomi_ble"] -xiaomi_mue4094rt_ns = cg.esphome_ns.namespace('xiaomi_mue4094rt') -XiaomiMUE4094RT = xiaomi_mue4094rt_ns.class_('XiaomiMUE4094RT', binary_sensor.BinarySensor, - cg.Component, esp32_ble_tracker.ESPBTDeviceListener) +xiaomi_mue4094rt_ns = cg.esphome_ns.namespace("xiaomi_mue4094rt") +XiaomiMUE4094RT = xiaomi_mue4094rt_ns.class_( + "XiaomiMUE4094RT", + binary_sensor.BinarySensor, + cg.Component, + esp32_ble_tracker.ESPBTDeviceListener, +) -CONFIG_SCHEMA = cv.All(binary_sensor.BINARY_SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(XiaomiMUE4094RT), - cv.Required(CONF_MAC_ADDRESS): cv.mac_address, - cv.Optional(CONF_DEVICE_CLASS, default='motion'): binary_sensor.device_class, - cv.Optional(CONF_TIMEOUT, default='5s'): cv.positive_time_period_milliseconds, -}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA)) +CONFIG_SCHEMA = cv.All( + binary_sensor.BINARY_SENSOR_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(XiaomiMUE4094RT), + cv.Required(CONF_MAC_ADDRESS): cv.mac_address, + cv.Optional( + CONF_DEVICE_CLASS, default="motion" + ): binary_sensor.device_class, + cv.Optional( + CONF_TIMEOUT, default="5s" + ): cv.positive_time_period_milliseconds, + } + ) + .extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA) + .extend(cv.COMPONENT_SCHEMA) +) def to_code(config): diff --git a/esphome/components/xiaomi_wx08zm/binary_sensor.py b/esphome/components/xiaomi_wx08zm/binary_sensor.py index becdce05c5..c13085b5eb 100644 --- a/esphome/components/xiaomi_wx08zm/binary_sensor.py +++ b/esphome/components/xiaomi_wx08zm/binary_sensor.py @@ -1,24 +1,46 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, binary_sensor, esp32_ble_tracker -from esphome.const import CONF_BATTERY_LEVEL, CONF_MAC_ADDRESS, CONF_TABLET, DEVICE_CLASS_BATTERY, \ - DEVICE_CLASS_EMPTY, ICON_EMPTY, UNIT_PERCENT, ICON_BUG, CONF_ID +from esphome.const import ( + CONF_BATTERY_LEVEL, + CONF_MAC_ADDRESS, + CONF_TABLET, + DEVICE_CLASS_BATTERY, + DEVICE_CLASS_EMPTY, + ICON_EMPTY, + UNIT_PERCENT, + ICON_BUG, + CONF_ID, +) -DEPENDENCIES = ['esp32_ble_tracker'] -AUTO_LOAD = ['xiaomi_ble'] +DEPENDENCIES = ["esp32_ble_tracker"] +AUTO_LOAD = ["xiaomi_ble"] -xiaomi_wx08zm_ns = cg.esphome_ns.namespace('xiaomi_wx08zm') -XiaomiWX08ZM = xiaomi_wx08zm_ns.class_('XiaomiWX08ZM', binary_sensor.BinarySensor, - esp32_ble_tracker.ESPBTDeviceListener, cg.Component) +xiaomi_wx08zm_ns = cg.esphome_ns.namespace("xiaomi_wx08zm") +XiaomiWX08ZM = xiaomi_wx08zm_ns.class_( + "XiaomiWX08ZM", + binary_sensor.BinarySensor, + esp32_ble_tracker.ESPBTDeviceListener, + cg.Component, +) -CONFIG_SCHEMA = cv.All(binary_sensor.BINARY_SENSOR_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(XiaomiWX08ZM), - cv.Required(CONF_MAC_ADDRESS): cv.mac_address, - cv.Optional(CONF_TABLET): sensor.sensor_schema(UNIT_PERCENT, ICON_BUG, 0, DEVICE_CLASS_EMPTY), - cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 0, - DEVICE_CLASS_BATTERY), -}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA)) +CONFIG_SCHEMA = cv.All( + binary_sensor.BINARY_SENSOR_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(XiaomiWX08ZM), + cv.Required(CONF_MAC_ADDRESS): cv.mac_address, + cv.Optional(CONF_TABLET): sensor.sensor_schema( + UNIT_PERCENT, ICON_BUG, 0, DEVICE_CLASS_EMPTY + ), + cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema( + UNIT_PERCENT, ICON_EMPTY, 0, DEVICE_CLASS_BATTERY + ), + } + ) + .extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA) + .extend(cv.COMPONENT_SCHEMA) +) def to_code(config): diff --git a/esphome/components/yashima/climate.py b/esphome/components/yashima/climate.py index 4c4b98d9e7..2965d4cdb8 100644 --- a/esphome/components/yashima/climate.py +++ b/esphome/components/yashima/climate.py @@ -4,18 +4,24 @@ from esphome.components import climate, remote_transmitter, sensor from esphome.components.remote_base import CONF_TRANSMITTER_ID from esphome.const import CONF_ID, CONF_SENSOR, CONF_SUPPORTS_COOL, CONF_SUPPORTS_HEAT -AUTO_LOAD = ['sensor'] +AUTO_LOAD = ["sensor"] -yashima_ns = cg.esphome_ns.namespace('yashima') -YashimaClimate = yashima_ns.class_('YashimaClimate', climate.Climate, cg.Component) +yashima_ns = cg.esphome_ns.namespace("yashima") +YashimaClimate = yashima_ns.class_("YashimaClimate", climate.Climate, cg.Component) -CONFIG_SCHEMA = cv.All(climate.CLIMATE_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(YashimaClimate), - cv.GenerateID(CONF_TRANSMITTER_ID): cv.use_id(remote_transmitter.RemoteTransmitterComponent), - cv.Optional(CONF_SUPPORTS_COOL, default=True): cv.boolean, - cv.Optional(CONF_SUPPORTS_HEAT, default=True): cv.boolean, - cv.Optional(CONF_SENSOR): cv.use_id(sensor.Sensor), -}).extend(cv.COMPONENT_SCHEMA)) +CONFIG_SCHEMA = cv.All( + climate.CLIMATE_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(YashimaClimate), + cv.GenerateID(CONF_TRANSMITTER_ID): cv.use_id( + remote_transmitter.RemoteTransmitterComponent + ), + cv.Optional(CONF_SUPPORTS_COOL, default=True): cv.boolean, + cv.Optional(CONF_SUPPORTS_HEAT, default=True): cv.boolean, + cv.Optional(CONF_SENSOR): cv.use_id(sensor.Sensor), + } + ).extend(cv.COMPONENT_SCHEMA) +) def to_code(config): diff --git a/esphome/components/zyaura/sensor.py b/esphome/components/zyaura/sensor.py index 4517ed9b2e..e9035ce106 100644 --- a/esphome/components/zyaura/sensor.py +++ b/esphome/components/zyaura/sensor.py @@ -2,27 +2,47 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins from esphome.components import sensor -from esphome.const import CONF_ID, CONF_CLOCK_PIN, CONF_DATA_PIN, CONF_CO2, CONF_TEMPERATURE, \ - CONF_HUMIDITY, DEVICE_CLASS_EMPTY, DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_TEMPERATURE, \ - ICON_EMPTY, UNIT_PARTS_PER_MILLION, UNIT_CELSIUS, UNIT_PERCENT, ICON_MOLECULE_CO2 +from esphome.const import ( + CONF_ID, + CONF_CLOCK_PIN, + CONF_DATA_PIN, + CONF_CO2, + CONF_TEMPERATURE, + CONF_HUMIDITY, + DEVICE_CLASS_EMPTY, + DEVICE_CLASS_HUMIDITY, + DEVICE_CLASS_TEMPERATURE, + ICON_EMPTY, + UNIT_PARTS_PER_MILLION, + UNIT_CELSIUS, + UNIT_PERCENT, + ICON_MOLECULE_CO2, +) from esphome.cpp_helpers import gpio_pin_expression -zyaura_ns = cg.esphome_ns.namespace('zyaura') -ZyAuraSensor = zyaura_ns.class_('ZyAuraSensor', cg.PollingComponent) +zyaura_ns = cg.esphome_ns.namespace("zyaura") +ZyAuraSensor = zyaura_ns.class_("ZyAuraSensor", cg.PollingComponent) -CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(ZyAuraSensor), - cv.Required(CONF_CLOCK_PIN): cv.All(pins.internal_gpio_input_pin_schema, - pins.validate_has_interrupt), - cv.Required(CONF_DATA_PIN): cv.All(pins.internal_gpio_input_pin_schema, - pins.validate_has_interrupt), - cv.Optional(CONF_CO2): sensor.sensor_schema(UNIT_PARTS_PER_MILLION, ICON_MOLECULE_CO2, 0, - DEVICE_CLASS_EMPTY), - cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, - DEVICE_CLASS_TEMPERATURE), - cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_EMPTY, 1, - DEVICE_CLASS_HUMIDITY), -}).extend(cv.polling_component_schema('60s')) +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(ZyAuraSensor), + cv.Required(CONF_CLOCK_PIN): cv.All( + pins.internal_gpio_input_pin_schema, pins.validate_has_interrupt + ), + cv.Required(CONF_DATA_PIN): cv.All( + pins.internal_gpio_input_pin_schema, pins.validate_has_interrupt + ), + cv.Optional(CONF_CO2): sensor.sensor_schema( + UNIT_PARTS_PER_MILLION, ICON_MOLECULE_CO2, 0, DEVICE_CLASS_EMPTY + ), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( + UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE + ), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema( + UNIT_PERCENT, ICON_EMPTY, 1, DEVICE_CLASS_HUMIDITY + ), + } +).extend(cv.polling_component_schema("60s")) def to_code(config): diff --git a/esphome/config.py b/esphome/config.py index 957494ceac..995861fa6e 100644 --- a/esphome/config.py +++ b/esphome/config.py @@ -11,8 +11,13 @@ from contextlib import contextmanager import voluptuous as vol from esphome import core, core_config, yaml_util -from esphome.const import CONF_ESPHOME, CONF_PLATFORM, ESP_PLATFORMS, CONF_PACKAGES, \ - CONF_SUBSTITUTIONS +from esphome.const import ( + CONF_ESPHOME, + CONF_PLATFORM, + ESP_PLATFORMS, + CONF_PACKAGES, + CONF_SUBSTITUTIONS, +) from esphome.core import CORE, EsphomeError # noqa from esphome.helpers import color, indent from esphome.util import safe_print, OrderedDict @@ -36,39 +41,39 @@ class ComponentManifest: @property def is_platform_component(self): - return getattr(self.module, 'IS_PLATFORM_COMPONENT', False) + return getattr(self.module, "IS_PLATFORM_COMPONENT", False) @property def config_schema(self): - return getattr(self.module, 'CONFIG_SCHEMA', None) + return getattr(self.module, "CONFIG_SCHEMA", None) @property def is_multi_conf(self): - return getattr(self.module, 'MULTI_CONF', False) + return getattr(self.module, "MULTI_CONF", False) @property def to_code(self): - return getattr(self.module, 'to_code', None) + return getattr(self.module, "to_code", None) @property def esp_platforms(self): - return getattr(self.module, 'ESP_PLATFORMS', ESP_PLATFORMS) + return getattr(self.module, "ESP_PLATFORMS", ESP_PLATFORMS) @property def dependencies(self): - return getattr(self.module, 'DEPENDENCIES', []) + return getattr(self.module, "DEPENDENCIES", []) @property def conflicts_with(self): - return getattr(self.module, 'CONFLICTS_WITH', []) + return getattr(self.module, "CONFLICTS_WITH", []) @property def auto_load(self): - return getattr(self.module, 'AUTO_LOAD', []) + return getattr(self.module, "AUTO_LOAD", []) @property def codeowners(self) -> List[str]: - return getattr(self.module, 'CODEOWNERS', []) + return getattr(self.module, "CODEOWNERS", []) def _get_flags_set(self, name, config): if not hasattr(self.module, name): @@ -85,11 +90,11 @@ class ComponentManifest: @property def source_files(self): if self._is_core: - core_p = os.path.abspath(os.path.join(os.path.dirname(__file__), 'core')) - source_files = core.find_source_files(os.path.join(core_p, 'dummy')) + core_p = os.path.abspath(os.path.join(os.path.dirname(__file__), "core")) + source_files = core.find_source_files(os.path.join(core_p, "dummy")) ret = {} for f in source_files: - ret[f'esphome/core/{f}'] = os.path.join(core_p, f) + ret[f"esphome/core/{f}"] = os.path.join(core_p, f) return ret source_files = core.find_source_files(self.module.__file__) @@ -100,13 +105,15 @@ class ComponentManifest: full_file = os.path.join(directory, x) rel = os.path.relpath(full_file, self.base_components_path) # Always use / for C++ include names - rel = rel.replace(os.sep, '/') - target_file = f'esphome/components/{rel}' + rel = rel.replace(os.sep, "/") + target_file = f"esphome/components/{rel}" ret[target_file] = full_file return ret -CORE_COMPONENTS_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), 'components')) +CORE_COMPONENTS_PATH = os.path.abspath( + os.path.join(os.path.dirname(__file__), "components") +) _UNDEF = object() CUSTOM_COMPONENTS_PATH = _UNDEF @@ -115,7 +122,7 @@ def _mount_config_dir(): global CUSTOM_COMPONENTS_PATH if CUSTOM_COMPONENTS_PATH is not _UNDEF: return - custom_path = os.path.abspath(os.path.join(CORE.config_dir, 'custom_components')) + custom_path = os.path.abspath(os.path.join(CORE.config_dir, "custom_components")) if not os.path.isdir(custom_path): CUSTOM_COMPONENTS_PATH = None return @@ -131,25 +138,29 @@ def _lookup_module(domain, is_platform): _mount_config_dir() # First look for custom_components try: - module = importlib.import_module(f'custom_components.{domain}') + module = importlib.import_module(f"custom_components.{domain}") except ImportError as e: # ImportError when no such module - if 'No module named' not in str(e): - _LOGGER.warning("Unable to import custom component %s:", domain, exc_info=True) + if "No module named" not in str(e): + _LOGGER.warning( + "Unable to import custom component %s:", domain, exc_info=True + ) except Exception: # pylint: disable=broad-except # Other error means component has an issue _LOGGER.error("Unable to load custom component %s:", domain, exc_info=True) return None else: # Found in custom components - manif = ComponentManifest(module, CUSTOM_COMPONENTS_PATH, is_platform=is_platform) + manif = ComponentManifest( + module, CUSTOM_COMPONENTS_PATH, is_platform=is_platform + ) _COMPONENT_CACHE[domain] = manif return manif try: - module = importlib.import_module(f'esphome.components.{domain}') + module = importlib.import_module(f"esphome.components.{domain}") except ImportError as e: - if 'No module named' not in str(e): + if "No module named" not in str(e): _LOGGER.error("Unable to import component %s:", domain, exc_info=True) return None except Exception: # pylint: disable=broad-except @@ -162,17 +173,20 @@ def _lookup_module(domain, is_platform): def get_component(domain): - assert '.' not in domain + assert "." not in domain return _lookup_module(domain, False) def get_platform(domain, platform): - full = f'{platform}.{domain}' + full = f"{platform}.{domain}" return _lookup_module(full, True) -_COMPONENT_CACHE['esphome'] = ComponentManifest( - core_config, CORE_COMPONENTS_PATH, is_core=True, is_platform=False, +_COMPONENT_CACHE["esphome"] = ComponentManifest( + core_config, + CORE_COMPONENTS_PATH, + is_core=True, + is_platform=False, ) @@ -197,7 +211,7 @@ ConfigPath = List[Union[str, int]] def _path_begins_with(path, other): # type: (ConfigPath, ConfigPath) -> bool if len(path) < len(other): return False - return path[:len(other)] == other + return path[: len(other)] == other class Config(OrderedDict): @@ -328,7 +342,7 @@ def do_id_pass(result): # type: (Config) -> None # Look for duplicate definitions match = next((v for v in declare_ids if v[0].id == id.id), None) if match is not None: - opath = '->'.join(str(v) for v in match[1]) + opath = "->".join(str(v) for v in match[1]) result.add_str_error(f"ID {id.id} redefined! Check {opath}", path) continue declare_ids.append((id, path)) @@ -348,21 +362,31 @@ def do_id_pass(result): # type: (Config) -> None if match is None: # No declared ID with this name import difflib - error = ("Couldn't find ID '{}'. Please check you have defined " - "an ID with that name in your configuration.".format(id.id)) + + error = ( + "Couldn't find ID '{}'. Please check you have defined " + "an ID with that name in your configuration.".format(id.id) + ) # Find candidates - matches = difflib.get_close_matches(id.id, [v[0].id for v in declare_ids]) + matches = difflib.get_close_matches( + id.id, [v[0].id for v in declare_ids] + ) if matches: - matches_s = ', '.join(f'"{x}"' for x in matches) + matches_s = ", ".join(f'"{x}"' for x in matches) error += f" These IDs look similar: {matches_s}." result.add_str_error(error, path) continue - if not isinstance(match.type, MockObjClass) or not isinstance(id.type, MockObjClass): + if not isinstance(match.type, MockObjClass) or not isinstance( + id.type, MockObjClass + ): continue if not match.type.inherits_from(id.type): - result.add_str_error("ID '{}' of type {} doesn't inherit from {}. Please " - "double check your ID is pointing to the correct value" - "".format(id.id, match.type, id.type), path) + result.add_str_error( + "ID '{}' of type {} doesn't inherit from {}. Please " + "double check your ID is pointing to the correct value" + "".format(id.id, match.type, id.type), + path, + ) if id.id is None and id.type is not None: for v in declare_ids: @@ -385,12 +409,14 @@ def recursive_check_replaceme(value): return cv.Schema({cv.valid: recursive_check_replaceme})(value) if isinstance(value, ESPForceValue): pass - if isinstance(value, str) and value == 'REPLACEME': - raise cv.Invalid("Found 'REPLACEME' in configuration, this is most likely an error. " - "Please make sure you have replaced all fields from the sample " - "configuration.\n" - "If you want to use the literal REPLACEME string, " - "please use \"!force REPLACEME\"") + if isinstance(value, str) and value == "REPLACEME": + raise cv.Invalid( + "Found 'REPLACEME' in configuration, this is most likely an error. " + "Please make sure you have replaced all fields from the sample " + "configuration.\n" + "If you want to use the literal REPLACEME string, " + 'please use "!force REPLACEME"' + ) return value @@ -400,6 +426,7 @@ def validate_config(config, command_line_substitutions): # 0. Load packages if CONF_PACKAGES in config: from esphome.components.packages import do_packages_pass + result.add_output_path([CONF_PACKAGES], CONF_PACKAGES) try: config = do_packages_pass(config) @@ -411,7 +438,11 @@ def validate_config(config, command_line_substitutions): # 1. Load substitutions if CONF_SUBSTITUTIONS in config: from esphome.components import substitutions - result[CONF_SUBSTITUTIONS] = {**config[CONF_SUBSTITUTIONS], **command_line_substitutions} + + result[CONF_SUBSTITUTIONS] = { + **config[CONF_SUBSTITUTIONS], + **command_line_substitutions, + } result.add_output_path([CONF_SUBSTITUTIONS], CONF_SUBSTITUTIONS) try: substitutions.do_substitution_pass(config, command_line_substitutions) @@ -425,14 +456,19 @@ def validate_config(config, command_line_substitutions): except vol.Invalid as err: result.add_error(err) - if 'esphomeyaml' in config: - _LOGGER.warning("The esphomeyaml section has been renamed to esphome in 1.11.0. " - "Please replace 'esphomeyaml:' in your configuration with 'esphome:'.") - config[CONF_ESPHOME] = config.pop('esphomeyaml') + if "esphomeyaml" in config: + _LOGGER.warning( + "The esphomeyaml section has been renamed to esphome in 1.11.0. " + "Please replace 'esphomeyaml:' in your configuration with 'esphome:'." + ) + config[CONF_ESPHOME] = config.pop("esphomeyaml") if CONF_ESPHOME not in config: - result.add_str_error("'esphome' section missing from configuration. Please make sure " - "your configuration has an 'esphome:' line in it.", []) + result.add_str_error( + "'esphome' section missing from configuration. Please make sure " + "your configuration has an 'esphome:' line in it.", + [], + ) return result # 2. Load partial core config @@ -454,7 +490,9 @@ def validate_config(config, command_line_substitutions): load_queue.append((domain, conf)) # List of items to enter next stage - check_queue = [] # type: List[Tuple[ConfigPath, str, ConfigType, ComponentManifest]] + check_queue = ( + [] + ) # type: List[Tuple[ConfigPath, str, ConfigType, ComponentManifest]] # This step handles: # - Adding output path @@ -463,7 +501,7 @@ def validate_config(config, command_line_substitutions): while load_queue: domain, conf = load_queue.popleft() - if domain.startswith('.'): + if domain.startswith("."): # Ignore top-level keys starting with a dot continue result.add_output_path([domain], domain) @@ -499,19 +537,19 @@ def validate_config(config, command_line_substitutions): for i, p_config in enumerate(conf): path = [domain, i] # Construct temporary unknown output path - p_domain = f'{domain}.unknown' + p_domain = f"{domain}.unknown" result.add_output_path(path, p_domain) result[domain][i] = p_config if not isinstance(p_config, dict): result.add_str_error("Platform schemas must be key-value pairs.", path) continue - p_name = p_config.get('platform') + p_name = p_config.get("platform") if p_name is None: result.add_str_error("No platform specified! See 'platform' key.", path) continue # Remove temp output path and construct new one result.remove_output_path(path, p_domain) - p_domain = f'{domain}.{p_name}' + p_domain = f"{domain}.{p_name}" result.add_output_path(path, p_domain) # Try Load platform platform = get_platform(domain, p_name) @@ -544,8 +582,10 @@ def validate_config(config, command_line_substitutions): success = True for dependency in comp.dependencies: if dependency not in config: - result.add_str_error("Component {} requires component {}" - "".format(domain, dependency), path) + result.add_str_error( + "Component {} requires component {}" "".format(domain, dependency), + path, + ) success = False if not success: continue @@ -553,22 +593,32 @@ def validate_config(config, command_line_substitutions): success = True for conflict in comp.conflicts_with: if conflict in config: - result.add_str_error("Component {} cannot be used together with component {}" - "".format(domain, conflict), path) + result.add_str_error( + "Component {} cannot be used together with component {}" + "".format(domain, conflict), + path, + ) success = False if not success: continue if CORE.esp_platform not in comp.esp_platforms: - result.add_str_error("Component {} doesn't support {}.".format(domain, - CORE.esp_platform), - path) + result.add_str_error( + "Component {} doesn't support {}.".format(domain, CORE.esp_platform), + path, + ) continue - if not comp.is_platform_component and comp.config_schema is None and \ - not isinstance(conf, core.AutoLoad): - result.add_str_error("Component {} cannot be loaded via YAML " - "(no CONFIG_SCHEMA).".format(domain), path) + if ( + not comp.is_platform_component + and comp.config_schema is None + and not isinstance(conf, core.AutoLoad) + ): + result.add_str_error( + "Component {} cannot be loaded via YAML " + "(no CONFIG_SCHEMA).".format(domain), + path, + ) continue if comp.is_multi_conf: @@ -588,13 +638,13 @@ def validate_config(config, command_line_substitutions): if comp.is_platform: # Remove 'platform' key for validation input_conf = OrderedDict(conf) - platform_val = input_conf.pop('platform') + platform_val = input_conf.pop("platform") validated = comp.config_schema(input_conf) # Ensure result is OrderedDict so we can call move_to_end if not isinstance(validated, OrderedDict): validated = OrderedDict(validated) - validated['platform'] = platform_val - validated.move_to_end('platform', last=False) + validated["platform"] = platform_val + validated.move_to_end("platform", last=False) result.set_by_path(path, validated) else: validated = comp.config_schema(conf) @@ -619,18 +669,20 @@ def _nested_getitem(data, path): def humanize_error(config, validation_error): validation_error = str(validation_error) - m = re.match(r'^(.*?)\s*(?:for dictionary value )?@ data\[.*$', validation_error, re.DOTALL) + m = re.match( + r"^(.*?)\s*(?:for dictionary value )?@ data\[.*$", validation_error, re.DOTALL + ) if m is not None: validation_error = m.group(1) validation_error = validation_error.strip() - if not validation_error.endswith('.'): - validation_error += '.' + if not validation_error.endswith("."): + validation_error += "." return validation_error def _get_parent_name(path, config): if not path: - return '' + return "" for domain_path, domain in config.output_paths: if _path_begins_with(path, domain_path): if len(path) > len(domain_path): @@ -642,20 +694,22 @@ def _get_parent_name(path, config): def _format_vol_invalid(ex, config): # type: (vol.Invalid, Config) -> str - message = '' + message = "" paren = _get_parent_name(ex.path[:-1], config) if isinstance(ex, ExtraKeysInvalid): if ex.candidates: - message += '[{}] is an invalid option for [{}]. Did you mean {}?'.format( - ex.path[-1], paren, ', '.join(f'[{x}]' for x in ex.candidates)) + message += "[{}] is an invalid option for [{}]. Did you mean {}?".format( + ex.path[-1], paren, ", ".join(f"[{x}]" for x in ex.candidates) + ) else: - message += '[{}] is an invalid option for [{}]. Please check the indentation.'.format( - ex.path[-1], paren) - elif 'extra keys not allowed' in str(ex): - message += '[{}] is an invalid option for [{}].'.format(ex.path[-1], paren) - elif 'required key not provided' in str(ex): + message += "[{}] is an invalid option for [{}]. Please check the indentation.".format( + ex.path[-1], paren + ) + elif "extra keys not allowed" in str(ex): + message += "[{}] is an invalid option for [{}].".format(ex.path[-1], paren) + elif "required key not provided" in str(ex): message += "'{}' is a required option for [{}].".format(ex.path[-1], paren) else: message += humanize_error(config, ex) @@ -707,8 +761,8 @@ def line_info(config, path, highlight=True): if obj: mark = obj.start_mark source = "[source {}:{}]".format(mark.document, mark.line + 1) - return color('cyan', source) - return 'None' + return color("cyan", source) + return "None" def _print_on_next_line(obj): @@ -724,90 +778,94 @@ def _print_on_next_line(obj): def dump_dict(config, path, at_root=True): # type: (Config, ConfigPath, bool) -> Tuple[str, bool] conf = config.get_nested_item(path) - ret = '' + ret = "" multiline = False if at_root: error = config.get_error_for_path(path) if error is not None: - ret += '\n' + color('bold_red', _format_vol_invalid(error, config)) + '\n' + ret += "\n" + color("bold_red", _format_vol_invalid(error, config)) + "\n" if isinstance(conf, (list, tuple)): multiline = True if not conf: - ret += '[]' + ret += "[]" multiline = False for i in range(len(conf)): path_ = path + [i] error = config.get_error_for_path(path_) if error is not None: - ret += '\n' + color('bold_red', _format_vol_invalid(error, config)) + '\n' + ret += ( + "\n" + color("bold_red", _format_vol_invalid(error, config)) + "\n" + ) - sep = '- ' + sep = "- " if config.is_in_error_path(path_): - sep = color('red', sep) + sep = color("red", sep) msg, _ = dump_dict(config, path_, at_root=False) msg = indent(msg) inf = line_info(config, path_, highlight=config.is_in_error_path(path_)) if inf is not None: - msg = inf + '\n' + msg + msg = inf + "\n" + msg elif msg: msg = msg[2:] - ret += sep + msg + '\n' + ret += sep + msg + "\n" elif isinstance(conf, dict): multiline = True if not conf: - ret += '{}' + ret += "{}" multiline = False for k in conf.keys(): path_ = path + [k] error = config.get_error_for_path(path_) if error is not None: - ret += '\n' + color('bold_red', _format_vol_invalid(error, config)) + '\n' + ret += ( + "\n" + color("bold_red", _format_vol_invalid(error, config)) + "\n" + ) - st = f'{k}: ' + st = f"{k}: " if config.is_in_error_path(path_): - st = color('red', st) + st = color("red", st) msg, m = dump_dict(config, path_, at_root=False) inf = line_info(config, path_, highlight=config.is_in_error_path(path_)) if m: - msg = '\n' + indent(msg) + msg = "\n" + indent(msg) if inf is not None: if m: - msg = ' ' + inf + msg + msg = " " + inf + msg else: - msg = msg + ' ' + inf - ret += st + msg + '\n' + msg = msg + " " + inf + ret += st + msg + "\n" elif isinstance(conf, str): if is_secret(conf): - conf = '!secret {}'.format(is_secret(conf)) + conf = "!secret {}".format(is_secret(conf)) if not conf: conf += "''" if len(conf) > 80: - conf = '|-\n' + indent(conf) + conf = "|-\n" + indent(conf) error = config.get_error_for_path(path) - col = 'bold_red' if error else 'white' + col = "bold_red" if error else "white" ret += color(col, str(conf)) elif isinstance(conf, core.Lambda): if is_secret(conf): - conf = '!secret {}'.format(is_secret(conf)) + conf = "!secret {}".format(is_secret(conf)) - conf = '!lambda |-\n' + indent(str(conf.value)) + conf = "!lambda |-\n" + indent(str(conf.value)) error = config.get_error_for_path(path) - col = 'bold_red' if error else 'white' + col = "bold_red" if error else "white" ret += color(col, conf) elif conf is None: pass else: error = config.get_error_for_path(path) - col = 'bold_red' if error else 'white' + col = "bold_red" if error else "white" ret += color(col, str(conf)) - multiline = '\n' in ret + multiline = "\n" in ret return ret, multiline @@ -817,7 +875,9 @@ def strip_default_ids(config): to_remove = [] for i, x in enumerate(config): x = config[i] = strip_default_ids(x) - if (isinstance(x, core.ID) and not x.is_manual) or isinstance(x, core.AutoLoad): + if (isinstance(x, core.ID) and not x.is_manual) or isinstance( + x, core.AutoLoad + ): to_remove.append(x) for x in to_remove: config.remove(x) @@ -825,7 +885,9 @@ def strip_default_ids(config): to_remove = [] for k, v in config.items(): v = config[k] = strip_default_ids(v) - if (isinstance(v, core.ID) and not v.is_manual) or isinstance(v, core.AutoLoad): + if (isinstance(v, core.ID) and not v.is_manual) or isinstance( + v, core.AutoLoad + ): to_remove.append(k) for k in to_remove: config.pop(k) @@ -843,16 +905,16 @@ def read_config(command_line_substitutions): if not CORE.verbose: res = strip_default_ids(res) - safe_print(color('bold_red', "Failed config")) - safe_print('') + safe_print(color("bold_red", "Failed config")) + safe_print("") for path, domain in res.output_paths: if not res.is_in_error_path(path): continue - errstr = color('bold_red', f'{domain}:') + errstr = color("bold_red", f"{domain}:") errline = line_info(res, path) if errline: - errstr += ' ' + errline + errstr += " " + errline safe_print(errstr) safe_print(indent(dump_dict(res, path)[0])) return None diff --git a/esphome/config_helpers.py b/esphome/config_helpers.py index dcbcb70efe..a88c5983b5 100644 --- a/esphome/config_helpers.py +++ b/esphome/config_helpers.py @@ -7,14 +7,19 @@ from esphome.helpers import read_file def read_config_file(path): # type: (str) -> str - if CORE.vscode and (not CORE.ace or - os.path.abspath(path) == os.path.abspath(CORE.config_path)): - print(json.dumps({ - 'type': 'read_file', - 'path': path, - })) + if CORE.vscode and ( + not CORE.ace or os.path.abspath(path) == os.path.abspath(CORE.config_path) + ): + print( + json.dumps( + { + "type": "read_file", + "path": path, + } + ) + ) data = json.loads(input()) - assert data['type'] == 'file_response' - return data['content'] + assert data["type"] == "file_response" + return data["content"] return read_file(path) diff --git a/esphome/config_validation.py b/esphome/config_validation.py index 046e9af185..46cc1fad50 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -11,13 +11,40 @@ from string import ascii_letters, digits import voluptuous as vol from esphome import core -from esphome.const import ALLOWED_NAME_CHARS, CONF_AVAILABILITY, CONF_COMMAND_TOPIC, \ - CONF_DISCOVERY, CONF_ID, CONF_INTERNAL, CONF_NAME, CONF_PAYLOAD_AVAILABLE, \ - CONF_PAYLOAD_NOT_AVAILABLE, CONF_RETAIN, CONF_SETUP_PRIORITY, CONF_STATE_TOPIC, CONF_TOPIC, \ - CONF_HOUR, CONF_MINUTE, CONF_SECOND, CONF_VALUE, CONF_UPDATE_INTERVAL, CONF_TYPE_ID, \ - CONF_TYPE, CONF_PACKAGES -from esphome.core import CORE, HexInt, IPAddress, Lambda, TimePeriod, TimePeriodMicroseconds, \ - TimePeriodMilliseconds, TimePeriodSeconds, TimePeriodMinutes +from esphome.const import ( + ALLOWED_NAME_CHARS, + CONF_AVAILABILITY, + CONF_COMMAND_TOPIC, + CONF_DISCOVERY, + CONF_ID, + CONF_INTERNAL, + CONF_NAME, + CONF_PAYLOAD_AVAILABLE, + CONF_PAYLOAD_NOT_AVAILABLE, + CONF_RETAIN, + CONF_SETUP_PRIORITY, + CONF_STATE_TOPIC, + CONF_TOPIC, + CONF_HOUR, + CONF_MINUTE, + CONF_SECOND, + CONF_VALUE, + CONF_UPDATE_INTERVAL, + CONF_TYPE_ID, + CONF_TYPE, + CONF_PACKAGES, +) +from esphome.core import ( + CORE, + HexInt, + IPAddress, + Lambda, + TimePeriod, + TimePeriodMicroseconds, + TimePeriodMilliseconds, + TimePeriodSeconds, + TimePeriodMinutes, +) from esphome.helpers import list_starts_with, add_class_to_obj from esphome.voluptuous_schema import _Schema from esphome.yaml_util import make_data_base @@ -44,21 +71,115 @@ RequiredFieldInvalid = vol.RequiredFieldInvalid RESERVED_IDS = [ # C++ keywords http://en.cppreference.com/w/cpp/keyword - 'alignas', 'alignof', 'and', 'and_eq', 'asm', 'auto', 'bitand', 'bitor', 'bool', 'break', - 'case', 'catch', 'char', 'char16_t', 'char32_t', 'class', 'compl', 'concept', 'const', - 'constexpr', 'const_cast', 'continue', 'decltype', 'default', 'delete', 'do', 'double', - 'dynamic_cast', 'else', 'enum', 'explicit', 'export', 'export', 'extern', 'false', 'float', - 'for', 'friend', 'goto', 'if', 'inline', 'int', 'long', 'mutable', 'namespace', 'new', - 'noexcept', 'not', 'not_eq', 'nullptr', 'operator', 'or', 'or_eq', 'private', 'protected', - 'public', 'register', 'reinterpret_cast', 'requires', 'return', 'short', 'signed', 'sizeof', - 'static', 'static_assert', 'static_cast', 'struct', 'switch', 'template', 'this', - 'thread_local', 'throw', 'true', 'try', 'typedef', 'typeid', 'typename', 'union', 'unsigned', - 'using', 'virtual', 'void', 'volatile', 'wchar_t', 'while', 'xor', 'xor_eq', - - 'App', 'pinMode', 'delay', 'delayMicroseconds', 'digitalRead', 'digitalWrite', 'INPUT', - 'OUTPUT', - 'uint8_t', 'uint16_t', 'uint32_t', 'uint64_t', 'int8_t', 'int16_t', 'int32_t', 'int64_t', - 'close', 'pause', 'sleep', 'open', 'setup', 'loop', + "alignas", + "alignof", + "and", + "and_eq", + "asm", + "auto", + "bitand", + "bitor", + "bool", + "break", + "case", + "catch", + "char", + "char16_t", + "char32_t", + "class", + "compl", + "concept", + "const", + "constexpr", + "const_cast", + "continue", + "decltype", + "default", + "delete", + "do", + "double", + "dynamic_cast", + "else", + "enum", + "explicit", + "export", + "export", + "extern", + "false", + "float", + "for", + "friend", + "goto", + "if", + "inline", + "int", + "long", + "mutable", + "namespace", + "new", + "noexcept", + "not", + "not_eq", + "nullptr", + "operator", + "or", + "or_eq", + "private", + "protected", + "public", + "register", + "reinterpret_cast", + "requires", + "return", + "short", + "signed", + "sizeof", + "static", + "static_assert", + "static_cast", + "struct", + "switch", + "template", + "this", + "thread_local", + "throw", + "true", + "try", + "typedef", + "typeid", + "typename", + "union", + "unsigned", + "using", + "virtual", + "void", + "volatile", + "wchar_t", + "while", + "xor", + "xor_eq", + "App", + "pinMode", + "delay", + "delayMicroseconds", + "digitalRead", + "digitalWrite", + "INPUT", + "OUTPUT", + "uint8_t", + "uint16_t", + "uint32_t", + "uint64_t", + "int8_t", + "int16_t", + "int32_t", + "int64_t", + "close", + "pause", + "sleep", + "open", + "setup", + "loop", ] @@ -112,8 +233,10 @@ def valid_name(value): value = string_strict(value) for c in value: if c not in ALLOWED_NAME_CHARS: - raise Invalid(f"'{c}' is an invalid character for names. Valid characters are: " - f"{ALLOWED_NAME_CHARS} (lowercase, no spaces)") + raise Invalid( + f"'{c}' is an invalid character for names. Valid characters are: " + f"{ALLOWED_NAME_CHARS} (lowercase, no spaces)" + ) return value @@ -127,7 +250,9 @@ def string(value): if isinstance(value, (dict, list)): raise Invalid("string value cannot be dictionary or list.") if isinstance(value, bool): - raise Invalid("Auto-converted this value to boolean, please wrap the value in quotes.") + raise Invalid( + "Auto-converted this value to boolean, please wrap the value in quotes." + ) if isinstance(value, str): return value if value is not None: @@ -141,8 +266,10 @@ def string_strict(value): check_not_templatable(value) if isinstance(value, str): return value - raise Invalid("Must be string, got {}. did you forget putting quotes " - "around the value?".format(type(value))) + raise Invalid( + "Must be string, got {}. did you forget putting quotes " + "around the value?".format(type(value)) + ) def icon(value): @@ -150,7 +277,7 @@ def icon(value): value = string_strict(value) if not value: return value - if value.startswith('mdi:'): + if value.startswith("mdi:"): return value raise Invalid('Icons should start with prefix "mdi:"') @@ -169,12 +296,14 @@ def boolean(value): return value if isinstance(value, str): value = value.lower() - if value in ('true', 'yes', 'on', 'enable'): + if value in ("true", "yes", "on", "enable"): return True - if value in ('false', 'no', 'off', 'disable'): + if value in ("false", "no", "off", "disable"): return False - raise Invalid("Expected boolean value, but cannot convert {} to a boolean. " - "Please use 'true' or 'false'".format(value)) + raise Invalid( + "Expected boolean value, but cannot convert {} to a boolean. " + "Please use 'true' or 'false'".format(value) + ) def ensure_list(*validators): @@ -217,11 +346,13 @@ def int_(value): if isinstance(value, float): if int(value) == value: return int(value) - raise Invalid("This option only accepts integers with no fractional part. Please remove " - "the fractional part from {}".format(value)) + raise Invalid( + "This option only accepts integers with no fractional part. Please remove " + "the fractional part from {}".format(value) + ) value = string_strict(value).lower() base = 10 - if value.startswith('0x'): + if value.startswith("0x"): base = 16 try: return int(value, base) @@ -236,13 +367,18 @@ def int_range(min=None, max=None, min_included=True, max_included=True): assert isinstance(min, int) if max is not None: assert isinstance(max, int) - return All(int_, Range(min=min, max=max, min_included=min_included, max_included=max_included)) + return All( + int_, + Range(min=min, max=max, min_included=min_included, max_included=max_included), + ) def hex_int_range(min=None, max=None, min_included=True, max_included=True): """Validate that the config option is an integer in the given range.""" - return All(hex_int, - Range(min=min, max=max, min_included=min_included, max_included=max_included)) + return All( + hex_int, + Range(min=min, max=max, min_included=min_included, max_included=max_included), + ) def float_range(min=None, max=None, min_included=True, max_included=True): @@ -251,8 +387,10 @@ def float_range(min=None, max=None, min_included=True, max_included=True): assert isinstance(min, (int, float)) if max is not None: assert isinstance(max, (int, float)) - return All(float_, Range(min=min, max=max, min_included=min_included, - max_included=max_included)) + return All( + float_, + Range(min=min, max=max, min_included=min_included, max_included=max_included), + ) port = int_range(min=1, max=65535) @@ -271,29 +409,40 @@ def validate_id_name(value): raise Invalid("ID must not be empty") if value[0].isdigit(): raise Invalid("First character in ID cannot be a digit.") - if '-' in value: - raise Invalid("Dashes are not supported in IDs, please use underscores instead.") - valid_chars = ascii_letters + digits + '_' + if "-" in value: + raise Invalid( + "Dashes are not supported in IDs, please use underscores instead." + ) + valid_chars = ascii_letters + digits + "_" for char in value: if char not in valid_chars: - raise Invalid("IDs must only consist of upper/lowercase characters, the underscore" - "character and numbers. The character '{}' cannot be used" - "".format(char)) + raise Invalid( + "IDs must only consist of upper/lowercase characters, the underscore" + "character and numbers. The character '{}' cannot be used" + "".format(char) + ) if value in RESERVED_IDS: raise Invalid(f"ID '{value}' is reserved internally and cannot be used") if value in CORE.loaded_integrations: - raise Invalid("ID '{}' conflicts with the name of an esphome integration, please use " - "another ID name.".format(value)) + raise Invalid( + "ID '{}' conflicts with the name of an esphome integration, please use " + "another ID name.".format(value) + ) return value def use_id(type): """Declare that this configuration option should point to an ID with the given type.""" + def validator(value): check_not_templatable(value) if value is None: return core.ID(None, is_declaration=False, type=type) - if isinstance(value, core.ID) and value.is_declaration is False and value.type is type: + if ( + isinstance(value, core.ID) + and value.is_declaration is False + and value.type is type + ): return value return core.ID(validate_id_name(value), is_declaration=False, type=type) @@ -307,6 +456,7 @@ def declare_id(type): If two IDs with the same name exist, a validation error is thrown. """ + def validator(value): check_not_templatable(value) if value is None: @@ -349,8 +499,8 @@ def only_on(platforms): return validator_ -only_on_esp32 = only_on('ESP32') -only_on_esp8266 = only_on('ESP8266') +only_on_esp32 = only_on("ESP32") +only_on_esp8266 = only_on("ESP8266") # Adapted from: @@ -361,10 +511,10 @@ def has_at_least_one_key(*keys): def validate(obj): """Test keys exist in dict.""" if not isinstance(obj, dict): - raise Invalid('expected dictionary') + raise Invalid("expected dictionary") if not any(k in keys for k in obj): - raise Invalid('Must contain at least one of {}.'.format(', '.join(keys))) + raise Invalid("Must contain at least one of {}.".format(", ".join(keys))) return obj return validate @@ -372,15 +522,16 @@ def has_at_least_one_key(*keys): def has_exactly_one_key(*keys): """Validate that exactly one of the given keys exist in the config.""" + def validate(obj): if not isinstance(obj, dict): - raise Invalid('expected dictionary') + raise Invalid("expected dictionary") number = sum(k in keys for k in obj) if number > 1: - raise Invalid("Cannot specify more than one of {}.".format(', '.join(keys))) + raise Invalid("Cannot specify more than one of {}.".format(", ".join(keys))) if number < 1: - raise Invalid('Must contain exactly one of {}.'.format(', '.join(keys))) + raise Invalid("Must contain exactly one of {}.".format(", ".join(keys))) return obj return validate @@ -388,43 +539,50 @@ def has_exactly_one_key(*keys): def has_at_most_one_key(*keys): """Validate that at most one of the given keys exist in the config.""" + def validate(obj): if not isinstance(obj, dict): - raise Invalid('expected dictionary') + raise Invalid("expected dictionary") number = sum(k in keys for k in obj) if number > 1: - raise Invalid("Cannot specify more than one of {}.".format(', '.join(keys))) + raise Invalid("Cannot specify more than one of {}.".format(", ".join(keys))) return obj return validate -TIME_PERIOD_ERROR = "Time period {} should be format number + unit, for example 5ms, 5s, 5min, 5h" +TIME_PERIOD_ERROR = ( + "Time period {} should be format number + unit, for example 5ms, 5s, 5min, 5h" +) time_period_dict = All( - Schema({ - Optional('days'): float_, - Optional('hours'): float_, - Optional('minutes'): float_, - Optional('seconds'): float_, - Optional('milliseconds'): float_, - Optional('microseconds'): float_, - }), - has_at_least_one_key('days', 'hours', 'minutes', 'seconds', 'milliseconds', 'microseconds'), - lambda value: TimePeriod(**value) + Schema( + { + Optional("days"): float_, + Optional("hours"): float_, + Optional("minutes"): float_, + Optional("seconds"): float_, + Optional("milliseconds"): float_, + Optional("microseconds"): float_, + } + ), + has_at_least_one_key( + "days", "hours", "minutes", "seconds", "milliseconds", "microseconds" + ), + lambda value: TimePeriod(**value), ) def time_period_str_colon(value): """Validate and transform time offset with format HH:MM[:SS].""" if isinstance(value, int): - raise Invalid('Make sure you wrap time values in quotes') + raise Invalid("Make sure you wrap time values in quotes") if not isinstance(value, str): raise Invalid(TIME_PERIOD_ERROR.format(value)) try: - parsed = [int(x) for x in value.split(':')] + parsed = [int(x) for x in value.split(":")] except ValueError: # pylint: disable=raise-missing-from raise Invalid(TIME_PERIOD_ERROR.format(value)) @@ -445,34 +603,35 @@ def time_period_str_unit(value): check_not_templatable(value) if isinstance(value, int): - raise Invalid("Don't know what '{0}' means as it has no time *unit*! Did you mean " - "'{0}s'?".format(value)) + raise Invalid( + "Don't know what '{0}' means as it has no time *unit*! Did you mean " + "'{0}s'?".format(value) + ) if isinstance(value, TimePeriod): value = str(value) if not isinstance(value, str): raise Invalid("Expected string for time period with unit.") unit_to_kwarg = { - 'us': 'microseconds', - 'microseconds': 'microseconds', - 'ms': 'milliseconds', - 'milliseconds': 'milliseconds', - 's': 'seconds', - 'sec': 'seconds', - 'seconds': 'seconds', - 'min': 'minutes', - 'minutes': 'minutes', - 'h': 'hours', - 'hours': 'hours', - 'd': 'days', - 'days': 'days', + "us": "microseconds", + "microseconds": "microseconds", + "ms": "milliseconds", + "milliseconds": "milliseconds", + "s": "seconds", + "sec": "seconds", + "seconds": "seconds", + "min": "minutes", + "minutes": "minutes", + "h": "hours", + "hours": "hours", + "d": "days", + "days": "days", } match = re.match(r"^([-+]?[0-9]*\.?[0-9]*)\s*(\w*)$", value) if match is None: - raise Invalid("Expected time period with unit, " - "got {}".format(value)) + raise Invalid("Expected time period with unit, " "got {}".format(value)) kwarg = unit_to_kwarg[one_of(*unit_to_kwarg)(match.group(2))] return TimePeriod(**{kwarg: float(match.group(1))}) @@ -507,29 +666,34 @@ def time_period_in_minutes_(value): def update_interval(value): - if value == 'never': + if value == "never": return 4294967295 # uint32_t max return positive_time_period_milliseconds(value) time_period = Any(time_period_str_unit, time_period_str_colon, time_period_dict) positive_time_period = All(time_period, Range(min=TimePeriod())) -positive_time_period_milliseconds = All(positive_time_period, time_period_in_milliseconds_) +positive_time_period_milliseconds = All( + positive_time_period, time_period_in_milliseconds_ +) positive_time_period_seconds = All(positive_time_period, time_period_in_seconds_) positive_time_period_minutes = All(positive_time_period, time_period_in_minutes_) time_period_microseconds = All(time_period, time_period_in_microseconds_) -positive_time_period_microseconds = All(positive_time_period, time_period_in_microseconds_) -positive_not_null_time_period = All(time_period, - Range(min=TimePeriod(), min_included=False)) +positive_time_period_microseconds = All( + positive_time_period, time_period_in_microseconds_ +) +positive_not_null_time_period = All( + time_period, Range(min=TimePeriod(), min_included=False) +) def time_of_day(value): value = string(value) try: - date = datetime.strptime(value, '%H:%M:%S') + date = datetime.strptime(value, "%H:%M:%S") except ValueError as err: try: - date = datetime.strptime(value, '%H:%M:%S %p') + date = datetime.strptime(value, "%H:%M:%S %p") except ValueError: # pylint: disable=raise-missing-from raise Invalid(f"Invalid time of day: {err}") @@ -543,7 +707,7 @@ def time_of_day(value): def mac_address(value): value = string_strict(value) - parts = value.split(':') + parts = value.split(":") if len(parts) != 6: raise Invalid("MAC Address must consist of 6 : (colon) separated parts") parts_int = [] @@ -561,7 +725,7 @@ def mac_address(value): def bind_key(value): value = string_strict(value) - parts = [value[i:i+2] for i in range(0, len(value), 2)] + parts = [value[i : i + 2] for i in range(0, len(value), 2)] if len(parts) != 16: raise Invalid("Bind key must consist of 16 hexadecimal numbers") parts_int = [] @@ -574,7 +738,7 @@ def bind_key(value): # pylint: disable=raise-missing-from raise Invalid("Bind key must be hex values from 00 to FF") - return ''.join(f'{part:02X}' for part in parts_int) + return "".join(f"{part:02X}" for part in parts_int) def uuid(value): @@ -582,14 +746,30 @@ def uuid(value): METRIC_SUFFIXES = { - 'E': 1e18, 'P': 1e15, 'T': 1e12, 'G': 1e9, 'M': 1e6, 'k': 1e3, 'da': 10, 'd': 1e-1, - 'c': 1e-2, 'm': 0.001, 'µ': 1e-6, 'u': 1e-6, 'n': 1e-9, 'p': 1e-12, 'f': 1e-15, 'a': 1e-18, - '': 1 + "E": 1e18, + "P": 1e15, + "T": 1e12, + "G": 1e9, + "M": 1e6, + "k": 1e3, + "da": 10, + "d": 1e-1, + "c": 1e-2, + "m": 0.001, + "µ": 1e-6, + "u": 1e-6, + "n": 1e-9, + "p": 1e-12, + "f": 1e-15, + "a": 1e-18, + "": 1, } def float_with_unit(quantity, regex_suffix, optional_unit=False): - pattern = re.compile(r"^([-+]?[0-9]*\.?[0-9]*)\s*(\w*?)" + regex_suffix + r"$", re.UNICODE) + pattern = re.compile( + r"^([-+]?[0-9]*\.?[0-9]*)\s*(\w*?)" + regex_suffix + r"$", re.UNICODE + ) def validator(value): if optional_unit: @@ -647,8 +827,8 @@ def temperature(value): raise orig_err # noqa -_color_temperature_mireds = float_with_unit('Color Temperature', r'(mireds|Mireds)') -_color_temperature_kelvin = float_with_unit('Color Temperature', r'(K|Kelvin)') +_color_temperature_mireds = float_with_unit("Color Temperature", r"(mireds|Mireds)") +_color_temperature_kelvin = float_with_unit("Color Temperature", r"(K|Kelvin)") def color_temperature(value): @@ -673,8 +853,10 @@ def validate_bytes(value): raise Invalid("Invalid metric suffix {}".format(match.group(2))) multiplier = METRIC_SUFFIXES[match.group(2)] if multiplier < 1: - raise Invalid("Only suffixes with positive exponents are supported. " - "Got {}".format(match.group(2))) + raise Invalid( + "Only suffixes with positive exponents are supported. " + "Got {}".format(match.group(2)) + ) return int(mantissa * multiplier) @@ -683,7 +865,7 @@ def hostname(value): if len(value) > 63: raise Invalid("Hostnames can only be 63 characters long") for c in value: - if not (c.isalnum() or c in '_-'): + if not (c.isalnum() or c in "_-"): raise Invalid("Hostname can only have alphanumeric characters and _ or -") return value @@ -702,13 +884,15 @@ def domain_name(value): value = string_strict(value) if not value: return value - if not value.startswith('.'): + if not value.startswith("."): raise Invalid("Domain name must start with .") - if value.startswith('..'): + if value.startswith(".."): raise Invalid("Domain name must start with single .") for c in value: - if not (c.isalnum() or c in '._-'): - raise Invalid("Domain name can only have alphanumeric characters and _ or -") + if not (c.isalnum() or c in "._-"): + raise Invalid( + "Domain name can only have alphanumeric characters and _ or -" + ) return value @@ -725,15 +909,13 @@ def ipv4(value): if isinstance(value, list): parts = value elif isinstance(value, str): - parts = value.split('.') + parts = value.split(".") elif isinstance(value, IPAddress): return value else: - raise Invalid("IPv4 address must consist of either string or " - "integer list") + raise Invalid("IPv4 address must consist of either string or " "integer list") if len(parts) != 4: - raise Invalid("IPv4 address must consist of four point-separated " - "integers") + raise Invalid("IPv4 address must consist of four point-separated " "integers") parts_ = list(map(int, parts)) if not all(0 <= x < 256 for x in parts_): raise Invalid("IPv4 address parts must be in range from 0 to 255") @@ -746,38 +928,43 @@ def _valid_topic(value): raise Invalid("Can't use dictionary with topic") value = string(value) try: - raw_value = value.encode('utf-8') + raw_value = value.encode("utf-8") except UnicodeError as err: raise Invalid("MQTT topic name/filter must be valid UTF-8 string.") from err if not raw_value: raise Invalid("MQTT topic name/filter must not be empty.") if len(raw_value) > 65535: - raise Invalid("MQTT topic name/filter must not be longer than " - "65535 encoded bytes.") - if '\0' in value: - raise Invalid("MQTT topic name/filter must not contain null " - "character.") + raise Invalid( + "MQTT topic name/filter must not be longer than " "65535 encoded bytes." + ) + if "\0" in value: + raise Invalid("MQTT topic name/filter must not contain null " "character.") return value def subscribe_topic(value): """Validate that we can subscribe using this MQTT topic.""" value = _valid_topic(value) - for i in (i for i, c in enumerate(value) if c == '+'): - if (i > 0 and value[i - 1] != '/') or \ - (i < len(value) - 1 and value[i + 1] != '/'): - raise Invalid("Single-level wildcard must occupy an entire " - "level of the filter") + for i in (i for i, c in enumerate(value) if c == "+"): + if (i > 0 and value[i - 1] != "/") or ( + i < len(value) - 1 and value[i + 1] != "/" + ): + raise Invalid( + "Single-level wildcard must occupy an entire " "level of the filter" + ) - index = value.find('#') + index = value.find("#") if index != -1: if index != len(value) - 1: # If there are multiple wildcards, this will also trigger - raise Invalid("Multi-level wildcard must be the last " - "character in the topic filter.") - if len(value) > 1 and value[index - 1] != '/': - raise Invalid("Multi-level wildcard must be after a topic " - "level separator.") + raise Invalid( + "Multi-level wildcard must be the last " + "character in the topic filter." + ) + if len(value) > 1 and value[index - 1] != "/": + raise Invalid( + "Multi-level wildcard must be after a topic " "level separator." + ) return value @@ -785,14 +972,14 @@ def subscribe_topic(value): def publish_topic(value): """Validate that we can publish using this MQTT topic.""" value = _valid_topic(value) - if '+' in value or '#' in value: + if "+" in value or "#" in value: raise Invalid("Wildcards can not be used in topic names") return value def mqtt_payload(value): if value is None: - return '' + return "" return string(value) @@ -839,7 +1026,7 @@ def possibly_negative_percentage(value): has_percent_sign = False if isinstance(value, str): try: - if value.endswith('%'): + if value.endswith("%"): has_percent_sign = False value = float(value[:-1].rstrip()) / 100.0 else: @@ -861,7 +1048,7 @@ def possibly_negative_percentage(value): def percentage_int(value): - if isinstance(value, str) and value.endswith('%'): + if isinstance(value, str) and value.endswith("%"): value = int(value[:-1].rstrip()) return value @@ -870,6 +1057,7 @@ def invalid(message): """Mark this value as invalid. Each time *any* value is passed here it will result in a validation error with the given message. """ + def validator(value): raise Invalid(message) @@ -921,20 +1109,20 @@ def one_of(*values, **kwargs): - *float* (``bool``, default=False): Whether to convert the incoming values to floats. - *space* (``str``, default=' '): What to convert spaces in the input string to. """ - options = ', '.join(f"'{x}'" for x in values) - lower = kwargs.pop('lower', False) - upper = kwargs.pop('upper', False) - string_ = kwargs.pop('string', False) or lower or upper - to_int = kwargs.pop('int', False) - to_float = kwargs.pop('float', False) - space = kwargs.pop('space', ' ') + options = ", ".join(f"'{x}'" for x in values) + lower = kwargs.pop("lower", False) + upper = kwargs.pop("upper", False) + string_ = kwargs.pop("string", False) or lower or upper + to_int = kwargs.pop("int", False) + to_float = kwargs.pop("float", False) + space = kwargs.pop("space", " ") if kwargs: raise ValueError def validator(value): if string_: value = string(value) - value = value.replace(' ', space) + value = value.replace(" ", space) if to_int: value = int_(value) if to_float: @@ -945,12 +1133,15 @@ def one_of(*values, **kwargs): value = Upper(value) if value not in values: import difflib + options_ = [str(x) for x in values] option = str(value) matches = difflib.get_close_matches(option, options_) if matches: - raise Invalid("Unknown value '{}', did you mean {}?" - "".format(value, ", ".join(f"'{x}'" for x in matches))) + raise Invalid( + "Unknown value '{}', did you mean {}?" + "".format(value, ", ".join(f"'{x}'" for x in matches)) + ) raise Invalid(f"Unknown value '{value}', valid options are {options}.") return value @@ -977,7 +1168,7 @@ def enum(mapping, **kwargs): return validator -LAMBDA_ENTITY_ID_PROG = re.compile(r'id\(\s*([a-zA-Z0-9_]+\.[.a-zA-Z0-9_]+)\s*\)') +LAMBDA_ENTITY_ID_PROG = re.compile(r"id\(\s*([a-zA-Z0-9_]+\.[.a-zA-Z0-9_]+)\s*\)") def lambda_(value): @@ -986,12 +1177,15 @@ def lambda_(value): value = make_data_base(Lambda(string_strict(value)), value) entity_id_parts = re.split(LAMBDA_ENTITY_ID_PROG, value.value) if len(entity_id_parts) != 1: - entity_ids = ' '.join("'{}'".format(entity_id_parts[i]) - for i in range(1, len(entity_id_parts), 2)) - raise Invalid("Lambda contains reference to entity-id-style ID {}. " - "The id() wrapper only works for ESPHome-internal types. For importing " - "states from Home Assistant use the 'homeassistant' sensor platforms." - "".format(entity_ids)) + entity_ids = " ".join( + "'{}'".format(entity_id_parts[i]) for i in range(1, len(entity_id_parts), 2) + ) + raise Invalid( + "Lambda contains reference to entity-id-style ID {}. " + "The id() wrapper only works for ESPHome-internal types. For importing " + "states from Home Assistant use the 'homeassistant' sensor platforms." + "".format(entity_ids) + ) return value @@ -1001,18 +1195,22 @@ def returning_lambda(value): Additionally, make sure the lambda returns something. """ value = lambda_(value) - if 'return' not in value.value: - raise Invalid("Lambda doesn't contain a 'return' statement, but the lambda " - "is expected to return a value. \n" - "Please make sure the lambda contains at least one " - "return statement.") + if "return" not in value.value: + raise Invalid( + "Lambda doesn't contain a 'return' statement, but the lambda " + "is expected to return a value. \n" + "Please make sure the lambda contains at least one " + "return statement." + ) return value def dimensions(value): if isinstance(value, list): if len(value) != 2: - raise Invalid("Dimensions must have a length of two, not {}".format(len(value))) + raise Invalid( + "Dimensions must have a length of two, not {}".format(len(value)) + ) try: width, height = int(value[0]), int(value[1]) except ValueError: @@ -1024,65 +1222,91 @@ def dimensions(value): value = string(value) match = re.match(r"\s*([0-9]+)\s*[xX]\s*([0-9]+)\s*", value) if not match: - raise Invalid("Invalid value '{}' for dimensions. Only WIDTHxHEIGHT is allowed.") + raise Invalid( + "Invalid value '{}' for dimensions. Only WIDTHxHEIGHT is allowed." + ) return dimensions([match.group(1), match.group(2)]) def directory(value): import json + value = string(value) path = CORE.relative_config_path(value) - if CORE.vscode and (not CORE.ace or - os.path.abspath(path) == os.path.abspath(CORE.config_path)): - print(json.dumps({ - 'type': 'check_directory_exists', - 'path': path, - })) + if CORE.vscode and ( + not CORE.ace or os.path.abspath(path) == os.path.abspath(CORE.config_path) + ): + print( + json.dumps( + { + "type": "check_directory_exists", + "path": path, + } + ) + ) data = json.loads(input()) - assert data['type'] == 'directory_exists_response' - if data['content']: + assert data["type"] == "directory_exists_response" + if data["content"]: return value - raise Invalid("Could not find directory '{}'. Please make sure it exists (full path: {})." - "".format(path, os.path.abspath(path))) + raise Invalid( + "Could not find directory '{}'. Please make sure it exists (full path: {})." + "".format(path, os.path.abspath(path)) + ) if not os.path.exists(path): - raise Invalid("Could not find directory '{}'. Please make sure it exists (full path: {})." - "".format(path, os.path.abspath(path))) + raise Invalid( + "Could not find directory '{}'. Please make sure it exists (full path: {})." + "".format(path, os.path.abspath(path)) + ) if not os.path.isdir(path): - raise Invalid("Path '{}' is not a directory (full path: {})." - "".format(path, os.path.abspath(path))) + raise Invalid( + "Path '{}' is not a directory (full path: {})." + "".format(path, os.path.abspath(path)) + ) return value def file_(value): import json + value = string(value) path = CORE.relative_config_path(value) - if CORE.vscode and (not CORE.ace or - os.path.abspath(path) == os.path.abspath(CORE.config_path)): - print(json.dumps({ - 'type': 'check_file_exists', - 'path': path, - })) + if CORE.vscode and ( + not CORE.ace or os.path.abspath(path) == os.path.abspath(CORE.config_path) + ): + print( + json.dumps( + { + "type": "check_file_exists", + "path": path, + } + ) + ) data = json.loads(input()) - assert data['type'] == 'file_exists_response' - if data['content']: + assert data["type"] == "file_exists_response" + if data["content"]: return value - raise Invalid("Could not find file '{}'. Please make sure it exists (full path: {})." - "".format(path, os.path.abspath(path))) + raise Invalid( + "Could not find file '{}'. Please make sure it exists (full path: {})." + "".format(path, os.path.abspath(path)) + ) if not os.path.exists(path): - raise Invalid("Could not find file '{}'. Please make sure it exists (full path: {})." - "".format(path, os.path.abspath(path))) + raise Invalid( + "Could not find file '{}'. Please make sure it exists (full path: {})." + "".format(path, os.path.abspath(path)) + ) if not os.path.isfile(path): - raise Invalid("Path '{}' is not a file (full path: {})." - "".format(path, os.path.abspath(path))) + raise Invalid( + "Path '{}' is not a file (full path: {})." + "".format(path, os.path.abspath(path)) + ) return value -ENTITY_ID_CHARACTERS = 'abcdefghijklmnopqrstuvwxyz0123456789_' +ENTITY_ID_CHARACTERS = "abcdefghijklmnopqrstuvwxyz0123456789_" def entity_id(value): @@ -1091,9 +1315,9 @@ def entity_id(value): Should only be used for 'homeassistant' platforms. """ value = string_strict(value).lower() - if value.count('.') != 1: + if value.count(".") != 1: raise Invalid("Entity ID must have exactly one dot in it") - for x in value.split('.'): + for x in value.split("."): for c in x: if c not in ENTITY_ID_CHARACTERS: raise Invalid(f"Invalid character for entity ID: {c}") @@ -1119,7 +1343,7 @@ def extract_keys(schema): def typed_schema(schemas, **kwargs): """Create a schema that has a key to distinguish between schemas""" - key = kwargs.pop('key', CONF_TYPE) + key = kwargs.pop("key", CONF_TYPE) key_validator = one_of(*schemas, **kwargs) def validator(value): @@ -1176,10 +1400,11 @@ class OnlyWith(Optional): @property def default(self): # pylint: disable=unsupported-membership-test - if (self._component in CORE.raw_config or - (CONF_PACKAGES in CORE.raw_config and - self._component in - {list(x.keys())[0] for x in CORE.raw_config[CONF_PACKAGES].values()})): + if self._component in CORE.raw_config or ( + CONF_PACKAGES in CORE.raw_config + and self._component + in {list(x.keys())[0] for x in CORE.raw_config[CONF_PACKAGES].values()} + ): return self._default return vol.UNDEFINED @@ -1209,17 +1434,22 @@ def ensure_schema(schema): def validate_registry_entry(name, registry): - base_schema = ensure_schema(registry.base_schema).extend({ - Optional(CONF_TYPE_ID): valid, - }, extra=ALLOW_EXTRA) + base_schema = ensure_schema(registry.base_schema).extend( + { + Optional(CONF_TYPE_ID): valid, + }, + extra=ALLOW_EXTRA, + ) ignore_keys = extract_keys(base_schema) def validator(value): if isinstance(value, str): value = {value: {}} if not isinstance(value, dict): - raise Invalid("{} must consist of key-value mapping! Got {}" - "".format(name.title(), value)) + raise Invalid( + "{} must consist of key-value mapping! Got {}" + "".format(name.title(), value) + ) key = next((x for x in value if x not in ignore_keys), None) if key is None: raise Invalid(f"Key missing from {name}! Got {value}") @@ -1227,9 +1457,11 @@ def validate_registry_entry(name, registry): raise Invalid(f"Unable to find {name} with the name '{key}'", [key]) key2 = next((x for x in value if x != key and x not in ignore_keys), None) if key2 is not None: - raise Invalid("Cannot have two {0}s in one item. Key '{1}' overrides '{2}'! " - "Did you forget to indent the block inside the {0}?" - "".format(name, key, key2)) + raise Invalid( + "Cannot have two {0}s in one item. Key '{1}' overrides '{2}'! " + "Did you forget to indent the block inside the {0}?" + "".format(name, key, key2) + ) if value[key] is None: value[key] = {} @@ -1242,9 +1474,9 @@ def validate_registry_entry(name, registry): value[key] = registry_entry.schema(value[key]) if registry_entry.type_id is not None: - my_base_schema = base_schema.extend({ - GenerateID(CONF_TYPE_ID): declare_id(registry_entry.type_id) - }) + my_base_schema = base_schema.extend( + {GenerateID(CONF_TYPE_ID): declare_id(registry_entry.type_id)} + ) value = my_base_schema(value) return value @@ -1257,7 +1489,7 @@ def validate_registry(name, registry): def maybe_simple_value(*validators, **kwargs): - key = kwargs.pop('key', CONF_VALUE) + key = kwargs.pop("key", CONF_VALUE) validator = All(*validators) def validate(value): @@ -1268,30 +1500,35 @@ def maybe_simple_value(*validators, **kwargs): return validate -MQTT_COMPONENT_AVAILABILITY_SCHEMA = Schema({ - Required(CONF_TOPIC): subscribe_topic, - Optional(CONF_PAYLOAD_AVAILABLE, default='online'): mqtt_payload, - Optional(CONF_PAYLOAD_NOT_AVAILABLE, default='offline'): mqtt_payload, -}) +MQTT_COMPONENT_AVAILABILITY_SCHEMA = Schema( + { + Required(CONF_TOPIC): subscribe_topic, + Optional(CONF_PAYLOAD_AVAILABLE, default="online"): mqtt_payload, + Optional(CONF_PAYLOAD_NOT_AVAILABLE, default="offline"): mqtt_payload, + } +) -MQTT_COMPONENT_SCHEMA = Schema({ - Optional(CONF_NAME): string, - Optional(CONF_RETAIN): All(requires_component('mqtt'), boolean), - Optional(CONF_DISCOVERY): All(requires_component('mqtt'), boolean), - Optional(CONF_STATE_TOPIC): All(requires_component('mqtt'), publish_topic), - Optional(CONF_AVAILABILITY): All(requires_component('mqtt'), - Any(None, MQTT_COMPONENT_AVAILABILITY_SCHEMA)), - Optional(CONF_INTERNAL): boolean, -}) +MQTT_COMPONENT_SCHEMA = Schema( + { + Optional(CONF_NAME): string, + Optional(CONF_RETAIN): All(requires_component("mqtt"), boolean), + Optional(CONF_DISCOVERY): All(requires_component("mqtt"), boolean), + Optional(CONF_STATE_TOPIC): All(requires_component("mqtt"), publish_topic), + Optional(CONF_AVAILABILITY): All( + requires_component("mqtt"), Any(None, MQTT_COMPONENT_AVAILABILITY_SCHEMA) + ), + Optional(CONF_INTERNAL): boolean, + } +) MQTT_COMPONENT_SCHEMA.add_extra(_nameable_validator) -MQTT_COMMAND_COMPONENT_SCHEMA = MQTT_COMPONENT_SCHEMA.extend({ - Optional(CONF_COMMAND_TOPIC): All(requires_component('mqtt'), subscribe_topic), -}) +MQTT_COMMAND_COMPONENT_SCHEMA = MQTT_COMPONENT_SCHEMA.extend( + { + Optional(CONF_COMMAND_TOPIC): All(requires_component("mqtt"), subscribe_topic), + } +) -COMPONENT_SCHEMA = Schema({ - Optional(CONF_SETUP_PRIORITY): float_ -}) +COMPONENT_SCHEMA = Schema({Optional(CONF_SETUP_PRIORITY): float_}) def polling_component_schema(default_update_interval): @@ -1301,10 +1538,16 @@ def polling_component_schema(default_update_interval): :param default_update_interval: The default update interval to set for the integration. """ if default_update_interval is None: - return COMPONENT_SCHEMA.extend({ - Required(CONF_UPDATE_INTERVAL): default_update_interval, - }) + return COMPONENT_SCHEMA.extend( + { + Required(CONF_UPDATE_INTERVAL): default_update_interval, + } + ) assert isinstance(default_update_interval, str) - return COMPONENT_SCHEMA.extend({ - Optional(CONF_UPDATE_INTERVAL, default=default_update_interval): update_interval, - }) + return COMPONENT_SCHEMA.extend( + { + Optional( + CONF_UPDATE_INTERVAL, default=default_update_interval + ): update_interval, + } + ) diff --git a/esphome/const.py b/esphome/const.py index e431784985..6bb842c22b 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -2,708 +2,708 @@ MAJOR_VERSION = 1 MINOR_VERSION = 17 -PATCH_VERSION = '0-dev' -__short_version__ = f'{MAJOR_VERSION}.{MINOR_VERSION}' -__version__ = f'{__short_version__}.{PATCH_VERSION}' +PATCH_VERSION = "0-dev" +__short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}" +__version__ = f"{__short_version__}.{PATCH_VERSION}" -ESP_PLATFORM_ESP32 = 'ESP32' -ESP_PLATFORM_ESP8266 = 'ESP8266' +ESP_PLATFORM_ESP32 = "ESP32" +ESP_PLATFORM_ESP8266 = "ESP8266" ESP_PLATFORMS = [ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266] -ALLOWED_NAME_CHARS = 'abcdefghijklmnopqrstuvwxyz0123456789_-' +ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789_-" # Lookup table from ESP32 arduino framework version to latest platformio # package with that version # See also https://github.com/platformio/platform-espressif32/releases ARDUINO_VERSION_ESP32 = { - 'dev': 'https://github.com/platformio/platform-espressif32.git', - '1.0.4': 'espressif32@1.12.4', - '1.0.3': 'espressif32@1.10.0', - '1.0.2': 'espressif32@1.9.0', - '1.0.1': 'espressif32@1.7.0', - '1.0.0': 'espressif32@1.5.0', + "dev": "https://github.com/platformio/platform-espressif32.git", + "1.0.4": "espressif32@1.12.4", + "1.0.3": "espressif32@1.10.0", + "1.0.2": "espressif32@1.9.0", + "1.0.1": "espressif32@1.7.0", + "1.0.0": "espressif32@1.5.0", } # See also https://github.com/platformio/platform-espressif8266/releases ARDUINO_VERSION_ESP8266 = { - 'dev': 'https://github.com/platformio/platform-espressif8266.git', - '2.7.4': 'espressif8266@2.6.2', - '2.7.3': 'espressif8266@2.6.1', - '2.7.2': 'espressif8266@2.6.0', - '2.7.1': 'espressif8266@2.5.3', - '2.7.0': 'espressif8266@2.5.0', - '2.6.3': 'espressif8266@2.4.0', - '2.6.2': 'espressif8266@2.3.1', - '2.6.1': 'espressif8266@2.3.0', - '2.5.2': 'espressif8266@2.2.3', - '2.5.1': 'espressif8266@2.1.1', - '2.5.0': 'espressif8266@2.0.4', - '2.4.2': 'espressif8266@1.8.0', - '2.4.1': 'espressif8266@1.7.3', - '2.4.0': 'espressif8266@1.6.0', - '2.3.0': 'espressif8266@1.5.0', + "dev": "https://github.com/platformio/platform-espressif8266.git", + "2.7.4": "espressif8266@2.6.2", + "2.7.3": "espressif8266@2.6.1", + "2.7.2": "espressif8266@2.6.0", + "2.7.1": "espressif8266@2.5.3", + "2.7.0": "espressif8266@2.5.0", + "2.6.3": "espressif8266@2.4.0", + "2.6.2": "espressif8266@2.3.1", + "2.6.1": "espressif8266@2.3.0", + "2.5.2": "espressif8266@2.2.3", + "2.5.1": "espressif8266@2.1.1", + "2.5.0": "espressif8266@2.0.4", + "2.4.2": "espressif8266@1.8.0", + "2.4.1": "espressif8266@1.7.3", + "2.4.0": "espressif8266@1.6.0", + "2.3.0": "espressif8266@1.5.0", } -SOURCE_FILE_EXTENSIONS = {'.cpp', '.hpp', '.h', '.c', '.tcc', '.ino'} -HEADER_FILE_EXTENSIONS = {'.h', '.hpp', '.tcc'} +SOURCE_FILE_EXTENSIONS = {".cpp", ".hpp", ".h", ".c", ".tcc", ".ino"} +HEADER_FILE_EXTENSIONS = {".h", ".hpp", ".tcc"} -CONF_ABOVE = 'above' -CONF_ACCELERATION = 'acceleration' -CONF_ACCELERATION_X = 'acceleration_x' -CONF_ACCELERATION_Y = 'acceleration_y' -CONF_ACCELERATION_Z = 'acceleration_z' -CONF_ACCURACY = 'accuracy' -CONF_ACCURACY_DECIMALS = 'accuracy_decimals' -CONF_ACTION_ID = 'action_id' -CONF_ADDRESS = 'address' -CONF_ALPHA = 'alpha' -CONF_AND = 'and' -CONF_AP = 'ap' -CONF_ARDUINO_VERSION = 'arduino_version' -CONF_ARGS = 'args' -CONF_ASSUMED_STATE = 'assumed_state' -CONF_AT = 'at' -CONF_ATTENUATION = 'attenuation' -CONF_AUTH = 'auth' -CONF_AUTO_MODE = 'auto_mode' -CONF_AUTOMATION_ID = 'automation_id' -CONF_AVAILABILITY = 'availability' -CONF_AWAY = 'away' -CONF_AWAY_CONFIG = 'away_config' -CONF_BACKLIGHT_PIN = 'backlight_pin' -CONF_BATTERY_LEVEL = 'battery_level' -CONF_BATTERY_VOLTAGE = 'battery_voltage' -CONF_BAUD_RATE = 'baud_rate' -CONF_BELOW = 'below' -CONF_BINARY = 'binary' -CONF_BINARY_SENSOR = 'binary_sensor' -CONF_BINARY_SENSORS = 'binary_sensors' -CONF_BINDKEY = 'bindkey' -CONF_BIRTH_MESSAGE = 'birth_message' -CONF_BIT_DEPTH = 'bit_depth' -CONF_BLUE = 'blue' -CONF_BOARD = 'board' -CONF_BOARD_FLASH_MODE = 'board_flash_mode' -CONF_BRANCH = 'branch' -CONF_BRIGHTNESS = 'brightness' -CONF_BROKER = 'broker' -CONF_BSSID = 'bssid' -CONF_BUFFER_SIZE = 'buffer_size' -CONF_BUILD_PATH = 'build_path' -CONF_BUS_VOLTAGE = 'bus_voltage' -CONF_BUSY_PIN = 'busy_pin' -CONF_CALIBRATE_LINEAR = 'calibrate_linear' -CONF_CALIBRATION = 'calibration' -CONF_CAPACITANCE = 'capacitance' -CONF_CARRIER_DUTY_PERCENT = 'carrier_duty_percent' -CONF_CARRIER_FREQUENCY = 'carrier_frequency' +CONF_ABOVE = "above" +CONF_ACCELERATION = "acceleration" +CONF_ACCELERATION_X = "acceleration_x" +CONF_ACCELERATION_Y = "acceleration_y" +CONF_ACCELERATION_Z = "acceleration_z" +CONF_ACCURACY = "accuracy" +CONF_ACCURACY_DECIMALS = "accuracy_decimals" +CONF_ACTION_ID = "action_id" +CONF_ADDRESS = "address" +CONF_ALPHA = "alpha" +CONF_AND = "and" +CONF_AP = "ap" +CONF_ARDUINO_VERSION = "arduino_version" +CONF_ARGS = "args" +CONF_ASSUMED_STATE = "assumed_state" +CONF_AT = "at" +CONF_ATTENUATION = "attenuation" +CONF_AUTH = "auth" +CONF_AUTO_MODE = "auto_mode" +CONF_AUTOMATION_ID = "automation_id" +CONF_AVAILABILITY = "availability" +CONF_AWAY = "away" +CONF_AWAY_CONFIG = "away_config" +CONF_BACKLIGHT_PIN = "backlight_pin" +CONF_BATTERY_LEVEL = "battery_level" +CONF_BATTERY_VOLTAGE = "battery_voltage" +CONF_BAUD_RATE = "baud_rate" +CONF_BELOW = "below" +CONF_BINARY = "binary" +CONF_BINARY_SENSOR = "binary_sensor" +CONF_BINARY_SENSORS = "binary_sensors" +CONF_BINDKEY = "bindkey" +CONF_BIRTH_MESSAGE = "birth_message" +CONF_BIT_DEPTH = "bit_depth" +CONF_BLUE = "blue" +CONF_BOARD = "board" +CONF_BOARD_FLASH_MODE = "board_flash_mode" +CONF_BRANCH = "branch" +CONF_BRIGHTNESS = "brightness" +CONF_BROKER = "broker" +CONF_BSSID = "bssid" +CONF_BUFFER_SIZE = "buffer_size" +CONF_BUILD_PATH = "build_path" +CONF_BUS_VOLTAGE = "bus_voltage" +CONF_BUSY_PIN = "busy_pin" +CONF_CALIBRATE_LINEAR = "calibrate_linear" +CONF_CALIBRATION = "calibration" +CONF_CAPACITANCE = "capacitance" +CONF_CARRIER_DUTY_PERCENT = "carrier_duty_percent" +CONF_CARRIER_FREQUENCY = "carrier_frequency" CONF_CERTIFICATE = "certificate" CONF_CERTIFICATE_AUTHORITY = "certificate_authority" -CONF_CHANGE_MODE_EVERY = 'change_mode_every' -CONF_CHANNEL = 'channel' -CONF_CHANNELS = 'channels' -CONF_CHIPSET = 'chipset' -CONF_CLIENT_ID = 'client_id' -CONF_CLK_PIN = 'clk_pin' -CONF_CLOCK_PIN = 'clock_pin' -CONF_CLOSE_ACTION = 'close_action' -CONF_CLOSE_DURATION = 'close_duration' -CONF_CLOSE_ENDSTOP = 'close_endstop' -CONF_CO2 = 'co2' -CONF_CODE = 'code' -CONF_COLD_WHITE = 'cold_white' -CONF_COLD_WHITE_COLOR_TEMPERATURE = 'cold_white_color_temperature' -CONF_COLOR_CORRECT = 'color_correct' -CONF_COLOR_TEMPERATURE = 'color_temperature' -CONF_COLORS = 'colors' -CONF_COMMAND = 'command' -CONF_COMMAND_TOPIC = 'command_topic' -CONF_COMMENT = 'comment' -CONF_COMMIT = 'commit' -CONF_COMPONENT_ID = 'component_id' -CONF_COMPONENTS = 'components' -CONF_CONDITION = 'condition' -CONF_CONDITION_ID = 'condition_id' -CONF_CONDUCTIVITY = 'conductivity' -CONF_CONTRAST = 'contrast' -CONF_COOL_ACTION = 'cool_action' -CONF_COOL_MODE = 'cool_mode' -CONF_COUNT_MODE = 'count_mode' -CONF_CRON = 'cron' -CONF_CS_PIN = 'cs_pin' -CONF_CSS_INCLUDE = 'css_include' -CONF_CSS_URL = 'css_url' -CONF_CURRENT = 'current' -CONF_CURRENT_OPERATION = 'current_operation' -CONF_CURRENT_RESISTOR = 'current_resistor' -CONF_DALLAS_ID = 'dallas_id' -CONF_DATA = 'data' -CONF_DATA_PIN = 'data_pin' -CONF_DATA_PINS = 'data_pins' -CONF_DATA_RATE = 'data_rate' -CONF_DATA_TEMPLATE = 'data_template' -CONF_DAYS_OF_MONTH = 'days_of_month' -CONF_DAYS_OF_WEEK = 'days_of_week' -CONF_DC_PIN = 'dc_pin' -CONF_DEBOUNCE = 'debounce' -CONF_DECELERATION = 'deceleration' -CONF_DEFAULT_TARGET_TEMPERATURE_HIGH = 'default_target_temperature_high' -CONF_DEFAULT_TARGET_TEMPERATURE_LOW = 'default_target_temperature_low' -CONF_DEFAULT_TRANSITION_LENGTH = 'default_transition_length' -CONF_DELAY = 'delay' -CONF_DELTA = 'delta' -CONF_DEVICE = 'device' -CONF_DEVICE_CLASS = 'device_class' -CONF_DIMENSIONS = 'dimensions' -CONF_DIO_PIN = 'dio_pin' -CONF_DIR_PIN = 'dir_pin' -CONF_DIRECTION = 'direction' -CONF_DIRECTION_OUTPUT = 'direction_output' -CONF_DISCOVERY = 'discovery' -CONF_DISCOVERY_PREFIX = 'discovery_prefix' -CONF_DISCOVERY_RETAIN = 'discovery_retain' -CONF_DISTANCE = 'distance' -CONF_DITHER = 'dither' -CONF_DIV_RATIO = 'div_ratio' -CONF_DNS1 = 'dns1' -CONF_DNS2 = 'dns2' -CONF_DOMAIN = 'domain' -CONF_DRY_ACTION = 'dry_action' -CONF_DRY_MODE = 'dry_mode' -CONF_DUMP = 'dump' -CONF_DURATION = 'duration' -CONF_EAP = 'eap' -CONF_ECHO_PIN = 'echo_pin' -CONF_EFFECT = 'effect' -CONF_EFFECTS = 'effects' -CONF_ELSE = 'else' -CONF_ENABLE_PIN = 'enable_pin' -CONF_ENABLE_TIME = 'enable_time' -CONF_ENERGY = 'energy' -CONF_ENTITY_ID = 'entity_id' -CONF_ESP8266_RESTORE_FROM_FLASH = 'esp8266_restore_from_flash' -CONF_ESPHOME = 'esphome' -CONF_ESPHOME_CORE_VERSION = 'esphome_core_version' -CONF_EVENT = 'event' -CONF_EXPIRE_AFTER = 'expire_after' -CONF_EXTERNAL_VCC = 'external_vcc' -CONF_FALLING_EDGE = 'falling_edge' -CONF_FAMILY = 'family' -CONF_FAN_MODE = 'fan_mode' -CONF_FAN_MODE_AUTO_ACTION = 'fan_mode_auto_action' -CONF_FAN_MODE_DIFFUSE_ACTION = 'fan_mode_diffuse_action' -CONF_FAN_MODE_FOCUS_ACTION = 'fan_mode_focus_action' -CONF_FAN_MODE_HIGH_ACTION = 'fan_mode_high_action' -CONF_FAN_MODE_LOW_ACTION = 'fan_mode_low_action' -CONF_FAN_MODE_MEDIUM_ACTION = 'fan_mode_medium_action' -CONF_FAN_MODE_MIDDLE_ACTION = 'fan_mode_middle_action' -CONF_FAN_MODE_OFF_ACTION = 'fan_mode_off_action' -CONF_FAN_MODE_ON_ACTION = 'fan_mode_on_action' -CONF_FAN_ONLY_ACTION = 'fan_only_action' -CONF_FAN_ONLY_MODE = 'fan_only_mode' -CONF_FAST_CONNECT = 'fast_connect' -CONF_FILE = 'file' -CONF_FILTER = 'filter' -CONF_FILTER_OUT = 'filter_out' -CONF_FILTERS = 'filters' -CONF_FLASH_LENGTH = 'flash_length' -CONF_FOR = 'for' -CONF_FORCE_UPDATE = 'force_update' -CONF_FORMALDEHYDE = 'formaldehyde' -CONF_FORMAT = 'format' -CONF_FREQUENCY = 'frequency' -CONF_FROM = 'from' -CONF_FULL_UPDATE_EVERY = 'full_update_every' -CONF_GAIN = 'gain' -CONF_GAMMA_CORRECT = 'gamma_correct' -CONF_GAS_RESISTANCE = 'gas_resistance' -CONF_GATEWAY = 'gateway' -CONF_GLYPHS = 'glyphs' -CONF_GPIO = 'gpio' -CONF_GREEN = 'green' -CONF_GROUP = 'group' -CONF_HARDWARE_UART = 'hardware_uart' -CONF_HEARTBEAT = 'heartbeat' -CONF_HEAT_ACTION = 'heat_action' -CONF_HEAT_MODE = 'heat_mode' -CONF_HEATER = 'heater' -CONF_HIDDEN = 'hidden' -CONF_HIDE_TIMESTAMP = 'hide_timestamp' -CONF_HIGH = 'high' -CONF_HIGH_VOLTAGE_REFERENCE = 'high_voltage_reference' -CONF_HOUR = 'hour' -CONF_HOURS = 'hours' -CONF_HUMIDITY = 'humidity' +CONF_CHANGE_MODE_EVERY = "change_mode_every" +CONF_CHANNEL = "channel" +CONF_CHANNELS = "channels" +CONF_CHIPSET = "chipset" +CONF_CLIENT_ID = "client_id" +CONF_CLK_PIN = "clk_pin" +CONF_CLOCK_PIN = "clock_pin" +CONF_CLOSE_ACTION = "close_action" +CONF_CLOSE_DURATION = "close_duration" +CONF_CLOSE_ENDSTOP = "close_endstop" +CONF_CO2 = "co2" +CONF_CODE = "code" +CONF_COLD_WHITE = "cold_white" +CONF_COLD_WHITE_COLOR_TEMPERATURE = "cold_white_color_temperature" +CONF_COLOR_CORRECT = "color_correct" +CONF_COLOR_TEMPERATURE = "color_temperature" +CONF_COLORS = "colors" +CONF_COMMAND = "command" +CONF_COMMAND_TOPIC = "command_topic" +CONF_COMMENT = "comment" +CONF_COMMIT = "commit" +CONF_COMPONENT_ID = "component_id" +CONF_COMPONENTS = "components" +CONF_CONDITION = "condition" +CONF_CONDITION_ID = "condition_id" +CONF_CONDUCTIVITY = "conductivity" +CONF_CONTRAST = "contrast" +CONF_COOL_ACTION = "cool_action" +CONF_COOL_MODE = "cool_mode" +CONF_COUNT_MODE = "count_mode" +CONF_CRON = "cron" +CONF_CS_PIN = "cs_pin" +CONF_CSS_INCLUDE = "css_include" +CONF_CSS_URL = "css_url" +CONF_CURRENT = "current" +CONF_CURRENT_OPERATION = "current_operation" +CONF_CURRENT_RESISTOR = "current_resistor" +CONF_DALLAS_ID = "dallas_id" +CONF_DATA = "data" +CONF_DATA_PIN = "data_pin" +CONF_DATA_PINS = "data_pins" +CONF_DATA_RATE = "data_rate" +CONF_DATA_TEMPLATE = "data_template" +CONF_DAYS_OF_MONTH = "days_of_month" +CONF_DAYS_OF_WEEK = "days_of_week" +CONF_DC_PIN = "dc_pin" +CONF_DEBOUNCE = "debounce" +CONF_DECELERATION = "deceleration" +CONF_DEFAULT_TARGET_TEMPERATURE_HIGH = "default_target_temperature_high" +CONF_DEFAULT_TARGET_TEMPERATURE_LOW = "default_target_temperature_low" +CONF_DEFAULT_TRANSITION_LENGTH = "default_transition_length" +CONF_DELAY = "delay" +CONF_DELTA = "delta" +CONF_DEVICE = "device" +CONF_DEVICE_CLASS = "device_class" +CONF_DIMENSIONS = "dimensions" +CONF_DIO_PIN = "dio_pin" +CONF_DIR_PIN = "dir_pin" +CONF_DIRECTION = "direction" +CONF_DIRECTION_OUTPUT = "direction_output" +CONF_DISCOVERY = "discovery" +CONF_DISCOVERY_PREFIX = "discovery_prefix" +CONF_DISCOVERY_RETAIN = "discovery_retain" +CONF_DISTANCE = "distance" +CONF_DITHER = "dither" +CONF_DIV_RATIO = "div_ratio" +CONF_DNS1 = "dns1" +CONF_DNS2 = "dns2" +CONF_DOMAIN = "domain" +CONF_DRY_ACTION = "dry_action" +CONF_DRY_MODE = "dry_mode" +CONF_DUMP = "dump" +CONF_DURATION = "duration" +CONF_EAP = "eap" +CONF_ECHO_PIN = "echo_pin" +CONF_EFFECT = "effect" +CONF_EFFECTS = "effects" +CONF_ELSE = "else" +CONF_ENABLE_PIN = "enable_pin" +CONF_ENABLE_TIME = "enable_time" +CONF_ENERGY = "energy" +CONF_ENTITY_ID = "entity_id" +CONF_ESP8266_RESTORE_FROM_FLASH = "esp8266_restore_from_flash" +CONF_ESPHOME = "esphome" +CONF_ESPHOME_CORE_VERSION = "esphome_core_version" +CONF_EVENT = "event" +CONF_EXPIRE_AFTER = "expire_after" +CONF_EXTERNAL_VCC = "external_vcc" +CONF_FALLING_EDGE = "falling_edge" +CONF_FAMILY = "family" +CONF_FAN_MODE = "fan_mode" +CONF_FAN_MODE_AUTO_ACTION = "fan_mode_auto_action" +CONF_FAN_MODE_DIFFUSE_ACTION = "fan_mode_diffuse_action" +CONF_FAN_MODE_FOCUS_ACTION = "fan_mode_focus_action" +CONF_FAN_MODE_HIGH_ACTION = "fan_mode_high_action" +CONF_FAN_MODE_LOW_ACTION = "fan_mode_low_action" +CONF_FAN_MODE_MEDIUM_ACTION = "fan_mode_medium_action" +CONF_FAN_MODE_MIDDLE_ACTION = "fan_mode_middle_action" +CONF_FAN_MODE_OFF_ACTION = "fan_mode_off_action" +CONF_FAN_MODE_ON_ACTION = "fan_mode_on_action" +CONF_FAN_ONLY_ACTION = "fan_only_action" +CONF_FAN_ONLY_MODE = "fan_only_mode" +CONF_FAST_CONNECT = "fast_connect" +CONF_FILE = "file" +CONF_FILTER = "filter" +CONF_FILTER_OUT = "filter_out" +CONF_FILTERS = "filters" +CONF_FLASH_LENGTH = "flash_length" +CONF_FOR = "for" +CONF_FORCE_UPDATE = "force_update" +CONF_FORMALDEHYDE = "formaldehyde" +CONF_FORMAT = "format" +CONF_FREQUENCY = "frequency" +CONF_FROM = "from" +CONF_FULL_UPDATE_EVERY = "full_update_every" +CONF_GAIN = "gain" +CONF_GAMMA_CORRECT = "gamma_correct" +CONF_GAS_RESISTANCE = "gas_resistance" +CONF_GATEWAY = "gateway" +CONF_GLYPHS = "glyphs" +CONF_GPIO = "gpio" +CONF_GREEN = "green" +CONF_GROUP = "group" +CONF_HARDWARE_UART = "hardware_uart" +CONF_HEARTBEAT = "heartbeat" +CONF_HEAT_ACTION = "heat_action" +CONF_HEAT_MODE = "heat_mode" +CONF_HEATER = "heater" +CONF_HIDDEN = "hidden" +CONF_HIDE_TIMESTAMP = "hide_timestamp" +CONF_HIGH = "high" +CONF_HIGH_VOLTAGE_REFERENCE = "high_voltage_reference" +CONF_HOUR = "hour" +CONF_HOURS = "hours" +CONF_HUMIDITY = "humidity" CONF_HYSTERESIS = "hysteresis" -CONF_I2C = 'i2c' -CONF_I2C_ID = 'i2c_id' -CONF_ICON = 'icon' -CONF_ID = 'id' -CONF_IDENTITY = 'identity' -CONF_IDLE = 'idle' -CONF_IDLE_ACTION = 'idle_action' -CONF_IDLE_LEVEL = 'idle_level' -CONF_IDLE_TIME = 'idle_time' -CONF_IF = 'if' -CONF_IIR_FILTER = 'iir_filter' -CONF_ILLUMINANCE = 'illuminance' -CONF_IMPEDANCE = 'impedance' -CONF_INCLUDES = 'includes' -CONF_INDEX = 'index' -CONF_INDOOR = 'indoor' -CONF_INITIAL_MODE = 'initial_mode' -CONF_INITIAL_VALUE = 'initial_value' -CONF_INTEGRATION_TIME = 'integration_time' -CONF_INTENSITY = 'intensity' -CONF_INTERLOCK = 'interlock' -CONF_INTERNAL = 'internal' -CONF_INTERNAL_FILTER = 'internal_filter' -CONF_INTERVAL = 'interval' -CONF_INVALID_COOLDOWN = 'invalid_cooldown' -CONF_INVERT = 'invert' -CONF_INVERTED = 'inverted' -CONF_IP_ADDRESS = 'ip_address' -CONF_JS_INCLUDE = 'js_include' -CONF_JS_URL = 'js_url' -CONF_JVC = 'jvc' -CONF_KEEP_ON_TIME = 'keep_on_time' -CONF_KEEPALIVE = 'keepalive' -CONF_KEY = 'key' -CONF_LAMBDA = 'lambda' -CONF_LENGTH = 'length' -CONF_LEVEL = 'level' -CONF_LG = 'lg' -CONF_LIBRARIES = 'libraries' -CONF_LIGHT = 'light' -CONF_LIGHTNING_ENERGY = 'lightning_energy' -CONF_LIGHTNING_THRESHOLD = 'lightning_threshold' -CONF_LOADED_INTEGRATIONS = 'loaded_integrations' -CONF_LOCAL = 'local' -CONF_LOG_TOPIC = 'log_topic' -CONF_LOGGER = 'logger' -CONF_LOGS = 'logs' -CONF_LOW = 'low' -CONF_LOW_VOLTAGE_REFERENCE = 'low_voltage_reference' -CONF_MAC_ADDRESS = 'mac_address' -CONF_MAINS_FILTER = 'mains_filter' -CONF_MAKE_ID = 'make_id' -CONF_MANUAL_IP = 'manual_ip' -CONF_MANUFACTURER_ID = 'manufacturer_id' -CONF_MASK_DISTURBER = 'mask_disturber' -CONF_MAX_CURRENT = 'max_current' -CONF_MAX_DURATION = 'max_duration' -CONF_MAX_LENGTH = 'max_length' -CONF_MAX_LEVEL = 'max_level' -CONF_MAX_POWER = 'max_power' -CONF_MAX_REFRESH_RATE = 'max_refresh_rate' -CONF_MAX_SPEED = 'max_speed' -CONF_MAX_TEMPERATURE = 'max_temperature' -CONF_MAX_VALUE = 'max_value' -CONF_MAX_VOLTAGE = 'max_voltage' -CONF_MEASUREMENT_DURATION = 'measurement_duration' -CONF_MEASUREMENT_SEQUENCE_NUMBER = 'measurement_sequence_number' -CONF_MEDIUM = 'medium' -CONF_MEMORY_BLOCKS = 'memory_blocks' -CONF_METHOD = 'method' -CONF_MIN_LENGTH = 'min_length' -CONF_MIN_LEVEL = 'min_level' -CONF_MIN_POWER = 'min_power' -CONF_MIN_TEMPERATURE = 'min_temperature' -CONF_MIN_VALUE = 'min_value' -CONF_MINUTE = 'minute' -CONF_MINUTES = 'minutes' -CONF_MISO_PIN = 'miso_pin' -CONF_MODE = 'mode' -CONF_MODEL = 'model' -CONF_MOISTURE = 'moisture' -CONF_MONTHS = 'months' -CONF_MOSI_PIN = 'mosi_pin' -CONF_MOTION = 'motion' -CONF_MOVEMENT_COUNTER = 'movement_counter' -CONF_MQTT = 'mqtt' -CONF_MQTT_ID = 'mqtt_id' -CONF_MULTIPLEXER = 'multiplexer' -CONF_MULTIPLY = 'multiply' -CONF_NAME = 'name' -CONF_NBITS = 'nbits' -CONF_NEC = 'nec' -CONF_NETWORKS = 'networks' -CONF_NOISE_LEVEL = 'noise_level' -CONF_NUM_ATTEMPTS = 'num_attempts' -CONF_NUM_CHANNELS = 'num_channels' -CONF_NUM_CHIPS = 'num_chips' -CONF_NUM_LEDS = 'num_leds' -CONF_NUMBER = 'number' -CONF_OFF_MODE = 'off_mode' -CONF_OFFSET = 'offset' -CONF_ON = 'on' -CONF_ON_BLE_ADVERTISE = 'on_ble_advertise' -CONF_ON_BLE_MANUFACTURER_DATA_ADVERTISE = 'on_ble_manufacturer_data_advertise' -CONF_ON_BLE_SERVICE_DATA_ADVERTISE = 'on_ble_service_data_advertise' -CONF_ON_BOOT = 'on_boot' -CONF_ON_CLICK = 'on_click' -CONF_ON_DOUBLE_CLICK = 'on_double_click' -CONF_ON_JSON_MESSAGE = 'on_json_message' -CONF_ON_LOOP = 'on_loop' -CONF_ON_MESSAGE = 'on_message' -CONF_ON_MULTI_CLICK = 'on_multi_click' -CONF_ON_PRESS = 'on_press' -CONF_ON_RAW_VALUE = 'on_raw_value' -CONF_ON_RELEASE = 'on_release' -CONF_ON_SHUTDOWN = 'on_shutdown' -CONF_ON_STATE = 'on_state' -CONF_ON_TAG = 'on_tag' -CONF_ON_TIME = 'on_time' -CONF_ON_TIME_SYNC = 'on_time_sync' -CONF_ON_TURN_OFF = 'on_turn_off' -CONF_ON_TURN_ON = 'on_turn_on' -CONF_ON_VALUE = 'on_value' -CONF_ON_VALUE_RANGE = 'on_value_range' -CONF_ONE = 'one' -CONF_OPEN_ACTION = 'open_action' -CONF_OPEN_DRAIN_INTERRUPT = 'open_drain_interrupt' -CONF_OPEN_DURATION = 'open_duration' -CONF_OPEN_ENDSTOP = 'open_endstop' -CONF_OPTIMISTIC = 'optimistic' -CONF_OR = 'or' -CONF_OSCILLATING = 'oscillating' -CONF_OSCILLATION_COMMAND_TOPIC = 'oscillation_command_topic' -CONF_OSCILLATION_OUTPUT = 'oscillation_output' -CONF_OSCILLATION_STATE_TOPIC = 'oscillation_state_topic' -CONF_OTA = 'ota' -CONF_OUTPUT = 'output' -CONF_OUTPUT_ID = 'output_id' -CONF_OUTPUTS = 'outputs' -CONF_OVERSAMPLING = 'oversampling' -CONF_PACKAGES = 'packages' -CONF_PAGE_ID = 'page_id' -CONF_PAGES = 'pages' -CONF_PANASONIC = 'panasonic' -CONF_PASSWORD = 'password' -CONF_PAYLOAD = 'payload' -CONF_PAYLOAD_AVAILABLE = 'payload_available' -CONF_PAYLOAD_NOT_AVAILABLE = 'payload_not_available' -CONF_PERIOD = 'period' -CONF_PHASE_BALANCER = 'phase_balancer' -CONF_PIN = 'pin' -CONF_PIN_A = 'pin_a' -CONF_PIN_B = 'pin_b' -CONF_PIN_C = 'pin_c' -CONF_PIN_D = 'pin_d' -CONF_PINS = 'pins' -CONF_PLATFORM = 'platform' -CONF_PLATFORMIO_OPTIONS = 'platformio_options' -CONF_PM_1_0 = 'pm_1_0' -CONF_PM_10_0 = 'pm_10_0' -CONF_PM_2_5 = 'pm_2_5' -CONF_PM_4_0 = 'pm_4_0' -CONF_PM_SIZE = 'pm_size' -CONF_PMC_0_5 = 'pmc_0_5' -CONF_PMC_1_0 = 'pmc_1_0' -CONF_PMC_10_0 = 'pmc_10_0' -CONF_PMC_2_5 = 'pmc_2_5' -CONF_PMC_4_0 = 'pmc_4_0' -CONF_PORT = 'port' -CONF_POSITION = 'position' -CONF_POSITION_ACTION = 'position_action' -CONF_POWER = 'power' -CONF_POWER_FACTOR = 'power_factor' -CONF_POWER_ON_VALUE = 'power_on_value' -CONF_POWER_SAVE_MODE = 'power_save_mode' -CONF_POWER_SUPPLY = 'power_supply' -CONF_PRESSURE = 'pressure' -CONF_PRIORITY = 'priority' -CONF_PROTOCOL = 'protocol' -CONF_PULL_MODE = 'pull_mode' -CONF_PULSE_LENGTH = 'pulse_length' -CONF_QOS = 'qos' -CONF_RANDOM = 'random' -CONF_RANGE = 'range' -CONF_RANGE_FROM = 'range_from' -CONF_RANGE_TO = 'range_to' -CONF_RATE = 'rate' -CONF_RAW = 'raw' -CONF_RC_CODE_1 = 'rc_code_1' -CONF_RC_CODE_2 = 'rc_code_2' -CONF_REBOOT_TIMEOUT = 'reboot_timeout' -CONF_RECEIVE_TIMEOUT = 'receive_timeout' -CONF_RED = 'red' -CONF_REFERENCE_RESISTANCE = 'reference_resistance' -CONF_REFERENCE_TEMPERATURE = 'reference_temperature' -CONF_REPEAT = 'repeat' -CONF_REPOSITORY = 'repository' -CONF_RESET_PIN = 'reset_pin' -CONF_RESIZE = 'resize' -CONF_RESOLUTION = 'resolution' -CONF_RESTORE = 'restore' -CONF_RESTORE_MODE = 'restore_mode' -CONF_RESTORE_STATE = 'restore_state' -CONF_RESTORE_VALUE = 'restore_value' -CONF_RETAIN = 'retain' -CONF_RGB_ORDER = 'rgb_order' -CONF_RGBW = 'rgbw' -CONF_RISING_EDGE = 'rising_edge' -CONF_ROTATION = 'rotation' -CONF_RS_PIN = 'rs_pin' -CONF_RTD_NOMINAL_RESISTANCE = 'rtd_nominal_resistance' -CONF_RTD_WIRES = 'rtd_wires' -CONF_RUN_CYCLES = 'run_cycles' -CONF_RUN_DURATION = 'run_duration' -CONF_RW_PIN = 'rw_pin' -CONF_RX_BUFFER_SIZE = 'rx_buffer_size' -CONF_RX_ONLY = 'rx_only' -CONF_RX_PIN = 'rx_pin' -CONF_SAFE_MODE = 'safe_mode' -CONF_SAMSUNG = 'samsung' -CONF_SCAN = 'scan' -CONF_SCL = 'scl' -CONF_SCL_PIN = 'scl_pin' -CONF_SDA = 'sda' -CONF_SDO_PIN = 'sdo_pin' -CONF_SECOND = 'second' -CONF_SECONDS = 'seconds' -CONF_SEGMENTS = 'segments' -CONF_SEL_PIN = 'sel_pin' -CONF_SEND_EVERY = 'send_every' -CONF_SEND_FIRST_AT = 'send_first_at' -CONF_SENSOR = 'sensor' -CONF_SENSOR_ID = 'sensor_id' -CONF_SENSORS = 'sensors' -CONF_SEQUENCE = 'sequence' -CONF_SERVERS = 'servers' -CONF_SERVICE = 'service' -CONF_SERVICE_UUID = 'service_uuid' -CONF_SERVICES = 'services' -CONF_SETUP_MODE = 'setup_mode' -CONF_SETUP_PRIORITY = 'setup_priority' -CONF_SHUNT_RESISTANCE = 'shunt_resistance' -CONF_SHUNT_VOLTAGE = 'shunt_voltage' -CONF_SHUTDOWN_MESSAGE = 'shutdown_message' -CONF_SIZE = 'size' -CONF_SLEEP_DURATION = 'sleep_duration' -CONF_SLEEP_PIN = 'sleep_pin' -CONF_SLEEP_WHEN_DONE = 'sleep_when_done' -CONF_SONY = 'sony' -CONF_SPEED = 'speed' -CONF_SPEED_COMMAND_TOPIC = 'speed_command_topic' -CONF_SPEED_STATE_TOPIC = 'speed_state_topic' -CONF_SPI_ID = 'spi_id' -CONF_SPIKE_REJECTION = 'spike_rejection' -CONF_SSID = 'ssid' -CONF_SSL_FINGERPRINTS = 'ssl_fingerprints' -CONF_STATE = 'state' -CONF_STATE_TOPIC = 'state_topic' -CONF_STATIC_IP = 'static_ip' -CONF_STEP_MODE = 'step_mode' -CONF_STEP_PIN = 'step_pin' -CONF_STOP = 'stop' -CONF_STOP_ACTION = 'stop_action' -CONF_SUBNET = 'subnet' -CONF_SUBSTITUTIONS = 'substitutions' -CONF_SUPPORTS_COOL = 'supports_cool' -CONF_SUPPORTS_HEAT = 'supports_heat' -CONF_SWING_BOTH_ACTION = 'swing_both_action' -CONF_SWING_HORIZONTAL_ACTION = 'swing_horizontal_action' -CONF_SWING_MODE = 'swing_mode' -CONF_SWING_OFF_ACTION = 'swing_off_action' -CONF_SWING_VERTICAL_ACTION = 'swing_vertical_action' +CONF_I2C = "i2c" +CONF_I2C_ID = "i2c_id" +CONF_ICON = "icon" +CONF_ID = "id" +CONF_IDENTITY = "identity" +CONF_IDLE = "idle" +CONF_IDLE_ACTION = "idle_action" +CONF_IDLE_LEVEL = "idle_level" +CONF_IDLE_TIME = "idle_time" +CONF_IF = "if" +CONF_IIR_FILTER = "iir_filter" +CONF_ILLUMINANCE = "illuminance" +CONF_IMPEDANCE = "impedance" +CONF_INCLUDES = "includes" +CONF_INDEX = "index" +CONF_INDOOR = "indoor" +CONF_INITIAL_MODE = "initial_mode" +CONF_INITIAL_VALUE = "initial_value" +CONF_INTEGRATION_TIME = "integration_time" +CONF_INTENSITY = "intensity" +CONF_INTERLOCK = "interlock" +CONF_INTERNAL = "internal" +CONF_INTERNAL_FILTER = "internal_filter" +CONF_INTERVAL = "interval" +CONF_INVALID_COOLDOWN = "invalid_cooldown" +CONF_INVERT = "invert" +CONF_INVERTED = "inverted" +CONF_IP_ADDRESS = "ip_address" +CONF_JS_INCLUDE = "js_include" +CONF_JS_URL = "js_url" +CONF_JVC = "jvc" +CONF_KEEP_ON_TIME = "keep_on_time" +CONF_KEEPALIVE = "keepalive" +CONF_KEY = "key" +CONF_LAMBDA = "lambda" +CONF_LENGTH = "length" +CONF_LEVEL = "level" +CONF_LG = "lg" +CONF_LIBRARIES = "libraries" +CONF_LIGHT = "light" +CONF_LIGHTNING_ENERGY = "lightning_energy" +CONF_LIGHTNING_THRESHOLD = "lightning_threshold" +CONF_LOADED_INTEGRATIONS = "loaded_integrations" +CONF_LOCAL = "local" +CONF_LOG_TOPIC = "log_topic" +CONF_LOGGER = "logger" +CONF_LOGS = "logs" +CONF_LOW = "low" +CONF_LOW_VOLTAGE_REFERENCE = "low_voltage_reference" +CONF_MAC_ADDRESS = "mac_address" +CONF_MAINS_FILTER = "mains_filter" +CONF_MAKE_ID = "make_id" +CONF_MANUAL_IP = "manual_ip" +CONF_MANUFACTURER_ID = "manufacturer_id" +CONF_MASK_DISTURBER = "mask_disturber" +CONF_MAX_CURRENT = "max_current" +CONF_MAX_DURATION = "max_duration" +CONF_MAX_LENGTH = "max_length" +CONF_MAX_LEVEL = "max_level" +CONF_MAX_POWER = "max_power" +CONF_MAX_REFRESH_RATE = "max_refresh_rate" +CONF_MAX_SPEED = "max_speed" +CONF_MAX_TEMPERATURE = "max_temperature" +CONF_MAX_VALUE = "max_value" +CONF_MAX_VOLTAGE = "max_voltage" +CONF_MEASUREMENT_DURATION = "measurement_duration" +CONF_MEASUREMENT_SEQUENCE_NUMBER = "measurement_sequence_number" +CONF_MEDIUM = "medium" +CONF_MEMORY_BLOCKS = "memory_blocks" +CONF_METHOD = "method" +CONF_MIN_LENGTH = "min_length" +CONF_MIN_LEVEL = "min_level" +CONF_MIN_POWER = "min_power" +CONF_MIN_TEMPERATURE = "min_temperature" +CONF_MIN_VALUE = "min_value" +CONF_MINUTE = "minute" +CONF_MINUTES = "minutes" +CONF_MISO_PIN = "miso_pin" +CONF_MODE = "mode" +CONF_MODEL = "model" +CONF_MOISTURE = "moisture" +CONF_MONTHS = "months" +CONF_MOSI_PIN = "mosi_pin" +CONF_MOTION = "motion" +CONF_MOVEMENT_COUNTER = "movement_counter" +CONF_MQTT = "mqtt" +CONF_MQTT_ID = "mqtt_id" +CONF_MULTIPLEXER = "multiplexer" +CONF_MULTIPLY = "multiply" +CONF_NAME = "name" +CONF_NBITS = "nbits" +CONF_NEC = "nec" +CONF_NETWORKS = "networks" +CONF_NOISE_LEVEL = "noise_level" +CONF_NUM_ATTEMPTS = "num_attempts" +CONF_NUM_CHANNELS = "num_channels" +CONF_NUM_CHIPS = "num_chips" +CONF_NUM_LEDS = "num_leds" +CONF_NUMBER = "number" +CONF_OFF_MODE = "off_mode" +CONF_OFFSET = "offset" +CONF_ON = "on" +CONF_ON_BLE_ADVERTISE = "on_ble_advertise" +CONF_ON_BLE_MANUFACTURER_DATA_ADVERTISE = "on_ble_manufacturer_data_advertise" +CONF_ON_BLE_SERVICE_DATA_ADVERTISE = "on_ble_service_data_advertise" +CONF_ON_BOOT = "on_boot" +CONF_ON_CLICK = "on_click" +CONF_ON_DOUBLE_CLICK = "on_double_click" +CONF_ON_JSON_MESSAGE = "on_json_message" +CONF_ON_LOOP = "on_loop" +CONF_ON_MESSAGE = "on_message" +CONF_ON_MULTI_CLICK = "on_multi_click" +CONF_ON_PRESS = "on_press" +CONF_ON_RAW_VALUE = "on_raw_value" +CONF_ON_RELEASE = "on_release" +CONF_ON_SHUTDOWN = "on_shutdown" +CONF_ON_STATE = "on_state" +CONF_ON_TAG = "on_tag" +CONF_ON_TIME = "on_time" +CONF_ON_TIME_SYNC = "on_time_sync" +CONF_ON_TURN_OFF = "on_turn_off" +CONF_ON_TURN_ON = "on_turn_on" +CONF_ON_VALUE = "on_value" +CONF_ON_VALUE_RANGE = "on_value_range" +CONF_ONE = "one" +CONF_OPEN_ACTION = "open_action" +CONF_OPEN_DRAIN_INTERRUPT = "open_drain_interrupt" +CONF_OPEN_DURATION = "open_duration" +CONF_OPEN_ENDSTOP = "open_endstop" +CONF_OPTIMISTIC = "optimistic" +CONF_OR = "or" +CONF_OSCILLATING = "oscillating" +CONF_OSCILLATION_COMMAND_TOPIC = "oscillation_command_topic" +CONF_OSCILLATION_OUTPUT = "oscillation_output" +CONF_OSCILLATION_STATE_TOPIC = "oscillation_state_topic" +CONF_OTA = "ota" +CONF_OUTPUT = "output" +CONF_OUTPUT_ID = "output_id" +CONF_OUTPUTS = "outputs" +CONF_OVERSAMPLING = "oversampling" +CONF_PACKAGES = "packages" +CONF_PAGE_ID = "page_id" +CONF_PAGES = "pages" +CONF_PANASONIC = "panasonic" +CONF_PASSWORD = "password" +CONF_PAYLOAD = "payload" +CONF_PAYLOAD_AVAILABLE = "payload_available" +CONF_PAYLOAD_NOT_AVAILABLE = "payload_not_available" +CONF_PERIOD = "period" +CONF_PHASE_BALANCER = "phase_balancer" +CONF_PIN = "pin" +CONF_PIN_A = "pin_a" +CONF_PIN_B = "pin_b" +CONF_PIN_C = "pin_c" +CONF_PIN_D = "pin_d" +CONF_PINS = "pins" +CONF_PLATFORM = "platform" +CONF_PLATFORMIO_OPTIONS = "platformio_options" +CONF_PM_1_0 = "pm_1_0" +CONF_PM_10_0 = "pm_10_0" +CONF_PM_2_5 = "pm_2_5" +CONF_PM_4_0 = "pm_4_0" +CONF_PM_SIZE = "pm_size" +CONF_PMC_0_5 = "pmc_0_5" +CONF_PMC_1_0 = "pmc_1_0" +CONF_PMC_10_0 = "pmc_10_0" +CONF_PMC_2_5 = "pmc_2_5" +CONF_PMC_4_0 = "pmc_4_0" +CONF_PORT = "port" +CONF_POSITION = "position" +CONF_POSITION_ACTION = "position_action" +CONF_POWER = "power" +CONF_POWER_FACTOR = "power_factor" +CONF_POWER_ON_VALUE = "power_on_value" +CONF_POWER_SAVE_MODE = "power_save_mode" +CONF_POWER_SUPPLY = "power_supply" +CONF_PRESSURE = "pressure" +CONF_PRIORITY = "priority" +CONF_PROTOCOL = "protocol" +CONF_PULL_MODE = "pull_mode" +CONF_PULSE_LENGTH = "pulse_length" +CONF_QOS = "qos" +CONF_RANDOM = "random" +CONF_RANGE = "range" +CONF_RANGE_FROM = "range_from" +CONF_RANGE_TO = "range_to" +CONF_RATE = "rate" +CONF_RAW = "raw" +CONF_RC_CODE_1 = "rc_code_1" +CONF_RC_CODE_2 = "rc_code_2" +CONF_REBOOT_TIMEOUT = "reboot_timeout" +CONF_RECEIVE_TIMEOUT = "receive_timeout" +CONF_RED = "red" +CONF_REFERENCE_RESISTANCE = "reference_resistance" +CONF_REFERENCE_TEMPERATURE = "reference_temperature" +CONF_REPEAT = "repeat" +CONF_REPOSITORY = "repository" +CONF_RESET_PIN = "reset_pin" +CONF_RESIZE = "resize" +CONF_RESOLUTION = "resolution" +CONF_RESTORE = "restore" +CONF_RESTORE_MODE = "restore_mode" +CONF_RESTORE_STATE = "restore_state" +CONF_RESTORE_VALUE = "restore_value" +CONF_RETAIN = "retain" +CONF_RGB_ORDER = "rgb_order" +CONF_RGBW = "rgbw" +CONF_RISING_EDGE = "rising_edge" +CONF_ROTATION = "rotation" +CONF_RS_PIN = "rs_pin" +CONF_RTD_NOMINAL_RESISTANCE = "rtd_nominal_resistance" +CONF_RTD_WIRES = "rtd_wires" +CONF_RUN_CYCLES = "run_cycles" +CONF_RUN_DURATION = "run_duration" +CONF_RW_PIN = "rw_pin" +CONF_RX_BUFFER_SIZE = "rx_buffer_size" +CONF_RX_ONLY = "rx_only" +CONF_RX_PIN = "rx_pin" +CONF_SAFE_MODE = "safe_mode" +CONF_SAMSUNG = "samsung" +CONF_SCAN = "scan" +CONF_SCL = "scl" +CONF_SCL_PIN = "scl_pin" +CONF_SDA = "sda" +CONF_SDO_PIN = "sdo_pin" +CONF_SECOND = "second" +CONF_SECONDS = "seconds" +CONF_SEGMENTS = "segments" +CONF_SEL_PIN = "sel_pin" +CONF_SEND_EVERY = "send_every" +CONF_SEND_FIRST_AT = "send_first_at" +CONF_SENSOR = "sensor" +CONF_SENSOR_ID = "sensor_id" +CONF_SENSORS = "sensors" +CONF_SEQUENCE = "sequence" +CONF_SERVERS = "servers" +CONF_SERVICE = "service" +CONF_SERVICE_UUID = "service_uuid" +CONF_SERVICES = "services" +CONF_SETUP_MODE = "setup_mode" +CONF_SETUP_PRIORITY = "setup_priority" +CONF_SHUNT_RESISTANCE = "shunt_resistance" +CONF_SHUNT_VOLTAGE = "shunt_voltage" +CONF_SHUTDOWN_MESSAGE = "shutdown_message" +CONF_SIZE = "size" +CONF_SLEEP_DURATION = "sleep_duration" +CONF_SLEEP_PIN = "sleep_pin" +CONF_SLEEP_WHEN_DONE = "sleep_when_done" +CONF_SONY = "sony" +CONF_SPEED = "speed" +CONF_SPEED_COMMAND_TOPIC = "speed_command_topic" +CONF_SPEED_STATE_TOPIC = "speed_state_topic" +CONF_SPI_ID = "spi_id" +CONF_SPIKE_REJECTION = "spike_rejection" +CONF_SSID = "ssid" +CONF_SSL_FINGERPRINTS = "ssl_fingerprints" +CONF_STATE = "state" +CONF_STATE_TOPIC = "state_topic" +CONF_STATIC_IP = "static_ip" +CONF_STEP_MODE = "step_mode" +CONF_STEP_PIN = "step_pin" +CONF_STOP = "stop" +CONF_STOP_ACTION = "stop_action" +CONF_SUBNET = "subnet" +CONF_SUBSTITUTIONS = "substitutions" +CONF_SUPPORTS_COOL = "supports_cool" +CONF_SUPPORTS_HEAT = "supports_heat" +CONF_SWING_BOTH_ACTION = "swing_both_action" +CONF_SWING_HORIZONTAL_ACTION = "swing_horizontal_action" +CONF_SWING_MODE = "swing_mode" +CONF_SWING_OFF_ACTION = "swing_off_action" +CONF_SWING_VERTICAL_ACTION = "swing_vertical_action" CONF_SWITCH_DATAPOINT = "switch_datapoint" -CONF_SWITCHES = 'switches' -CONF_SYNC = 'sync' -CONF_TABLET = 'tablet' -CONF_TAG = 'tag' -CONF_TARGET = 'target' -CONF_TARGET_TEMPERATURE = 'target_temperature' -CONF_TARGET_TEMPERATURE_HIGH = 'target_temperature_high' -CONF_TARGET_TEMPERATURE_LOW = 'target_temperature_low' -CONF_TEMPERATURE = 'temperature' -CONF_TEMPERATURE_STEP = 'temperature_step' -CONF_TEXT_SENSORS = 'text_sensors' -CONF_THEN = 'then' -CONF_THRESHOLD = 'threshold' -CONF_THROTTLE = 'throttle' -CONF_TILT = 'tilt' -CONF_TILT_ACTION = 'tilt_action' -CONF_TILT_LAMBDA = 'tilt_lambda' -CONF_TIME = 'time' -CONF_TIME_ID = 'time_id' -CONF_TIMEOUT = 'timeout' -CONF_TIMES = 'times' -CONF_TIMEZONE = 'timezone' -CONF_TIMING = 'timing' -CONF_TO = 'to' -CONF_TOLERANCE = 'tolerance' -CONF_TOPIC = 'topic' -CONF_TOPIC_PREFIX = 'topic_prefix' +CONF_SWITCHES = "switches" +CONF_SYNC = "sync" +CONF_TABLET = "tablet" +CONF_TAG = "tag" +CONF_TARGET = "target" +CONF_TARGET_TEMPERATURE = "target_temperature" +CONF_TARGET_TEMPERATURE_HIGH = "target_temperature_high" +CONF_TARGET_TEMPERATURE_LOW = "target_temperature_low" +CONF_TEMPERATURE = "temperature" +CONF_TEMPERATURE_STEP = "temperature_step" +CONF_TEXT_SENSORS = "text_sensors" +CONF_THEN = "then" +CONF_THRESHOLD = "threshold" +CONF_THROTTLE = "throttle" +CONF_TILT = "tilt" +CONF_TILT_ACTION = "tilt_action" +CONF_TILT_LAMBDA = "tilt_lambda" +CONF_TIME = "time" +CONF_TIME_ID = "time_id" +CONF_TIMEOUT = "timeout" +CONF_TIMES = "times" +CONF_TIMEZONE = "timezone" +CONF_TIMING = "timing" +CONF_TO = "to" +CONF_TOLERANCE = "tolerance" +CONF_TOPIC = "topic" +CONF_TOPIC_PREFIX = "topic_prefix" CONF_TOTAL = "total" -CONF_TRANSITION_LENGTH = 'transition_length' -CONF_TRIGGER_ID = 'trigger_id' -CONF_TRIGGER_PIN = 'trigger_pin' -CONF_TURN_OFF_ACTION = 'turn_off_action' -CONF_TURN_ON_ACTION = 'turn_on_action' -CONF_TVOC = 'tvoc' -CONF_TX_BUFFER_SIZE = 'tx_buffer_size' -CONF_TX_PIN = 'tx_pin' -CONF_TX_POWER = 'tx_power' -CONF_TYPE = 'type' -CONF_TYPE_ID = 'type_id' -CONF_UART_ID = 'uart_id' -CONF_UID = 'uid' -CONF_UNIQUE = 'unique' -CONF_UNIT_OF_MEASUREMENT = 'unit_of_measurement' -CONF_UPDATE_INTERVAL = 'update_interval' -CONF_UPDATE_ON_BOOT = 'update_on_boot' -CONF_URL = 'url' -CONF_USE_ADDRESS = 'use_address' -CONF_USERNAME = 'username' -CONF_UUID = 'uuid' -CONF_VALUE = 'value' -CONF_VARIABLES = 'variables' -CONF_VARIANT = 'variant' -CONF_VISUAL = 'visual' -CONF_VOLTAGE = 'voltage' -CONF_VOLTAGE_ATTENUATION = 'voltage_attenuation' -CONF_VOLTAGE_DIVIDER = 'voltage_divider' -CONF_WAIT_TIME = 'wait_time' -CONF_WAIT_UNTIL = 'wait_until' -CONF_WAKEUP_PIN = 'wakeup_pin' -CONF_WARM_WHITE = 'warm_white' -CONF_WARM_WHITE_COLOR_TEMPERATURE = 'warm_white_color_temperature' -CONF_WATCHDOG_THRESHOLD = 'watchdog_threshold' -CONF_WEIGHT = 'weight' -CONF_WHILE = 'while' -CONF_WHITE = 'white' -CONF_WIDTH = 'width' -CONF_WIFI = 'wifi' -CONF_WILL_MESSAGE = 'will_message' -CONF_WIND_DIRECTION_DEGREES = 'wind_direction_degrees' -CONF_WIND_SPEED = 'wind_speed' -CONF_WINDOW_SIZE = 'window_size' -CONF_ZERO = 'zero' +CONF_TRANSITION_LENGTH = "transition_length" +CONF_TRIGGER_ID = "trigger_id" +CONF_TRIGGER_PIN = "trigger_pin" +CONF_TURN_OFF_ACTION = "turn_off_action" +CONF_TURN_ON_ACTION = "turn_on_action" +CONF_TVOC = "tvoc" +CONF_TX_BUFFER_SIZE = "tx_buffer_size" +CONF_TX_PIN = "tx_pin" +CONF_TX_POWER = "tx_power" +CONF_TYPE = "type" +CONF_TYPE_ID = "type_id" +CONF_UART_ID = "uart_id" +CONF_UID = "uid" +CONF_UNIQUE = "unique" +CONF_UNIT_OF_MEASUREMENT = "unit_of_measurement" +CONF_UPDATE_INTERVAL = "update_interval" +CONF_UPDATE_ON_BOOT = "update_on_boot" +CONF_URL = "url" +CONF_USE_ADDRESS = "use_address" +CONF_USERNAME = "username" +CONF_UUID = "uuid" +CONF_VALUE = "value" +CONF_VARIABLES = "variables" +CONF_VARIANT = "variant" +CONF_VISUAL = "visual" +CONF_VOLTAGE = "voltage" +CONF_VOLTAGE_ATTENUATION = "voltage_attenuation" +CONF_VOLTAGE_DIVIDER = "voltage_divider" +CONF_WAIT_TIME = "wait_time" +CONF_WAIT_UNTIL = "wait_until" +CONF_WAKEUP_PIN = "wakeup_pin" +CONF_WARM_WHITE = "warm_white" +CONF_WARM_WHITE_COLOR_TEMPERATURE = "warm_white_color_temperature" +CONF_WATCHDOG_THRESHOLD = "watchdog_threshold" +CONF_WEIGHT = "weight" +CONF_WHILE = "while" +CONF_WHITE = "white" +CONF_WIDTH = "width" +CONF_WIFI = "wifi" +CONF_WILL_MESSAGE = "will_message" +CONF_WIND_DIRECTION_DEGREES = "wind_direction_degrees" +CONF_WIND_SPEED = "wind_speed" +CONF_WINDOW_SIZE = "window_size" +CONF_ZERO = "zero" -ENV_NOGITIGNORE = 'ESPHOME_NOGITIGNORE' -ENV_QUICKWIZARD = 'ESPHOME_QUICKWIZARD' +ENV_NOGITIGNORE = "ESPHOME_NOGITIGNORE" +ENV_QUICKWIZARD = "ESPHOME_QUICKWIZARD" -ICON_ACCELERATION = 'mdi:axis-arrow' -ICON_ACCELERATION_X = 'mdi:axis-x-arrow' -ICON_ACCELERATION_Y = 'mdi:axis-y-arrow' -ICON_ACCELERATION_Z = 'mdi:axis-z-arrow' -ICON_ARROW_EXPAND_VERTICAL = 'mdi:arrow-expand-vertical' -ICON_BATTERY = 'mdi:battery' -ICON_BRIEFCASE_DOWNLOAD = 'mdi:briefcase-download' -ICON_BRIGHTNESS_5 = 'mdi:brightness-5' -ICON_BUG = 'mdi:bug' -ICON_CHECK_CIRCLE_OUTLINE = 'mdi:check-circle-outline' -ICON_CHEMICAL_WEAPON = 'mdi:chemical-weapon' -ICON_COUNTER = 'mdi:counter' -ICON_CURRENT_AC = 'mdi:current-ac' -ICON_EMPTY = '' -ICON_FLASH = 'mdi:flash' -ICON_FLASK = 'mdi:flask' -ICON_FLASK_OUTLINE = 'mdi:flask-outline' -ICON_FLOWER = 'mdi:flower' -ICON_GAS_CYLINDER = 'mdi:gas-cylinder' -ICON_GAUGE = 'mdi:gauge' -ICON_GRAIN = 'mdi:grain' -ICON_LIGHTBULB = 'mdi:lightbulb' -ICON_MAGNET = 'mdi:magnet' -ICON_MOLECULE_CO2 = 'mdi:molecule-co2' -ICON_MOTION_SENSOR = 'mdi:motion-sensor' -ICON_NEW_BOX = 'mdi:new-box' -ICON_OMEGA = 'mdi:omega' -ICON_PERCENT = 'mdi:percent' -ICON_POWER = 'mdi:power' -ICON_PULSE = 'mdi:pulse' -ICON_RADIATOR = 'mdi:radiator' -ICON_RESTART = 'mdi:restart' -ICON_ROTATE_RIGHT = 'mdi:rotate-right' -ICON_RULER = 'mdi:ruler' -ICON_SCALE = 'mdi:scale' -ICON_SCALE_BATHROOM = 'mdi:scale-bathroom' -ICON_SCREEN_ROTATION = 'mdi:screen-rotation' -ICON_SIGN_DIRECTION = 'mdi:sign-direction' -ICON_SIGNAL = 'mdi:signal-distance-variant' -ICON_SIGNAL_DISTANCE_VARIANT = 'mdi:signal' -ICON_THERMOMETER = 'mdi:thermometer' -ICON_TIMELAPSE = 'mdi:timelapse' -ICON_TIMER = 'mdi:timer-outline' -ICON_WATER_PERCENT = 'mdi:water-percent' -ICON_WEATHER_SUNSET = 'mdi:weather-sunset' -ICON_WEATHER_SUNSET_DOWN = 'mdi:weather-sunset-down' -ICON_WEATHER_SUNSET_UP = 'mdi:weather-sunset-up' -ICON_WEATHER_WINDY = 'mdi:weather-windy' -ICON_WIFI = 'mdi:wifi' +ICON_ACCELERATION = "mdi:axis-arrow" +ICON_ACCELERATION_X = "mdi:axis-x-arrow" +ICON_ACCELERATION_Y = "mdi:axis-y-arrow" +ICON_ACCELERATION_Z = "mdi:axis-z-arrow" +ICON_ARROW_EXPAND_VERTICAL = "mdi:arrow-expand-vertical" +ICON_BATTERY = "mdi:battery" +ICON_BRIEFCASE_DOWNLOAD = "mdi:briefcase-download" +ICON_BRIGHTNESS_5 = "mdi:brightness-5" +ICON_BUG = "mdi:bug" +ICON_CHECK_CIRCLE_OUTLINE = "mdi:check-circle-outline" +ICON_CHEMICAL_WEAPON = "mdi:chemical-weapon" +ICON_COUNTER = "mdi:counter" +ICON_CURRENT_AC = "mdi:current-ac" +ICON_EMPTY = "" +ICON_FLASH = "mdi:flash" +ICON_FLASK = "mdi:flask" +ICON_FLASK_OUTLINE = "mdi:flask-outline" +ICON_FLOWER = "mdi:flower" +ICON_GAS_CYLINDER = "mdi:gas-cylinder" +ICON_GAUGE = "mdi:gauge" +ICON_GRAIN = "mdi:grain" +ICON_LIGHTBULB = "mdi:lightbulb" +ICON_MAGNET = "mdi:magnet" +ICON_MOLECULE_CO2 = "mdi:molecule-co2" +ICON_MOTION_SENSOR = "mdi:motion-sensor" +ICON_NEW_BOX = "mdi:new-box" +ICON_OMEGA = "mdi:omega" +ICON_PERCENT = "mdi:percent" +ICON_POWER = "mdi:power" +ICON_PULSE = "mdi:pulse" +ICON_RADIATOR = "mdi:radiator" +ICON_RESTART = "mdi:restart" +ICON_ROTATE_RIGHT = "mdi:rotate-right" +ICON_RULER = "mdi:ruler" +ICON_SCALE = "mdi:scale" +ICON_SCALE_BATHROOM = "mdi:scale-bathroom" +ICON_SCREEN_ROTATION = "mdi:screen-rotation" +ICON_SIGN_DIRECTION = "mdi:sign-direction" +ICON_SIGNAL = "mdi:signal-distance-variant" +ICON_SIGNAL_DISTANCE_VARIANT = "mdi:signal" +ICON_THERMOMETER = "mdi:thermometer" +ICON_TIMELAPSE = "mdi:timelapse" +ICON_TIMER = "mdi:timer-outline" +ICON_WATER_PERCENT = "mdi:water-percent" +ICON_WEATHER_SUNSET = "mdi:weather-sunset" +ICON_WEATHER_SUNSET_DOWN = "mdi:weather-sunset-down" +ICON_WEATHER_SUNSET_UP = "mdi:weather-sunset-up" +ICON_WEATHER_WINDY = "mdi:weather-windy" +ICON_WIFI = "mdi:wifi" -UNIT_AMPERE = 'A' -UNIT_CELSIUS = '°C' -UNIT_COUNTS_PER_CUBIC_METER = '#/m³' -UNIT_DECIBEL = 'dB' -UNIT_DECIBEL_MILLIWATT = 'dBm' -UNIT_DEGREE_PER_SECOND = '°/s' -UNIT_DEGREES = '°' -UNIT_EMPTY = '' -UNIT_G = 'G' -UNIT_HECTOPASCAL = 'hPa' -UNIT_HERTZ = 'Hz' -UNIT_KELVIN = 'K' -UNIT_KILOGRAM = 'kg' -UNIT_KILOMETER = 'km' -UNIT_KILOMETER_PER_HOUR = 'km/h' -UNIT_LUX = 'lx' -UNIT_METER = 'm' -UNIT_METER_PER_SECOND_SQUARED = 'm/s²' -UNIT_MICROGRAMS_PER_CUBIC_METER = 'µg/m³' -UNIT_MICROMETER = 'µm' -UNIT_MICROSIEMENS_PER_CENTIMETER = 'µS/cm' -UNIT_MICROTESLA = 'µT' -UNIT_MILLIGRAMS_PER_CUBIC_METER = 'mg/m³' -UNIT_MINUTE = 'min' -UNIT_OHM = 'Ω' -UNIT_PARTS_PER_BILLION = 'ppb' -UNIT_PARTS_PER_MILLION = 'ppm' -UNIT_PERCENT = '%' +UNIT_AMPERE = "A" +UNIT_CELSIUS = "°C" +UNIT_COUNTS_PER_CUBIC_METER = "#/m³" +UNIT_DECIBEL = "dB" +UNIT_DECIBEL_MILLIWATT = "dBm" +UNIT_DEGREE_PER_SECOND = "°/s" +UNIT_DEGREES = "°" +UNIT_EMPTY = "" +UNIT_G = "G" +UNIT_HECTOPASCAL = "hPa" +UNIT_HERTZ = "Hz" +UNIT_KELVIN = "K" +UNIT_KILOGRAM = "kg" +UNIT_KILOMETER = "km" +UNIT_KILOMETER_PER_HOUR = "km/h" +UNIT_LUX = "lx" +UNIT_METER = "m" +UNIT_METER_PER_SECOND_SQUARED = "m/s²" +UNIT_MICROGRAMS_PER_CUBIC_METER = "µg/m³" +UNIT_MICROMETER = "µm" +UNIT_MICROSIEMENS_PER_CENTIMETER = "µS/cm" +UNIT_MICROTESLA = "µT" +UNIT_MILLIGRAMS_PER_CUBIC_METER = "mg/m³" +UNIT_MINUTE = "min" +UNIT_OHM = "Ω" +UNIT_PARTS_PER_BILLION = "ppb" +UNIT_PARTS_PER_MILLION = "ppm" +UNIT_PERCENT = "%" UNIT_PULSES = "pulses" -UNIT_PULSES_PER_MINUTE = 'pulses/min' -UNIT_SECOND = 's' -UNIT_STEPS = 'steps' -UNIT_VOLT = 'V' -UNIT_VOLT_AMPS = 'VA' -UNIT_VOLT_AMPS_REACTIVE = 'VAR' -UNIT_WATT = 'W' -UNIT_WATT_HOURS = 'Wh' +UNIT_PULSES_PER_MINUTE = "pulses/min" +UNIT_SECOND = "s" +UNIT_STEPS = "steps" +UNIT_VOLT = "V" +UNIT_VOLT_AMPS = "VA" +UNIT_VOLT_AMPS_REACTIVE = "VAR" +UNIT_WATT = "W" +UNIT_WATT_HOURS = "Wh" # device classes of binary_sensor component -DEVICE_CLASS_BATTERY_CHARGING = 'battery_charging' -DEVICE_CLASS_COLD = 'cold' -DEVICE_CLASS_CONNECTIVITY = 'connectivity' -DEVICE_CLASS_DOOR = 'door' -DEVICE_CLASS_GARAGE_DOOR = 'garage_door' -DEVICE_CLASS_GAS = 'gas' -DEVICE_CLASS_HEAT = 'heat' -DEVICE_CLASS_LIGHT = 'light' -DEVICE_CLASS_LOCK = 'lock' -DEVICE_CLASS_MOISTURE = 'moisture' -DEVICE_CLASS_MOTION = 'motion' -DEVICE_CLASS_MOVING = 'moving' -DEVICE_CLASS_OCCUPANCY = 'occupancy' -DEVICE_CLASS_OPENING = 'opening' -DEVICE_CLASS_PLUG = 'plug' -DEVICE_CLASS_PRESENCE = 'presence' -DEVICE_CLASS_PROBLEM = 'problem' -DEVICE_CLASS_SAFETY = 'safety' -DEVICE_CLASS_SMOKE = 'smoke' -DEVICE_CLASS_SOUND = 'sound' -DEVICE_CLASS_VIBRATION = 'vibration' -DEVICE_CLASS_WINDOW = 'window' +DEVICE_CLASS_BATTERY_CHARGING = "battery_charging" +DEVICE_CLASS_COLD = "cold" +DEVICE_CLASS_CONNECTIVITY = "connectivity" +DEVICE_CLASS_DOOR = "door" +DEVICE_CLASS_GARAGE_DOOR = "garage_door" +DEVICE_CLASS_GAS = "gas" +DEVICE_CLASS_HEAT = "heat" +DEVICE_CLASS_LIGHT = "light" +DEVICE_CLASS_LOCK = "lock" +DEVICE_CLASS_MOISTURE = "moisture" +DEVICE_CLASS_MOTION = "motion" +DEVICE_CLASS_MOVING = "moving" +DEVICE_CLASS_OCCUPANCY = "occupancy" +DEVICE_CLASS_OPENING = "opening" +DEVICE_CLASS_PLUG = "plug" +DEVICE_CLASS_PRESENCE = "presence" +DEVICE_CLASS_PROBLEM = "problem" +DEVICE_CLASS_SAFETY = "safety" +DEVICE_CLASS_SMOKE = "smoke" +DEVICE_CLASS_SOUND = "sound" +DEVICE_CLASS_VIBRATION = "vibration" +DEVICE_CLASS_WINDOW = "window" # device classes of both binary_sensor and sensor component -DEVICE_CLASS_EMPTY = '' -DEVICE_CLASS_BATTERY = 'battery' -DEVICE_CLASS_POWER = 'power' +DEVICE_CLASS_EMPTY = "" +DEVICE_CLASS_BATTERY = "battery" +DEVICE_CLASS_POWER = "power" # device classes of sensor component -DEVICE_CLASS_CURRENT = 'current' -DEVICE_CLASS_ENERGY = 'energy' -DEVICE_CLASS_HUMIDITY = 'humidity' -DEVICE_CLASS_ILLUMINANCE = 'illuminance' -DEVICE_CLASS_SIGNAL_STRENGTH = 'signal_strength' -DEVICE_CLASS_TEMPERATURE = 'temperature' -DEVICE_CLASS_POWER_FACTOR = 'power_factor' -DEVICE_CLASS_PRESSURE = 'pressure' -DEVICE_CLASS_TIMESTAMP = 'timestamp' -DEVICE_CLASS_VOLTAGE = 'voltage' +DEVICE_CLASS_CURRENT = "current" +DEVICE_CLASS_ENERGY = "energy" +DEVICE_CLASS_HUMIDITY = "humidity" +DEVICE_CLASS_ILLUMINANCE = "illuminance" +DEVICE_CLASS_SIGNAL_STRENGTH = "signal_strength" +DEVICE_CLASS_TEMPERATURE = "temperature" +DEVICE_CLASS_POWER_FACTOR = "power_factor" +DEVICE_CLASS_PRESSURE = "pressure" +DEVICE_CLASS_TIMESTAMP = "timestamp" +DEVICE_CLASS_VOLTAGE = "voltage" diff --git a/esphome/core.py b/esphome/core.py index 2295e42753..cf2a07d35f 100644 --- a/esphome/core.py +++ b/esphome/core.py @@ -10,8 +10,14 @@ import re # pylint: disable=unused-import, wrong-import-order from typing import Any, Dict, List, Optional, Set, TYPE_CHECKING # noqa -from esphome.const import CONF_ARDUINO_VERSION, SOURCE_FILE_EXTENSIONS, \ - CONF_COMMENT, CONF_ESPHOME, CONF_USE_ADDRESS, CONF_WIFI +from esphome.const import ( + CONF_ARDUINO_VERSION, + SOURCE_FILE_EXTENSIONS, + CONF_COMMENT, + CONF_ESPHOME, + CONF_USE_ADDRESS, + CONF_WIFI, +) from esphome.helpers import ensure_unique_string, is_hassio from esphome.util import OrderedDict @@ -42,7 +48,7 @@ class IPAddress: self.args = args def __str__(self): - return '.'.join(str(x) for x in self.args) + return ".".join(str(x) for x in self.args) class MACAddress: @@ -52,14 +58,14 @@ class MACAddress: self.parts = parts def __str__(self): - return ':'.join(f'{part:02X}' for part in self.parts) + return ":".join(f"{part:02X}" for part in self.parts) @property def as_hex(self): from esphome.cpp_generator import RawExpression - num = ''.join(f'{part:02X}' for part in self.parts) - return RawExpression(f'0x{num}ULL') + num = "".join(f"{part:02X}" for part in self.parts) + return RawExpression(f"0x{num}ULL") def is_approximately_integer(value): @@ -69,8 +75,15 @@ def is_approximately_integer(value): class TimePeriod: - def __init__(self, microseconds=None, milliseconds=None, seconds=None, - minutes=None, hours=None, days=None): + def __init__( + self, + microseconds=None, + milliseconds=None, + seconds=None, + minutes=None, + hours=None, + days=None, + ): if days is not None: if not is_approximately_integer(days): frac_days, days = math.modf(days) @@ -121,33 +134,33 @@ class TimePeriod: def as_dict(self): out = OrderedDict() if self.microseconds is not None: - out['microseconds'] = self.microseconds + out["microseconds"] = self.microseconds if self.milliseconds is not None: - out['milliseconds'] = self.milliseconds + out["milliseconds"] = self.milliseconds if self.seconds is not None: - out['seconds'] = self.seconds + out["seconds"] = self.seconds if self.minutes is not None: - out['minutes'] = self.minutes + out["minutes"] = self.minutes if self.hours is not None: - out['hours'] = self.hours + out["hours"] = self.hours if self.days is not None: - out['days'] = self.days + out["days"] = self.days return out def __str__(self): if self.microseconds is not None: - return f'{self.total_microseconds}us' + return f"{self.total_microseconds}us" if self.milliseconds is not None: - return f'{self.total_milliseconds}ms' + return f"{self.total_milliseconds}ms" if self.seconds is not None: - return f'{self.total_seconds}s' + return f"{self.total_seconds}s" if self.minutes is not None: - return f'{self.total_minutes}min' + return f"{self.total_minutes}min" if self.hours is not None: - return f'{self.total_hours}h' + return f"{self.total_hours}h" if self.days is not None: - return f'{self.total_days}d' - return '0s' + return f"{self.total_days}d" + return "0s" def __repr__(self): return f"TimePeriod<{self.total_microseconds}>" @@ -223,7 +236,7 @@ class TimePeriodMinutes(TimePeriod): pass -LAMBDA_PROG = re.compile(r'id\(\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\)(\.?)') +LAMBDA_PROG = re.compile(r"id\(\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\)(\.?)") class Lambda: @@ -240,12 +253,13 @@ class Lambda: def comment_remover(self, text): def replacer(match): s = match.group(0) - if s.startswith('/'): + if s.startswith("/"): return " " # note: a space and not an empty string return s + pattern = re.compile( r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"', - re.DOTALL | re.MULTILINE + re.DOTALL | re.MULTILINE, ) return re.sub(pattern, replacer, text) @@ -258,7 +272,9 @@ class Lambda: @property def requires_ids(self): if self._requires_ids is None: - self._requires_ids = [ID(self.parts[i]) for i in range(1, len(self.parts), 3)] + self._requires_ids = [ + ID(self.parts[i]) for i in range(1, len(self.parts), 3) + ] return self._requires_ids @property @@ -275,7 +291,7 @@ class Lambda: return self.value def __repr__(self): - return f'Lambda<{self.value}>' + return f"Lambda<{self.value}>" class ID: @@ -286,26 +302,28 @@ class ID: else: self.is_manual = is_manual self.is_declaration = is_declaration - self.type: Optional['MockObjClass'] = type + self.type: Optional["MockObjClass"] = type def resolve(self, registered_ids): from esphome.config_validation import RESERVED_IDS if self.id is None: - base = str(self.type).replace('::', '_').lower() - name = ''.join(c for c in base if c.isalnum() or c == '_') + base = str(self.type).replace("::", "_").lower() + name = "".join(c for c in base if c.isalnum() or c == "_") used = set(registered_ids) | set(RESERVED_IDS) self.id = ensure_unique_string(name, used) return self.id def __str__(self): if self.id is None: - return '' + return "" return self.id def __repr__(self): - return (f'ID<{self.id} declaration={self.is_declaration}, ' - f'type={self.type}, manual={self.is_manual}>') + return ( + f"ID<{self.id} declaration={self.is_declaration}, " + f"type={self.type}, manual={self.is_manual}>" + ) def __eq__(self, other): if isinstance(other, ID): @@ -316,8 +334,12 @@ class ID: return hash(self.id) def copy(self): - return ID(self.id, is_declaration=self.is_declaration, type=self.type, - is_manual=self.is_manual) + return ID( + self.id, + is_declaration=self.is_declaration, + type=self.type, + is_manual=self.is_manual, + ) class DocumentLocation: @@ -328,18 +350,14 @@ class DocumentLocation: @classmethod def from_mark(cls, mark): - return cls( - mark.name, - mark.line, - mark.column - ) + return cls(mark.name, mark.line, mark.column) def __str__(self): - return f'{self.document} {self.line}:{self.column}' + return f"{self.document} {self.line}:{self.column}" @property def as_line_directive(self): - document_path = str(self.document).replace('\\', '\\\\') + document_path = str(self.document).replace("\\", "\\\\") return f'#line {self.line + 1} "{document_path}"' @@ -351,12 +369,11 @@ class DocumentRange: @classmethod def from_marks(cls, start_mark, end_mark): return cls( - DocumentLocation.from_mark(start_mark), - DocumentLocation.from_mark(end_mark) + DocumentLocation.from_mark(start_mark), DocumentLocation.from_mark(end_mark) ) def __str__(self): - return f'[{self.start_mark} - {self.end_mark}]' + return f"[{self.start_mark} - {self.end_mark}]" class Define: @@ -367,14 +384,14 @@ class Define: @property def as_build_flag(self): if self.value is None: - return f'-D{self.name}' - return f'-D{self.name}={self.value}' + return f"-D{self.name}" + return f"-D{self.name}={self.value}" @property def as_macro(self): if self.value is None: - return f'#define {self.name}' - return f'#define {self.name} {self.value}' + return f"#define {self.name}" + return f"#define {self.name} {self.value}" @property def as_tuple(self): @@ -398,7 +415,7 @@ class Library: def as_lib_dep(self): if self.version is None: return self.name - return f'{self.name}@{self.version}' + return f"{self.name}@{self.version}" @property def as_tuple(self): @@ -419,13 +436,13 @@ def coroutine(func): def coroutine_with_priority(priority): def decorator(func): - if getattr(func, '_esphome_coroutine', False): + if getattr(func, "_esphome_coroutine", False): # If func is already a coroutine, do not re-wrap it (performance) return func @functools.wraps(func) def _wrapper_generator(*args, **kwargs): - instance_id = kwargs.pop('__esphome_coroutine_instance__') + instance_id = kwargs.pop("__esphome_coroutine_instance__") if not inspect.isgeneratorfunction(func): # If func is not a generator, return result immediately yield func(*args, **kwargs) @@ -455,8 +472,9 @@ def coroutine_with_priority(priority): @functools.wraps(func) def wrapper(*args, **kwargs): import random - instance_id = random.randint(0, 2**32) - kwargs['__esphome_coroutine_instance__'] = instance_id + + instance_id = random.randint(0, 2 ** 32) + kwargs["__esphome_coroutine_instance__"] = instance_id gen = _wrapper_generator(*args, **kwargs) # pylint: disable=protected-access CORE._add_active_coroutine(instance_id, gen) @@ -466,6 +484,7 @@ def coroutine_with_priority(priority): wrapper._esphome_coroutine = True wrapper.priority = priority return wrapper + return decorator @@ -511,17 +530,17 @@ class EsphomeCore: # Task counter for pending tasks self.task_counter = 0 # The variable cache, for each ID this holds a MockObj of the variable obj - self.variables: Dict[str, 'MockObj'] = {} + self.variables: Dict[str, "MockObj"] = {} # A list of statements that go in the main setup() block - self.main_statements: List['Statement'] = [] + self.main_statements: List["Statement"] = [] # A list of statements to insert in the global block (includes and global variables) - self.global_statements: List['Statement'] = [] + self.global_statements: List["Statement"] = [] # A set of platformio libraries to add to the project self.libraries: List[Library] = [] # A set of build flags to set in the platformio project self.build_flags: Set[str] = set() # A set of defines to set for the compile process in esphome/core/defines.h - self.defines: Set['Define'] = set() + self.defines: Set["Define"] = set() # A dictionary of started coroutines, used to warn when a coroutine was not # awaited. self.active_coroutines: Dict[int, Any] = {} @@ -558,11 +577,11 @@ class EsphomeCore: if self.config is None: raise ValueError("Config has not been loaded yet") - if 'wifi' in self.config: + if "wifi" in self.config: return self.config[CONF_WIFI][CONF_USE_ADDRESS] - if 'ethernet' in self.config: - return self.config['ethernet'][CONF_USE_ADDRESS] + if "ethernet" in self.config: + return self.config["ethernet"][CONF_USE_ADDRESS] return None @@ -608,33 +627,33 @@ class EsphomeCore: return os.path.join(self.build_path, path_) def relative_src_path(self, *path): - return self.relative_build_path('src', *path) + return self.relative_build_path("src", *path) def relative_pioenvs_path(self, *path): if is_hassio(): - return os.path.join('/data', self.name, '.pioenvs', *path) - return self.relative_build_path('.pioenvs', *path) + return os.path.join("/data", self.name, ".pioenvs", *path) + return self.relative_build_path(".pioenvs", *path) def relative_piolibdeps_path(self, *path): if is_hassio(): - return os.path.join('/data', self.name, '.piolibdeps', *path) - return self.relative_build_path('.piolibdeps', *path) + return os.path.join("/data", self.name, ".piolibdeps", *path) + return self.relative_build_path(".piolibdeps", *path) @property def firmware_bin(self): - return self.relative_pioenvs_path(self.name, 'firmware.bin') + return self.relative_pioenvs_path(self.name, "firmware.bin") @property def is_esp8266(self): if self.esp_platform is None: raise ValueError("No platform specified") - return self.esp_platform == 'ESP8266' + return self.esp_platform == "ESP8266" @property def is_esp32(self): if self.esp_platform is None: raise ValueError("No platform specified") - return self.esp_platform == 'ESP32' + return self.esp_platform == "ESP32" def add_job(self, func, *args, **kwargs): coro = coroutine(func) @@ -667,14 +686,17 @@ class EsphomeCore: # Print not-awaited coroutines for obj in self.active_coroutines.values(): - _LOGGER.warning("Coroutine '%s' %s was never awaited with 'yield'.", obj.__name__, obj) + _LOGGER.warning( + "Coroutine '%s' %s was never awaited with 'yield'.", obj.__name__, obj + ) _LOGGER.warning("Please file a bug report with your configuration.") if self.active_coroutines: raise EsphomeError() if self.component_ids: - comps = ', '.join(f"'{x}'" for x in self.component_ids) - _LOGGER.warning("Components %s were never registered. Please create a bug report", - comps) + comps = ", ".join(f"'{x}'" for x in self.component_ids) + _LOGGER.warning( + "Components %s were never registered. Please create a bug report", comps + ) _LOGGER.warning("with your configuration.") raise EsphomeError() self.active_coroutines.clear() @@ -685,8 +707,10 @@ class EsphomeCore: if isinstance(expression, Expression): expression = statement(expression) if not isinstance(expression, Statement): - raise ValueError("Add '{}' must be expression or statement, not {}" - "".format(expression, type(expression))) + raise ValueError( + "Add '{}' must be expression or statement, not {}" + "".format(expression, type(expression)) + ) self.main_statements.append(expression) _LOGGER.debug("Adding: %s", expression) @@ -698,16 +722,20 @@ class EsphomeCore: if isinstance(expression, Expression): expression = statement(expression) if not isinstance(expression, Statement): - raise ValueError("Add '{}' must be expression or statement, not {}" - "".format(expression, type(expression))) + raise ValueError( + "Add '{}' must be expression or statement, not {}" + "".format(expression, type(expression)) + ) self.global_statements.append(expression) _LOGGER.debug("Adding global: %s", expression) return expression def add_library(self, library): if not isinstance(library, Library): - raise ValueError("Library {} must be instance of Library, not {}" - "".format(library, type(library))) + raise ValueError( + "Library {} must be instance of Library, not {}" + "".format(library, type(library)) + ) _LOGGER.debug("Adding library: %s", library) for other in self.libraries[:]: if other.name != library.name: @@ -722,9 +750,11 @@ class EsphomeCore: if other.version == library.version: break - raise ValueError("Version pinning failed! Libraries {} and {} " - "requested with conflicting versions!" - "".format(library, other)) + raise ValueError( + "Version pinning failed! Libraries {} and {} " + "requested with conflicting versions!" + "".format(library, other) + ) else: self.libraries.append(library) return library @@ -740,8 +770,10 @@ class EsphomeCore: elif isinstance(define, Define): pass else: - raise ValueError("Define {} must be string or Define, not {}" - "".format(define, type(define))) + raise ValueError( + "Define {} must be string or Define, not {}" + "".format(define, type(define)) + ) self.defines.add(define) _LOGGER.debug("Adding define: %s", define) return define @@ -784,7 +816,7 @@ class EsphomeCore: text = str(statement(exp)) text = text.rstrip() main_code.append(text) - return '\n'.join(main_code) + '\n\n' + return "\n".join(main_code) + "\n\n" @property def cpp_global_section(self): @@ -795,7 +827,7 @@ class EsphomeCore: text = str(statement(exp)) text = text.rstrip() global_code.append(text) - return '\n'.join(global_code) + '\n' + return "\n".join(global_code) + "\n" class AutoLoad(OrderedDict): @@ -804,13 +836,14 @@ class AutoLoad(OrderedDict): class EnumValue: """Special type used by ESPHome to mark enum values for cv.enum.""" + @property def enum_value(self): - return getattr(self, '_enum_value', None) + return getattr(self, "_enum_value", None) @enum_value.setter def enum_value(self, value): - setattr(self, '_enum_value', value) + setattr(self, "_enum_value", value) CORE = EsphomeCore() diff --git a/esphome/core_config.py b/esphome/core_config.py index f1bb18eef5..c0498a8fa9 100644 --- a/esphome/core_config.py +++ b/esphome/core_config.py @@ -5,25 +5,45 @@ import re import esphome.codegen as cg import esphome.config_validation as cv from esphome import automation, pins -from esphome.const import CONF_ARDUINO_VERSION, CONF_BOARD, CONF_BOARD_FLASH_MODE, \ - CONF_BUILD_PATH, CONF_COMMENT, CONF_ESPHOME, CONF_INCLUDES, CONF_LIBRARIES, \ - CONF_NAME, CONF_ON_BOOT, CONF_ON_LOOP, CONF_ON_SHUTDOWN, CONF_PLATFORM, \ - CONF_PLATFORMIO_OPTIONS, CONF_PRIORITY, CONF_TRIGGER_ID, \ - CONF_ESP8266_RESTORE_FROM_FLASH, ARDUINO_VERSION_ESP8266, \ - ARDUINO_VERSION_ESP32, ESP_PLATFORMS +from esphome.const import ( + CONF_ARDUINO_VERSION, + CONF_BOARD, + CONF_BOARD_FLASH_MODE, + CONF_BUILD_PATH, + CONF_COMMENT, + CONF_ESPHOME, + CONF_INCLUDES, + CONF_LIBRARIES, + CONF_NAME, + CONF_ON_BOOT, + CONF_ON_LOOP, + CONF_ON_SHUTDOWN, + CONF_PLATFORM, + CONF_PLATFORMIO_OPTIONS, + CONF_PRIORITY, + CONF_TRIGGER_ID, + CONF_ESP8266_RESTORE_FROM_FLASH, + ARDUINO_VERSION_ESP8266, + ARDUINO_VERSION_ESP32, + ESP_PLATFORMS, +) from esphome.core import CORE, coroutine_with_priority from esphome.helpers import copy_file_if_changed, walk_files _LOGGER = logging.getLogger(__name__) -BUILD_FLASH_MODES = ['qio', 'qout', 'dio', 'dout'] -StartupTrigger = cg.esphome_ns.class_('StartupTrigger', cg.Component, automation.Trigger.template()) -ShutdownTrigger = cg.esphome_ns.class_('ShutdownTrigger', cg.Component, - automation.Trigger.template()) -LoopTrigger = cg.esphome_ns.class_('LoopTrigger', cg.Component, - automation.Trigger.template()) +BUILD_FLASH_MODES = ["qio", "qout", "dio", "dout"] +StartupTrigger = cg.esphome_ns.class_( + "StartupTrigger", cg.Component, automation.Trigger.template() +) +ShutdownTrigger = cg.esphome_ns.class_( + "ShutdownTrigger", cg.Component, automation.Trigger.template() +) +LoopTrigger = cg.esphome_ns.class_( + "LoopTrigger", cg.Component, automation.Trigger.template() +) -VERSION_REGEX = re.compile(r'^[0-9]+\.[0-9]+\.[0-9]+(?:[ab]\d+)?$') +VERSION_REGEX = re.compile(r"^[0-9]+\.[0-9]+\.[0-9]+(?:[ab]\d+)?$") def validate_board(value): @@ -35,8 +55,11 @@ def validate_board(value): raise NotImplementedError if value not in board_pins: - raise cv.Invalid("Could not find board '{}'. Valid boards are {}".format( - value, ', '.join(sorted(board_pins.keys())))) + raise cv.Invalid( + "Could not find board '{}'. Valid boards are {}".format( + value, ", ".join(sorted(board_pins.keys())) + ) + ) return value @@ -44,16 +67,16 @@ validate_platform = cv.one_of(*ESP_PLATFORMS, upper=True) PLATFORMIO_ESP8266_LUT = { **ARDUINO_VERSION_ESP8266, - 'RECOMMENDED': ARDUINO_VERSION_ESP8266['2.7.4'], - 'LATEST': 'espressif8266', - 'DEV': ARDUINO_VERSION_ESP8266['dev'], + "RECOMMENDED": ARDUINO_VERSION_ESP8266["2.7.4"], + "LATEST": "espressif8266", + "DEV": ARDUINO_VERSION_ESP8266["dev"], } PLATFORMIO_ESP32_LUT = { **ARDUINO_VERSION_ESP32, - 'RECOMMENDED': ARDUINO_VERSION_ESP32['1.0.4'], - 'LATEST': 'espressif32', - 'DEV': ARDUINO_VERSION_ESP32['dev'], + "RECOMMENDED": ARDUINO_VERSION_ESP32["1.0.4"], + "LATEST": "espressif32", + "DEV": ARDUINO_VERSION_ESP32["dev"], } @@ -61,18 +84,28 @@ def validate_arduino_version(value): value = cv.string_strict(value) value_ = value.upper() if CORE.is_esp8266: - if VERSION_REGEX.match(value) is not None and value_ not in PLATFORMIO_ESP8266_LUT: - raise cv.Invalid("Unfortunately the arduino framework version '{}' is unsupported " - "at this time. You can override this by manually using " - "espressif8266@".format(value)) + if ( + VERSION_REGEX.match(value) is not None + and value_ not in PLATFORMIO_ESP8266_LUT + ): + raise cv.Invalid( + "Unfortunately the arduino framework version '{}' is unsupported " + "at this time. You can override this by manually using " + "espressif8266@".format(value) + ) if value_ in PLATFORMIO_ESP8266_LUT: return PLATFORMIO_ESP8266_LUT[value_] return value if CORE.is_esp32: - if VERSION_REGEX.match(value) is not None and value_ not in PLATFORMIO_ESP32_LUT: - raise cv.Invalid("Unfortunately the arduino framework version '{}' is unsupported " - "at this time. You can override this by manually using " - "espressif32@".format(value)) + if ( + VERSION_REGEX.match(value) is not None + and value_ not in PLATFORMIO_ESP32_LUT + ): + raise cv.Invalid( + "Unfortunately the arduino framework version '{}' is unsupported " + "at this time. You can override this by manually using " + "espressif32@".format(value) + ) if value_ in PLATFORMIO_ESP32_LUT: return PLATFORMIO_ESP32_LUT[value_] return value @@ -83,7 +116,7 @@ def default_build_path(): return CORE.name -VALID_INCLUDE_EXTS = {'.h', '.hpp', '.tcc', '.ino', '.cpp', '.c'} +VALID_INCLUDE_EXTS = {".h", ".hpp", ".tcc", ".ino", ".cpp", ".c"} def valid_include(value): @@ -94,62 +127,85 @@ def valid_include(value): value = cv.file_(value) _, ext = os.path.splitext(value) if ext not in VALID_INCLUDE_EXTS: - raise cv.Invalid("Include has invalid file extension {} - valid extensions are {}" - "".format(ext, ', '.join(VALID_INCLUDE_EXTS))) + raise cv.Invalid( + "Include has invalid file extension {} - valid extensions are {}" + "".format(ext, ", ".join(VALID_INCLUDE_EXTS)) + ) return value -CONFIG_SCHEMA = cv.Schema({ - cv.Required(CONF_NAME): cv.valid_name, - cv.Required(CONF_PLATFORM): cv.one_of('ESP8266', 'ESP32', upper=True), - cv.Required(CONF_BOARD): validate_board, - cv.Optional(CONF_COMMENT): cv.string, - cv.Optional(CONF_ARDUINO_VERSION, default='recommended'): validate_arduino_version, - cv.Optional(CONF_BUILD_PATH, default=default_build_path): cv.string, - cv.Optional(CONF_PLATFORMIO_OPTIONS, default={}): cv.Schema({ - cv.string_strict: cv.Any([cv.string], cv.string), - }), - cv.SplitDefault(CONF_ESP8266_RESTORE_FROM_FLASH, esp8266=False): cv.All(cv.only_on_esp8266, - cv.boolean), +CONFIG_SCHEMA = cv.Schema( + { + cv.Required(CONF_NAME): cv.valid_name, + cv.Required(CONF_PLATFORM): cv.one_of("ESP8266", "ESP32", upper=True), + cv.Required(CONF_BOARD): validate_board, + cv.Optional(CONF_COMMENT): cv.string, + cv.Optional( + CONF_ARDUINO_VERSION, default="recommended" + ): validate_arduino_version, + cv.Optional(CONF_BUILD_PATH, default=default_build_path): cv.string, + cv.Optional(CONF_PLATFORMIO_OPTIONS, default={}): cv.Schema( + { + cv.string_strict: cv.Any([cv.string], cv.string), + } + ), + cv.SplitDefault(CONF_ESP8266_RESTORE_FROM_FLASH, esp8266=False): cv.All( + cv.only_on_esp8266, cv.boolean + ), + cv.SplitDefault(CONF_BOARD_FLASH_MODE, esp8266="dout"): cv.one_of( + *BUILD_FLASH_MODES, lower=True + ), + cv.Optional(CONF_ON_BOOT): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StartupTrigger), + cv.Optional(CONF_PRIORITY, default=600.0): cv.float_, + } + ), + cv.Optional(CONF_ON_SHUTDOWN): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ShutdownTrigger), + } + ), + cv.Optional(CONF_ON_LOOP): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(LoopTrigger), + } + ), + cv.Optional(CONF_INCLUDES, default=[]): cv.ensure_list(valid_include), + cv.Optional(CONF_LIBRARIES, default=[]): cv.ensure_list(cv.string_strict), + cv.Optional("esphome_core_version"): cv.invalid( + "The esphome_core_version option has been " + "removed in 1.13 - the esphome core source " + "files are now bundled with ESPHome." + ), + } +) - cv.SplitDefault(CONF_BOARD_FLASH_MODE, esp8266='dout'): cv.one_of(*BUILD_FLASH_MODES, - lower=True), - cv.Optional(CONF_ON_BOOT): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StartupTrigger), - cv.Optional(CONF_PRIORITY, default=600.0): cv.float_, - }), - cv.Optional(CONF_ON_SHUTDOWN): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ShutdownTrigger), - }), - cv.Optional(CONF_ON_LOOP): automation.validate_automation({ - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(LoopTrigger), - }), - cv.Optional(CONF_INCLUDES, default=[]): cv.ensure_list(valid_include), - cv.Optional(CONF_LIBRARIES, default=[]): cv.ensure_list(cv.string_strict), +PRELOAD_CONFIG_SCHEMA = cv.Schema( + { + cv.Required(CONF_NAME): cv.valid_name, + cv.Required(CONF_PLATFORM): validate_platform, + }, + extra=cv.ALLOW_EXTRA, +) - cv.Optional('esphome_core_version'): cv.invalid("The esphome_core_version option has been " - "removed in 1.13 - the esphome core source " - "files are now bundled with ESPHome.") -}) - -PRELOAD_CONFIG_SCHEMA = cv.Schema({ - cv.Required(CONF_NAME): cv.valid_name, - cv.Required(CONF_PLATFORM): validate_platform, -}, extra=cv.ALLOW_EXTRA) - -PRELOAD_CONFIG_SCHEMA2 = PRELOAD_CONFIG_SCHEMA.extend({ - cv.Required(CONF_BOARD): validate_board, - cv.Optional(CONF_BUILD_PATH, default=default_build_path): cv.string, -}) +PRELOAD_CONFIG_SCHEMA2 = PRELOAD_CONFIG_SCHEMA.extend( + { + cv.Required(CONF_BOARD): validate_board, + cv.Optional(CONF_BUILD_PATH, default=default_build_path): cv.string, + } +) def preload_core_config(config): - core_key = 'esphome' - if 'esphomeyaml' in config: - _LOGGER.warning("The esphomeyaml section has been renamed to esphome in 1.11.0. " - "Please replace 'esphomeyaml:' in your configuration with 'esphome:'.") - config[CONF_ESPHOME] = config.pop('esphomeyaml') - core_key = 'esphomeyaml' + core_key = "esphome" + if "esphomeyaml" in config: + _LOGGER.warning( + "The esphomeyaml section has been renamed to esphome in 1.11.0. " + "Please replace 'esphomeyaml:' in your configuration with 'esphome:'." + ) + config[CONF_ESPHOME] = config.pop("esphomeyaml") + core_key = "esphomeyaml" if CONF_ESPHOME not in config: raise cv.RequiredFieldInvalid("required key not provided", CONF_ESPHOME) with cv.prepend_path(core_key): @@ -168,7 +224,7 @@ def include_file(path, basename): copy_file_if_changed(path, dst) _, ext = os.path.splitext(path) - if ext in ['.h', '.hpp', '.tcc']: + if ext in [".h", ".hpp", ".tcc"]: # Header, add include statement cg.add_global(cg.RawStatement(f'#include "{basename}"')) @@ -192,7 +248,9 @@ def add_includes(includes): @coroutine_with_priority(-1000.0) def _esp8266_add_lwip_type(): # If any component has already set this, do not change it - if any(flag.startswith('-DPIO_FRAMEWORK_ARDUINO_LWIP2_') for flag in CORE.build_flags): + if any( + flag.startswith("-DPIO_FRAMEWORK_ARDUINO_LWIP2_") for flag in CORE.build_flags + ): return # Default for platformio is LWIP2_LOW_MEMORY with: @@ -206,7 +264,7 @@ def _esp8266_add_lwip_type(): # - MSS=1460 # - LWIP_FEATURES disabled (because we don't need them) # Other projects like Tasmota & ESPEasy also use this - cg.add_build_flag('-DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH') + cg.add_build_flag("-DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH") @coroutine_with_priority(30.0) @@ -229,8 +287,10 @@ def _add_automations(config): @coroutine_with_priority(100.0) def to_code(config): - cg.add_global(cg.global_ns.namespace('esphome').using) - cg.add(cg.App.pre_setup(config[CONF_NAME], cg.RawExpression('__DATE__ ", " __TIME__'))) + cg.add_global(cg.global_ns.namespace("esphome").using) + cg.add( + cg.App.pre_setup(config[CONF_NAME], cg.RawExpression('__DATE__ ", " __TIME__')) + ) CORE.add_job(_add_automations, config) @@ -238,27 +298,27 @@ def to_code(config): if CORE.is_esp8266: CORE.add_job(_esp8266_add_lwip_type) - cg.add_build_flag('-fno-exceptions') + cg.add_build_flag("-fno-exceptions") # Libraries if CORE.is_esp32: - cg.add_library('ESPmDNS', None) + cg.add_library("ESPmDNS", None) elif CORE.is_esp8266: - cg.add_library('ESP8266WiFi', None) - cg.add_library('ESP8266mDNS', None) + cg.add_library("ESP8266WiFi", None) + cg.add_library("ESP8266mDNS", None) for lib in config[CONF_LIBRARIES]: - if '@' in lib: - name, vers = lib.split('@', 1) + if "@" in lib: + name, vers = lib.split("@", 1) cg.add_library(name, vers) else: cg.add_library(lib, None) - cg.add_build_flag('-Wno-unused-variable') - cg.add_build_flag('-Wno-unused-but-set-variable') - cg.add_build_flag('-Wno-sign-compare') + cg.add_build_flag("-Wno-unused-variable") + cg.add_build_flag("-Wno-unused-but-set-variable") + cg.add_build_flag("-Wno-sign-compare") if config.get(CONF_ESP8266_RESTORE_FROM_FLASH, False): - cg.add_define('USE_ESP8266_PREFERENCES_FLASH') + cg.add_define("USE_ESP8266_PREFERENCES_FLASH") if config[CONF_INCLUDES]: CORE.add_job(add_includes, config[CONF_INCLUDES]) diff --git a/esphome/cpp_generator.py b/esphome/cpp_generator.py index cfa178c4f9..754ce229a9 100644 --- a/esphome/cpp_generator.py +++ b/esphome/cpp_generator.py @@ -8,9 +8,20 @@ from esphome.yaml_util import ESPHomeDataBase from typing import Any, Generator, List, Optional, Tuple, Type, Union, Sequence from esphome.core import ( # noqa - CORE, HexInt, ID, Lambda, TimePeriod, TimePeriodMicroseconds, - TimePeriodMilliseconds, TimePeriodMinutes, TimePeriodSeconds, coroutine, Library, Define, - EnumValue) + CORE, + HexInt, + ID, + Lambda, + TimePeriod, + TimePeriodMicroseconds, + TimePeriodMilliseconds, + TimePeriodMinutes, + TimePeriodSeconds, + coroutine, + Library, + Define, + EnumValue, +) from esphome.helpers import cpp_string_escape, indent_all_but_first_and_last from esphome.util import OrderedDict @@ -25,12 +36,23 @@ class Expression(abc.ABC): """ -SafeExpType = Union[Expression, bool, str, str, int, float, TimePeriod, - Type[bool], Type[int], Type[float], Sequence[Any]] +SafeExpType = Union[ + Expression, + bool, + str, + str, + int, + float, + TimePeriod, + Type[bool], + Type[int], + Type[float], + Sequence[Any], +] class RawExpression(Expression): - __slots__ = ("text", ) + __slots__ = ("text",) def __init__(self, text: str): self.text = text @@ -68,7 +90,7 @@ class VariableDeclarationExpression(Expression): class ExpressionList(Expression): - __slots__ = ("args", ) + __slots__ = ("args",) def __init__(self, *args: Optional[SafeExpType]): # Remove every None on end @@ -86,13 +108,13 @@ class ExpressionList(Expression): class TemplateArguments(Expression): - __slots__ = ("args", ) + __slots__ = ("args",) def __init__(self, *args: SafeExpType): self.args = ExpressionList(*args) def __str__(self): - return f'<{self.args}>' + return f"<{self.args}>" def __iter__(self): return iter(self.args) @@ -112,8 +134,8 @@ class CallExpression(Expression): def __str__(self): if self.template_args is not None: - return f'{self.base}{self.template_args}({self.args})' - return f'{self.base}({self.args})' + return f"{self.base}{self.template_args}({self.args})" + return f"{self.base}({self.args})" class StructInitializer(Expression): @@ -132,10 +154,10 @@ class StructInitializer(Expression): self.args[key] = exp def __str__(self): - cpp = f'{self.base}{{\n' + cpp = f"{self.base}{{\n" for key, value in self.args.items(): - cpp += f' .{key} = {value},\n' - cpp += '}' + cpp += f" .{key} = {value},\n" + cpp += "}" return cpp @@ -153,14 +175,14 @@ class ArrayInitializer(Expression): def __str__(self): if not self.args: - return '{}' + return "{}" if self.multiline: - cpp = '{\n' + cpp = "{\n" for arg in self.args: - cpp += f' {arg},\n' - cpp += '}' + cpp += f" {arg},\n" + cpp += "}" else: - cpp = '{' + ', '.join(str(arg) for arg in self.args) + '}' + cpp = "{" + ", ".join(str(arg) for arg in self.args) + "}" return cpp @@ -176,9 +198,11 @@ class ParameterExpression(Expression): class ParameterListExpression(Expression): - __slots__ = ("parameters", ) + __slots__ = ("parameters",) - def __init__(self, *parameters: Union[ParameterExpression, Tuple[SafeExpType, str]]): + def __init__( + self, *parameters: Union[ParameterExpression, Tuple[SafeExpType, str]] + ): self.parameters = [] for parameter in parameters: if not isinstance(parameter, ParameterExpression): @@ -192,7 +216,9 @@ class ParameterListExpression(Expression): class LambdaExpression(Expression): __slots__ = ("parts", "parameters", "capture", "return_type", "source") - def __init__(self, parts, parameters, capture: str = '=', return_type=None, source=None): + def __init__( + self, parts, parameters, capture: str = "=", return_type=None, source=None + ): self.parts = parts if not isinstance(parameters, ParameterListExpression): parameters = ParameterListExpression(*parameters) @@ -202,18 +228,18 @@ class LambdaExpression(Expression): self.return_type = safe_exp(return_type) if return_type is not None else None def __str__(self): - cpp = f'[{self.capture}]({self.parameters})' + cpp = f"[{self.capture}]({self.parameters})" if self.return_type is not None: - cpp += f' -> {self.return_type}' - cpp += ' {\n' + cpp += f" -> {self.return_type}" + cpp += " {\n" if self.source is not None: - cpp += f'{self.source.as_line_directive}\n' - cpp += f'{self.content}\n}}' + cpp += f"{self.source.as_line_directive}\n" + cpp += f"{self.content}\n}}" return indent_all_but_first_and_last(cpp) @property def content(self): - return ''.join(str(part) for part in self.parts) + return "".join(str(part) for part in self.parts) # pylint: disable=abstract-method @@ -222,7 +248,7 @@ class Literal(Expression, metaclass=abc.ABCMeta): class StringLiteral(Literal): - __slots__ = ("string", ) + __slots__ = ("string",) def __init__(self, string: str): super().__init__() @@ -233,7 +259,7 @@ class StringLiteral(Literal): class IntLiteral(Literal): - __slots__ = ("i", ) + __slots__ = ("i",) def __init__(self, i: int): super().__init__() @@ -241,16 +267,16 @@ class IntLiteral(Literal): def __str__(self): if self.i > 4294967295: - return f'{self.i}ULL' + return f"{self.i}ULL" if self.i > 2147483647: - return f'{self.i}UL' + return f"{self.i}UL" if self.i < -2147483648: - return f'{self.i}LL' + return f"{self.i}LL" return str(self.i) class BoolLiteral(Literal): - __slots__ = ("binary", ) + __slots__ = ("binary",) def __init__(self, binary: bool): super().__init__() @@ -261,7 +287,7 @@ class BoolLiteral(Literal): class HexIntLiteral(Literal): - __slots__ = ("i", ) + __slots__ = ("i",) def __init__(self, i: int): super().__init__() @@ -272,7 +298,7 @@ class HexIntLiteral(Literal): class FloatLiteral(Literal): - __slots__ = ("f", ) + __slots__ = ("f",) def __init__(self, value: float): super().__init__() @@ -321,11 +347,15 @@ def safe_exp(obj: SafeExpType) -> Expression: if obj is float: return float_ if isinstance(obj, ID): - raise ValueError("Object {} is an ID. Did you forget to register the variable?" - "".format(obj)) + raise ValueError( + "Object {} is an ID. Did you forget to register the variable?" + "".format(obj) + ) if inspect.isgenerator(obj): - raise ValueError("Object {} is a coroutine. Did you forget to await the expression with " - "'yield'?".format(obj)) + raise ValueError( + "Object {} is a coroutine. Did you forget to await the expression with " + "'yield'?".format(obj) + ) raise ValueError("Object is not an expression", obj) @@ -340,7 +370,7 @@ class Statement(abc.ABC): class RawStatement(Statement): - __slots__ = ("text", ) + __slots__ = ("text",) def __init__(self, text: str): self.text = text @@ -350,7 +380,7 @@ class RawStatement(Statement): class ExpressionStatement(Statement): - __slots__ = ("expression", ) + __slots__ = ("expression",) def __init__(self, expression): self.expression = safe_exp(expression) @@ -360,22 +390,22 @@ class ExpressionStatement(Statement): class LineComment(Statement): - __slots__ = ("value", ) + __slots__ = ("value",) def __init__(self, value: str): self.value = value def __str__(self): - parts = re.sub(r'\\\s*\n', r'\n', self.value, re.MULTILINE).split('\n') - parts = [f'// {x}' for x in parts] - return '\n'.join(parts) + parts = re.sub(r"\\\s*\n", r"\n", self.value, re.MULTILINE).split("\n") + parts = [f"// {x}" for x in parts] + return "\n".join(parts) class ProgmemAssignmentExpression(AssignmentExpression): __slots__ = () def __init__(self, type_, name, rhs, obj): - super().__init__(type_, '', name, rhs, obj) + super().__init__(type_, "", name, rhs, obj) def __str__(self): return f"static const {self.type} {self.name}[] PROGMEM = {self.rhs}" @@ -383,7 +413,7 @@ class ProgmemAssignmentExpression(AssignmentExpression): def progmem_array(id_, rhs) -> "MockObj": rhs = safe_exp(rhs) - obj = MockObj(id_, '.') + obj = MockObj(id_, ".") assignment = ProgmemAssignmentExpression(id_.type, id_, rhs, obj) CORE.add(assignment) CORE.register_variable(id_, obj) @@ -391,15 +421,14 @@ def progmem_array(id_, rhs) -> "MockObj": def statement(expression: Union[Expression, Statement]) -> Statement: - """Convert expression into a statement unless is already a statement. - """ + """Convert expression into a statement unless is already a statement.""" if isinstance(expression, Statement): return expression return ExpressionStatement(expression) def variable(id_: ID, rhs: SafeExpType, type_: "MockObj" = None) -> "MockObj": - """Declare a new variable (not pointer type) in the code generation. + """Declare a new variable, not pointer type, in the code generation. :param id_: The ID used to declare the variable. :param rhs: The expression to place on the right hand side of the assignment. @@ -410,10 +439,10 @@ def variable(id_: ID, rhs: SafeExpType, type_: "MockObj" = None) -> "MockObj": """ assert isinstance(id_, ID) rhs = safe_exp(rhs) - obj = MockObj(id_, '.') + obj = MockObj(id_, ".") if type_ is not None: id_.type = type_ - assignment = AssignmentExpression(id_.type, '', id_, rhs, obj) + assignment = AssignmentExpression(id_.type, "", id_, rhs, obj) CORE.add(assignment) CORE.register_variable(id_, obj) return obj @@ -430,10 +459,10 @@ def Pvariable(id_: ID, rhs: SafeExpType, type_: "MockObj" = None) -> "MockObj": :returns The new variable as a MockObj. """ rhs = safe_exp(rhs) - obj = MockObj(id_, '->') + obj = MockObj(id_, "->") if type_ is not None: id_.type = type_ - decl = VariableDeclarationExpression(id_.type, '*', id_) + decl = VariableDeclarationExpression(id_.type, "*", id_) CORE.add_global(decl) assignment = AssignmentExpression(None, None, id_, rhs, obj) CORE.add(assignment) @@ -529,8 +558,10 @@ def get_variable_with_full_id(id_: ID) -> Generator[Tuple[ID, "MockObj"], None, @coroutine def process_lambda( - value: Lambda, parameters: List[Tuple[SafeExpType, str]], - capture: str = '=', return_type: SafeExpType = None + value: Lambda, + parameters: List[Tuple[SafeExpType, str]], + capture: str = "=", + return_type: SafeExpType = None, ) -> Generator[LambdaExpression, None, None]: """Process the given lambda value into a LambdaExpression. @@ -551,16 +582,19 @@ def process_lambda( parts = value.parts[:] for i, id in enumerate(value.requires_ids): full_id, var = yield CORE.get_variable_with_full_id(id) - if full_id is not None and isinstance(full_id.type, MockObjClass) and \ - full_id.type.inherits_from(GlobalsComponent): + if ( + full_id is not None + and isinstance(full_id.type, MockObjClass) + and full_id.type.inherits_from(GlobalsComponent) + ): parts[i * 3 + 1] = var.value() continue - if parts[i * 3 + 2] == '.': + if parts[i * 3 + 2] == ".": parts[i * 3 + 1] = var._ else: parts[i * 3 + 1] = var - parts[i * 3 + 2] = '' + parts[i * 3 + 2] = "" if isinstance(value, ESPHomeDataBase) and value.esp_range is not None: location = value.esp_range.start_mark @@ -576,10 +610,12 @@ def is_template(value): @coroutine -def templatable(value: Any, - args: List[Tuple[SafeExpType, str]], - output_type: Optional[SafeExpType], - to_exp: Any = None): +def templatable( + value: Any, + args: List[Tuple[SafeExpType, str]], + output_type: Optional[SafeExpType], + to_exp: Any = None, +): """Generate code for a templatable config option. If `value` is a templated value, the lambda expression is returned. @@ -608,20 +644,21 @@ class MockObj(Expression): Mostly consists of magic methods that allow ESPHome's codegen syntax. """ + __slots__ = ("base", "op") - def __init__(self, base, op='.'): + def __init__(self, base, op="."): self.base = base self.op = op def __getattr__(self, attr: str) -> "MockObj": - next_op = '.' - if attr.startswith('P') and self.op not in ['::', '']: + next_op = "." + if attr.startswith("P") and self.op not in ["::", ""]: attr = attr[1:] - next_op = '->' - if attr.startswith('_'): + next_op = "->" + if attr.startswith("_"): attr = attr[1:] - return MockObj(f'{self.base}{self.op}{attr}', next_op) + return MockObj(f"{self.base}{self.op}{attr}", next_op) def __call__(self, *args): # type: (SafeExpType) -> MockObj call = CallExpression(self.base, *args) @@ -631,29 +668,29 @@ class MockObj(Expression): return str(self.base) def __repr__(self): - return 'MockObj<{}>'.format(str(self.base)) + return "MockObj<{}>".format(str(self.base)) @property def _(self) -> "MockObj": - return MockObj(f'{self.base}{self.op}') + return MockObj(f"{self.base}{self.op}") @property def new(self) -> "MockObj": - return MockObj(f'new {self.base}', '->') + return MockObj(f"new {self.base}", "->") def template(self, *args: SafeExpType) -> "MockObj": if len(args) != 1 or not isinstance(args[0], TemplateArguments): args = TemplateArguments(*args) else: args = args[0] - return MockObj(f'{self.base}{args}') + return MockObj(f"{self.base}{args}") def namespace(self, name: str) -> "MockObj": - return MockObj(f'{self._}{name}', '::') + return MockObj(f"{self._}{name}", "::") def class_(self, name: str, *parents: "MockObjClass") -> "MockObjClass": - op = '' if self.op == '' else '::' - return MockObjClass(f'{self.base}{op}{name}', '.', parents=parents) + op = "" if self.op == "" else "::" + return MockObjClass(f"{self.base}{op}{name}", ".", parents=parents) def struct(self, name: str) -> "MockObjClass": return self.class_(name) @@ -662,50 +699,50 @@ class MockObj(Expression): return MockObjEnum(enum=name, is_class=is_class, base=self.base, op=self.op) def operator(self, name: str) -> "MockObj": - if name == 'ref': - return MockObj(f'{self.base} &', '') - if name == 'ptr': - return MockObj(f'{self.base} *', '') + if name == "ref": + return MockObj(f"{self.base} &", "") + if name == "ptr": + return MockObj(f"{self.base} *", "") if name == "const": - return MockObj(f'const {self.base}', '') + return MockObj(f"const {self.base}", "") raise ValueError("Expected one of ref, ptr, const.") @property def using(self) -> "MockObj": - assert self.op == '::' - return MockObj(f'using namespace {self.base}') + assert self.op == "::" + return MockObj(f"using namespace {self.base}") def __getitem__(self, item: Union[str, Expression]) -> "MockObj": - next_op = '.' - if isinstance(item, str) and item.startswith('P'): + next_op = "." + if isinstance(item, str) and item.startswith("P"): item = item[1:] - next_op = '->' - return MockObj(f'{self.base}[{item}]', next_op) + next_op = "->" + return MockObj(f"{self.base}[{item}]", next_op) class MockObjEnum(MockObj): def __init__(self, *args, **kwargs): - self._enum = kwargs.pop('enum') - self._is_class = kwargs.pop('is_class') - base = kwargs.pop('base') + self._enum = kwargs.pop("enum") + self._is_class = kwargs.pop("is_class") + base = kwargs.pop("base") if self._is_class: - base = base + '::' + self._enum - kwargs['op'] = '::' - kwargs['base'] = base + base = base + "::" + self._enum + kwargs["op"] = "::" + kwargs["base"] = base MockObj.__init__(self, *args, **kwargs) def __str__(self): if self._is_class: return super().__str__() - return f'{self.base}{self.op}{self._enum}' + return f"{self.base}{self.op}{self._enum}" def __repr__(self): - return f'MockObj<{str(self.base)}>' + return f"MockObj<{str(self.base)}>" class MockObjClass(MockObj): def __init__(self, *args, **kwargs): - parens = kwargs.pop('parents') + parens = kwargs.pop("parents") MockObj.__init__(self, *args, **kwargs) self._parents = [] for paren in parens: @@ -730,7 +767,7 @@ class MockObjClass(MockObj): args = args[0] new_parents = self._parents[:] new_parents.append(self) - return MockObjClass(f'{self.base}{args}', parents=new_parents) + return MockObjClass(f"{self.base}{args}", parents=new_parents) def __repr__(self): - return f'MockObjClass<{str(self.base)}, parents={self._parents}>' + return f"MockObjClass<{str(self.base)}, parents={self._parents}>" diff --git a/esphome/cpp_helpers.py b/esphome/cpp_helpers.py index f01981acc8..e83f989d7a 100644 --- a/esphome/cpp_helpers.py +++ b/esphome/cpp_helpers.py @@ -1,5 +1,12 @@ -from esphome.const import CONF_INVERTED, CONF_MODE, CONF_NUMBER, CONF_SETUP_PRIORITY, \ - CONF_UPDATE_INTERVAL, CONF_TYPE_ID +from esphome.const import ( + CONF_INVERTED, + CONF_MODE, + CONF_NUMBER, + CONF_SETUP_PRIORITY, + CONF_UPDATE_INTERVAL, + CONF_TYPE_ID, +) + # pylint: disable=unused-import from esphome.core import coroutine, ID, CORE, ConfigType from esphome.cpp_generator import RawExpression, add, get_variable @@ -16,6 +23,7 @@ def gpio_pin_expression(conf): if conf is None: return from esphome import pins + for key, (func, _) in pins.PIN_SCHEMA_REGISTRY.items(): if key in conf: yield coroutine(func)(conf) @@ -38,9 +46,11 @@ def register_component(var, config): """ id_ = str(var.base) if id_ not in CORE.component_ids: - raise ValueError("Component ID {} was not declared to inherit from Component, " - "or was registered twice. Please create a bug report with your " - "configuration.".format(id_)) + raise ValueError( + "Component ID {} was not declared to inherit from Component, " + "or was registered twice. Please create a bug report with your " + "configuration.".format(id_) + ) CORE.component_ids.remove(id_) if CONF_SETUP_PRIORITY in config: add(var.set_setup_priority(config[CONF_SETUP_PRIORITY])) diff --git a/esphome/cpp_types.py b/esphome/cpp_types.py index 4a9dce332b..3036249a03 100644 --- a/esphome/cpp_types.py +++ b/esphome/cpp_types.py @@ -1,33 +1,33 @@ from esphome.cpp_generator import MockObj -global_ns = MockObj('', '') -void = global_ns.namespace('void') -nullptr = global_ns.namespace('nullptr') -float_ = global_ns.namespace('float') -double = global_ns.namespace('double') -bool_ = global_ns.namespace('bool') -int_ = global_ns.namespace('int') -std_ns = global_ns.namespace('std') -std_string = std_ns.class_('string') -std_vector = std_ns.class_('vector') -uint8 = global_ns.namespace('uint8_t') -uint16 = global_ns.namespace('uint16_t') -uint32 = global_ns.namespace('uint32_t') -int32 = global_ns.namespace('int32_t') -const_char_ptr = global_ns.namespace('const char *') -NAN = global_ns.namespace('NAN') +global_ns = MockObj("", "") +void = global_ns.namespace("void") +nullptr = global_ns.namespace("nullptr") +float_ = global_ns.namespace("float") +double = global_ns.namespace("double") +bool_ = global_ns.namespace("bool") +int_ = global_ns.namespace("int") +std_ns = global_ns.namespace("std") +std_string = std_ns.class_("string") +std_vector = std_ns.class_("vector") +uint8 = global_ns.namespace("uint8_t") +uint16 = global_ns.namespace("uint16_t") +uint32 = global_ns.namespace("uint32_t") +int32 = global_ns.namespace("int32_t") +const_char_ptr = global_ns.namespace("const char *") +NAN = global_ns.namespace("NAN") esphome_ns = global_ns # using namespace esphome; App = esphome_ns.App -Nameable = esphome_ns.class_('Nameable') -Component = esphome_ns.class_('Component') -ComponentPtr = Component.operator('ptr') -PollingComponent = esphome_ns.class_('PollingComponent', Component) -Application = esphome_ns.class_('Application') -optional = esphome_ns.class_('optional') -arduino_json_ns = global_ns.namespace('ArduinoJson') -JsonObject = arduino_json_ns.class_('JsonObject') -JsonObjectRef = JsonObject.operator('ref') -JsonObjectConstRef = JsonObjectRef.operator('const') -Controller = esphome_ns.class_('Controller') +Nameable = esphome_ns.class_("Nameable") +Component = esphome_ns.class_("Component") +ComponentPtr = Component.operator("ptr") +PollingComponent = esphome_ns.class_("PollingComponent", Component) +Application = esphome_ns.class_("Application") +optional = esphome_ns.class_("optional") +arduino_json_ns = global_ns.namespace("ArduinoJson") +JsonObject = arduino_json_ns.class_("JsonObject") +JsonObjectRef = JsonObject.operator("ref") +JsonObjectConstRef = JsonObjectRef.operator("const") +Controller = esphome_ns.class_("Controller") -GPIOPin = esphome_ns.class_('GPIOPin') +GPIOPin = esphome_ns.class_("GPIOPin") diff --git a/esphome/dashboard/dashboard.py b/esphome/dashboard/dashboard.py index c53c68b3a3..1575d9f9b1 100644 --- a/esphome/dashboard/dashboard.py +++ b/esphome/dashboard/dashboard.py @@ -27,8 +27,13 @@ import tornado.websocket from esphome import const, util from esphome.helpers import mkdir_p, get_bool_env, run_system_command -from esphome.storage_json import EsphomeStorageJSON, StorageJSON, \ - esphome_storage_path, ext_storage_path, trash_storage_path +from esphome.storage_json import ( + EsphomeStorageJSON, + StorageJSON, + esphome_storage_path, + ext_storage_path, + trash_storage_path, +) from esphome.util import shlex_quote, get_serial_ports from .util import password_hash @@ -42,18 +47,18 @@ _LOGGER = logging.getLogger(__name__) class DashboardSettings: def __init__(self): - self.config_dir = '' - self.password_hash = '' - self.username = '' + self.config_dir = "" + self.password_hash = "" + self.username = "" self.using_password = False self.on_hassio = False self.cookie_secret = None def parse_args(self, args): self.on_hassio = args.hassio - password = args.password or os.getenv('PASSWORD', '') + password = args.password or os.getenv("PASSWORD", "") if not self.on_hassio: - self.username = args.username or os.getenv('USERNAME', '') + self.username = args.username or os.getenv("USERNAME", "") self.using_password = bool(password) if self.using_password: self.password_hash = password_hash(password) @@ -61,17 +66,17 @@ class DashboardSettings: @property def relative_url(self): - return os.getenv('ESPHOME_DASHBOARD_RELATIVE_URL', '/') + return os.getenv("ESPHOME_DASHBOARD_RELATIVE_URL", "/") @property def status_use_ping(self): - return get_bool_env('ESPHOME_DASHBOARD_USE_PING') + return get_bool_env("ESPHOME_DASHBOARD_USE_PING") @property def using_hassio_auth(self): if not self.on_hassio: return False - return not get_bool_env('DISABLE_HA_AUTHENTICATION') + return not get_bool_env("DISABLE_HA_AUTHENTICATION") @property def using_auth(self): @@ -84,10 +89,7 @@ class DashboardSettings: return False # Compare password in constant running time (to prevent timing attacks) - return hmac.compare_digest( - self.password_hash, - password_hash(password) - ) + return hmac.compare_digest(self.password_hash, password_hash(password)) def rel_path(self, *args): return os.path.join(self.config_dir, *args) @@ -98,24 +100,24 @@ class DashboardSettings: settings = DashboardSettings() -cookie_authenticated_yes = b'yes' +cookie_authenticated_yes = b"yes" def template_args(): version = const.__version__ - if 'b' in version: - docs_link = 'https://beta.esphome.io/' - elif 'dev' in version: - docs_link = 'https://next.esphome.io/' + if "b" in version: + docs_link = "https://beta.esphome.io/" + elif "dev" in version: + docs_link = "https://next.esphome.io/" else: - docs_link = 'https://www.esphome.io/' + docs_link = "https://www.esphome.io/" return { - 'version': version, - 'docs_link': docs_link, - 'get_static_file_url': get_static_file_url, - 'relative_url': settings.relative_url, - 'streamer_mode': get_bool_env('ESPHOME_STREAMER_MODE'), - 'config_dir': settings.config_dir, + "version": version, + "docs_link": docs_link, + "get_static_file_url": get_static_file_url, + "relative_url": settings.relative_url, + "streamer_mode": get_bool_env("ESPHOME_STREAMER_MODE"), + "config_dir": settings.config_dir, } @@ -123,9 +125,10 @@ def authenticated(func): @functools.wraps(func) def decorator(self, *args, **kwargs): if not is_authenticated(self): - self.redirect('./login') + self.redirect("./login") return None return func(self, *args, **kwargs) + return decorator @@ -133,23 +136,27 @@ def is_authenticated(request_handler): if settings.on_hassio: # Handle ingress - disable auth on ingress port # X-Hassio-Ingress is automatically stripped on the non-ingress server in nginx - header = request_handler.request.headers.get('X-Hassio-Ingress', 'NO') - if str(header) == 'YES': + header = request_handler.request.headers.get("X-Hassio-Ingress", "NO") + if str(header) == "YES": return True if settings.using_auth: - return request_handler.get_secure_cookie('authenticated') == cookie_authenticated_yes + return ( + request_handler.get_secure_cookie("authenticated") + == cookie_authenticated_yes + ) return True def bind_config(func): def decorator(self, *args, **kwargs): - configuration = self.get_argument('configuration') + configuration = self.get_argument("configuration") if not is_allowed(configuration): self.set_status(500) return None kwargs = kwargs.copy() - kwargs['configuration'] = configuration + kwargs["configuration"] = configuration return func(self, *args, **kwargs) + return decorator @@ -160,7 +167,7 @@ class BaseHandler(tornado.web.RequestHandler): def websocket_class(cls): # pylint: disable=protected-access - if not hasattr(cls, '_message_handlers'): + if not hasattr(cls, "_message_handlers"): cls._message_handlers = {} for _, method in cls.__dict__.items(): @@ -175,6 +182,7 @@ def websocket_method(name): # pylint: disable=protected-access fn._message_handler = name return fn + return wrap @@ -190,7 +198,7 @@ class EsphomeCommandWebSocket(tornado.websocket.WebSocketHandler): def on_message(self, message): # Messages are always JSON, 500 when not json_message = json.loads(message) - type_ = json_message['type'] + type_ = json_message["type"] # pylint: disable=no-member handlers = type(self)._message_handlers if type_ not in handlers: @@ -199,17 +207,19 @@ class EsphomeCommandWebSocket(tornado.websocket.WebSocketHandler): handlers[type_](self, json_message) - @websocket_method('spawn') + @websocket_method("spawn") def handle_spawn(self, json_message): if self._proc is not None: # spawn can only be called once return command = self.build_command(json_message) - _LOGGER.info("Running command '%s'", ' '.join(shlex_quote(x) for x in command)) - self._proc = tornado.process.Subprocess(command, - stdout=tornado.process.Subprocess.STREAM, - stderr=subprocess.STDOUT, - stdin=tornado.process.Subprocess.STREAM) + _LOGGER.info("Running command '%s'", " ".join(shlex_quote(x) for x in command)) + self._proc = tornado.process.Subprocess( + command, + stdout=tornado.process.Subprocess.STREAM, + stderr=subprocess.STDOUT, + stdin=tornado.process.Subprocess.STREAM, + ) self._proc.set_exit_callback(self._proc_on_exit) tornado.ioloop.IOLoop.current().spawn_callback(self._redirect_stdout) @@ -217,34 +227,34 @@ class EsphomeCommandWebSocket(tornado.websocket.WebSocketHandler): def is_process_active(self): return self._proc is not None and self._proc.returncode is None - @websocket_method('stdin') + @websocket_method("stdin") def handle_stdin(self, json_message): if not self.is_process_active: return - data = json_message['data'] - data = codecs.encode(data, 'utf8', 'replace') + data = json_message["data"] + data = codecs.encode(data, "utf8", "replace") _LOGGER.debug("< stdin: %s", data) self._proc.stdin.write(data) @tornado.gen.coroutine def _redirect_stdout(self): - reg = b'[\n\r]' + reg = b"[\n\r]" while True: try: data = yield self._proc.stdout.read_until_regex(reg) except tornado.iostream.StreamClosedError: break - data = codecs.decode(data, 'utf8', 'replace') + data = codecs.decode(data, "utf8", "replace") _LOGGER.debug("> stdout: %s", data) - self.write_message({'event': 'line', 'data': data}) + self.write_message({"event": "line", "data": data}) def _proc_on_exit(self, returncode): if not self._is_closed: # Check if the proc was not forcibly closed _LOGGER.info("Process exited with return code %s", returncode) - self.write_message({'event': 'exit', 'code': returncode}) + self.write_message({"event": "exit", "code": returncode}) def on_close(self): # Check if proc exists (if 'start' has been run) @@ -260,45 +270,57 @@ class EsphomeCommandWebSocket(tornado.websocket.WebSocketHandler): class EsphomeLogsHandler(EsphomeCommandWebSocket): def build_command(self, json_message): - config_file = settings.rel_path(json_message['configuration']) - return ["esphome", "--dashboard", config_file, "logs", '--serial-port', - json_message["port"]] + config_file = settings.rel_path(json_message["configuration"]) + return [ + "esphome", + "--dashboard", + config_file, + "logs", + "--serial-port", + json_message["port"], + ] class EsphomeUploadHandler(EsphomeCommandWebSocket): def build_command(self, json_message): - config_file = settings.rel_path(json_message['configuration']) - return ["esphome", "--dashboard", config_file, "run", '--upload-port', - json_message["port"]] + config_file = settings.rel_path(json_message["configuration"]) + return [ + "esphome", + "--dashboard", + config_file, + "run", + "--upload-port", + json_message["port"], + ] class EsphomeCompileHandler(EsphomeCommandWebSocket): def build_command(self, json_message): - config_file = settings.rel_path(json_message['configuration']) + config_file = settings.rel_path(json_message["configuration"]) return ["esphome", "--dashboard", config_file, "compile"] class EsphomeValidateHandler(EsphomeCommandWebSocket): def build_command(self, json_message): - config_file = settings.rel_path(json_message['configuration']) + config_file = settings.rel_path(json_message["configuration"]) return ["esphome", "--dashboard", config_file, "config"] class EsphomeCleanMqttHandler(EsphomeCommandWebSocket): def build_command(self, json_message): - config_file = settings.rel_path(json_message['configuration']) + config_file = settings.rel_path(json_message["configuration"]) return ["esphome", "--dashboard", config_file, "clean-mqtt"] class EsphomeCleanHandler(EsphomeCommandWebSocket): def build_command(self, json_message): - config_file = settings.rel_path(json_message['configuration']) + config_file = settings.rel_path(json_message["configuration"]) return ["esphome", "--dashboard", config_file, "clean"] class EsphomeVscodeHandler(EsphomeCommandWebSocket): def build_command(self, json_message): - return ["esphome", "--dashboard", "-q", 'dummy', "vscode"] + return ["esphome", "--dashboard", "-q", "dummy", "vscode"] class EsphomeAceEditorHandler(EsphomeCommandWebSocket): @@ -318,15 +340,15 @@ class SerialPortRequestHandler(BaseHandler): data = [] for port in ports: desc = port.description - if port.path == '/dev/ttyAMA0': - desc = 'UART pins on GPIO header' - split_desc = desc.split(' - ') + if port.path == "/dev/ttyAMA0": + desc = "UART pins on GPIO header" + split_desc = desc.split(" - ") if len(split_desc) == 2 and split_desc[0] == split_desc[1]: # Some serial ports repeat their values desc = split_desc[0] - data.append({'port': port.path, 'desc': desc}) - data.append({'port': 'OTA', 'desc': 'Over-The-Air'}) - data.sort(key=lambda x: x['port'], reverse=True) + data.append({"port": port.path, "desc": desc}) + data.append({"port": "OTA", "desc": "Over-The-Air"}) + data.sort(key=lambda x: x["port"], reverse=True) self.write(json.dumps(data)) @@ -336,12 +358,11 @@ class WizardRequestHandler(BaseHandler): from esphome import wizard kwargs = { - k: ''.join(x.decode() for x in v) - for k, v in self.request.arguments.items() + k: "".join(x.decode() for x in v) for k, v in self.request.arguments.items() } - destination = settings.rel_path(kwargs['name'] + '.yaml') + destination = settings.rel_path(kwargs["name"] + ".yaml") wizard.wizard_write(path=destination, **kwargs) - self.redirect('./?begin=True') + self.redirect("./?begin=True") class DownloadBinaryRequestHandler(BaseHandler): @@ -356,10 +377,10 @@ class DownloadBinaryRequestHandler(BaseHandler): return path = storage_json.firmware_bin_path - self.set_header('Content-Type', 'application/octet-stream') - filename = f'{storage_json.name}.bin' + self.set_header("Content-Type", "application/octet-stream") + filename = f"{storage_json.name}.bin" self.set_header("Content-Disposition", f'attachment; filename="{filename}"') - with open(path, 'rb') as f: + with open(path, "rb") as f: while True: data = f.read(16384) if not data: @@ -386,7 +407,9 @@ class DashboardEntry: @property def storage(self): # type: () -> Optional[StorageJSON] if not self._loaded_storage: - self._storage = StorageJSON.load(ext_storage_path(settings.config_dir, self.filename)) + self._storage = StorageJSON.load( + ext_storage_path(settings.config_dir, self.filename) + ) self._loaded_storage = True return self._storage @@ -399,7 +422,7 @@ class DashboardEntry: @property def name(self): if self.storage is None: - return self.filename[:-len('.yaml')] + return self.filename[: -len(".yaml")] return self.storage.name @property @@ -429,8 +452,8 @@ class DashboardEntry: @property def update_old(self): if self.storage is None: - return '' - return self.storage.esphome_version or '' + return "" + return self.storage.esphome_version or "" @property def update_new(self): @@ -446,18 +469,23 @@ class DashboardEntry: class MainRequestHandler(BaseHandler): @authenticated def get(self): - begin = bool(self.get_argument('begin', False)) + begin = bool(self.get_argument("begin", False)) entries = _list_dashboard_entries() - self.render("templates/index.html", entries=entries, begin=begin, - **template_args(), login_enabled=settings.using_auth) + self.render( + "templates/index.html", + entries=entries, + begin=begin, + **template_args(), + login_enabled=settings.using_auth, + ) def _ping_func(filename, address): - if os.name == 'nt': - command = ['ping', '-n', '1', address] + if os.name == "nt": + command = ["ping", "-n", "1", address] else: - command = ['ping', '-c', '1', address] + command = ["ping", "-c", "1", address] rc, _, _ = run_system_command(*command) return filename, rc == 0 @@ -474,7 +502,9 @@ class MDNSStatusThread(threading.Thread): stat.start() while not STOP_EVENT.is_set(): entries = _list_dashboard_entries() - stat.request_query({entry.filename: entry.name + '.local.' for entry in entries}) + stat.request_query( + {entry.filename: entry.name + ".local." for entry in entries} + ) PING_REQUEST.wait() PING_REQUEST.clear() @@ -499,8 +529,9 @@ class PingStatusThread(threading.Thread): PING_RESULT[entry.filename] = None continue - result = pool.apply_async(_ping_func, (entry.filename, entry.address), - callback=callback) + result = pool.apply_async( + _ping_func, (entry.filename, entry.address), callback=callback + ) queue.append(result) while queue: @@ -541,10 +572,10 @@ class EditRequestHandler(BaseHandler): @bind_config def get(self, configuration=None): filename = settings.rel_path(configuration) - content = '' + content = "" if os.path.isfile(filename): # pylint: disable=no-value-for-parameter - with open(filename, 'r') as f: + with open(filename, "r") as f: content = f.read() self.write(content) @@ -552,7 +583,7 @@ class EditRequestHandler(BaseHandler): @bind_config def post(self, configuration=None): # pylint: disable=no-value-for-parameter - with open(settings.rel_path(configuration), 'wb') as f: + with open(settings.rel_path(configuration), "wb") as f: f.write(self.request.body) self.set_status(200) @@ -596,29 +627,34 @@ PING_REQUEST = threading.Event() class LoginHandler(BaseHandler): def get(self): if is_authenticated(self): - self.redirect('/') + self.redirect("/") else: self.render_login_page() def render_login_page(self, error=None): - self.render("templates/login.html", error=error, hassio=settings.using_hassio_auth, - has_username=bool(settings.username), **template_args()) + self.render( + "templates/login.html", + error=error, + hassio=settings.using_hassio_auth, + has_username=bool(settings.username), + **template_args(), + ) def post_hassio_login(self): import requests headers = { - 'X-HASSIO-KEY': os.getenv('HASSIO_TOKEN'), + "X-HASSIO-KEY": os.getenv("HASSIO_TOKEN"), } data = { - 'username': self.get_argument('username', ''), - 'password': self.get_argument('password', '') + "username": self.get_argument("username", ""), + "password": self.get_argument("password", ""), } try: - req = requests.post('http://hassio/auth', headers=headers, data=data) + req = requests.post("http://hassio/auth", headers=headers, data=data) if req.status_code == 200: self.set_secure_cookie("authenticated", cookie_authenticated_yes) - self.redirect('/') + self.redirect("/") return except Exception as err: # pylint: disable=broad-except _LOGGER.warning("Error during Hass.io auth request: %s", err) @@ -629,13 +665,15 @@ class LoginHandler(BaseHandler): self.render_login_page(error="Invalid username or password") def post_native_login(self): - username = self.get_argument("username", '') - password = self.get_argument("password", '') + username = self.get_argument("username", "") + password = self.get_argument("password", "") if settings.check_password(username, password): self.set_secure_cookie("authenticated", cookie_authenticated_yes) self.redirect("/") return - error_str = "Invalid username or password" if settings.username else "Invalid password" + error_str = ( + "Invalid username or password" if settings.username else "Invalid password" + ) self.set_status(401) self.render_login_page(error=error_str) @@ -650,22 +688,22 @@ class LogoutHandler(BaseHandler): @authenticated def get(self): self.clear_cookie("authenticated") - self.redirect('./login') + self.redirect("./login") _STATIC_FILE_HASHES = {} def get_static_file_url(name): - static_path = os.path.join(os.path.dirname(__file__), 'static') + static_path = os.path.join(os.path.dirname(__file__), "static") if name in _STATIC_FILE_HASHES: hash_ = _STATIC_FILE_HASHES[name] else: path = os.path.join(static_path, name) - with open(path, 'rb') as f_handle: + with open(path, "rb") as f_handle: hash_ = hashlib.md5(f_handle.read()).hexdigest()[:8] _STATIC_FILE_HASHES[name] = hash_ - return f'./static/{name}?hash={hash_}' + return f"./static/{name}?hash={hash_}" def make_app(debug=False): @@ -684,44 +722,53 @@ def make_app(debug=False): request_time = 1000.0 * handler.request.request_time() # pylint: disable=protected-access - log_method("%d %s %.2fms", handler.get_status(), - handler._request_summary(), request_time) + log_method( + "%d %s %.2fms", + handler.get_status(), + handler._request_summary(), + request_time, + ) class StaticFileHandler(tornado.web.StaticFileHandler): def set_extra_headers(self, path): if debug: - self.set_header('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0') + self.set_header( + "Cache-Control", "no-store, no-cache, must-revalidate, max-age=0" + ) - static_path = os.path.join(os.path.dirname(__file__), 'static') + static_path = os.path.join(os.path.dirname(__file__), "static") app_settings = { - 'debug': debug, - 'cookie_secret': settings.cookie_secret, - 'log_function': log_function, - 'websocket_ping_interval': 30.0, + "debug": debug, + "cookie_secret": settings.cookie_secret, + "log_function": log_function, + "websocket_ping_interval": 30.0, } rel = settings.relative_url - app = tornado.web.Application([ - (rel + "", MainRequestHandler), - (rel + "login", LoginHandler), - (rel + "logout", LogoutHandler), - (rel + "logs", EsphomeLogsHandler), - (rel + "upload", EsphomeUploadHandler), - (rel + "compile", EsphomeCompileHandler), - (rel + "validate", EsphomeValidateHandler), - (rel + "clean-mqtt", EsphomeCleanMqttHandler), - (rel + "clean", EsphomeCleanHandler), - (rel + "vscode", EsphomeVscodeHandler), - (rel + "ace", EsphomeAceEditorHandler), - (rel + "update-all", EsphomeUpdateAllHandler), - (rel + "edit", EditRequestHandler), - (rel + "download.bin", DownloadBinaryRequestHandler), - (rel + "serial-ports", SerialPortRequestHandler), - (rel + "ping", PingRequestHandler), - (rel + "delete", DeleteRequestHandler), - (rel + "undo-delete", UndoDeleteRequestHandler), - (rel + "wizard.html", WizardRequestHandler), - (rel + r"static/(.*)", StaticFileHandler, {'path': static_path}), - ], **app_settings) + app = tornado.web.Application( + [ + (rel + "", MainRequestHandler), + (rel + "login", LoginHandler), + (rel + "logout", LogoutHandler), + (rel + "logs", EsphomeLogsHandler), + (rel + "upload", EsphomeUploadHandler), + (rel + "compile", EsphomeCompileHandler), + (rel + "validate", EsphomeValidateHandler), + (rel + "clean-mqtt", EsphomeCleanMqttHandler), + (rel + "clean", EsphomeCleanHandler), + (rel + "vscode", EsphomeVscodeHandler), + (rel + "ace", EsphomeAceEditorHandler), + (rel + "update-all", EsphomeUpdateAllHandler), + (rel + "edit", EditRequestHandler), + (rel + "download.bin", DownloadBinaryRequestHandler), + (rel + "serial-ports", SerialPortRequestHandler), + (rel + "ping", PingRequestHandler), + (rel + "delete", DeleteRequestHandler), + (rel + "undo-delete", UndoDeleteRequestHandler), + (rel + "wizard.html", WizardRequestHandler), + (rel + r"static/(.*)", StaticFileHandler, {"path": static_path}), + ], + **app_settings, + ) if debug: _STATIC_FILE_HASHES.clear() @@ -743,20 +790,26 @@ def start_web_server(args): app = make_app(args.verbose) if args.socket is not None: - _LOGGER.info("Starting dashboard web server on unix socket %s and configuration dir %s...", - args.socket, settings.config_dir) + _LOGGER.info( + "Starting dashboard web server on unix socket %s and configuration dir %s...", + args.socket, + settings.config_dir, + ) server = tornado.httpserver.HTTPServer(app) socket = tornado.netutil.bind_unix_socket(args.socket, mode=0o666) server.add_socket(socket) else: - _LOGGER.info("Starting dashboard web server on port %s and configuration dir %s...", - args.port, settings.config_dir) + _LOGGER.info( + "Starting dashboard web server on port %s and configuration dir %s...", + args.port, + settings.config_dir, + ) app.listen(args.port) if args.open_ui: import webbrowser - webbrowser.open(f'localhost:{args.port}') + webbrowser.open(f"localhost:{args.port}") if settings.status_use_ping: status_thread = PingStatusThread() diff --git a/esphome/espota2.py b/esphome/espota2.py index a1408a7d44..785cb155df 100644 --- a/esphome/espota2.py +++ b/esphome/espota2.py @@ -52,14 +52,15 @@ class ProgressBar: return self.last_progress = new_progress block = int(round(bar_length * progress)) - text = "\rUploading: [{0}] {1}% {2}".format("=" * block + " " * (bar_length - block), - new_progress, status) + text = "\rUploading: [{0}] {1}% {2}".format( + "=" * block + " " * (bar_length - block), new_progress, status + ) sys.stderr.write(text) sys.stderr.flush() # pylint: disable=no-self-use def done(self): - sys.stderr.write('\n') + sys.stderr.write("\n") sys.stderr.flush() @@ -78,7 +79,7 @@ def receive_exactly(sock, amount, msg, expect, decode=True): if decode: data = [] else: - data = b'' + data = b"" try: data += recv_decode(sock, 1, decode=decode) @@ -106,32 +107,48 @@ def check_error(data, expect): if dat == RESPONSE_ERROR_MAGIC: raise OTAError("Error: Invalid magic byte") if dat == RESPONSE_ERROR_UPDATE_PREPARE: - raise OTAError("Error: Couldn't prepare flash memory for update. Is the binary too big? " - "Please try restarting the ESP.") + raise OTAError( + "Error: Couldn't prepare flash memory for update. Is the binary too big? " + "Please try restarting the ESP." + ) if dat == RESPONSE_ERROR_AUTH_INVALID: raise OTAError("Error: Authentication invalid. Is the password correct?") if dat == RESPONSE_ERROR_WRITING_FLASH: - raise OTAError("Error: Wring OTA data to flash memory failed. See USB logs for more " - "information.") + raise OTAError( + "Error: Wring OTA data to flash memory failed. See USB logs for more " + "information." + ) if dat == RESPONSE_ERROR_UPDATE_END: - raise OTAError("Error: Finishing update failed. See the MQTT/USB logs for more " - "information.") + raise OTAError( + "Error: Finishing update failed. See the MQTT/USB logs for more " + "information." + ) if dat == RESPONSE_ERROR_INVALID_BOOTSTRAPPING: - raise OTAError("Error: Please press the reset button on the ESP. A manual reset is " - "required on the first OTA-Update after flashing via USB.") + raise OTAError( + "Error: Please press the reset button on the ESP. A manual reset is " + "required on the first OTA-Update after flashing via USB." + ) if dat == RESPONSE_ERROR_WRONG_CURRENT_FLASH_CONFIG: - raise OTAError("Error: ESP has been flashed with wrong flash size. Please choose the " - "correct 'board' option (esp01_1m always works) and then flash over USB.") + raise OTAError( + "Error: ESP has been flashed with wrong flash size. Please choose the " + "correct 'board' option (esp01_1m always works) and then flash over USB." + ) if dat == RESPONSE_ERROR_WRONG_NEW_FLASH_CONFIG: - raise OTAError("Error: ESP does not have the requested flash size (wrong board). Please " - "choose the correct 'board' option (esp01_1m always works) and try " - "uploading again.") + raise OTAError( + "Error: ESP does not have the requested flash size (wrong board). Please " + "choose the correct 'board' option (esp01_1m always works) and try " + "uploading again." + ) if dat == RESPONSE_ERROR_ESP8266_NOT_ENOUGH_SPACE: - raise OTAError("Error: ESP does not have enough space to store OTA file. Please try " - "flashing a minimal firmware (remove everything except ota)") + raise OTAError( + "Error: ESP does not have enough space to store OTA file. Please try " + "flashing a minimal firmware (remove everything except ota)" + ) if dat == RESPONSE_ERROR_ESP32_NOT_ENOUGH_SPACE: - raise OTAError("Error: The OTA partition on the ESP is too small. ESPHome needs to resize " - "this partition, please flash over USB.") + raise OTAError( + "Error: The OTA partition on the ESP is too small. ESPHome needs to resize " + "this partition, please flash over USB." + ) if dat == RESPONSE_ERROR_UNKNOWN: raise OTAError("Unknown error from ESP") if not isinstance(expect, (list, tuple)): @@ -147,7 +164,7 @@ def send_check(sock, data, msg): elif isinstance(data, int): data = bytes([data]) elif isinstance(data, str): - data = data.encode('utf8') + data = data.encode("utf8") sock.sendall(data) except OSError as err: @@ -157,42 +174,46 @@ def send_check(sock, data, msg): def perform_ota(sock, password, file_handle, filename): file_md5 = hashlib.md5(file_handle.read()).hexdigest() file_size = file_handle.tell() - _LOGGER.info('Uploading %s (%s bytes)', filename, file_size) + _LOGGER.info("Uploading %s (%s bytes)", filename, file_size) file_handle.seek(0) _LOGGER.debug("MD5 of binary is %s", file_md5) # Enable nodelay, we need it for phase 1 sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) - send_check(sock, MAGIC_BYTES, 'magic bytes') + send_check(sock, MAGIC_BYTES, "magic bytes") - _, version = receive_exactly(sock, 2, 'version', RESPONSE_OK) + _, version = receive_exactly(sock, 2, "version", RESPONSE_OK) if version != OTA_VERSION_1_0: raise OTAError(f"Unsupported OTA version {version}") # Features - send_check(sock, 0x00, 'features') - receive_exactly(sock, 1, 'features', RESPONSE_HEADER_OK) + send_check(sock, 0x00, "features") + receive_exactly(sock, 1, "features", RESPONSE_HEADER_OK) - auth, = receive_exactly(sock, 1, 'auth', [RESPONSE_REQUEST_AUTH, RESPONSE_AUTH_OK]) + (auth,) = receive_exactly( + sock, 1, "auth", [RESPONSE_REQUEST_AUTH, RESPONSE_AUTH_OK] + ) if auth == RESPONSE_REQUEST_AUTH: if not password: raise OTAError("ESP requests password, but no password given!") - nonce = receive_exactly(sock, 32, 'authentication nonce', [], decode=False).decode() + nonce = receive_exactly( + sock, 32, "authentication nonce", [], decode=False + ).decode() _LOGGER.debug("Auth: Nonce is %s", nonce) cnonce = hashlib.md5(str(random.random()).encode()).hexdigest() _LOGGER.debug("Auth: CNonce is %s", cnonce) - send_check(sock, cnonce, 'auth cnonce') + send_check(sock, cnonce, "auth cnonce") result_md5 = hashlib.md5() - result_md5.update(password.encode('utf-8')) + result_md5.update(password.encode("utf-8")) result_md5.update(nonce.encode()) result_md5.update(cnonce.encode()) result = result_md5.hexdigest() _LOGGER.debug("Auth: Result is %s", result) - send_check(sock, result, 'auth result') - receive_exactly(sock, 1, 'auth result', RESPONSE_AUTH_OK) + send_check(sock, result, "auth result") + receive_exactly(sock, 1, "auth result", RESPONSE_AUTH_OK) file_size_encoded = [ (file_size >> 24) & 0xFF, @@ -200,11 +221,11 @@ def perform_ota(sock, password, file_handle, filename): (file_size >> 8) & 0xFF, (file_size >> 0) & 0xFF, ] - send_check(sock, file_size_encoded, 'binary size') - receive_exactly(sock, 1, 'binary size', RESPONSE_UPDATE_PREPARE_OK) + send_check(sock, file_size_encoded, "binary size") + receive_exactly(sock, 1, "binary size", RESPONSE_UPDATE_PREPARE_OK) - send_check(sock, file_md5, 'file checksum') - receive_exactly(sock, 1, 'file checksum', RESPONSE_BIN_MD5_OK) + send_check(sock, file_md5, "file checksum") + receive_exactly(sock, 1, "file checksum", RESPONSE_BIN_MD5_OK) # Disable nodelay for transfer sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 0) @@ -225,7 +246,7 @@ def perform_ota(sock, password, file_handle, filename): try: sock.sendall(chunk) except OSError as err: - sys.stderr.write('\n') + sys.stderr.write("\n") raise OTAError(f"Error sending data: {err}") from err progress.update(offset / float(file_size)) @@ -236,9 +257,9 @@ def perform_ota(sock, password, file_handle, filename): _LOGGER.info("Waiting for result...") - receive_exactly(sock, 1, 'receive OK', RESPONSE_RECEIVE_OK) - receive_exactly(sock, 1, 'Update end', RESPONSE_UPDATE_END_OK) - send_check(sock, RESPONSE_OK, 'end acknowledgement') + receive_exactly(sock, 1, "receive OK", RESPONSE_RECEIVE_OK) + receive_exactly(sock, 1, "Update end", RESPONSE_UPDATE_END_OK) + send_check(sock, RESPONSE_OK, "end acknowledgement") _LOGGER.info("OTA successful") @@ -255,10 +276,14 @@ def run_ota_impl_(remote_host, remote_port, password, filename): try: ip = resolve_ip_address(remote_host) except EsphomeError as err: - _LOGGER.error("Error resolving IP address of %s. Is it connected to WiFi?", - remote_host) - _LOGGER.error("(If this error persists, please set a static IP address: " - "https://esphome.io/components/wifi.html#manual-ips)") + _LOGGER.error( + "Error resolving IP address of %s. Is it connected to WiFi?", + remote_host, + ) + _LOGGER.error( + "(If this error persists, please set a static IP address: " + "https://esphome.io/components/wifi.html#manual-ips)" + ) raise OTAError(err) from err _LOGGER.info(" -> %s", ip) @@ -271,7 +296,7 @@ def run_ota_impl_(remote_host, remote_port, password, filename): _LOGGER.error("Connecting to %s:%s failed: %s", remote_host, remote_port, err) return 1 - file_handle = open(filename, 'rb') + file_handle = open(filename, "rb") try: perform_ota(sock, password, file_handle, filename) except OTAError as err: diff --git a/esphome/helpers.py b/esphome/helpers.py index 1389804fd9..780a2aa88e 100644 --- a/esphome/helpers.py +++ b/esphome/helpers.py @@ -22,48 +22,48 @@ def ensure_unique_string(preferred_string, current_strings): return test_string -def indent_all_but_first_and_last(text, padding=' '): +def indent_all_but_first_and_last(text, padding=" "): lines = text.splitlines(True) if len(lines) <= 2: return text - return lines[0] + ''.join(padding + line for line in lines[1:-1]) + lines[-1] + return lines[0] + "".join(padding + line for line in lines[1:-1]) + lines[-1] -def indent_list(text, padding=' '): +def indent_list(text, padding=" "): return [padding + line for line in text.splitlines()] -def indent(text, padding=' '): - return '\n'.join(indent_list(text, padding)) +def indent(text, padding=" "): + return "\n".join(indent_list(text, padding)) # From https://stackoverflow.com/a/14945195/8924614 -def cpp_string_escape(string, encoding='utf-8'): +def cpp_string_escape(string, encoding="utf-8"): def _should_escape(byte): # type: (int) -> bool if not 32 <= byte < 127: return True - if byte in (ord('\\'), ord('"')): + if byte in (ord("\\"), ord('"')): return True return False if isinstance(string, str): string = string.encode(encoding) - result = '' + result = "" for character in string: if _should_escape(character): - result += f'\\{character:03o}' + result += f"\\{character:03o}" else: result += chr(character) return '"' + result + '"' -def color(the_color, message=''): +def color(the_color, message=""): from colorlog.escape_codes import escape_codes, parse_colors if not message: res = parse_colors(the_color) else: - res = parse_colors(the_color) + message + escape_codes['reset'] + res = parse_colors(the_color) + message + escape_codes["reset"] return res @@ -85,15 +85,17 @@ def mkdir_p(path): os.makedirs(path) except OSError as err: import errno + if err.errno == errno.EEXIST and os.path.isdir(path): pass else: from esphome.core import EsphomeError + raise EsphomeError(f"Error creating directories {path}: {err}") from err def is_ip_address(host): - parts = host.split('.') + parts = host.split(".") if len(parts) != 4: return False try: @@ -111,17 +113,21 @@ def _resolve_with_zeroconf(host): try: zc = Zeroconf() except Exception as err: - raise EsphomeError("Cannot start mDNS sockets, is this a docker container without " - "host network mode?") from err + raise EsphomeError( + "Cannot start mDNS sockets, is this a docker container without " + "host network mode?" + ) from err try: - info = zc.resolve_host(host + '.') + info = zc.resolve_host(host + ".") except Exception as err: raise EsphomeError(f"Error resolving mDNS hostname: {err}") from err finally: zc.close() if info is None: - raise EsphomeError("Error resolving address with mDNS: Did not respond. " - "Maybe the device is offline.") + raise EsphomeError( + "Error resolving address with mDNS: Did not respond. " + "Maybe the device is offline." + ) return info @@ -131,7 +137,7 @@ def resolve_ip_address(host): errs = [] - if host.endswith('.local'): + if host.endswith(".local"): try: return _resolve_with_zeroconf(host) except EsphomeError as err: @@ -141,8 +147,9 @@ def resolve_ip_address(host): return socket.gethostbyname(host) except OSError as err: errs.append(str(err)) - raise EsphomeError("Error resolving IP address: {}" - "".format(', '.join(errs))) from err + raise EsphomeError( + "Error resolving IP address: {}" "".format(", ".join(errs)) + ) from err def get_bool_env(var, default=False): @@ -150,7 +157,7 @@ def get_bool_env(var, default=False): def is_hassio(): - return get_bool_env('ESPHOME_IS_HASSIO') + return get_bool_env("ESPHOME_IS_HASSIO") def walk_files(path): @@ -161,13 +168,15 @@ def walk_files(path): def read_file(path): try: - with codecs.open(path, 'r', encoding='utf-8') as f_handle: + with codecs.open(path, "r", encoding="utf-8") as f_handle: return f_handle.read() except OSError as err: from esphome.core import EsphomeError + raise EsphomeError(f"Error reading file {path}: {err}") from err except UnicodeDecodeError as err: from esphome.core import EsphomeError + raise EsphomeError(f"Error reading file {path}: {err}") from err @@ -187,7 +196,9 @@ def _write_file(path: Union[Path, str], text: Union[str, bytes]): tmp_path = None try: - with tempfile.NamedTemporaryFile(mode="wb", dir=directory, delete=False) as f_handle: + with tempfile.NamedTemporaryFile( + mode="wb", dir=directory, delete=False + ) as f_handle: tmp_path = f_handle.name f_handle.write(data) # Newer tempfile implementations create the file with mode 0o600 @@ -207,6 +218,7 @@ def write_file(path: Union[Path, str], text: str): _write_file(path, text) except OSError as err: from esphome.core import EsphomeError + raise EsphomeError(f"Could not write file at {path}") from err @@ -223,6 +235,7 @@ def write_file_if_changed(path: Union[Path, str], text: str): def copy_file_if_changed(src, dst): import shutil + if file_compare(src, dst): return mkdir_p(os.path.dirname(dst)) @@ -230,6 +243,7 @@ def copy_file_if_changed(src, dst): shutil.copy(src, dst) except OSError as err: from esphome.core import EsphomeError + raise EsphomeError(f"Error copying file {src} to {dst}: {err}") from err @@ -247,16 +261,19 @@ def file_compare(path1, path2): # File doesn't exist or another error -> not equal return False - if stat.S_IFMT(stat1.st_mode) != stat.S_IFREG or stat.S_IFMT(stat2.st_mode) != stat.S_IFREG: + if ( + stat.S_IFMT(stat1.st_mode) != stat.S_IFREG + or stat.S_IFMT(stat2.st_mode) != stat.S_IFREG + ): # At least one of them is not a regular file (or does not exist) return False if stat1.st_size != stat2.st_size: # Different sizes return False - bufsize = 8*1024 + bufsize = 8 * 1024 # Read files in blocks until a mismatch is found - with open(path1, 'rb') as fh1, open(path2, 'rb') as fh2: + with open(path1, "rb") as fh1, open(path2, "rb") as fh2: while True: blob1, blob2 = fh1.read(bufsize), fh2.read(bufsize) if blob1 != blob2: @@ -270,11 +287,11 @@ def file_compare(path1, path2): # A dict of types that need to be converted to heaptypes before a class can be added # to the object _TYPE_OVERLOADS = { - int: type('EInt', (int,), dict()), - float: type('EFloat', (float,), dict()), - str: type('EStr', (str,), dict()), - dict: type('EDict', (str,), dict()), - list: type('EList', (list,), dict()), + int: type("EInt", (int,), dict()), + float: type("EFloat", (float,), dict()), + str: type("EStr", (str,), dict()), + dict: type("EDict", (str,), dict()), + list: type("EList", (list,), dict()), } # cache created classes here diff --git a/esphome/legacy.py b/esphome/legacy.py index 27373ee1a3..6b3b1d6c99 100644 --- a/esphome/legacy.py +++ b/esphome/legacy.py @@ -4,7 +4,7 @@ import sys def main(): print("The esphomeyaml command has been renamed to esphome.") print("") - print("$ esphome {}".format(' '.join(sys.argv[1:]))) + print("$ esphome {}".format(" ".join(sys.argv[1:]))) return 1 diff --git a/esphome/mqtt.py b/esphome/mqtt.py index 499ccbe7f1..86937ba37e 100644 --- a/esphome/mqtt.py +++ b/esphome/mqtt.py @@ -7,9 +7,20 @@ import time import paho.mqtt.client as mqtt -from esphome.const import CONF_BROKER, CONF_DISCOVERY_PREFIX, CONF_ESPHOME, \ - CONF_LOG_TOPIC, CONF_MQTT, CONF_NAME, CONF_PASSWORD, CONF_PORT, CONF_SSL_FINGERPRINTS, \ - CONF_TOPIC, CONF_TOPIC_PREFIX, CONF_USERNAME +from esphome.const import ( + CONF_BROKER, + CONF_DISCOVERY_PREFIX, + CONF_ESPHOME, + CONF_LOG_TOPIC, + CONF_MQTT, + CONF_NAME, + CONF_PASSWORD, + CONF_PORT, + CONF_SSL_FINGERPRINTS, + CONF_TOPIC, + CONF_TOPIC_PREFIX, + CONF_USERNAME, +) from esphome.core import CORE, EsphomeError from esphome.helpers import color from esphome.util import safe_print @@ -36,21 +47,24 @@ def initialize(config, subscriptions, on_message, username, password, client_id) except OSError: pass - wait_time = min(2**tries, 300) + wait_time = min(2 ** tries, 300) _LOGGER.warning( "Disconnected from MQTT (%s). Trying to reconnect in %s s", - result_code, wait_time) + result_code, + wait_time, + ) time.sleep(wait_time) tries += 1 - client = mqtt.Client(client_id or '') + client = mqtt.Client(client_id or "") client.on_connect = on_connect client.on_message = on_message client.on_disconnect = on_disconnect if username is None: if config[CONF_MQTT].get(CONF_USERNAME): - client.username_pw_set(config[CONF_MQTT][CONF_USERNAME], - config[CONF_MQTT][CONF_PASSWORD]) + client.username_pw_set( + config[CONF_MQTT][CONF_USERNAME], config[CONF_MQTT][CONF_PASSWORD] + ) elif username: client.username_pw_set(username, password) @@ -59,8 +73,14 @@ def initialize(config, subscriptions, on_message, username, password, client_id) tls_version = ssl.PROTOCOL_TLS # pylint: disable=no-member else: tls_version = ssl.PROTOCOL_SSLv23 - client.tls_set(ca_certs=None, certfile=None, keyfile=None, cert_reqs=ssl.CERT_REQUIRED, - tls_version=tls_version, ciphers=None) + client.tls_set( + ca_certs=None, + certfile=None, + keyfile=None, + cert_reqs=ssl.CERT_REQUIRED, + tls_version=tls_version, + ciphers=None, + ) try: host = str(config[CONF_MQTT][CONF_BROKER]) @@ -84,17 +104,17 @@ def show_logs(config, topic=None, username=None, password=None, client_id=None): if CONF_LOG_TOPIC in conf: topic = config[CONF_MQTT][CONF_LOG_TOPIC][CONF_TOPIC] elif CONF_TOPIC_PREFIX in config[CONF_MQTT]: - topic = config[CONF_MQTT][CONF_TOPIC_PREFIX] + '/debug' + topic = config[CONF_MQTT][CONF_TOPIC_PREFIX] + "/debug" else: - topic = config[CONF_ESPHOME][CONF_NAME] + '/debug' + topic = config[CONF_ESPHOME][CONF_NAME] + "/debug" else: _LOGGER.error("MQTT isn't setup, can't start MQTT logs") return 1 _LOGGER.info("Starting log output from %s", topic) def on_message(client, userdata, msg): - time_ = datetime.now().time().strftime('[%H:%M:%S]') - payload = msg.payload.decode(errors='backslashreplace') + time_ = datetime.now().time().strftime("[%H:%M:%S]") + payload = msg.payload.decode(errors="backslashreplace") message = time_ + payload safe_print(message) @@ -103,12 +123,14 @@ def show_logs(config, topic=None, username=None, password=None, client_id=None): def clear_topic(config, topic, username=None, password=None, client_id=None): if topic is None: - discovery_prefix = config[CONF_MQTT].get(CONF_DISCOVERY_PREFIX, 'homeassistant') + discovery_prefix = config[CONF_MQTT].get(CONF_DISCOVERY_PREFIX, "homeassistant") name = config[CONF_ESPHOME][CONF_NAME] - topic = f'{discovery_prefix}/+/{name}/#' + topic = f"{discovery_prefix}/+/{name}/#" _LOGGER.info("Clearing messages from '%s'", topic) - _LOGGER.info("Please close this window when no more messages appear and the " - "MQTT topic has been cleared of retained messages.") + _LOGGER.info( + "Please close this window when no more messages appear and the " + "MQTT topic has been cleared of retained messages." + ) def on_message(client, userdata, msg): if not msg.payload or not msg.retain: @@ -136,7 +158,9 @@ def get_fingerprint(config): sha1 = hashlib.sha1(cert_der).hexdigest() - safe_print("SHA1 Fingerprint: " + color('cyan', sha1)) - safe_print("Copy the string above into mqtt.ssl_fingerprints section of {}" - "".format(CORE.config_path)) + safe_print("SHA1 Fingerprint: " + color("cyan", sha1)) + safe_print( + "Copy the string above into mqtt.ssl_fingerprints section of {}" + "".format(CORE.config_path) + ) return 0 diff --git a/esphome/pins.py b/esphome/pins.py index 2a154c4dbf..fef77f3946 100644 --- a/esphome/pins.py +++ b/esphome/pins.py @@ -8,53 +8,149 @@ from esphome.util import SimpleRegistry _LOGGER = logging.getLogger(__name__) ESP8266_BASE_PINS = { - 'A0': 17, 'SS': 15, 'MOSI': 13, 'MISO': 12, 'SCK': 14, 'SDA': 4, 'SCL': 5, 'RX': 3, 'TX': 1 + "A0": 17, + "SS": 15, + "MOSI": 13, + "MISO": 12, + "SCK": 14, + "SDA": 4, + "SCL": 5, + "RX": 3, + "TX": 1, } ESP8266_BOARD_PINS = { - 'd1': {'D0': 3, 'D1': 1, 'D2': 16, 'D3': 5, 'D4': 4, 'D5': 14, 'D6': 12, 'D7': 13, 'D8': 0, - 'D9': 2, 'D10': 15, 'D11': 13, 'D12': 14, 'D13': 14, 'D14': 4, 'D15': 5, 'LED': 2}, - 'd1_mini': {'D0': 16, 'D1': 5, 'D2': 4, 'D3': 0, 'D4': 2, 'D5': 14, 'D6': 12, 'D7': 13, - 'D8': 15, 'LED': 2}, - 'd1_mini_lite': 'd1_mini', - 'd1_mini_pro': 'd1_mini', - 'esp01': {}, - 'esp01_1m': {}, - 'esp07': {}, - 'esp12e': {}, - 'esp210': {}, - 'esp8285': {}, - 'esp_wroom_02': {}, - 'espduino': {'LED': 16}, - 'espectro': {'LED': 15, 'BUTTON': 2}, - 'espino': {'LED': 2, 'LED_RED': 2, 'LED_GREEN': 4, 'LED_BLUE': 5, 'BUTTON': 0}, - 'espinotee': {'LED': 16}, - 'espresso_lite_v1': {'LED': 16}, - 'espresso_lite_v2': {'LED': 2}, - 'gen4iod': {}, - 'heltec_wifi_kit_8': 'd1_mini', - 'huzzah': {'LED': 0, 'LED_RED': 0, 'LED_BLUE': 2, 'D4': 4, 'D5': 5, 'D12': 12, - 'D13': 13, 'D14': 14, 'D15': 15, 'D16': 16}, - 'inventone': {}, - 'modwifi': {}, - 'nodemcu': {'D0': 16, 'D1': 5, 'D2': 4, 'D3': 0, 'D4': 2, 'D5': 14, 'D6': 12, 'D7': 13, - 'D8': 15, 'D9': 3, 'D10': 1, 'LED': 16}, - 'nodemcuv2': 'nodemcu', - 'oak': {'P0': 2, 'P1': 5, 'P2': 0, 'P3': 3, 'P4': 1, 'P5': 4, 'P6': 15, 'P7': 13, 'P8': 12, - 'P9': 14, 'P10': 16, 'P11': 17, 'LED': 5}, - 'phoenix_v1': {'LED': 16}, - 'phoenix_v2': {'LED': 2}, - 'sparkfunBlynk': 'thing', - 'thing': {'LED': 5, 'SDA': 2, 'SCL': 14}, - 'thingdev': 'thing', - 'wifi_slot': {'LED': 2}, - 'wifiduino': {'D0': 3, 'D1': 1, 'D2': 2, 'D3': 0, 'D4': 4, 'D5': 5, 'D6': 16, 'D7': 14, - 'D8': 12, 'D9': 13, 'D10': 15, 'D11': 13, 'D12': 12, 'D13': 14}, - 'wifinfo': {'LED': 12, 'D0': 16, 'D1': 5, 'D2': 4, 'D3': 0, 'D4': 2, 'D5': 14, 'D6': 12, - 'D7': 13, 'D8': 15, 'D9': 3, 'D10': 1}, - 'wio_link': {'LED': 2, 'GROVE': 15, 'D0': 14, 'D1': 12, 'D2': 13, 'BUTTON': 0}, - 'wio_node': {'LED': 2, 'GROVE': 15, 'D0': 3, 'D1': 5, 'BUTTON': 0}, - 'xinabox_cw01': {'SDA': 2, 'SCL': 14, 'LED': 5, 'LED_RED': 12, 'LED_GREEN': 13} + "d1": { + "D0": 3, + "D1": 1, + "D2": 16, + "D3": 5, + "D4": 4, + "D5": 14, + "D6": 12, + "D7": 13, + "D8": 0, + "D9": 2, + "D10": 15, + "D11": 13, + "D12": 14, + "D13": 14, + "D14": 4, + "D15": 5, + "LED": 2, + }, + "d1_mini": { + "D0": 16, + "D1": 5, + "D2": 4, + "D3": 0, + "D4": 2, + "D5": 14, + "D6": 12, + "D7": 13, + "D8": 15, + "LED": 2, + }, + "d1_mini_lite": "d1_mini", + "d1_mini_pro": "d1_mini", + "esp01": {}, + "esp01_1m": {}, + "esp07": {}, + "esp12e": {}, + "esp210": {}, + "esp8285": {}, + "esp_wroom_02": {}, + "espduino": {"LED": 16}, + "espectro": {"LED": 15, "BUTTON": 2}, + "espino": {"LED": 2, "LED_RED": 2, "LED_GREEN": 4, "LED_BLUE": 5, "BUTTON": 0}, + "espinotee": {"LED": 16}, + "espresso_lite_v1": {"LED": 16}, + "espresso_lite_v2": {"LED": 2}, + "gen4iod": {}, + "heltec_wifi_kit_8": "d1_mini", + "huzzah": { + "LED": 0, + "LED_RED": 0, + "LED_BLUE": 2, + "D4": 4, + "D5": 5, + "D12": 12, + "D13": 13, + "D14": 14, + "D15": 15, + "D16": 16, + }, + "inventone": {}, + "modwifi": {}, + "nodemcu": { + "D0": 16, + "D1": 5, + "D2": 4, + "D3": 0, + "D4": 2, + "D5": 14, + "D6": 12, + "D7": 13, + "D8": 15, + "D9": 3, + "D10": 1, + "LED": 16, + }, + "nodemcuv2": "nodemcu", + "oak": { + "P0": 2, + "P1": 5, + "P2": 0, + "P3": 3, + "P4": 1, + "P5": 4, + "P6": 15, + "P7": 13, + "P8": 12, + "P9": 14, + "P10": 16, + "P11": 17, + "LED": 5, + }, + "phoenix_v1": {"LED": 16}, + "phoenix_v2": {"LED": 2}, + "sparkfunBlynk": "thing", + "thing": {"LED": 5, "SDA": 2, "SCL": 14}, + "thingdev": "thing", + "wifi_slot": {"LED": 2}, + "wifiduino": { + "D0": 3, + "D1": 1, + "D2": 2, + "D3": 0, + "D4": 4, + "D5": 5, + "D6": 16, + "D7": 14, + "D8": 12, + "D9": 13, + "D10": 15, + "D11": 13, + "D12": 12, + "D13": 14, + }, + "wifinfo": { + "LED": 12, + "D0": 16, + "D1": 5, + "D2": 4, + "D3": 0, + "D4": 2, + "D5": 14, + "D6": 12, + "D7": 13, + "D8": 15, + "D9": 3, + "D10": 1, + }, + "wio_link": {"LED": 2, "GROVE": 15, "D0": 14, "D1": 12, "D2": 13, "BUTTON": 0}, + "wio_node": {"LED": 2, "GROVE": 15, "D0": 3, "D1": 5, "BUTTON": 0}, + "xinabox_cw01": {"SDA": 2, "SCL": 14, "LED": 5, "LED_RED": 12, "LED_GREEN": 13}, } FLASH_SIZE_1_MB = 2 ** 20 @@ -64,196 +160,711 @@ FLASH_SIZE_4_MB = 4 * FLASH_SIZE_1_MB FLASH_SIZE_16_MB = 16 * FLASH_SIZE_1_MB ESP8266_FLASH_SIZES = { - 'd1': FLASH_SIZE_4_MB, - 'd1_mini': FLASH_SIZE_4_MB, - 'd1_mini_lite': FLASH_SIZE_1_MB, - 'd1_mini_pro': FLASH_SIZE_16_MB, - 'esp01': FLASH_SIZE_512_KB, - 'esp01_1m': FLASH_SIZE_1_MB, - 'esp07': FLASH_SIZE_4_MB, - 'esp12e': FLASH_SIZE_4_MB, - 'esp210': FLASH_SIZE_4_MB, - 'esp8285': FLASH_SIZE_1_MB, - 'esp_wroom_02': FLASH_SIZE_2_MB, - 'espduino': FLASH_SIZE_4_MB, - 'espectro': FLASH_SIZE_4_MB, - 'espino': FLASH_SIZE_4_MB, - 'espinotee': FLASH_SIZE_4_MB, - 'espresso_lite_v1': FLASH_SIZE_4_MB, - 'espresso_lite_v2': FLASH_SIZE_4_MB, - 'gen4iod': FLASH_SIZE_512_KB, - 'heltec_wifi_kit_8': FLASH_SIZE_4_MB, - 'huzzah': FLASH_SIZE_4_MB, - 'inventone': FLASH_SIZE_4_MB, - 'modwifi': FLASH_SIZE_2_MB, - 'nodemcu': FLASH_SIZE_4_MB, - 'nodemcuv2': FLASH_SIZE_4_MB, - 'oak': FLASH_SIZE_4_MB, - 'phoenix_v1': FLASH_SIZE_4_MB, - 'phoenix_v2': FLASH_SIZE_4_MB, - 'sparkfunBlynk': FLASH_SIZE_4_MB, - 'thing': FLASH_SIZE_512_KB, - 'thingdev': FLASH_SIZE_512_KB, - 'wifi_slot': FLASH_SIZE_1_MB, - 'wifiduino': FLASH_SIZE_4_MB, - 'wifinfo': FLASH_SIZE_1_MB, - 'wio_link': FLASH_SIZE_4_MB, - 'wio_node': FLASH_SIZE_4_MB, - 'xinabox_cw01': FLASH_SIZE_4_MB, + "d1": FLASH_SIZE_4_MB, + "d1_mini": FLASH_SIZE_4_MB, + "d1_mini_lite": FLASH_SIZE_1_MB, + "d1_mini_pro": FLASH_SIZE_16_MB, + "esp01": FLASH_SIZE_512_KB, + "esp01_1m": FLASH_SIZE_1_MB, + "esp07": FLASH_SIZE_4_MB, + "esp12e": FLASH_SIZE_4_MB, + "esp210": FLASH_SIZE_4_MB, + "esp8285": FLASH_SIZE_1_MB, + "esp_wroom_02": FLASH_SIZE_2_MB, + "espduino": FLASH_SIZE_4_MB, + "espectro": FLASH_SIZE_4_MB, + "espino": FLASH_SIZE_4_MB, + "espinotee": FLASH_SIZE_4_MB, + "espresso_lite_v1": FLASH_SIZE_4_MB, + "espresso_lite_v2": FLASH_SIZE_4_MB, + "gen4iod": FLASH_SIZE_512_KB, + "heltec_wifi_kit_8": FLASH_SIZE_4_MB, + "huzzah": FLASH_SIZE_4_MB, + "inventone": FLASH_SIZE_4_MB, + "modwifi": FLASH_SIZE_2_MB, + "nodemcu": FLASH_SIZE_4_MB, + "nodemcuv2": FLASH_SIZE_4_MB, + "oak": FLASH_SIZE_4_MB, + "phoenix_v1": FLASH_SIZE_4_MB, + "phoenix_v2": FLASH_SIZE_4_MB, + "sparkfunBlynk": FLASH_SIZE_4_MB, + "thing": FLASH_SIZE_512_KB, + "thingdev": FLASH_SIZE_512_KB, + "wifi_slot": FLASH_SIZE_1_MB, + "wifiduino": FLASH_SIZE_4_MB, + "wifinfo": FLASH_SIZE_1_MB, + "wio_link": FLASH_SIZE_4_MB, + "wio_node": FLASH_SIZE_4_MB, + "xinabox_cw01": FLASH_SIZE_4_MB, } ESP8266_LD_SCRIPTS = { - FLASH_SIZE_512_KB: ('eagle.flash.512k0.ld', 'eagle.flash.512k.ld'), - FLASH_SIZE_1_MB: ('eagle.flash.1m0.ld', 'eagle.flash.1m.ld'), - FLASH_SIZE_2_MB: ('eagle.flash.2m.ld', 'eagle.flash.2m.ld'), - FLASH_SIZE_4_MB: ('eagle.flash.4m.ld', 'eagle.flash.4m.ld'), - FLASH_SIZE_16_MB: ('eagle.flash.16m.ld', 'eagle.flash.16m14m.ld'), + FLASH_SIZE_512_KB: ("eagle.flash.512k0.ld", "eagle.flash.512k.ld"), + FLASH_SIZE_1_MB: ("eagle.flash.1m0.ld", "eagle.flash.1m.ld"), + FLASH_SIZE_2_MB: ("eagle.flash.2m.ld", "eagle.flash.2m.ld"), + FLASH_SIZE_4_MB: ("eagle.flash.4m.ld", "eagle.flash.4m.ld"), + FLASH_SIZE_16_MB: ("eagle.flash.16m.ld", "eagle.flash.16m14m.ld"), } ESP32_BASE_PINS = { - 'TX': 1, 'RX': 3, 'SDA': 21, 'SCL': 22, 'SS': 5, 'MOSI': 23, 'MISO': 19, 'SCK': 18, 'A0': 36, - 'A3': 39, 'A4': 32, 'A5': 33, 'A6': 34, 'A7': 35, 'A10': 4, 'A11': 0, 'A12': 2, 'A13': 15, - 'A14': 13, 'A15': 12, 'A16': 14, 'A17': 27, 'A18': 25, 'A19': 26, 'T0': 4, 'T1': 0, 'T2': 2, - 'T3': 15, 'T4': 13, 'T5': 12, 'T6': 14, 'T7': 27, 'T8': 33, 'T9': 32, 'DAC1': 25, 'DAC2': 26, - 'SVP': 36, 'SVN': 39, + "TX": 1, + "RX": 3, + "SDA": 21, + "SCL": 22, + "SS": 5, + "MOSI": 23, + "MISO": 19, + "SCK": 18, + "A0": 36, + "A3": 39, + "A4": 32, + "A5": 33, + "A6": 34, + "A7": 35, + "A10": 4, + "A11": 0, + "A12": 2, + "A13": 15, + "A14": 13, + "A15": 12, + "A16": 14, + "A17": 27, + "A18": 25, + "A19": 26, + "T0": 4, + "T1": 0, + "T2": 2, + "T3": 15, + "T4": 13, + "T5": 12, + "T6": 14, + "T7": 27, + "T8": 33, + "T9": 32, + "DAC1": 25, + "DAC2": 26, + "SVP": 36, + "SVN": 39, } ESP32_BOARD_PINS = { - 'alksesp32': {'A0': 32, 'A1': 33, 'A2': 25, 'A3': 26, 'A4': 27, 'A5': 14, 'A6': 12, 'A7': 15, - 'D0': 40, 'D1': 41, 'D10': 19, 'D11': 21, 'D12': 22, 'D13': 23, 'D2': 15, - 'D3': 2, 'D4': 0, 'D5': 4, 'D6': 16, 'D7': 17, 'D8': 5, 'D9': 18, 'DHT_PIN': 26, - 'LED': 23, 'L_B': 5, 'L_G': 17, 'L_R': 22, 'L_RGB_B': 16, 'L_RGB_G': 21, - 'L_RGB_R': 4, 'L_Y': 23, 'MISO': 22, 'MOSI': 21, 'PHOTO': 25, 'PIEZO1': 19, - 'PIEZO2': 18, 'POT1': 32, 'POT2': 33, 'S1': 4, 'S2': 16, 'S3': 18, 'S4': 19, - 'S5': 21, 'SCK': 23, 'SCL': 14, 'SDA': 27, 'SS': 19, 'SW1': 15, 'SW2': 2, - 'SW3': 0}, - 'bpi-bit': {'BUTTON_A': 35, 'BUTTON_B': 27, 'BUZZER': 25, 'LIGHT_SENSOR1': 36, - 'LIGHT_SENSOR2': 39, 'MPU9250_INT': 0, 'P0': 25, 'P1': 32, 'P10': 26, 'P11': 27, - 'P12': 2, 'P13': 18, 'P14': 19, 'P15': 23, 'P16': 5, 'P19': 22, 'P2': 33, - 'P20': 21, 'P3': 13, 'P4': 15, 'P5': 35, 'P6': 12, 'P7': 14, 'P8': 16, 'P9': 17, - 'RGB_LED': 4, 'TEMPERATURE_SENSOR': 34}, - 'd-duino-32': {'D1': 5, 'D10': 1, 'D2': 4, 'D3': 0, 'D4': 2, 'D5': 14, 'D6': 12, 'D7': 13, - 'D8': 15, 'D9': 3, 'MISO': 12, 'MOSI': 13, 'SCK': 14, 'SCL': 4, 'SDA': 5, - 'SS': 15}, - 'esp-wrover-kit': {}, - 'esp32-devkitlipo': {}, - 'esp32-evb': {'BUTTON': 34, 'MISO': 15, 'MOSI': 2, 'SCK': 14, 'SCL': 16, 'SDA': 13, 'SS': 17}, - 'esp32-gateway': {'BUTTON': 34, 'LED': 33, 'SCL': 16, 'SDA': 32}, - 'esp32-poe-iso': {'BUTTON': 34, 'MISO': 15, 'MOSI': 2, 'SCK': 14, 'SCL': 16, 'SDA': 13}, - 'esp32-poe': {'BUTTON': 34, 'MISO': 15, 'MOSI': 2, 'SCK': 14, 'SCL': 16, 'SDA': 13}, - 'esp32-pro': {'BUTTON': 34, 'MISO': 15, 'MOSI': 2, 'SCK': 14, 'SCL': 16, 'SDA': 13, 'SS': 17}, - 'esp320': {'LED': 5, 'MISO': 12, 'MOSI': 13, 'SCK': 14, 'SCL': 14, 'SDA': 2, 'SS': 15}, - 'esp32cam': {}, - 'esp32dev': {}, - 'esp32doit-devkit-v1': {'LED': 2}, - 'esp32thing': {'BUTTON': 0, 'LED': 5, 'SS': 2}, - 'esp32vn-iot-uno': {}, - 'espea32': {'BUTTON': 0, 'LED': 5}, - 'espectro32': {'LED': 15, 'SD_SS': 33}, - 'espino32': {'BUTTON': 0, 'LED': 16}, - 'featheresp32': {'A0': 26, 'A1': 25, 'A10': 27, 'A11': 12, 'A12': 13, 'A13': 35, 'A2': 34, - 'A4': 36, 'A5': 4, 'A6': 14, 'A7': 32, 'A8': 15, 'A9': 33, 'Ax': 2, - 'LED': 13, 'MOSI': 18, 'RX': 16, 'SCK': 5, 'SDA': 23, 'SS': 33, 'TX': 17}, - 'firebeetle32': {'LED': 2}, - 'fm-devkit': {'D0': 34, 'D1': 35, 'D10': 0, 'D2': 32, 'D3': 33, 'D4': 27, 'D5': 14, 'D6': 12, - 'D7': 13, 'D8': 15, 'D9': 23, 'I2S_DOUT': 22, 'I2S_LRCLK': 25, 'I2S_MCLK': 2, - 'I2S_SCLK': 26, 'LED': 5, 'SCL': 17, 'SDA': 16, 'SW1': 4, 'SW2': 18, 'SW3': 19, - 'SW4': 21}, - 'frogboard': {}, - 'heltec_wifi_kit_32': {'A1': 37, 'A2': 38, 'BUTTON': 0, 'LED': 25, 'RST_OLED': 16, - 'SCL_OLED': 15, 'SDA_OLED': 4, 'Vext': 21}, - 'heltec_wifi_lora_32': {'BUTTON': 0, 'DIO0': 26, 'DIO1': 33, 'DIO2': 32, 'LED': 25, - 'MOSI': 27, 'RST_LoRa': 14, 'RST_OLED': 16, 'SCK': 5, 'SCL_OLED': 15, - 'SDA_OLED': 4, 'SS': 18, 'Vext': 21}, - 'heltec_wifi_lora_32_V2': {'BUTTON': 0, 'DIO0': 26, 'DIO1': 35, 'DIO2': 34, 'LED': 25, - 'MOSI': 27, 'RST_LoRa': 14, 'RST_OLED': 16, 'SCK': 5, - 'SCL_OLED': 15, 'SDA_OLED': 4, 'SS': 18, 'Vext': 21}, - 'heltec_wireless_stick': {'BUTTON': 0, 'DIO0': 26, 'DIO1': 35, 'DIO2': 34, 'LED': 25, - 'MOSI': 27, 'RST_LoRa': 14, 'RST_OLED': 16, 'SCK': 5, - 'SCL_OLED': 15, 'SDA_OLED': 4, 'SS': 18, 'Vext': 21}, - 'hornbill32dev': {'BUTTON': 0, 'LED': 13}, - 'hornbill32minima': {'SS': 2}, - 'intorobot': {'A1': 39, 'A2': 35, 'A3': 25, 'A4': 26, 'A5': 14, 'A6': 12, 'A7': 15, 'A8': 13, - 'A9': 2, 'BUTTON': 0, 'D0': 19, 'D1': 23, 'D2': 18, 'D3': 17, 'D4': 16, 'D5': 5, - 'D6': 4, 'LED': 4, 'MISO': 17, 'MOSI': 16, 'RGB_B_BUILTIN': 22, - 'RGB_G_BUILTIN': 21, 'RGB_R_BUILTIN': 27, 'SCL': 19, 'SDA': 23, 'T0': 19, - 'T1': 23, 'T2': 18, 'T3': 17, 'T4': 16, 'T5': 5, 'T6': 4}, - 'iotaap_magnolia': {}, - 'iotbusio': {}, - 'iotbusproteus': {}, - 'lolin32': {'LED': 5}, - 'lolin32-lite': {'LED': 22}, - 'lolin_d32': {'LED': 5, '_VBAT': 35}, - 'lolin_d32_pro': {'LED': 5, '_VBAT': 35}, - 'lopy': {'A1': 37, 'A2': 38, 'LED': 0, 'MISO': 37, 'MOSI': 22, 'SCK': 13, 'SCL': 13, - 'SDA': 12, 'SS': 17}, - 'lopy4': {'A1': 37, 'A2': 38, 'LED': 0, 'MISO': 37, 'MOSI': 22, 'SCK': 13, 'SCL': 13, - 'SDA': 12, 'SS': 18}, - 'm5stack-core-esp32': {'ADC1': 35, 'ADC2': 36, 'G0': 0, 'G1': 1, 'G12': 12, 'G13': 13, - 'G15': 15, 'G16': 16, 'G17': 17, 'G18': 18, 'G19': 19, 'G2': 2, - 'G21': 21, 'G22': 22, 'G23': 23, 'G25': 25, 'G26': 26, 'G3': 3, - 'G34': 34, 'G35': 35, 'G36': 36, 'G5': 5, 'RXD2': 16, 'TXD2': 17}, - 'm5stack-fire': {'ADC1': 35, 'ADC2': 36, 'G0': 0, 'G1': 1, 'G12': 12, 'G13': 13, 'G15': 15, - 'G16': 16, 'G17': 17, 'G18': 18, 'G19': 19, 'G2': 2, 'G21': 21, 'G22': 22, - 'G23': 23, 'G25': 25, 'G26': 26, 'G3': 3, 'G34': 34, 'G35': 35, 'G36': 36, - 'G5': 5}, - 'm5stack-grey': {'ADC1': 35, 'ADC2': 36, 'G0': 0, 'G1': 1, 'G12': 12, 'G13': 13, 'G15': 15, - 'G16': 16, 'G17': 17, 'G18': 18, 'G19': 19, 'G2': 2, 'G21': 21, 'G22': 22, - 'G23': 23, 'G25': 25, 'G26': 26, 'G3': 3, 'G34': 34, 'G35': 35, 'G36': 36, - 'G5': 5, 'RXD2': 16, 'TXD2': 17}, - 'm5stick-c': {'ADC1': 35, 'ADC2': 36, 'G0': 0, 'G10': 10, 'G26': 26, 'G32': 32, 'G33': 33, - 'G36': 36, 'G37': 37, 'G39': 39, 'G9': 9, 'MISO': 36, 'MOSI': 15, 'SCK': 13, - 'SCL': 33, 'SDA': 32}, - 'magicbit': {'BLUE_LED': 17, 'BUZZER': 25, 'GREEN_LED': 16, 'LDR': 36, 'LED': 16, - 'LEFT_BUTTON': 35, 'MOTOR1A': 27, 'MOTOR1B': 18, 'MOTOR2A': 16, 'MOTOR2B': 17, - 'POT': 39, 'RED_LED': 27, 'RIGHT_PUTTON': 34, 'YELLOW_LED': 18}, - 'mhetesp32devkit': {'LED': 2}, - 'mhetesp32minikit': {'LED': 2}, - 'microduino-core-esp32': {'A0': 12, 'A1': 13, 'A10': 25, 'A11': 26, 'A12': 27, 'A13': 14, - 'A2': 15, 'A3': 4, 'A6': 38, 'A7': 37, 'A8': 32, 'A9': 33, 'D0': 3, - 'D1': 1, 'D10': 5, 'D11': 23, 'D12': 19, 'D13': 18, 'D14': 12, - 'D15': 13, 'D16': 15, 'D17': 4, 'D18': 22, 'D19': 21, 'D2': 16, - 'D20': 38, 'D21': 37, 'D3': 17, 'D4': 32, 'D5': 33, 'D6': 25, - 'D7': 26, 'D8': 27, 'D9': 14, 'SCL': 21, 'SCL1': 13, 'SDA': 22, - 'SDA1': 12}, - 'nano32': {'BUTTON': 0, 'LED': 16}, - 'nina_w10': {'D0': 3, 'D1': 1, 'D10': 5, 'D11': 19, 'D12': 23, 'D13': 18, 'D14': 13, - 'D15': 12, 'D16': 32, 'D17': 33, 'D18': 21, 'D19': 34, 'D2': 26, 'D20': 36, - 'D21': 39, 'D3': 25, 'D4': 35, 'D5': 27, 'D6': 22, 'D7': 0, 'D8': 15, 'D9': 14, - 'LED_BLUE': 21, 'LED_GREEN': 33, 'LED_RED': 23, 'SCL': 13, 'SDA': 12, 'SW1': 33, - 'SW2': 27}, - 'node32s': {}, - 'nodemcu-32s': {'BUTTON': 0, 'LED': 2}, - 'odroid_esp32': {'ADC1': 35, 'ADC2': 36, 'LED': 2, 'SCL': 4, 'SDA': 15, 'SS': 22}, - 'onehorse32dev': {'A1': 37, 'A2': 38, 'BUTTON': 0, 'LED': 5}, - 'oroca_edubot': {'A0': 34, 'A1': 39, 'A2': 36, 'A3': 33, 'D0': 4, 'D1': 16, 'D2': 17, - 'D3': 22, 'D4': 23, 'D5': 5, 'D6': 18, 'D7': 19, 'D8': 33, 'LED': 13, - 'MOSI': 18, 'RX': 16, 'SCK': 5, 'SDA': 23, 'SS': 2, 'TX': 17, 'VBAT': 35}, - 'pico32': {}, - 'pocket_32': {'LED': 16}, - 'pycom_gpy': {'A1': 37, 'A2': 38, 'LED': 0, 'MISO': 37, 'MOSI': 22, 'SCK': 13, 'SCL': 13, - 'SDA': 12, 'SS': 17}, - 'quantum': {}, - 'sparkfun_lora_gateway_1-channel': {'MISO': 12, 'MOSI': 13, 'SCK': 14, 'SS': 16}, - 'tinypico': {}, - 'ttgo-lora32-v1': {'A1': 37, 'A2': 38, 'BUTTON': 0, 'LED': 2, 'MOSI': 27, 'SCK': 5, 'SS': 18}, - 'ttgo-t-beam': {'BUTTON': 39, 'LED': 14, 'MOSI': 27, 'SCK': 5, 'SS': 18}, - 'ttgo-t-watch': {'BUTTON': 36, 'MISO': 2, 'MOSI': 15, 'SCK': 14, 'SS': 13}, - 'ttgo-t1': {'LED': 22, 'MISO': 2, 'MOSI': 15, 'SCK': 14, 'SCL': 23, 'SS': 13}, - 'ttgo-t7-v13-mini32': {'LED': 22}, - 'ttgo-t7-v14-mini32': {'LED': 19}, - 'turta_iot_node': {}, - 'vintlabs-devkit-v1': {'LED': 2, 'PWM0': 12, 'PWM1': 13, 'PWM2': 14, 'PWM3': 15, 'PWM4': 16, - 'PWM5': 17, 'PWM6': 18, 'PWM7': 19}, - 'wemos_d1_mini32': {'D0': 26, 'D1': 22, 'D2': 21, 'D3': 17, 'D4': 16, 'D5': 18, 'D6': 19, - 'D7': 23, 'D8': 5, 'LED': 2, 'RXD': 3, 'TXD': 1, '_VBAT': 35}, - 'wemosbat': {'LED': 16}, - 'wesp32': {'MISO': 32, 'SCL': 4, 'SDA': 15}, - 'widora-air': {'A1': 39, 'A2': 35, 'A3': 25, 'A4': 26, 'A5': 14, 'A6': 12, 'A7': 15, 'A8': 13, - 'A9': 2, 'BUTTON': 0, 'D0': 19, 'D1': 23, 'D2': 18, 'D3': 17, 'D4': 16, - 'D5': 5, 'D6': 4, 'LED': 25, 'MISO': 17, 'MOSI': 16, 'SCL': 19, 'SDA': 23, - 'T0': 19, 'T1': 23, 'T2': 18, 'T3': 17, 'T4': 16, 'T5': 5, 'T6': 4}, - 'xinabox_cw02': {'LED': 27}, + "alksesp32": { + "A0": 32, + "A1": 33, + "A2": 25, + "A3": 26, + "A4": 27, + "A5": 14, + "A6": 12, + "A7": 15, + "D0": 40, + "D1": 41, + "D10": 19, + "D11": 21, + "D12": 22, + "D13": 23, + "D2": 15, + "D3": 2, + "D4": 0, + "D5": 4, + "D6": 16, + "D7": 17, + "D8": 5, + "D9": 18, + "DHT_PIN": 26, + "LED": 23, + "L_B": 5, + "L_G": 17, + "L_R": 22, + "L_RGB_B": 16, + "L_RGB_G": 21, + "L_RGB_R": 4, + "L_Y": 23, + "MISO": 22, + "MOSI": 21, + "PHOTO": 25, + "PIEZO1": 19, + "PIEZO2": 18, + "POT1": 32, + "POT2": 33, + "S1": 4, + "S2": 16, + "S3": 18, + "S4": 19, + "S5": 21, + "SCK": 23, + "SCL": 14, + "SDA": 27, + "SS": 19, + "SW1": 15, + "SW2": 2, + "SW3": 0, + }, + "bpi-bit": { + "BUTTON_A": 35, + "BUTTON_B": 27, + "BUZZER": 25, + "LIGHT_SENSOR1": 36, + "LIGHT_SENSOR2": 39, + "MPU9250_INT": 0, + "P0": 25, + "P1": 32, + "P10": 26, + "P11": 27, + "P12": 2, + "P13": 18, + "P14": 19, + "P15": 23, + "P16": 5, + "P19": 22, + "P2": 33, + "P20": 21, + "P3": 13, + "P4": 15, + "P5": 35, + "P6": 12, + "P7": 14, + "P8": 16, + "P9": 17, + "RGB_LED": 4, + "TEMPERATURE_SENSOR": 34, + }, + "d-duino-32": { + "D1": 5, + "D10": 1, + "D2": 4, + "D3": 0, + "D4": 2, + "D5": 14, + "D6": 12, + "D7": 13, + "D8": 15, + "D9": 3, + "MISO": 12, + "MOSI": 13, + "SCK": 14, + "SCL": 4, + "SDA": 5, + "SS": 15, + }, + "esp-wrover-kit": {}, + "esp32-devkitlipo": {}, + "esp32-evb": { + "BUTTON": 34, + "MISO": 15, + "MOSI": 2, + "SCK": 14, + "SCL": 16, + "SDA": 13, + "SS": 17, + }, + "esp32-gateway": {"BUTTON": 34, "LED": 33, "SCL": 16, "SDA": 32}, + "esp32-poe-iso": { + "BUTTON": 34, + "MISO": 15, + "MOSI": 2, + "SCK": 14, + "SCL": 16, + "SDA": 13, + }, + "esp32-poe": {"BUTTON": 34, "MISO": 15, "MOSI": 2, "SCK": 14, "SCL": 16, "SDA": 13}, + "esp32-pro": { + "BUTTON": 34, + "MISO": 15, + "MOSI": 2, + "SCK": 14, + "SCL": 16, + "SDA": 13, + "SS": 17, + }, + "esp320": { + "LED": 5, + "MISO": 12, + "MOSI": 13, + "SCK": 14, + "SCL": 14, + "SDA": 2, + "SS": 15, + }, + "esp32cam": {}, + "esp32dev": {}, + "esp32doit-devkit-v1": {"LED": 2}, + "esp32thing": {"BUTTON": 0, "LED": 5, "SS": 2}, + "esp32vn-iot-uno": {}, + "espea32": {"BUTTON": 0, "LED": 5}, + "espectro32": {"LED": 15, "SD_SS": 33}, + "espino32": {"BUTTON": 0, "LED": 16}, + "featheresp32": { + "A0": 26, + "A1": 25, + "A10": 27, + "A11": 12, + "A12": 13, + "A13": 35, + "A2": 34, + "A4": 36, + "A5": 4, + "A6": 14, + "A7": 32, + "A8": 15, + "A9": 33, + "Ax": 2, + "LED": 13, + "MOSI": 18, + "RX": 16, + "SCK": 5, + "SDA": 23, + "SS": 33, + "TX": 17, + }, + "firebeetle32": {"LED": 2}, + "fm-devkit": { + "D0": 34, + "D1": 35, + "D10": 0, + "D2": 32, + "D3": 33, + "D4": 27, + "D5": 14, + "D6": 12, + "D7": 13, + "D8": 15, + "D9": 23, + "I2S_DOUT": 22, + "I2S_LRCLK": 25, + "I2S_MCLK": 2, + "I2S_SCLK": 26, + "LED": 5, + "SCL": 17, + "SDA": 16, + "SW1": 4, + "SW2": 18, + "SW3": 19, + "SW4": 21, + }, + "frogboard": {}, + "heltec_wifi_kit_32": { + "A1": 37, + "A2": 38, + "BUTTON": 0, + "LED": 25, + "RST_OLED": 16, + "SCL_OLED": 15, + "SDA_OLED": 4, + "Vext": 21, + }, + "heltec_wifi_lora_32": { + "BUTTON": 0, + "DIO0": 26, + "DIO1": 33, + "DIO2": 32, + "LED": 25, + "MOSI": 27, + "RST_LoRa": 14, + "RST_OLED": 16, + "SCK": 5, + "SCL_OLED": 15, + "SDA_OLED": 4, + "SS": 18, + "Vext": 21, + }, + "heltec_wifi_lora_32_V2": { + "BUTTON": 0, + "DIO0": 26, + "DIO1": 35, + "DIO2": 34, + "LED": 25, + "MOSI": 27, + "RST_LoRa": 14, + "RST_OLED": 16, + "SCK": 5, + "SCL_OLED": 15, + "SDA_OLED": 4, + "SS": 18, + "Vext": 21, + }, + "heltec_wireless_stick": { + "BUTTON": 0, + "DIO0": 26, + "DIO1": 35, + "DIO2": 34, + "LED": 25, + "MOSI": 27, + "RST_LoRa": 14, + "RST_OLED": 16, + "SCK": 5, + "SCL_OLED": 15, + "SDA_OLED": 4, + "SS": 18, + "Vext": 21, + }, + "hornbill32dev": {"BUTTON": 0, "LED": 13}, + "hornbill32minima": {"SS": 2}, + "intorobot": { + "A1": 39, + "A2": 35, + "A3": 25, + "A4": 26, + "A5": 14, + "A6": 12, + "A7": 15, + "A8": 13, + "A9": 2, + "BUTTON": 0, + "D0": 19, + "D1": 23, + "D2": 18, + "D3": 17, + "D4": 16, + "D5": 5, + "D6": 4, + "LED": 4, + "MISO": 17, + "MOSI": 16, + "RGB_B_BUILTIN": 22, + "RGB_G_BUILTIN": 21, + "RGB_R_BUILTIN": 27, + "SCL": 19, + "SDA": 23, + "T0": 19, + "T1": 23, + "T2": 18, + "T3": 17, + "T4": 16, + "T5": 5, + "T6": 4, + }, + "iotaap_magnolia": {}, + "iotbusio": {}, + "iotbusproteus": {}, + "lolin32": {"LED": 5}, + "lolin32-lite": {"LED": 22}, + "lolin_d32": {"LED": 5, "_VBAT": 35}, + "lolin_d32_pro": {"LED": 5, "_VBAT": 35}, + "lopy": { + "A1": 37, + "A2": 38, + "LED": 0, + "MISO": 37, + "MOSI": 22, + "SCK": 13, + "SCL": 13, + "SDA": 12, + "SS": 17, + }, + "lopy4": { + "A1": 37, + "A2": 38, + "LED": 0, + "MISO": 37, + "MOSI": 22, + "SCK": 13, + "SCL": 13, + "SDA": 12, + "SS": 18, + }, + "m5stack-core-esp32": { + "ADC1": 35, + "ADC2": 36, + "G0": 0, + "G1": 1, + "G12": 12, + "G13": 13, + "G15": 15, + "G16": 16, + "G17": 17, + "G18": 18, + "G19": 19, + "G2": 2, + "G21": 21, + "G22": 22, + "G23": 23, + "G25": 25, + "G26": 26, + "G3": 3, + "G34": 34, + "G35": 35, + "G36": 36, + "G5": 5, + "RXD2": 16, + "TXD2": 17, + }, + "m5stack-fire": { + "ADC1": 35, + "ADC2": 36, + "G0": 0, + "G1": 1, + "G12": 12, + "G13": 13, + "G15": 15, + "G16": 16, + "G17": 17, + "G18": 18, + "G19": 19, + "G2": 2, + "G21": 21, + "G22": 22, + "G23": 23, + "G25": 25, + "G26": 26, + "G3": 3, + "G34": 34, + "G35": 35, + "G36": 36, + "G5": 5, + }, + "m5stack-grey": { + "ADC1": 35, + "ADC2": 36, + "G0": 0, + "G1": 1, + "G12": 12, + "G13": 13, + "G15": 15, + "G16": 16, + "G17": 17, + "G18": 18, + "G19": 19, + "G2": 2, + "G21": 21, + "G22": 22, + "G23": 23, + "G25": 25, + "G26": 26, + "G3": 3, + "G34": 34, + "G35": 35, + "G36": 36, + "G5": 5, + "RXD2": 16, + "TXD2": 17, + }, + "m5stick-c": { + "ADC1": 35, + "ADC2": 36, + "G0": 0, + "G10": 10, + "G26": 26, + "G32": 32, + "G33": 33, + "G36": 36, + "G37": 37, + "G39": 39, + "G9": 9, + "MISO": 36, + "MOSI": 15, + "SCK": 13, + "SCL": 33, + "SDA": 32, + }, + "magicbit": { + "BLUE_LED": 17, + "BUZZER": 25, + "GREEN_LED": 16, + "LDR": 36, + "LED": 16, + "LEFT_BUTTON": 35, + "MOTOR1A": 27, + "MOTOR1B": 18, + "MOTOR2A": 16, + "MOTOR2B": 17, + "POT": 39, + "RED_LED": 27, + "RIGHT_PUTTON": 34, + "YELLOW_LED": 18, + }, + "mhetesp32devkit": {"LED": 2}, + "mhetesp32minikit": {"LED": 2}, + "microduino-core-esp32": { + "A0": 12, + "A1": 13, + "A10": 25, + "A11": 26, + "A12": 27, + "A13": 14, + "A2": 15, + "A3": 4, + "A6": 38, + "A7": 37, + "A8": 32, + "A9": 33, + "D0": 3, + "D1": 1, + "D10": 5, + "D11": 23, + "D12": 19, + "D13": 18, + "D14": 12, + "D15": 13, + "D16": 15, + "D17": 4, + "D18": 22, + "D19": 21, + "D2": 16, + "D20": 38, + "D21": 37, + "D3": 17, + "D4": 32, + "D5": 33, + "D6": 25, + "D7": 26, + "D8": 27, + "D9": 14, + "SCL": 21, + "SCL1": 13, + "SDA": 22, + "SDA1": 12, + }, + "nano32": {"BUTTON": 0, "LED": 16}, + "nina_w10": { + "D0": 3, + "D1": 1, + "D10": 5, + "D11": 19, + "D12": 23, + "D13": 18, + "D14": 13, + "D15": 12, + "D16": 32, + "D17": 33, + "D18": 21, + "D19": 34, + "D2": 26, + "D20": 36, + "D21": 39, + "D3": 25, + "D4": 35, + "D5": 27, + "D6": 22, + "D7": 0, + "D8": 15, + "D9": 14, + "LED_BLUE": 21, + "LED_GREEN": 33, + "LED_RED": 23, + "SCL": 13, + "SDA": 12, + "SW1": 33, + "SW2": 27, + }, + "node32s": {}, + "nodemcu-32s": {"BUTTON": 0, "LED": 2}, + "odroid_esp32": {"ADC1": 35, "ADC2": 36, "LED": 2, "SCL": 4, "SDA": 15, "SS": 22}, + "onehorse32dev": {"A1": 37, "A2": 38, "BUTTON": 0, "LED": 5}, + "oroca_edubot": { + "A0": 34, + "A1": 39, + "A2": 36, + "A3": 33, + "D0": 4, + "D1": 16, + "D2": 17, + "D3": 22, + "D4": 23, + "D5": 5, + "D6": 18, + "D7": 19, + "D8": 33, + "LED": 13, + "MOSI": 18, + "RX": 16, + "SCK": 5, + "SDA": 23, + "SS": 2, + "TX": 17, + "VBAT": 35, + }, + "pico32": {}, + "pocket_32": {"LED": 16}, + "pycom_gpy": { + "A1": 37, + "A2": 38, + "LED": 0, + "MISO": 37, + "MOSI": 22, + "SCK": 13, + "SCL": 13, + "SDA": 12, + "SS": 17, + }, + "quantum": {}, + "sparkfun_lora_gateway_1-channel": {"MISO": 12, "MOSI": 13, "SCK": 14, "SS": 16}, + "tinypico": {}, + "ttgo-lora32-v1": { + "A1": 37, + "A2": 38, + "BUTTON": 0, + "LED": 2, + "MOSI": 27, + "SCK": 5, + "SS": 18, + }, + "ttgo-t-beam": {"BUTTON": 39, "LED": 14, "MOSI": 27, "SCK": 5, "SS": 18}, + "ttgo-t-watch": {"BUTTON": 36, "MISO": 2, "MOSI": 15, "SCK": 14, "SS": 13}, + "ttgo-t1": {"LED": 22, "MISO": 2, "MOSI": 15, "SCK": 14, "SCL": 23, "SS": 13}, + "ttgo-t7-v13-mini32": {"LED": 22}, + "ttgo-t7-v14-mini32": {"LED": 19}, + "turta_iot_node": {}, + "vintlabs-devkit-v1": { + "LED": 2, + "PWM0": 12, + "PWM1": 13, + "PWM2": 14, + "PWM3": 15, + "PWM4": 16, + "PWM5": 17, + "PWM6": 18, + "PWM7": 19, + }, + "wemos_d1_mini32": { + "D0": 26, + "D1": 22, + "D2": 21, + "D3": 17, + "D4": 16, + "D5": 18, + "D6": 19, + "D7": 23, + "D8": 5, + "LED": 2, + "RXD": 3, + "TXD": 1, + "_VBAT": 35, + }, + "wemosbat": {"LED": 16}, + "wesp32": {"MISO": 32, "SCL": 4, "SDA": 15}, + "widora-air": { + "A1": 39, + "A2": 35, + "A3": 25, + "A4": 26, + "A5": 14, + "A6": 12, + "A7": 15, + "A8": 13, + "A9": 2, + "BUTTON": 0, + "D0": 19, + "D1": 23, + "D2": 18, + "D3": 17, + "D4": 16, + "D5": 5, + "D6": 4, + "LED": 25, + "MISO": 17, + "MOSI": 16, + "SCL": 19, + "SDA": 23, + "T0": 19, + "T1": 23, + "T2": 18, + "T3": 17, + "T4": 16, + "T5": 5, + "T6": 4, + }, + "xinabox_cw02": {"LED": 27}, } @@ -282,24 +893,26 @@ def _lookup_pin(value): def _translate_pin(value): if isinstance(value, dict) or value is None: - raise cv.Invalid("This variable only supports pin numbers, not full pin schemas " - "(with inverted and mode).") + raise cv.Invalid( + "This variable only supports pin numbers, not full pin schemas " + "(with inverted and mode)." + ) if isinstance(value, int): return value try: return int(value) except ValueError: pass - if value.startswith('GPIO'): - return cv.Coerce(int)(value[len('GPIO'):].strip()) + if value.startswith("GPIO"): + return cv.Coerce(int)(value[len("GPIO") :].strip()) return _lookup_pin(value) _ESP_SDIO_PINS = { - 6: 'Flash Clock', - 7: 'Flash Data 0', - 8: 'Flash Data 1', - 11: 'Flash Command', + 6: "Flash Clock", + 7: "Flash Data 0", + 8: "Flash Data 1", + 11: "Flash Command", } @@ -309,11 +922,16 @@ def validate_gpio_pin(value): if value < 0 or value > 39: raise cv.Invalid(f"ESP32: Invalid pin number: {value}") if value in _ESP_SDIO_PINS: - raise cv.Invalid("This pin cannot be used on ESP32s and is already used by " - "the flash interface (function: {})".format(_ESP_SDIO_PINS[value])) + raise cv.Invalid( + "This pin cannot be used on ESP32s and is already used by " + "the flash interface (function: {})".format(_ESP_SDIO_PINS[value]) + ) if 9 <= value <= 10: - _LOGGER.warning("ESP32: Pin %s (9-10) might already be used by the " - "flash interface in QUAD IO flash mode.", value) + _LOGGER.warning( + "ESP32: Pin %s (9-10) might already be used by the " + "flash interface in QUAD IO flash mode.", + value, + ) if value in (20, 24, 28, 29, 30, 31): # These pins are not exposed in GPIO mux (reason unknown) # but they're missing from IO_MUX list in datasheet @@ -323,11 +941,16 @@ def validate_gpio_pin(value): if value < 0 or value > 17: raise cv.Invalid(f"ESP8266: Invalid pin number: {value}") if value in _ESP_SDIO_PINS: - raise cv.Invalid("This pin cannot be used on ESP8266s and is already used by " - "the flash interface (function: {})".format(_ESP_SDIO_PINS[value])) + raise cv.Invalid( + "This pin cannot be used on ESP8266s and is already used by " + "the flash interface (function: {})".format(_ESP_SDIO_PINS[value]) + ) if 9 <= value <= 10: - _LOGGER.warning("ESP8266: Pin %s (9-10) might already be used by the " - "flash interface in QUAD IO flash mode.", value) + _LOGGER.warning( + "ESP8266: Pin %s (9-10) might already be used by the " + "flash interface in QUAD IO flash mode.", + value, + ) return value raise NotImplementedError @@ -345,8 +968,10 @@ def input_pullup_pin(value): return output_pin(value) if CORE.is_esp8266: if value == 0: - raise cv.Invalid("GPIO Pin 0 does not support pullup pin mode. " - "Please choose another pin.") + raise cv.Invalid( + "GPIO Pin 0 does not support pullup pin mode. " + "Please choose another pin." + ) return value raise NotImplementedError @@ -355,8 +980,10 @@ def output_pin(value): value = validate_gpio_pin(value) if CORE.is_esp32: if 34 <= value <= 39: - raise cv.Invalid("ESP32: GPIO{} (34-39) can only be used as an " - "input pin.".format(value)) + raise cv.Invalid( + "ESP32: GPIO{} (34-39) can only be used as an " + "input pin.".format(value) + ) return value if CORE.is_esp8266: if value == 17: @@ -381,15 +1008,37 @@ def analog_pin(value): input_output_pin = cv.All(input_pin, output_pin) PIN_MODES_ESP8266 = [ - 'INPUT', 'OUTPUT', 'INPUT_PULLUP', 'OUTPUT_OPEN_DRAIN', 'SPECIAL', 'FUNCTION_1', - 'FUNCTION_2', 'FUNCTION_3', 'FUNCTION_4', - 'FUNCTION_0', 'WAKEUP_PULLUP', 'WAKEUP_PULLDOWN', 'INPUT_PULLDOWN_16', + "INPUT", + "OUTPUT", + "INPUT_PULLUP", + "OUTPUT_OPEN_DRAIN", + "SPECIAL", + "FUNCTION_1", + "FUNCTION_2", + "FUNCTION_3", + "FUNCTION_4", + "FUNCTION_0", + "WAKEUP_PULLUP", + "WAKEUP_PULLDOWN", + "INPUT_PULLDOWN_16", ] PIN_MODES_ESP32 = [ - 'INPUT', 'OUTPUT', 'INPUT_PULLUP', 'OUTPUT_OPEN_DRAIN', 'SPECIAL', 'FUNCTION_1', - 'FUNCTION_2', 'FUNCTION_3', 'FUNCTION_4', - 'PULLUP', 'PULLDOWN', 'INPUT_PULLDOWN', 'OPEN_DRAIN', 'FUNCTION_5', - 'FUNCTION_6', 'ANALOG', + "INPUT", + "OUTPUT", + "INPUT_PULLUP", + "OUTPUT_OPEN_DRAIN", + "SPECIAL", + "FUNCTION_1", + "FUNCTION_2", + "FUNCTION_3", + "FUNCTION_4", + "PULLUP", + "PULLDOWN", + "INPUT_PULLDOWN", + "OPEN_DRAIN", + "FUNCTION_5", + "FUNCTION_6", + "ANALOG", ] @@ -401,28 +1050,36 @@ def pin_mode(value): raise NotImplementedError -GPIO_FULL_OUTPUT_PIN_SCHEMA = cv.Schema({ - cv.Required(CONF_NUMBER): output_pin, - cv.Optional(CONF_MODE, default='OUTPUT'): pin_mode, - cv.Optional(CONF_INVERTED, default=False): cv.boolean, -}) +GPIO_FULL_OUTPUT_PIN_SCHEMA = cv.Schema( + { + cv.Required(CONF_NUMBER): output_pin, + cv.Optional(CONF_MODE, default="OUTPUT"): pin_mode, + cv.Optional(CONF_INVERTED, default=False): cv.boolean, + } +) -GPIO_FULL_INPUT_PIN_SCHEMA = cv.Schema({ - cv.Required(CONF_NUMBER): input_pin, - cv.Optional(CONF_MODE, default='INPUT'): pin_mode, - cv.Optional(CONF_INVERTED, default=False): cv.boolean, -}) +GPIO_FULL_INPUT_PIN_SCHEMA = cv.Schema( + { + cv.Required(CONF_NUMBER): input_pin, + cv.Optional(CONF_MODE, default="INPUT"): pin_mode, + cv.Optional(CONF_INVERTED, default=False): cv.boolean, + } +) -GPIO_FULL_INPUT_PULLUP_PIN_SCHEMA = cv.Schema({ - cv.Required(CONF_NUMBER): input_pin, - cv.Optional(CONF_MODE, default='INPUT_PULLUP'): pin_mode, - cv.Optional(CONF_INVERTED, default=False): cv.boolean, -}) +GPIO_FULL_INPUT_PULLUP_PIN_SCHEMA = cv.Schema( + { + cv.Required(CONF_NUMBER): input_pin, + cv.Optional(CONF_MODE, default="INPUT_PULLUP"): pin_mode, + cv.Optional(CONF_INVERTED, default=False): cv.boolean, + } +) -GPIO_FULL_ANALOG_PIN_SCHEMA = cv.Schema({ - cv.Required(CONF_NUMBER): analog_pin, - cv.Optional(CONF_MODE, default='INPUT'): pin_mode, -}) +GPIO_FULL_ANALOG_PIN_SCHEMA = cv.Schema( + { + cv.Required(CONF_NUMBER): analog_pin, + cv.Optional(CONF_MODE, default="INPUT"): pin_mode, + } +) def shorthand_output_pin(value): @@ -437,10 +1094,12 @@ def shorthand_input_pin(value): def shorthand_input_pullup_pin(value): value = input_pullup_pin(value) - return GPIO_FULL_INPUT_PIN_SCHEMA({ - CONF_NUMBER: value, - CONF_MODE: 'INPUT_PULLUP', - }) + return GPIO_FULL_INPUT_PIN_SCHEMA( + { + CONF_NUMBER: value, + CONF_MODE: "INPUT_PULLUP", + } + ) def shorthand_analog_pin(value): @@ -451,8 +1110,10 @@ def shorthand_analog_pin(value): def validate_has_interrupt(value): if CORE.is_esp8266: if value[CONF_NUMBER] >= 16: - raise cv.Invalid("Pins GPIO16 and GPIO17 do not support interrupts and cannot be used " - "here, got {}".format(value[CONF_NUMBER])) + raise cv.Invalid( + "Pins GPIO16 and GPIO17 do not support interrupts and cannot be used " + "here, got {}".format(value[CONF_NUMBER]) + ) return value diff --git a/esphome/platformio_api.py b/esphome/platformio_api.py index 1cda141a54..87ca12c9a8 100644 --- a/esphome/platformio_api.py +++ b/esphome/platformio_api.py @@ -24,6 +24,7 @@ def patch_structhash(): def patched_clean_build_dir(build_dir, *args): from platformio import fs from platformio.project.helpers import get_project_dir + platformio_ini = join(get_project_dir(), "platformio.ini") # if project's config is modified @@ -38,54 +39,59 @@ def patch_structhash(): command.clean_build_dir = patched_clean_build_dir -IGNORE_LIB_WARNINGS = r'(?:' + '|'.join(['Hash', 'Update']) + r')' +IGNORE_LIB_WARNINGS = r"(?:" + "|".join(["Hash", "Update"]) + r")" FILTER_PLATFORMIO_LINES = [ - r'Verbose mode can be enabled via `-v, --verbose` option.*', - r'CONFIGURATION: https://docs.platformio.org/.*', - r'PLATFORM: .*', - r'DEBUG: Current.*', - r'PACKAGES: .*', - r'LDF: Library Dependency Finder -> http://bit.ly/configure-pio-ldf.*', - r'LDF Modes: Finder ~ chain, Compatibility ~ soft.*', - r'Looking for ' + IGNORE_LIB_WARNINGS + r' library in registry', - r"Warning! Library `.*'" + IGNORE_LIB_WARNINGS + - r".*` has not been found in PlatformIO Registry.", - r"You can ignore this message, if `.*" + IGNORE_LIB_WARNINGS + r".*` is a built-in library.*", - r'Scanning dependencies...', + r"Verbose mode can be enabled via `-v, --verbose` option.*", + r"CONFIGURATION: https://docs.platformio.org/.*", + r"PLATFORM: .*", + r"DEBUG: Current.*", + r"PACKAGES: .*", + r"LDF: Library Dependency Finder -> http://bit.ly/configure-pio-ldf.*", + r"LDF Modes: Finder ~ chain, Compatibility ~ soft.*", + r"Looking for " + IGNORE_LIB_WARNINGS + r" library in registry", + r"Warning! Library `.*'" + + IGNORE_LIB_WARNINGS + + r".*` has not been found in PlatformIO Registry.", + r"You can ignore this message, if `.*" + + IGNORE_LIB_WARNINGS + + r".*` is a built-in library.*", + r"Scanning dependencies...", r"Found \d+ compatible libraries", - r'Memory Usage -> http://bit.ly/pio-memory-usage', - r'esptool.py v.*', + r"Memory Usage -> http://bit.ly/pio-memory-usage", + r"esptool.py v.*", r"Found: https://platformio.org/lib/show/.*", r"Using cache: .*", - r'Installing dependencies', - r'.* @ .* is already installed', - r'Building in .* mode', - r'Advanced Memory Usage is available via .*', + r"Installing dependencies", + r".* @ .* is already installed", + r"Building in .* mode", + r"Advanced Memory Usage is available via .*", ] def run_platformio_cli(*args, **kwargs) -> Union[str, int]: os.environ["PLATFORMIO_FORCE_COLOR"] = "true" os.environ["PLATFORMIO_BUILD_DIR"] = os.path.abspath(CORE.relative_pioenvs_path()) - os.environ["PLATFORMIO_LIBDEPS_DIR"] = os.path.abspath(CORE.relative_piolibdeps_path()) - cmd = ['platformio'] + list(args) + os.environ["PLATFORMIO_LIBDEPS_DIR"] = os.path.abspath( + CORE.relative_piolibdeps_path() + ) + cmd = ["platformio"] + list(args) if not CORE.verbose: - kwargs['filter_lines'] = FILTER_PLATFORMIO_LINES + kwargs["filter_lines"] = FILTER_PLATFORMIO_LINES - if os.environ.get('ESPHOME_USE_SUBPROCESS') is not None: + if os.environ.get("ESPHOME_USE_SUBPROCESS") is not None: return run_external_process(*cmd, **kwargs) import platformio.__main__ + patch_structhash() - return run_external_command(platformio.__main__.main, - *cmd, **kwargs) + return run_external_command(platformio.__main__.main, *cmd, **kwargs) def run_platformio_cli_run(config, verbose, *args, **kwargs) -> Union[str, int]: - command = ['run', '-d', CORE.build_path] + command = ["run", "-d", CORE.build_path] if verbose: - command += ['-v'] + command += ["-v"] command += list(args) return run_platformio_cli(*command, **kwargs) @@ -95,11 +101,13 @@ def run_compile(config, verbose): def run_upload(config, verbose, port): - return run_platformio_cli_run(config, verbose, '-t', 'upload', '--upload-port', port) + return run_platformio_cli_run( + config, verbose, "-t", "upload", "--upload-port", port + ) def run_idedata(config): - args = ['-t', 'idedata'] + args = ["-t", "idedata"] stdout = run_platformio_cli_run(config, False, *args, capture_stdout=True) match = re.search(r'{\s*".*}', stdout) if match is None: @@ -129,10 +137,10 @@ ESP8266_EXCEPTION_CODES = { 0: "Illegal instruction (Is the flash damaged?)", 1: "SYSCALL instruction", 2: "InstructionFetchError: Processor internal physical address or data error during " - "instruction fetch", + "instruction fetch", 3: "LoadStoreError: Processor internal physical address or data error during load or store", 4: "Level1Interrupt: Level-1 interrupt as indicated by set level-1 bits in the INTERRUPT " - "register", + "register", 5: "Alloca: MOVSP instruction, if caller's registers are not in the register file", 6: "Integer Divide By Zero", 7: "reserved", @@ -147,17 +155,17 @@ ESP8266_EXCEPTION_CODES = { 16: "InstTLBMiss: Error during Instruction TLB refill", 17: "InstTLBMultiHit: Multiple instruction TLB entries matched", 18: "InstFetchPrivilege: An instruction fetch referenced a virtual address at a ring level " - "less than CRING", + "less than CRING", 19: "reserved", 20: "InstFetchProhibited: An instruction fetch referenced a page mapped with an attribute " - "that does not permit instruction fetch", + "that does not permit instruction fetch", 21: "reserved", 22: "reserved", 23: "reserved", 24: "LoadStoreTLBMiss: Error during TLB refill for a load or store", 25: "LoadStoreTLBMultiHit: Multiple TLB entries matched for a load or store", 26: "LoadStorePrivilege: A load or store referenced a virtual address at a ring level less " - "than ", + "than ", 27: "reserved", 28: "Access to invalid address: LOAD (wild pointer?)", 29: "Access to invalid address: STORE (wild pointer?)", @@ -169,7 +177,7 @@ def _decode_pc(config, addr): if not idedata.addr2line_path or not idedata.firmware_elf_path: _LOGGER.debug("decode_pc no addr2line") return - command = [idedata.addr2line_path, '-pfiaC', '-e', idedata.firmware_elf_path, addr] + command = [idedata.addr2line_path, "-pfiaC", "-e", idedata.firmware_elf_path, addr] try: translation = subprocess.check_output(command).decode().strip() except Exception: # pylint: disable=broad-except @@ -179,7 +187,7 @@ def _decode_pc(config, addr): if "?? ??:0" in translation: # Nothing useful return - translation = translation.replace(' at ??:?', '').replace(':?', '') + translation = translation.replace(" at ??:?", "").replace(":?", "") _LOGGER.warning("Decoded %s", translation) @@ -189,15 +197,19 @@ def _parse_register(config, regex, line): _decode_pc(config, match.group(1)) -STACKTRACE_ESP8266_EXCEPTION_TYPE_RE = re.compile(r'[eE]xception \((\d+)\):') -STACKTRACE_ESP8266_PC_RE = re.compile(r'epc1=0x(4[0-9a-fA-F]{7})') -STACKTRACE_ESP8266_EXCVADDR_RE = re.compile(r'excvaddr=0x(4[0-9a-fA-F]{7})') -STACKTRACE_ESP32_PC_RE = re.compile(r'PC\s*:\s*(?:0x)?(4[0-9a-fA-F]{7})') -STACKTRACE_ESP32_EXCVADDR_RE = re.compile(r'EXCVADDR\s*:\s*(?:0x)?(4[0-9a-fA-F]{7})') -STACKTRACE_BAD_ALLOC_RE = re.compile(r'^last failed alloc call: (4[0-9a-fA-F]{7})\((\d+)\)$') -STACKTRACE_ESP32_BACKTRACE_RE = re.compile(r'Backtrace:(?:\s+0x[0-9a-fA-F]{8}:0x[0-9a-fA-F]{8})+') -STACKTRACE_ESP32_BACKTRACE_PC_RE = re.compile(r'4[0-9a-f]{7}') -STACKTRACE_ESP8266_BACKTRACE_PC_RE = re.compile(r'4[0-9a-f]{7}') +STACKTRACE_ESP8266_EXCEPTION_TYPE_RE = re.compile(r"[eE]xception \((\d+)\):") +STACKTRACE_ESP8266_PC_RE = re.compile(r"epc1=0x(4[0-9a-fA-F]{7})") +STACKTRACE_ESP8266_EXCVADDR_RE = re.compile(r"excvaddr=0x(4[0-9a-fA-F]{7})") +STACKTRACE_ESP32_PC_RE = re.compile(r"PC\s*:\s*(?:0x)?(4[0-9a-fA-F]{7})") +STACKTRACE_ESP32_EXCVADDR_RE = re.compile(r"EXCVADDR\s*:\s*(?:0x)?(4[0-9a-fA-F]{7})") +STACKTRACE_BAD_ALLOC_RE = re.compile( + r"^last failed alloc call: (4[0-9a-fA-F]{7})\((\d+)\)$" +) +STACKTRACE_ESP32_BACKTRACE_RE = re.compile( + r"Backtrace:(?:\s+0x[0-9a-fA-F]{8}:0x[0-9a-fA-F]{8})+" +) +STACKTRACE_ESP32_BACKTRACE_PC_RE = re.compile(r"4[0-9a-f]{7}") +STACKTRACE_ESP8266_BACKTRACE_PC_RE = re.compile(r"4[0-9a-f]{7}") def process_stacktrace(config, line, backtrace_state): @@ -206,7 +218,9 @@ def process_stacktrace(config, line, backtrace_state): match = re.match(STACKTRACE_ESP8266_EXCEPTION_TYPE_RE, line) if match is not None: code = int(match.group(1)) - _LOGGER.warning("Exception type: %s", ESP8266_EXCEPTION_CODES.get(code, 'unknown')) + _LOGGER.warning( + "Exception type: %s", ESP8266_EXCEPTION_CODES.get(code, "unknown") + ) # ESP8266 PC/EXCVADDR _parse_register(config, STACKTRACE_ESP8266_PC_RE, line) @@ -218,8 +232,9 @@ def process_stacktrace(config, line, backtrace_state): # bad alloc match = re.match(STACKTRACE_BAD_ALLOC_RE, line) if match is not None: - _LOGGER.warning("Memory allocation of %s bytes failed at %s", - match.group(2), match.group(1)) + _LOGGER.warning( + "Memory allocation of %s bytes failed at %s", match.group(2), match.group(1) + ) _decode_pc(config, match.group(1)) # ESP32 single-line backtrace @@ -230,11 +245,11 @@ def process_stacktrace(config, line, backtrace_state): _decode_pc(config, addr.group()) # ESP8266 multi-line backtrace - if '>>>stack>>>' in line: + if ">>>stack>>>" in line: # Start of backtrace backtrace_state = True _LOGGER.warning("Found stack trace! Trying to decode it") - elif '<< str - return CORE.relative_config_path('.esphome', f'{CORE.config_filename}.json') + return CORE.relative_config_path(".esphome", f"{CORE.config_filename}.json") def ext_storage_path(base_path, config_filename): # type: (str, str) -> str - return os.path.join(base_path, '.esphome', f'{config_filename}.json') + return os.path.join(base_path, ".esphome", f"{config_filename}.json") def esphome_storage_path(base_path): # type: (str) -> str - return os.path.join(base_path, '.esphome', 'esphome.json') + return os.path.join(base_path, ".esphome", "esphome.json") def trash_storage_path(base_path): # type: (str) -> str - return os.path.join(base_path, '.esphome', 'trash') + return os.path.join(base_path, ".esphome", "trash") # pylint: disable=too-many-instance-attributes class StorageJSON: - def __init__(self, storage_version, name, comment, esphome_version, - src_version, arduino_version, address, esp_platform, board, build_path, - firmware_bin_path, loaded_integrations): + def __init__( + self, + storage_version, + name, + comment, + esphome_version, + src_version, + arduino_version, + address, + esp_platform, + board, + build_path, + firmware_bin_path, + loaded_integrations, + ): # Version of the storage JSON schema assert storage_version is None or isinstance(storage_version, int) self.storage_version = storage_version # type: int @@ -63,33 +75,35 @@ class StorageJSON: # The absolute path to the firmware binary self.firmware_bin_path = firmware_bin_path # type: str # A list of strings of names of loaded integrations - self.loaded_integrations = loaded_integrations # type: List[str] + self.loaded_integrations = loaded_integrations # type: List[str] self.loaded_integrations.sort() def as_dict(self): return { - 'storage_version': self.storage_version, - 'name': self.name, - 'comment': self.comment, - 'esphome_version': self.esphome_version, - 'src_version': self.src_version, - 'arduino_version': self.arduino_version, - 'address': self.address, - 'esp_platform': self.esp_platform, - 'board': self.board, - 'build_path': self.build_path, - 'firmware_bin_path': self.firmware_bin_path, - 'loaded_integrations': self.loaded_integrations, + "storage_version": self.storage_version, + "name": self.name, + "comment": self.comment, + "esphome_version": self.esphome_version, + "src_version": self.src_version, + "arduino_version": self.arduino_version, + "address": self.address, + "esp_platform": self.esp_platform, + "board": self.board, + "build_path": self.build_path, + "firmware_bin_path": self.firmware_bin_path, + "loaded_integrations": self.loaded_integrations, } def to_json(self): - return json.dumps(self.as_dict(), indent=2) + '\n' + return json.dumps(self.as_dict(), indent=2) + "\n" def save(self, path): write_file_if_changed(path, self.to_json()) @staticmethod - def from_esphome_core(esph, old): # type: (CoreType, Optional[StorageJSON]) -> StorageJSON + def from_esphome_core( + esph, old + ): # type: (CoreType, Optional[StorageJSON]) -> StorageJSON return StorageJSON( storage_version=1, name=esph.name, @@ -125,23 +139,36 @@ class StorageJSON: @staticmethod def _load_impl(path): # type: (str) -> Optional[StorageJSON] - with codecs.open(path, 'r', encoding='utf-8') as f_handle: + with codecs.open(path, "r", encoding="utf-8") as f_handle: storage = json.load(f_handle) - storage_version = storage['storage_version'] - name = storage.get('name') - comment = storage.get('comment') - esphome_version = storage.get('esphome_version', storage.get('esphomeyaml_version')) - src_version = storage.get('src_version') - arduino_version = storage.get('arduino_version') - address = storage.get('address') - esp_platform = storage.get('esp_platform') - board = storage.get('board') - build_path = storage.get('build_path') - firmware_bin_path = storage.get('firmware_bin_path') - loaded_integrations = storage.get('loaded_integrations', []) - return StorageJSON(storage_version, name, comment, esphome_version, - src_version, arduino_version, address, esp_platform, board, build_path, - firmware_bin_path, loaded_integrations) + storage_version = storage["storage_version"] + name = storage.get("name") + comment = storage.get("comment") + esphome_version = storage.get( + "esphome_version", storage.get("esphomeyaml_version") + ) + src_version = storage.get("src_version") + arduino_version = storage.get("arduino_version") + address = storage.get("address") + esp_platform = storage.get("esp_platform") + board = storage.get("board") + build_path = storage.get("build_path") + firmware_bin_path = storage.get("firmware_bin_path") + loaded_integrations = storage.get("loaded_integrations", []) + return StorageJSON( + storage_version, + name, + comment, + esphome_version, + src_version, + arduino_version, + address, + esp_platform, + board, + build_path, + firmware_bin_path, + loaded_integrations, + ) @staticmethod def load(path): # type: (str) -> Optional[StorageJSON] @@ -155,8 +182,9 @@ class StorageJSON: class EsphomeStorageJSON: - def __init__(self, storage_version, cookie_secret, last_update_check, - remote_version): + def __init__( + self, storage_version, cookie_secret, last_update_check, remote_version + ): # Version of the storage JSON schema assert storage_version is None or isinstance(storage_version, int) self.storage_version = storage_version # type: int @@ -169,10 +197,10 @@ class EsphomeStorageJSON: def as_dict(self): # type: () -> dict return { - 'storage_version': self.storage_version, - 'cookie_secret': self.cookie_secret, - 'last_update_check': self.last_update_check_str, - 'remote_version': self.remote_version, + "storage_version": self.storage_version, + "cookie_secret": self.cookie_secret, + "last_update_check": self.last_update_check_str, + "remote_version": self.remote_version, } @property @@ -187,21 +215,22 @@ class EsphomeStorageJSON: self.last_update_check_str = new.strftime("%Y-%m-%dT%H:%M:%S") def to_json(self): # type: () -> dict - return json.dumps(self.as_dict(), indent=2) + '\n' + return json.dumps(self.as_dict(), indent=2) + "\n" def save(self, path): # type: (str) -> None write_file_if_changed(path, self.to_json()) @staticmethod def _load_impl(path): # type: (str) -> Optional[EsphomeStorageJSON] - with codecs.open(path, 'r', encoding='utf-8') as f_handle: + with codecs.open(path, "r", encoding="utf-8") as f_handle: storage = json.load(f_handle) - storage_version = storage['storage_version'] - cookie_secret = storage.get('cookie_secret') - last_update_check = storage.get('last_update_check') - remote_version = storage.get('remote_version') - return EsphomeStorageJSON(storage_version, cookie_secret, last_update_check, - remote_version) + storage_version = storage["storage_version"] + cookie_secret = storage.get("cookie_secret") + last_update_check = storage.get("last_update_check") + remote_version = storage.get("remote_version") + return EsphomeStorageJSON( + storage_version, cookie_secret, last_update_check, remote_version + ) @staticmethod def load(path): # type: (str) -> Optional[EsphomeStorageJSON] diff --git a/esphome/util.py b/esphome/util.py index 3fe7ba8eb8..10f9923c44 100644 --- a/esphome/util.py +++ b/esphome/util.py @@ -24,11 +24,13 @@ class RegistryEntry: @property def coroutine_fun(self): from esphome.core import coroutine + return coroutine(self.fun) @property def schema(self): from esphome.config_validation import Schema + return Schema(self.raw_schema) @@ -60,7 +62,7 @@ def safe_print(message=""): if CORE.dashboard: try: - message = message.replace('\033', '\\033') + message = message.replace("\033", "\\033") except UnicodeEncodeError: pass @@ -71,10 +73,10 @@ def safe_print(message=""): pass try: - print(message.encode('utf-8', 'backslashreplace')) + print(message.encode("utf-8", "backslashreplace")) except UnicodeEncodeError: try: - print(message.encode('ascii', 'backslashreplace')) + print(message.encode("ascii", "backslashreplace")) except UnicodeEncodeError: print("Cannot print line because of invalid locale!") @@ -82,13 +84,13 @@ def safe_print(message=""): def shlex_quote(s): if not s: return "''" - if re.search(r'[^\w@%+=:,./-]', s) is None: + if re.search(r"[^\w@%+=:,./-]", s) is None: return s return "'" + s.replace("'", "'\"'\"'") + "'" -ANSI_ESCAPE = re.compile(r'\033[@-_][0-?]*[ -/]*[@-~]') +ANSI_ESCAPE = re.compile(r"\033[@-_][0-?]*[ -/]*[@-~]") class RedirectText: @@ -97,9 +99,9 @@ class RedirectText: if filter_lines is None: self._filter_pattern = None else: - pattern = r'|'.join(r'(?:' + pattern + r')' for pattern in filter_lines) + pattern = r"|".join(r"(?:" + pattern + r")" for pattern in filter_lines) self._filter_pattern = re.compile(pattern) - self._line_buffer = '' + self._line_buffer = "" def __getattr__(self, item): return getattr(self._out, item) @@ -112,7 +114,7 @@ class RedirectText: # work. The shell we create in the dashboard is not a tty, so python removes # all color codes from the resulting stream. We just convert them to something # we can easily recognize later here. - s = s.replace('\033', '\\033') + s = s.replace("\033", "\\033") self._out.write(s) def write(self, s): @@ -128,13 +130,13 @@ class RedirectText: self._line_buffer += s lines = self._line_buffer.splitlines(True) for line in lines: - if '\n' not in line and '\r' not in line: + if "\n" not in line and "\r" not in line: # Not a complete line, set line buffer self._line_buffer = line break - self._line_buffer = '' + self._line_buffer = "" - line_without_ansi = ANSI_ESCAPE.sub('', line) + line_without_ansi = ANSI_ESCAPE.sub("", line) line_without_end = line_without_ansi.rstrip() if self._filter_pattern.match(line_without_end) is not None: # Filter pattern matched, ignore the line @@ -154,9 +156,9 @@ class RedirectText: return True -def run_external_command(func, *cmd, - capture_stdout: bool = False, - filter_lines: str = None) -> Union[int, str]: +def run_external_command( + func, *cmd, capture_stdout: bool = False, filter_lines: str = None +) -> Union[int, str]: """ Run a function from an external package that acts like a main method. @@ -169,12 +171,13 @@ def run_external_command(func, *cmd, :return: str if `capture_stdout` is set else int exit code. """ + def mock_exit(return_code): raise SystemExit(return_code) orig_argv = sys.argv orig_exit = sys.exit # mock sys.exit - full_cmd = ' '.join(shlex_quote(x) for x in cmd) + full_cmd = " ".join(shlex_quote(x) for x in cmd) _LOGGER.info("Running: %s", full_cmd) orig_stdout = sys.stdout @@ -210,11 +213,11 @@ def run_external_command(func, *cmd, def run_external_process(*cmd, **kwargs): - full_cmd = ' '.join(shlex_quote(x) for x in cmd) + full_cmd = " ".join(shlex_quote(x) for x in cmd) _LOGGER.info("Running: %s", full_cmd) - filter_lines = kwargs.get('filter_lines') + filter_lines = kwargs.get("filter_lines") - capture_stdout = kwargs.get('capture_stdout', False) + capture_stdout = kwargs.get("capture_stdout", False) if capture_stdout: sub_stdout = io.BytesIO() else: @@ -223,9 +226,7 @@ def run_external_process(*cmd, **kwargs): sub_stderr = RedirectText(sys.stderr, filter_lines=filter_lines) try: - return subprocess.call(cmd, - stdout=sub_stdout, - stderr=sub_stderr) + return subprocess.call(cmd, stdout=sub_stdout, stderr=sub_stderr) except Exception as err: # pylint: disable=broad-except _LOGGER.error("Running command failed: %s", err) _LOGGER.error("Please try running %s locally.", full_cmd) @@ -237,7 +238,7 @@ def run_external_process(*cmd, **kwargs): def is_dev_esphome_version(): - return 'dev' in const.__version__ + return "dev" in const.__version__ # Custom OrderedDict with nicer repr method for debugging @@ -253,9 +254,9 @@ def list_yaml_files(folder): def filter_yaml_files(files): - files = [f for f in files if os.path.splitext(f)[1] == '.yaml'] - files = [f for f in files if os.path.basename(f) != 'secrets.yaml'] - files = [f for f in files if not os.path.basename(f).startswith('.')] + files = [f for f in files if os.path.splitext(f)[1] == ".yaml"] + files = [f for f in files if os.path.basename(f) != "secrets.yaml"] + files = [f for f in files if not os.path.basename(f).startswith(".")] return files @@ -268,6 +269,7 @@ class SerialPort: # from https://github.com/pyserial/pyserial/blob/master/serial/tools/list_ports.py def get_serial_ports() -> List[SerialPort]: from serial.tools.list_ports import comports + result = [] for port, desc, info in comports(include_links=True): if not port: @@ -277,13 +279,13 @@ def get_serial_ports() -> List[SerialPort]: # Also add objects in /dev/serial/by-id/ # ref: https://github.com/esphome/issues/issues/1346 - by_id_path = Path('/dev/serial/by-id') - if sys.platform.lower().startswith('linux') and by_id_path.exists(): + by_id_path = Path("/dev/serial/by-id") + if sys.platform.lower().startswith("linux") and by_id_path.exists(): from serial.tools.list_ports_linux import SysFS - for path in by_id_path.glob('*'): + for path in by_id_path.glob("*"): device = SysFS(path) - if device.subsystem == 'platform': + if device.subsystem == "platform": result.append(SerialPort(path=str(path), description=info[1])) result.sort(key=lambda x: x.path) diff --git a/esphome/voluptuous_schema.py b/esphome/voluptuous_schema.py index d10801fb95..86c20510aa 100644 --- a/esphome/voluptuous_schema.py +++ b/esphome/voluptuous_schema.py @@ -6,7 +6,7 @@ import voluptuous as vol class ExtraKeysInvalid(vol.Invalid): def __init__(self, *arg, **kwargs): - self.candidates = kwargs.pop('candidates') + self.candidates = kwargs.pop("candidates") vol.Invalid.__init__(self, *arg, **kwargs) @@ -19,7 +19,10 @@ def ensure_multiple_invalid(err): # pylint: disable=protected-access, unidiomatic-typecheck class _Schema(vol.Schema): """Custom cv.Schema that prints similar keys on error.""" - def __init__(self, schema, required=False, extra=vol.PREVENT_EXTRA, extra_schemas=None): + + def __init__( + self, schema, required=False, extra=vol.PREVENT_EXTRA, extra_schemas=None + ): super().__init__(schema, required=required, extra=extra) # List of extra schemas to apply after validation # Should be used sparingly, as it's not a very voluptuous-way/clean way of @@ -37,7 +40,7 @@ class _Schema(vol.Schema): return res def _compile_mapping(self, schema, invalid_msg=None): - invalid_msg = invalid_msg or 'mapping value' + invalid_msg = invalid_msg or "mapping value" # Check some things that ESPHome's schemas do not allow # mostly to keep the logic in this method sane (so these may be re-added if needed). @@ -47,7 +50,9 @@ class _Schema(vol.Schema): if isinstance(key, vol.Remove): raise ValueError("ESPHome does not allow vol.Remove") if isinstance(key, vol.primitive_types): - raise ValueError("All schema keys must be wrapped in cv.Required or cv.Optional") + raise ValueError( + "All schema keys must be wrapped in cv.Required or cv.Optional" + ) # Keys that may be required all_required_keys = {key for key in schema if isinstance(key, vol.Required)} @@ -64,7 +69,9 @@ class _Schema(vol.Schema): _compiled_schema[skey] = (new_key, new_value) # Sort compiled schema (probably not necessary for esphome, but leave it here just in case) - candidates = list(vol.schema_builder._iterate_mapping_candidates(_compiled_schema)) + candidates = list( + vol.schema_builder._iterate_mapping_candidates(_compiled_schema) + ) # After we have the list of candidates in the correct order, we want to apply some # optimization so that each @@ -75,8 +82,13 @@ class _Schema(vol.Schema): for skey, (ckey, cvalue) in candidates: if type(skey) in vol.primitive_types: candidates_by_key.setdefault(skey, []).append((skey, (ckey, cvalue))) - elif isinstance(skey, vol.Marker) and type(skey.schema) in vol.primitive_types: - candidates_by_key.setdefault(skey.schema, []).append((skey, (ckey, cvalue))) + elif ( + isinstance(skey, vol.Marker) + and type(skey.schema) in vol.primitive_types + ): + candidates_by_key.setdefault(skey.schema, []).append( + (skey, (ckey, cvalue)) + ) else: # These are wildcards such as 'int', 'str', 'Remove' and others which should be # applied to all keys @@ -101,7 +113,10 @@ class _Schema(vol.Schema): # Insert default values for non-existing keys. for key in all_default_keys: - if not isinstance(key.default, vol.Undefined) and key.schema not in key_value_map: + if ( + not isinstance(key.default, vol.Undefined) + and key.schema not in key_value_map + ): # A default value has been specified for this missing key, insert it. key_value_map[key.schema] = key.default() @@ -110,8 +125,9 @@ class _Schema(vol.Schema): for key, value in key_value_map.items(): key_path = path + [key] # Optimization. Validate against the matching key first, then fallback to the rest - relevant_candidates = itertools.chain(candidates_by_key.get(key, []), - additional_candidates) + relevant_candidates = itertools.chain( + candidates_by_key.get(key, []), additional_candidates + ) # compare each given key/value against all compiled key/values # schema key, (compiled key, compiled value) @@ -158,14 +174,21 @@ class _Schema(vol.Schema): elif self.extra != vol.REMOVE_EXTRA: if isinstance(key, str) and key_names: matches = difflib.get_close_matches(key, key_names) - errors.append(ExtraKeysInvalid('extra keys not allowed', key_path, - candidates=matches)) + errors.append( + ExtraKeysInvalid( + "extra keys not allowed", + key_path, + candidates=matches, + ) + ) else: - errors.append(vol.Invalid('extra keys not allowed', key_path)) + errors.append( + vol.Invalid("extra keys not allowed", key_path) + ) # for any required keys left that weren't found and don't have defaults: for key in required_keys: - msg = getattr(key, 'msg', None) or 'required key not provided' + msg = getattr(key, "msg", None) or "required key not provided" errors.append(vol.RequiredFieldInvalid(msg, path + [key])) if errors: raise vol.MultipleInvalid(errors) @@ -181,7 +204,7 @@ class _Schema(vol.Schema): # pylint: disable=signature-differs def extend(self, *schemas, **kwargs): - extra = kwargs.pop('extra', None) + extra = kwargs.pop("extra", None) if kwargs: raise ValueError if not schemas: diff --git a/esphome/vscode.py b/esphome/vscode.py index 8782ed6e5c..6e1a0270be 100644 --- a/esphome/vscode.py +++ b/esphome/vscode.py @@ -20,11 +20,11 @@ def _dump_range(range): if range is None: return None return { - 'document': range.start_mark.document, - 'start_line': range.start_mark.line, - 'start_col': range.start_mark.column, - 'end_line': range.end_mark.line, - 'end_col': range.end_mark.column, + "document": range.start_mark.document, + "start_line": range.start_mark.line, + "start_col": range.start_mark.column, + "end_line": range.end_mark.line, + "end_col": range.end_mark.column, } @@ -34,36 +34,42 @@ class VSCodeResult: self.validation_errors = [] def dump(self): - return json.dumps({ - 'type': 'result', - 'yaml_errors': self.yaml_errors, - 'validation_errors': self.validation_errors, - }) + return json.dumps( + { + "type": "result", + "yaml_errors": self.yaml_errors, + "validation_errors": self.validation_errors, + } + ) def add_yaml_error(self, message): - self.yaml_errors.append({ - 'message': message, - }) + self.yaml_errors.append( + { + "message": message, + } + ) def add_validation_error(self, range_, message): - self.validation_errors.append({ - 'range': _dump_range(range_), - 'message': message, - }) + self.validation_errors.append( + { + "range": _dump_range(range_), + "message": message, + } + ) def read_config(args): while True: CORE.reset() data = json.loads(input()) - assert data['type'] == 'validate' + assert data["type"] == "validate" CORE.vscode = True CORE.ace = args.ace - f = data['file'] + f = data["file"] if CORE.ace: CORE.config_path = os.path.join(args.configuration[0], f) else: - CORE.config_path = data['file'] + CORE.config_path = data["file"] vs = VSCodeResult() try: res = load_config(dict(args.substitution) if args.substitution else {}) diff --git a/esphome/wizard.py b/esphome/wizard.py index 9b6237acf3..620ceb9b59 100644 --- a/esphome/wizard.py +++ b/esphome/wizard.py @@ -7,6 +7,7 @@ import voluptuous as vol import esphome.config_validation as cv from esphome.helpers import color, get_bool_env, write_file + # pylint: disable=anomalous-backslash-in-string from esphome.pins import ESP32_BOARD_PINS, ESP8266_BOARD_PINS from esphome.storage_json import StorageJSON, ext_storage_path @@ -67,22 +68,24 @@ api: def sanitize_double_quotes(value): - return value.replace('\\', '\\\\').replace('"', '\\"') + return value.replace("\\", "\\\\").replace('"', '\\"') def wizard_file(**kwargs): letters = string.ascii_letters + string.digits - ap_name_base = kwargs['name'].replace('_', ' ').title() + ap_name_base = kwargs["name"].replace("_", " ").title() ap_name = f"{ap_name_base} Fallback Hotspot" if len(ap_name) > 32: ap_name = ap_name_base - kwargs['fallback_name'] = ap_name - kwargs['fallback_psk'] = ''.join(random.choice(letters) for _ in range(12)) + kwargs["fallback_name"] = ap_name + kwargs["fallback_psk"] = "".join(random.choice(letters) for _ in range(12)) config = BASE_CONFIG.format(**kwargs) - if kwargs['password']: - config += ' password: "{0}"\n\nota:\n password: "{0}"\n'.format(kwargs['password']) + if kwargs["password"]: + config += ' password: "{0}"\n\nota:\n password: "{0}"\n'.format( + kwargs["password"] + ) else: config += "\nota:\n" @@ -90,26 +93,29 @@ def wizard_file(**kwargs): def wizard_write(path, **kwargs): - name = kwargs['name'] - board = kwargs['board'] + name = kwargs["name"] + board = kwargs["board"] - kwargs['ssid'] = sanitize_double_quotes(kwargs['ssid']) - kwargs['psk'] = sanitize_double_quotes(kwargs['psk']) - kwargs['password'] = sanitize_double_quotes(kwargs['password']) + kwargs["ssid"] = sanitize_double_quotes(kwargs["ssid"]) + kwargs["psk"] = sanitize_double_quotes(kwargs["psk"]) + kwargs["password"] = sanitize_double_quotes(kwargs["password"]) - if 'platform' not in kwargs: - kwargs['platform'] = 'ESP8266' if board in ESP8266_BOARD_PINS else 'ESP32' - platform = kwargs['platform'] + if "platform" not in kwargs: + kwargs["platform"] = "ESP8266" if board in ESP8266_BOARD_PINS else "ESP32" + platform = kwargs["platform"] write_file(path, wizard_file(**kwargs)) - storage = StorageJSON.from_wizard(name, name + '.local', platform, board) + storage = StorageJSON.from_wizard(name, name + ".local", platform, board) storage_path = ext_storage_path(os.path.dirname(path), os.path.basename(path)) storage.save(storage_path) if get_bool_env(ENV_QUICKWIZARD): + def sleep(time): pass + + else: from time import sleep @@ -131,18 +137,25 @@ def default_input(text, default): # From https://stackoverflow.com/a/518232/8924614 def strip_accents(value): - return ''.join(c for c in unicodedata.normalize('NFD', str(value)) - if unicodedata.category(c) != 'Mn') + return "".join( + c + for c in unicodedata.normalize("NFD", str(value)) + if unicodedata.category(c) != "Mn" + ) def wizard(path): - if not path.endswith('.yaml') and not path.endswith('.yml'): - safe_print("Please make your configuration file {} have the extension .yaml or .yml" - "".format(color('cyan', path))) + if not path.endswith(".yaml") and not path.endswith(".yml"): + safe_print( + "Please make your configuration file {} have the extension .yaml or .yml" + "".format(color("cyan", path)) + ) return 1 if os.path.exists(path): - safe_print("Uh oh, it seems like {} already exists, please delete that file first " - "or chose another configuration file.".format(color('cyan', path))) + safe_print( + "Uh oh, it seems like {} already exists, please delete that file first " + "or chose another configuration file.".format(color("cyan", path)) + ) return 2 safe_print("Hi there!") sleep(1.5) @@ -150,16 +163,25 @@ def wizard(path): sleep(1.25) safe_print("And I'm here to help you get started with ESPHome.") sleep(2.0) - safe_print("In 4 steps I'm going to guide you through creating a basic " - "configuration file for your custom ESP8266/ESP32 firmware. Yay!") + safe_print( + "In 4 steps I'm going to guide you through creating a basic " + "configuration file for your custom ESP8266/ESP32 firmware. Yay!" + ) sleep(3.0) safe_print() safe_print_step(1, CORE_BIG) - safe_print("First up, please choose a " + color('green', 'name') + " for your node.") - safe_print("It should be a unique name that can be used to identify the device later.") + safe_print( + "First up, please choose a " + color("green", "name") + " for your node." + ) + safe_print( + "It should be a unique name that can be used to identify the device later." + ) sleep(1) - safe_print("For example, I like calling the node in my living room {}.".format( - color('bold_white', "livingroom"))) + safe_print( + "For example, I like calling the node in my living room {}.".format( + color("bold_white", "livingroom") + ) + ) safe_print() sleep(1) name = input(color("bold_white", "(name): ")) @@ -169,56 +191,80 @@ def wizard(path): name = cv.valid_name(name) break except vol.Invalid: - safe_print(color("red", f"Oh noes, \"{name}\" isn't a valid name. Names can only " - f"include numbers, lower-case letters, underscores and " - f"hyphens.")) - name = strip_accents(name).lower().replace(' ', '_') - name = ''.join(c for c in name if c in ALLOWED_NAME_CHARS) - safe_print("Shall I use \"{}\" as the name instead?".format(color('cyan', name))) + safe_print( + color( + "red", + f'Oh noes, "{name}" isn\'t a valid name. Names can only ' + f"include numbers, lower-case letters, underscores and " + f"hyphens.", + ) + ) + name = strip_accents(name).lower().replace(" ", "_") + name = "".join(c for c in name if c in ALLOWED_NAME_CHARS) + safe_print( + 'Shall I use "{}" as the name instead?'.format(color("cyan", name)) + ) sleep(0.5) name = default_input("(name [{}]): ", name) - safe_print("Great! Your node is now called \"{}\".".format(color('cyan', name))) + safe_print('Great! Your node is now called "{}".'.format(color("cyan", name))) sleep(1) safe_print_step(2, ESP_BIG) - safe_print("Now I'd like to know what microcontroller you're using so that I can compile " - "firmwares for it.") - safe_print("Are you using an " + color('green', 'ESP32') + " or " + - color('green', 'ESP8266') + " platform? (Choose ESP8266 for Sonoff devices)") + safe_print( + "Now I'd like to know what microcontroller you're using so that I can compile " + "firmwares for it." + ) + safe_print( + "Are you using an " + + color("green", "ESP32") + + " or " + + color("green", "ESP8266") + + " platform? (Choose ESP8266 for Sonoff devices)" + ) while True: sleep(0.5) safe_print() safe_print("Please enter either ESP32 or ESP8266.") platform = input(color("bold_white", "(ESP32/ESP8266): ")) try: - platform = vol.All(vol.Upper, vol.Any('ESP32', 'ESP8266'))(platform) + platform = vol.All(vol.Upper, vol.Any("ESP32", "ESP8266"))(platform) break except vol.Invalid: - safe_print("Unfortunately, I can't find an espressif microcontroller called " - "\"{}\". Please try again.".format(platform)) - safe_print("Thanks! You've chosen {} as your platform.".format(color('cyan', platform))) + safe_print( + "Unfortunately, I can't find an espressif microcontroller called " + '"{}". Please try again.'.format(platform) + ) + safe_print( + "Thanks! You've chosen {} as your platform.".format(color("cyan", platform)) + ) safe_print() sleep(1) - if platform == 'ESP32': - board_link = 'http://docs.platformio.org/en/latest/platforms/espressif32.html#boards' + if platform == "ESP32": + board_link = ( + "http://docs.platformio.org/en/latest/platforms/espressif32.html#boards" + ) else: - board_link = 'http://docs.platformio.org/en/latest/platforms/espressif8266.html#boards' + board_link = ( + "http://docs.platformio.org/en/latest/platforms/espressif8266.html#boards" + ) - safe_print("Next, I need to know what " + color('green', 'board') + " you're using.") + safe_print( + "Next, I need to know what " + color("green", "board") + " you're using." + ) sleep(0.5) - safe_print("Please go to {} and choose a board.".format(color('green', board_link))) - if platform == 'ESP32': - safe_print("(Type " + color('green', 'esp01_1m') + " for Sonoff devices)") + safe_print("Please go to {} and choose a board.".format(color("green", board_link))) + if platform == "ESP32": + safe_print("(Type " + color("green", "esp01_1m") + " for Sonoff devices)") safe_print() # Don't sleep because user needs to copy link - if platform == 'ESP32': - safe_print("For example \"{}\".".format(color("bold_white", 'nodemcu-32s'))) + if platform == "ESP32": + safe_print('For example "{}".'.format(color("bold_white", "nodemcu-32s"))) boards = list(ESP32_BOARD_PINS.keys()) else: - safe_print("For example \"{}\".".format(color("bold_white", 'nodemcuv2'))) + safe_print('For example "{}".'.format(color("bold_white", "nodemcuv2"))) boards = list(ESP8266_BOARD_PINS.keys()) - safe_print("Options: {}".format(', '.join(sorted(boards)))) + safe_print("Options: {}".format(", ".join(sorted(boards)))) while True: board = input(color("bold_white", "(board): ")) @@ -226,69 +272,102 @@ def wizard(path): board = vol.All(vol.Lower, vol.Any(*boards))(board) break except vol.Invalid: - safe_print(color('red', f"Sorry, I don't think the board \"{board}\" exists.")) + safe_print( + color("red", f'Sorry, I don\'t think the board "{board}" exists.') + ) safe_print() sleep(0.25) safe_print() - safe_print("Way to go! You've chosen {} as your board.".format(color('cyan', board))) + safe_print( + "Way to go! You've chosen {} as your board.".format(color("cyan", board)) + ) safe_print() sleep(1) safe_print_step(3, WIFI_BIG) - safe_print("In this step, I'm going to create the configuration for " - "WiFi.") + safe_print("In this step, I'm going to create the configuration for " "WiFi.") safe_print() sleep(1) - safe_print("First, what's the " + color('green', 'SSID') + - f" (the name) of the WiFi network {name} I should connect to?") + safe_print( + "First, what's the " + + color("green", "SSID") + + f" (the name) of the WiFi network {name} I should connect to?" + ) sleep(1.5) - safe_print("For example \"{}\".".format(color('bold_white', "Abraham Linksys"))) + safe_print('For example "{}".'.format(color("bold_white", "Abraham Linksys"))) while True: - ssid = input(color('bold_white', "(ssid): ")) + ssid = input(color("bold_white", "(ssid): ")) try: ssid = cv.ssid(ssid) break except vol.Invalid: - safe_print(color('red', "Unfortunately, \"{}\" doesn't seem to be a valid SSID. " - "Please try again.".format(ssid))) + safe_print( + color( + "red", + 'Unfortunately, "{}" doesn\'t seem to be a valid SSID. ' + "Please try again.".format(ssid), + ) + ) safe_print() sleep(1) - safe_print("Thank you very much! You've just chosen \"{}\" as your SSID." - "".format(color('cyan', ssid))) + safe_print( + 'Thank you very much! You\'ve just chosen "{}" as your SSID.' + "".format(color("cyan", ssid)) + ) safe_print() sleep(0.75) - safe_print("Now please state the " + color('green', 'password') + - " of the WiFi network so that I can connect to it (Leave empty for no password)") + safe_print( + "Now please state the " + + color("green", "password") + + " of the WiFi network so that I can connect to it (Leave empty for no password)" + ) safe_print() - safe_print("For example \"{}\"".format(color('bold_white', 'PASSWORD42'))) + safe_print('For example "{}"'.format(color("bold_white", "PASSWORD42"))) sleep(0.5) - psk = input(color('bold_white', '(PSK): ')) - safe_print("Perfect! WiFi is now set up (you can create static IPs and so on later).") + psk = input(color("bold_white", "(PSK): ")) + safe_print( + "Perfect! WiFi is now set up (you can create static IPs and so on later)." + ) sleep(1.5) safe_print_step(4, OTA_BIG) - safe_print("Almost there! ESPHome can automatically upload custom firmwares over WiFi " - "(over the air) and integrates into Home Assistant with a native API.") - safe_print("This can be insecure if you do not trust the WiFi network. Do you want to set " - "a " + color('green', 'password') + " for connecting to this ESP?") + safe_print( + "Almost there! ESPHome can automatically upload custom firmwares over WiFi " + "(over the air) and integrates into Home Assistant with a native API." + ) + safe_print( + "This can be insecure if you do not trust the WiFi network. Do you want to set " + "a " + color("green", "password") + " for connecting to this ESP?" + ) safe_print() sleep(0.25) safe_print("Press ENTER for no password") - password = input(color('bold_white', '(password): ')) + password = input(color("bold_white", "(password): ")) - wizard_write(path=path, name=name, platform=platform, board=board, - ssid=ssid, psk=psk, password=password) + wizard_write( + path=path, + name=name, + platform=platform, + board=board, + ssid=ssid, + psk=psk, + password=password, + ) safe_print() - safe_print(color('cyan', "DONE! I've now written a new configuration file to ") + - color('bold_cyan', path)) + safe_print( + color("cyan", "DONE! I've now written a new configuration file to ") + + color("bold_cyan", path) + ) safe_print() safe_print("Next steps:") - safe_print(" > Check your Home Assistant \"integrations\" screen. If all goes well, you " - "should see your ESP being discovered automatically.") + safe_print( + ' > Check your Home Assistant "integrations" screen. If all goes well, you ' + "should see your ESP being discovered automatically." + ) safe_print(" > Then follow the rest of the getting started guide:") safe_print(" > https://esphome.io/guides/getting_started_command_line.html") return 0 diff --git a/esphome/writer.py b/esphome/writer.py index 237f0fb4b7..ec772b5127 100644 --- a/esphome/writer.py +++ b/esphome/writer.py @@ -3,30 +3,46 @@ import os import re from esphome.config import iter_components -from esphome.const import CONF_BOARD_FLASH_MODE, CONF_ESPHOME, CONF_PLATFORMIO_OPTIONS, \ - HEADER_FILE_EXTENSIONS, SOURCE_FILE_EXTENSIONS, __version__, ARDUINO_VERSION_ESP8266, \ - ENV_NOGITIGNORE +from esphome.const import ( + CONF_BOARD_FLASH_MODE, + CONF_ESPHOME, + CONF_PLATFORMIO_OPTIONS, + HEADER_FILE_EXTENSIONS, + SOURCE_FILE_EXTENSIONS, + __version__, + ARDUINO_VERSION_ESP8266, + ENV_NOGITIGNORE, +) from esphome.core import CORE, EsphomeError -from esphome.helpers import mkdir_p, read_file, write_file_if_changed, walk_files, \ - copy_file_if_changed, get_bool_env +from esphome.helpers import ( + mkdir_p, + read_file, + write_file_if_changed, + walk_files, + copy_file_if_changed, + get_bool_env, +) from esphome.storage_json import StorageJSON, storage_path from esphome.pins import ESP8266_FLASH_SIZES, ESP8266_LD_SCRIPTS _LOGGER = logging.getLogger(__name__) -CPP_AUTO_GENERATE_BEGIN = '// ========== AUTO GENERATED CODE BEGIN ===========' -CPP_AUTO_GENERATE_END = '// =========== AUTO GENERATED CODE END ============' -CPP_INCLUDE_BEGIN = '// ========== AUTO GENERATED INCLUDE BLOCK BEGIN ===========' -CPP_INCLUDE_END = '// ========== AUTO GENERATED INCLUDE BLOCK END ===========' -INI_AUTO_GENERATE_BEGIN = '; ========== AUTO GENERATED CODE BEGIN ===========' -INI_AUTO_GENERATE_END = '; =========== AUTO GENERATED CODE END ============' +CPP_AUTO_GENERATE_BEGIN = "// ========== AUTO GENERATED CODE BEGIN ===========" +CPP_AUTO_GENERATE_END = "// =========== AUTO GENERATED CODE END ============" +CPP_INCLUDE_BEGIN = "// ========== AUTO GENERATED INCLUDE BLOCK BEGIN ===========" +CPP_INCLUDE_END = "// ========== AUTO GENERATED INCLUDE BLOCK END ===========" +INI_AUTO_GENERATE_BEGIN = "; ========== AUTO GENERATED CODE BEGIN ===========" +INI_AUTO_GENERATE_END = "; =========== AUTO GENERATED CODE END ============" -CPP_BASE_FORMAT = ("""// Auto generated code by esphome -""", """" +CPP_BASE_FORMAT = ( + """// Auto generated code by esphome +""", + """" void setup() { // ===== DO NOT EDIT ANYTHING BELOW THIS LINE ===== - """, """ + """, + """ // ========= YOU CAN EDIT AFTER THIS LINE ========= App.setup(); } @@ -34,9 +50,11 @@ void setup() { void loop() { App.loop(); } -""") +""", +) -INI_BASE_FORMAT = ("""; Auto generated code by esphome +INI_BASE_FORMAT = ( + """; Auto generated code by esphome [common] lib_deps = @@ -44,13 +62,15 @@ build_flags = upload_flags = ; ===== DO NOT EDIT ANYTHING BELOW THIS LINE ===== -""", """ +""", + """ ; ========= YOU CAN EDIT AFTER THIS LINE ========= -""") +""", +) UPLOAD_SPEED_OVERRIDE = { - 'esp210': 57600, + "esp210": 57600, } @@ -62,10 +82,9 @@ def get_flags(key): def get_include_text(): - include_text = '#include "esphome.h"\n' \ - 'using namespace esphome;\n' + include_text = '#include "esphome.h"\nusing namespace esphome;\n' for _, component, conf in iter_components(CORE.config): - if not hasattr(component, 'includes'): + if not hasattr(component, "includes"): continue includes = component.includes if callable(includes): @@ -73,10 +92,10 @@ def get_include_text(): if includes is None: continue if isinstance(includes, list): - includes = '\n'.join(includes) + includes = "\n".join(includes) if not includes: continue - include_text += includes + '\n' + include_text += includes + "\n" return include_text @@ -86,7 +105,7 @@ def replace_file_content(text, pattern, repl): def migrate_src_version_0_to_1(): - main_cpp = CORE.relative_build_path('src', 'main.cpp') + main_cpp = CORE.relative_build_path("src", "main.cpp") if not os.path.isfile(main_cpp): return @@ -95,22 +114,35 @@ def migrate_src_version_0_to_1(): if CPP_INCLUDE_BEGIN in content: return - content, count = replace_file_content(content, r'\s*delay\((?:16|20)\);', '') + content, count = replace_file_content(content, r"\s*delay\((?:16|20)\);", "") if count != 0: - _LOGGER.info("Migration: Removed %s occurrence of 'delay(16);' in %s", count, main_cpp) + _LOGGER.info( + "Migration: Removed %s occurrence of 'delay(16);' in %s", count, main_cpp + ) - content, count = replace_file_content(content, r'using namespace esphomelib;', '') + content, count = replace_file_content(content, r"using namespace esphomelib;", "") if count != 0: - _LOGGER.info("Migration: Removed %s occurrence of 'using namespace esphomelib;' " - "in %s", count, main_cpp) + _LOGGER.info( + "Migration: Removed %s occurrence of 'using namespace esphomelib;' " + "in %s", + count, + main_cpp, + ) if CPP_INCLUDE_BEGIN not in content: - content, count = replace_file_content(content, r'#include "esphomelib/application.h"', - CPP_INCLUDE_BEGIN + '\n' + CPP_INCLUDE_END) + content, count = replace_file_content( + content, + r'#include "esphomelib/application.h"', + CPP_INCLUDE_BEGIN + "\n" + CPP_INCLUDE_END, + ) if count == 0: - _LOGGER.error("Migration failed. ESPHome 1.10.0 needs to have a new auto-generated " - "include section in the %s file. Please remove %s and let it be " - "auto-generated again.", main_cpp, main_cpp) + _LOGGER.error( + "Migration failed. ESPHome 1.10.0 needs to have a new auto-generated " + "include section in the %s file. Please remove %s and let it be " + "auto-generated again.", + main_cpp, + main_cpp, + ) _LOGGER.info("Migration: Added include section to %s", main_cpp) write_file_if_changed(main_cpp, content) @@ -160,14 +192,14 @@ def update_storage_json(): def format_ini(data): - content = '' + content = "" for key, value in sorted(data.items()): if isinstance(value, (list, set, tuple)): - content += f'{key} =\n' + content += f"{key} =\n" for x in value: - content += f' {x}\n' + content += f" {x}\n" else: - content += f'{key} = {value}\n' + content += f"{key} = {value}\n" return content @@ -197,23 +229,23 @@ def get_ini_content(): build_flags = gather_build_flags() data = { - 'platform': CORE.arduino_version, - 'board': CORE.board, - 'framework': 'arduino', - 'lib_deps': lib_deps + ['${common.lib_deps}'], - 'build_flags': build_flags + ['${common.build_flags}'], - 'upload_speed': UPLOAD_SPEED_OVERRIDE.get(CORE.board, 115200), + "platform": CORE.arduino_version, + "board": CORE.board, + "framework": "arduino", + "lib_deps": lib_deps + ["${common.lib_deps}"], + "build_flags": build_flags + ["${common.build_flags}"], + "upload_speed": UPLOAD_SPEED_OVERRIDE.get(CORE.board, 115200), } if CORE.is_esp32: - data['board_build.partitions'] = "partitions.csv" - partitions_csv = CORE.relative_build_path('partitions.csv') + data["board_build.partitions"] = "partitions.csv" + partitions_csv = CORE.relative_build_path("partitions.csv") write_file_if_changed(partitions_csv, ESP32_LARGE_PARTITIONS_CSV) # pylint: disable=unsubscriptable-object if CONF_BOARD_FLASH_MODE in CORE.config[CONF_ESPHOME]: flash_mode = CORE.config[CONF_ESPHOME][CONF_BOARD_FLASH_MODE] - data['board_build.flash_mode'] = flash_mode + data["board_build.flash_mode"] = flash_mode # Build flags if CORE.is_esp8266 and CORE.board in ESP8266_FLASH_SIZES: @@ -221,11 +253,11 @@ def get_ini_content(): ld_scripts = ESP8266_LD_SCRIPTS[flash_size] versions_with_old_ldscripts = [ - ARDUINO_VERSION_ESP8266['2.4.0'], - ARDUINO_VERSION_ESP8266['2.4.1'], - ARDUINO_VERSION_ESP8266['2.4.2'], + ARDUINO_VERSION_ESP8266["2.4.0"], + ARDUINO_VERSION_ESP8266["2.4.1"], + ARDUINO_VERSION_ESP8266["2.4.2"], ] - if CORE.arduino_version == ARDUINO_VERSION_ESP8266['2.3.0']: + if CORE.arduino_version == ARDUINO_VERSION_ESP8266["2.3.0"]: # No ld script support ld_script = None if CORE.arduino_version in versions_with_old_ldscripts: @@ -235,14 +267,14 @@ def get_ini_content(): ld_script = ld_scripts[1] if ld_script is not None: - data['board_build.ldscript'] = ld_script + data["board_build.ldscript"] = ld_script # Ignore libraries that are not explicitly used, but may # be added by LDF # data['lib_ldf_mode'] = 'chain' data.update(CORE.config[CONF_ESPHOME].get(CONF_PLATFORMIO_OPTIONS, {})) - content = f'[env:{CORE.name}]\n' + content = f"[env:{CORE.name}]\n" content += format_ini(data) return content @@ -251,32 +283,42 @@ def get_ini_content(): def find_begin_end(text, begin_s, end_s): begin_index = text.find(begin_s) if begin_index == -1: - raise EsphomeError("Could not find auto generated code begin in file, either " - "delete the main sketch file or insert the comment again.") + raise EsphomeError( + "Could not find auto generated code begin in file, either " + "delete the main sketch file or insert the comment again." + ) if text.find(begin_s, begin_index + 1) != -1: - raise EsphomeError("Found multiple auto generate code begins, don't know " - "which to chose, please remove one of them.") + raise EsphomeError( + "Found multiple auto generate code begins, don't know " + "which to chose, please remove one of them." + ) end_index = text.find(end_s) if end_index == -1: - raise EsphomeError("Could not find auto generated code end in file, either " - "delete the main sketch file or insert the comment again.") + raise EsphomeError( + "Could not find auto generated code end in file, either " + "delete the main sketch file or insert the comment again." + ) if text.find(end_s, end_index + 1) != -1: - raise EsphomeError("Found multiple auto generate code endings, don't know " - "which to chose, please remove one of them.") + raise EsphomeError( + "Found multiple auto generate code endings, don't know " + "which to chose, please remove one of them." + ) - return text[:begin_index], text[(end_index + len(end_s)):] + return text[:begin_index], text[(end_index + len(end_s)) :] def write_platformio_ini(content): update_storage_json() - path = CORE.relative_build_path('platformio.ini') + path = CORE.relative_build_path("platformio.ini") if os.path.isfile(path): text = read_file(path) - content_format = find_begin_end(text, INI_AUTO_GENERATE_BEGIN, INI_AUTO_GENERATE_END) + content_format = find_begin_end( + text, INI_AUTO_GENERATE_BEGIN, INI_AUTO_GENERATE_END + ) else: content_format = INI_BASE_FORMAT - full_file = content_format[0] + INI_AUTO_GENERATE_BEGIN + '\n' + content + full_file = content_format[0] + INI_AUTO_GENERATE_BEGIN + "\n" + content full_file += INI_AUTO_GENERATE_END + content_format[1] write_file_if_changed(path, full_file) @@ -298,8 +340,8 @@ VERSION_H_FORMAT = """\ #pragma once #define ESPHOME_VERSION "{}" """ -DEFINES_H_TARGET = 'esphome/core/defines.h' -VERSION_H_TARGET = 'esphome/core/version.h' +DEFINES_H_TARGET = "esphome/core/defines.h" +VERSION_H_TARGET = "esphome/core/version.h" ESPHOME_README_TXT = """ THIS DIRECTORY IS AUTO-GENERATED, DO NOT MODIFY @@ -326,18 +368,20 @@ def copy_src_tree(): for target, path in source_files_l: if os.path.splitext(path)[1] in HEADER_FILE_EXTENSIONS: include_l.append(f'#include "{target}"') - include_l.append('') - include_s = '\n'.join(include_l) + include_l.append("") + include_s = "\n".join(include_l) source_files_copy = source_files.copy() source_files_copy.pop(DEFINES_H_TARGET) - for path in walk_files(CORE.relative_src_path('esphome')): + for path in walk_files(CORE.relative_src_path("esphome")): if os.path.splitext(path)[1] not in SOURCE_FILE_EXTENSIONS: # Not a source file, ignore continue # Transform path to target path name - target = os.path.relpath(path, CORE.relative_src_path()).replace(os.path.sep, '/') + target = os.path.relpath(path, CORE.relative_src_path()).replace( + os.path.sep, "/" + ) if target in (DEFINES_H_TARGET, VERSION_H_TARGET): # Ignore defines.h, will be dealt with later continue @@ -350,32 +394,41 @@ def copy_src_tree(): # Now copy new files for target, src_path in source_files_copy.items(): - dst_path = CORE.relative_src_path(*target.split('/')) + dst_path = CORE.relative_src_path(*target.split("/")) copy_file_if_changed(src_path, dst_path) # Finally copy defines - write_file_if_changed(CORE.relative_src_path('esphome', 'core', 'defines.h'), - generate_defines_h()) - write_file_if_changed(CORE.relative_src_path('esphome', 'README.txt'), - ESPHOME_README_TXT) - write_file_if_changed(CORE.relative_src_path('esphome.h'), - ESPHOME_H_FORMAT.format(include_s)) - write_file_if_changed(CORE.relative_src_path('esphome', 'core', 'version.h'), - VERSION_H_FORMAT.format(__version__)) + write_file_if_changed( + CORE.relative_src_path("esphome", "core", "defines.h"), generate_defines_h() + ) + write_file_if_changed( + CORE.relative_src_path("esphome", "README.txt"), ESPHOME_README_TXT + ) + write_file_if_changed( + CORE.relative_src_path("esphome.h"), ESPHOME_H_FORMAT.format(include_s) + ) + write_file_if_changed( + CORE.relative_src_path("esphome", "core", "version.h"), + VERSION_H_FORMAT.format(__version__), + ) def generate_defines_h(): define_content_l = [x.as_macro for x in CORE.defines] define_content_l.sort() - return DEFINES_H_FORMAT.format('\n'.join(define_content_l)) + return DEFINES_H_FORMAT.format("\n".join(define_content_l)) def write_cpp(code_s): - path = CORE.relative_src_path('main.cpp') + path = CORE.relative_src_path("main.cpp") if os.path.isfile(path): text = read_file(path) - code_format = find_begin_end(text, CPP_AUTO_GENERATE_BEGIN, CPP_AUTO_GENERATE_END) - code_format_ = find_begin_end(code_format[0], CPP_INCLUDE_BEGIN, CPP_INCLUDE_END) + code_format = find_begin_end( + text, CPP_AUTO_GENERATE_BEGIN, CPP_AUTO_GENERATE_END + ) + code_format_ = find_begin_end( + code_format[0], CPP_INCLUDE_BEGIN, CPP_INCLUDE_END + ) code_format = (code_format_[0], code_format_[1], code_format[1]) else: code_format = CPP_BASE_FORMAT @@ -384,8 +437,10 @@ def write_cpp(code_s): global_s = '#include "esphome.h"\n' global_s += CORE.cpp_global_section - full_file = code_format[0] + CPP_INCLUDE_BEGIN + '\n' + global_s + CPP_INCLUDE_END - full_file += code_format[1] + CPP_AUTO_GENERATE_BEGIN + '\n' + code_s + CPP_AUTO_GENERATE_END + full_file = code_format[0] + CPP_INCLUDE_BEGIN + "\n" + global_s + CPP_INCLUDE_END + full_file += ( + code_format[1] + CPP_AUTO_GENERATE_BEGIN + "\n" + code_s + CPP_AUTO_GENERATE_END + ) full_file += code_format[2] write_file_if_changed(path, full_file) @@ -417,7 +472,7 @@ GITIGNORE_CONTENT = """# Gitignore settings for ESPHome def write_gitignore(): - path = CORE.relative_config_path('.gitignore') + path = CORE.relative_config_path(".gitignore") if not os.path.isfile(path): - with open(path, 'w') as f: + with open(path, "w") as f: f.write(GITIGNORE_CONTENT) diff --git a/esphome/yaml_util.py b/esphome/yaml_util.py index eb88a8da62..f98bb272b8 100644 --- a/esphome/yaml_util.py +++ b/esphome/yaml_util.py @@ -11,8 +11,14 @@ import yaml.constructor from esphome import core from esphome.config_helpers import read_config_file -from esphome.core import EsphomeError, IPAddress, Lambda, MACAddress, TimePeriod, \ - DocumentRange +from esphome.core import ( + EsphomeError, + IPAddress, + Lambda, + MACAddress, + TimePeriod, + DocumentRange, +) from esphome.helpers import add_class_to_obj from esphome.util import OrderedDict, filter_yaml_files @@ -21,7 +27,7 @@ _LOGGER = logging.getLogger(__name__) # Mostly copied from Home Assistant because that code works fine and # let's not reinvent the wheel here -SECRET_YAML = 'secrets.yaml' +SECRET_YAML = "secrets.yaml" _SECRET_CACHE = {} _SECRET_VALUES = {} @@ -29,17 +35,17 @@ _SECRET_VALUES = {} class ESPHomeDataBase: @property def esp_range(self): - return getattr(self, '_esp_range', None) + return getattr(self, "_esp_range", None) @property def content_offset(self): - return getattr(self, '_content_offset', 0) + return getattr(self, "_content_offset", 0) def from_node(self, node): # pylint: disable=attribute-defined-outside-init self._esp_range = DocumentRange.from_marks(node.start_mark, node.end_mark) if isinstance(node, yaml.ScalarNode): - if node.style is not None and node.style in '|>': + if node.style is not None and node.style in "|>": self._content_offset = 1 def from_database(self, database): @@ -125,13 +131,13 @@ class ESPHomeLoader(yaml.SafeLoader): # pylint: disable=too-many-ancestors for key_node, value_node in node.value: # merge key is '<<' - is_merge_key = key_node.tag == 'tag:yaml.org,2002:merge' + is_merge_key = key_node.tag == "tag:yaml.org,2002:merge" # key has no explicit tag set - is_default_tag = key_node.tag == 'tag:yaml.org,2002:value' + is_default_tag = key_node.tag == "tag:yaml.org,2002:value" if is_default_tag: # Default tag for mapping keys is string - key_node.tag = 'tag:yaml.org,2002:str' + key_node.tag = "tag:yaml.org,2002:str" if not is_merge_key: # base case, this is a simple key-value pair @@ -144,7 +150,8 @@ class ESPHomeLoader(yaml.SafeLoader): # pylint: disable=too-many-ancestors except TypeError: # pylint: disable=raise-missing-from raise yaml.constructor.ConstructorError( - f'Invalid key "{key}" (not hashable)', key_node.start_mark) + f'Invalid key "{key}" (not hashable)', key_node.start_mark + ) key = make_data_base(str(key)) key.from_node(key_node) @@ -152,8 +159,10 @@ class ESPHomeLoader(yaml.SafeLoader): # pylint: disable=too-many-ancestors # Check if it is a duplicate key if key in seen_keys: raise yaml.constructor.ConstructorError( - f'Duplicate key "{key}"', key_node.start_mark, - 'NOTE: Previous declaration here:', seen_keys[key], + f'Duplicate key "{key}"', + key_node.start_mark, + "NOTE: Previous declaration here:", + seen_keys[key], ) seen_keys[key] = key_node.start_mark @@ -172,15 +181,22 @@ class ESPHomeLoader(yaml.SafeLoader): # pylint: disable=too-many-ancestors for item in value: if not isinstance(item, dict): raise yaml.constructor.ConstructorError( - "While constructing a mapping", node.start_mark, - "Expected a mapping for merging, but found {}".format(type(item)), - value_node.start_mark) + "While constructing a mapping", + node.start_mark, + "Expected a mapping for merging, but found {}".format( + type(item) + ), + value_node.start_mark, + ) merge_pairs.extend(item.items()) else: raise yaml.constructor.ConstructorError( - "While constructing a mapping", node.start_mark, + "While constructing a mapping", + node.start_mark, "Expected a mapping or list of mappings for merging, " - "but found {}".format(type(value)), value_node.start_mark) + "but found {}".format(type(value)), + value_node.start_mark, + ) if merge_pairs: # We found some merge keys along the way, merge them into base pairs @@ -211,7 +227,7 @@ class ESPHomeLoader(yaml.SafeLoader): # pylint: disable=too-many-ancestors args = node.value.split() # Check for a default value if len(args) > 1: - return os.getenv(args[0], ' '.join(args[1:])) + return os.getenv(args[0], " ".join(args[1:])) if args[0] in os.environ: return os.environ[args[0]] raise yaml.MarkedYAMLError( @@ -242,12 +258,12 @@ class ESPHomeLoader(yaml.SafeLoader): # pylint: disable=too-many-ancestors @_add_data_ref def construct_include_dir_list(self, node): - files = filter_yaml_files(_find_files(self._rel_path(node.value), '*.yaml')) + files = filter_yaml_files(_find_files(self._rel_path(node.value), "*.yaml")) return [_load_yaml_internal(f) for f in files] @_add_data_ref def construct_include_dir_merge_list(self, node): - files = filter_yaml_files(_find_files(self._rel_path(node.value), '*.yaml')) + files = filter_yaml_files(_find_files(self._rel_path(node.value), "*.yaml")) merged_list = [] for fname in files: loaded_yaml = _load_yaml_internal(fname) @@ -257,7 +273,7 @@ class ESPHomeLoader(yaml.SafeLoader): # pylint: disable=too-many-ancestors @_add_data_ref def construct_include_dir_named(self, node): - files = filter_yaml_files(_find_files(self._rel_path(node.value), '*.yaml')) + files = filter_yaml_files(_find_files(self._rel_path(node.value), "*.yaml")) mapping = OrderedDict() for fname in files: filename = os.path.splitext(os.path.basename(fname))[0] @@ -266,7 +282,7 @@ class ESPHomeLoader(yaml.SafeLoader): # pylint: disable=too-many-ancestors @_add_data_ref def construct_include_dir_merge_named(self, node): - files = filter_yaml_files(_find_files(self._rel_path(node.value), '*.yaml')) + files = filter_yaml_files(_find_files(self._rel_path(node.value), "*.yaml")) mapping = OrderedDict() for fname in files: loaded_yaml = _load_yaml_internal(fname) @@ -284,24 +300,36 @@ class ESPHomeLoader(yaml.SafeLoader): # pylint: disable=too-many-ancestors return add_class_to_obj(obj, ESPForceValue) -ESPHomeLoader.add_constructor('tag:yaml.org,2002:int', ESPHomeLoader.construct_yaml_int) -ESPHomeLoader.add_constructor('tag:yaml.org,2002:float', ESPHomeLoader.construct_yaml_float) -ESPHomeLoader.add_constructor('tag:yaml.org,2002:binary', ESPHomeLoader.construct_yaml_binary) -ESPHomeLoader.add_constructor('tag:yaml.org,2002:omap', ESPHomeLoader.construct_yaml_omap) -ESPHomeLoader.add_constructor('tag:yaml.org,2002:str', ESPHomeLoader.construct_yaml_str) -ESPHomeLoader.add_constructor('tag:yaml.org,2002:seq', ESPHomeLoader.construct_yaml_seq) -ESPHomeLoader.add_constructor('tag:yaml.org,2002:map', ESPHomeLoader.construct_yaml_map) -ESPHomeLoader.add_constructor('!env_var', ESPHomeLoader.construct_env_var) -ESPHomeLoader.add_constructor('!secret', ESPHomeLoader.construct_secret) -ESPHomeLoader.add_constructor('!include', ESPHomeLoader.construct_include) -ESPHomeLoader.add_constructor('!include_dir_list', ESPHomeLoader.construct_include_dir_list) -ESPHomeLoader.add_constructor('!include_dir_merge_list', - ESPHomeLoader.construct_include_dir_merge_list) -ESPHomeLoader.add_constructor('!include_dir_named', ESPHomeLoader.construct_include_dir_named) -ESPHomeLoader.add_constructor('!include_dir_merge_named', - ESPHomeLoader.construct_include_dir_merge_named) -ESPHomeLoader.add_constructor('!lambda', ESPHomeLoader.construct_lambda) -ESPHomeLoader.add_constructor('!force', ESPHomeLoader.construct_force) +ESPHomeLoader.add_constructor("tag:yaml.org,2002:int", ESPHomeLoader.construct_yaml_int) +ESPHomeLoader.add_constructor( + "tag:yaml.org,2002:float", ESPHomeLoader.construct_yaml_float +) +ESPHomeLoader.add_constructor( + "tag:yaml.org,2002:binary", ESPHomeLoader.construct_yaml_binary +) +ESPHomeLoader.add_constructor( + "tag:yaml.org,2002:omap", ESPHomeLoader.construct_yaml_omap +) +ESPHomeLoader.add_constructor("tag:yaml.org,2002:str", ESPHomeLoader.construct_yaml_str) +ESPHomeLoader.add_constructor("tag:yaml.org,2002:seq", ESPHomeLoader.construct_yaml_seq) +ESPHomeLoader.add_constructor("tag:yaml.org,2002:map", ESPHomeLoader.construct_yaml_map) +ESPHomeLoader.add_constructor("!env_var", ESPHomeLoader.construct_env_var) +ESPHomeLoader.add_constructor("!secret", ESPHomeLoader.construct_secret) +ESPHomeLoader.add_constructor("!include", ESPHomeLoader.construct_include) +ESPHomeLoader.add_constructor( + "!include_dir_list", ESPHomeLoader.construct_include_dir_list +) +ESPHomeLoader.add_constructor( + "!include_dir_merge_list", ESPHomeLoader.construct_include_dir_merge_list +) +ESPHomeLoader.add_constructor( + "!include_dir_named", ESPHomeLoader.construct_include_dir_named +) +ESPHomeLoader.add_constructor( + "!include_dir_merge_named", ESPHomeLoader.construct_include_dir_merge_named +) +ESPHomeLoader.add_constructor("!lambda", ESPHomeLoader.construct_lambda) +ESPHomeLoader.add_constructor("!force", ESPHomeLoader.construct_force) def load_yaml(fname): @@ -324,13 +352,14 @@ def _load_yaml_internal(fname): def dump(dict_): """Dump YAML to a string and remove null.""" - return yaml.dump(dict_, default_flow_style=False, allow_unicode=True, - Dumper=ESPHomeDumper) + return yaml.dump( + dict_, default_flow_style=False, allow_unicode=True, Dumper=ESPHomeDumper + ) def _is_file_valid(name): """Decide if a file is valid.""" - return not name.startswith('.') + return not name.startswith(".") def _find_files(directory, pattern): @@ -357,7 +386,7 @@ class ESPHomeDumper(yaml.SafeDumper): # pylint: disable=too-many-ancestors if self.alias_key is not None: self.represented_objects[self.alias_key] = node best_style = True - if hasattr(mapping, 'items'): + if hasattr(mapping, "items"): mapping = list(mapping.items()) for item_key, item_value in mapping: node_key = self.represent_data(item_key) @@ -375,29 +404,31 @@ class ESPHomeDumper(yaml.SafeDumper): # pylint: disable=too-many-ancestors return node def represent_secret(self, value): - return self.represent_scalar(tag='!secret', value=_SECRET_VALUES[str(value)]) + return self.represent_scalar(tag="!secret", value=_SECRET_VALUES[str(value)]) def represent_stringify(self, value): if is_secret(value): return self.represent_secret(value) - return self.represent_scalar(tag='tag:yaml.org,2002:str', value=str(value)) + return self.represent_scalar(tag="tag:yaml.org,2002:str", value=str(value)) # pylint: disable=arguments-differ def represent_bool(self, value): - return self.represent_scalar('tag:yaml.org,2002:bool', 'true' if value else 'false') + return self.represent_scalar( + "tag:yaml.org,2002:bool", "true" if value else "false" + ) def represent_int(self, value): if is_secret(value): return self.represent_secret(value) - return self.represent_scalar(tag='tag:yaml.org,2002:int', value=str(value)) + return self.represent_scalar(tag="tag:yaml.org,2002:int", value=str(value)) def represent_float(self, value): if is_secret(value): return self.represent_secret(value) if math.isnan(value): - value = '.nan' + value = ".nan" elif math.isinf(value): - value = '.inf' if value > 0 else '-.inf' + value = ".inf" if value > 0 else "-.inf" else: value = str(repr(value)).lower() # Note that in some cases `repr(data)` represents a float number @@ -407,14 +438,14 @@ class ESPHomeDumper(yaml.SafeDumper): # pylint: disable=too-many-ancestors # Unfortunately, this is not a valid float representation according # to the definition of the `!!float` tag. We fix this by adding # '.0' before the 'e' symbol. - if '.' not in value and 'e' in value: - value = value.replace('e', '.0e', 1) - return self.represent_scalar(tag='tag:yaml.org,2002:float', value=value) + if "." not in value and "e" in value: + value = value.replace("e", ".0e", 1) + return self.represent_scalar(tag="tag:yaml.org,2002:float", value=value) def represent_lambda(self, value): if is_secret(value.value): return self.represent_secret(value.value) - return self.represent_scalar(tag='!lambda', value=value.value, style='|') + return self.represent_scalar(tag="!lambda", value=value.value, style="|") def represent_id(self, value): if is_secret(value.id): @@ -423,12 +454,11 @@ class ESPHomeDumper(yaml.SafeDumper): # pylint: disable=too-many-ancestors ESPHomeDumper.add_multi_representer( - dict, - lambda dumper, value: dumper.represent_mapping('tag:yaml.org,2002:map', value) + dict, lambda dumper, value: dumper.represent_mapping("tag:yaml.org,2002:map", value) ) ESPHomeDumper.add_multi_representer( list, - lambda dumper, value: dumper.represent_sequence('tag:yaml.org,2002:seq', value) + lambda dumper, value: dumper.represent_sequence("tag:yaml.org,2002:seq", value), ) ESPHomeDumper.add_multi_representer(bool, ESPHomeDumper.represent_bool) ESPHomeDumper.add_multi_representer(str, ESPHomeDumper.represent_stringify) diff --git a/esphome/zeroconf.py b/esphome/zeroconf.py index a8ca5b3c53..a44c7c9114 100644 --- a/esphome/zeroconf.py +++ b/esphome/zeroconf.py @@ -20,7 +20,7 @@ _LISTENER_TIME = 200 # Some DNS constants -_MDNS_ADDR = '224.0.0.251' +_MDNS_ADDR = "224.0.0.251" _MDNS_PORT = 5353 _MAX_MSG_ABSOLUTE = 8966 @@ -96,7 +96,7 @@ class QuietLogger: logger = log.debug if logger_data is not None: logger(*logger_data) - logger('Exception occurred:', exc_info=True) + logger("Exception occurred:", exc_info=True) @classmethod def log_warning_once(cls, *args): @@ -129,9 +129,11 @@ class DNSQuestion(DNSEntry): def answered_by(self, rec): """Returns true if the question is answered by the record""" - return (self.class_ == rec.class_ and - (self.type == rec.type or self.type == _TYPE_ANY) and - self.name == rec.name) + return ( + self.class_ == rec.class_ + and (self.type == rec.type or self.type == _TYPE_ANY) + and self.name == rec.name + ) class DNSRecord(DNSEntry): @@ -202,26 +204,32 @@ class DNSIncoming(QuietLogger): self.valid = True except (IndexError, struct.error, IncomingDecodeError): - self.log_exception_warning(( - 'Choked at offset %d while unpacking %r', self.offset, data)) + self.log_exception_warning( + ("Choked at offset %d while unpacking %r", self.offset, data) + ) def unpack(self, format_): length = struct.calcsize(format_) - info = struct.unpack( - format_, self.data[self.offset:self.offset + length]) + info = struct.unpack(format_, self.data[self.offset : self.offset + length]) self.offset += length return info def read_header(self): """Reads header portion of packet""" - (self.id, self.flags, self.num_questions, self.num_answers, - self.num_authorities, self.num_additionals) = self.unpack(b'!6H') + ( + self.id, + self.flags, + self.num_questions, + self.num_answers, + self.num_authorities, + self.num_additionals, + ) = self.unpack(b"!6H") def read_questions(self): """Reads questions section of packet""" for _ in range(self.num_questions): name = self.read_name() - type_, class_ = self.unpack(b'!HH') + type_, class_ = self.unpack(b"!HH") question = DNSQuestion(name, type_, class_) self.questions.append(question) @@ -234,13 +242,13 @@ class DNSIncoming(QuietLogger): def read_string(self, length): """Reads a string of a given length from the packet""" - info = self.data[self.offset:self.offset + length] + info = self.data[self.offset : self.offset + length] self.offset += length return info def read_unsigned_short(self): """Reads an unsigned short from the packet""" - return self.unpack(b'!H')[0] + return self.unpack(b"!H")[0] def read_others(self): """Reads the answers, authorities and additionals section of the @@ -248,18 +256,15 @@ class DNSIncoming(QuietLogger): n = self.num_answers + self.num_authorities + self.num_additionals for _ in range(n): domain = self.read_name() - type_, class_, ttl, length = self.unpack(b'!HHiH') + type_, class_, ttl, length = self.unpack(b"!HHiH") rec = None if type_ == _TYPE_A: - rec = DNSAddress( - domain, type_, class_, ttl, self.read_string(4)) + rec = DNSAddress(domain, type_, class_, ttl, self.read_string(4)) elif type_ == _TYPE_TXT: - rec = DNSText( - domain, type_, class_, ttl, self.read_string(length)) + rec = DNSText(domain, type_, class_, ttl, self.read_string(length)) elif type_ == _TYPE_AAAA: - rec = DNSAddress( - domain, type_, class_, ttl, self.read_string(16)) + rec = DNSAddress(domain, type_, class_, ttl, self.read_string(16)) else: # Try to ignore types we don't know about # Skip the payload for the resource record so the next @@ -279,11 +284,11 @@ class DNSIncoming(QuietLogger): def read_utf(self, offset, length): """Reads a UTF-8 string of a given length from the packet""" - return str(self.data[offset:offset + length], 'utf-8', 'replace') + return str(self.data[offset : offset + length], "utf-8", "replace") def read_name(self): """Reads a domain name from the packet""" - result = '' + result = "" off = self.offset next_ = -1 first = off @@ -295,15 +300,14 @@ class DNSIncoming(QuietLogger): break t = length & 0xC0 if t == 0x00: - result = ''.join((result, self.read_utf(off, length) + '.')) + result = "".join((result, self.read_utf(off, length) + ".")) off += length elif t == 0xC0: if next_ < 0: next_ = off + 1 off = ((length & 0x3F) << 8) | self.data[off] if off >= first: - raise IncomingDecodeError( - f"Bad domain name (circular) at {off}") + raise IncomingDecodeError(f"Bad domain name (circular) at {off}") first = off else: raise IncomingDecodeError(f"Bad domain name at {off}") @@ -341,20 +345,20 @@ class DNSOutgoing: def write_byte(self, value): """Writes a single byte to the packet""" - self.pack(b'!c', int2byte(value)) + self.pack(b"!c", int2byte(value)) def insert_short(self, index, value): """Inserts an unsigned short in a certain position in the packet""" - self.data.insert(index, struct.pack(b'!H', value)) + self.data.insert(index, struct.pack(b"!H", value)) self.size += 2 def write_short(self, value): """Writes an unsigned short to the packet""" - self.pack(b'!H', value) + self.pack(b"!H", value) def write_int(self, value): """Writes an unsigned integer to the packet""" - self.pack(b'!I', int(value)) + self.pack(b"!I", int(value)) def write_string(self, value): """Writes a string to the packet""" @@ -364,7 +368,7 @@ class DNSOutgoing: def write_utf(self, s): """Writes a UTF-8 string of a given length to the packet""" - utfstr = s.encode('utf-8') + utfstr = s.encode("utf-8") length = len(utfstr) self.write_byte(length) self.write_string(utfstr) @@ -377,12 +381,12 @@ class DNSOutgoing: def write_name(self, name): # split name into each label - parts = name.split('.') + parts = name.split(".") if not parts[-1]: parts.pop() # construct each suffix - name_suffices = ['.'.join(parts[i:]) for i in range(len(parts))] + name_suffices = [".".join(parts[i:]) for i in range(len(parts))] # look for an existing name or suffix for count, sub_name in enumerate(name_suffices): @@ -392,9 +396,11 @@ class DNSOutgoing: count = len(name_suffices) # note the new names we are saving into the packet - name_length = len(name.encode('utf-8')) + name_length = len(name.encode("utf-8")) for suffix in name_suffices[:count]: - self.names[suffix] = self.size + name_length - len(suffix.encode('utf-8')) - 1 + self.names[suffix] = ( + self.size + name_length - len(suffix.encode("utf-8")) - 1 + ) # write the new names out. for part in parts[:count]: @@ -427,12 +433,12 @@ class DNSOutgoing: self.insert_short(0, len(self.questions)) self.insert_short(0, self.flags) # _FLAGS_QR_QUERY self.insert_short(0, 0) - return b''.join(self.data) + return b"".join(self.data) class Engine(threading.Thread): def __init__(self, zc): - threading.Thread.__init__(self, name='zeroconf-Engine') + threading.Thread.__init__(self, name="zeroconf-Engine") self.daemon = True self.zc = zc self.readers = {} @@ -488,7 +494,7 @@ class Listener(QuietLogger): self.log_exception_warning() return - log.debug('Received from %r:%r: %r ', addr, port, data) + log.debug("Received from %r:%r: %r ", addr, port, data) self.data = data msg = DNSIncoming(data) @@ -530,8 +536,7 @@ class HostResolver(RecordUpdateListener): return False if next_ <= now: out = DNSOutgoing(_FLAGS_QR_QUERY) - out.add_question( - DNSQuestion(self.name, _TYPE_A, _CLASS_IN)) + out.add_question(DNSQuestion(self.name, _TYPE_A, _CLASS_IN)) zc.send(out) next_ = now + delay delay *= 2 @@ -592,10 +597,12 @@ class DashboardStatus(RecordUpdateListener, threading.Thread): while not self.stop_event.is_set(): self.purge_cache() for host in self.query_hosts: - if all(record.is_expired(time.time()) for record in self.cache.get(host, [])): + if all( + record.is_expired(time.time()) + for record in self.cache.get(host, []) + ): out = DNSOutgoing(_FLAGS_QR_QUERY) - out.add_question( - DNSQuestion(host, _TYPE_A, _CLASS_IN)) + out.add_question(DNSQuestion(host, _TYPE_A, _CLASS_IN)) self.zc.send(out) self.query_event.wait() self.query_event.clear() @@ -603,12 +610,15 @@ class DashboardStatus(RecordUpdateListener, threading.Thread): def get_all_addresses(): - return list({ - addr.ip - for iface in ifaddr.get_adapters() - for addr in iface.ips - if addr.is_IPv4 and addr.network_prefix != 32 # Host only netmask 255.255.255.255 - }) + return list( + { + addr.ip + for iface in ifaddr.get_adapters() + for addr in iface.ips + if addr.is_IPv4 + and addr.network_prefix != 32 # Host only netmask 255.255.255.255 + } + ) def new_socket(): @@ -636,12 +646,12 @@ def new_socket(): # OpenBSD needs the ttl and loop values for the IP_MULTICAST_TTL and # IP_MULTICAST_LOOP socket options as an unsigned char. - ttl = struct.pack(b'B', 255) + ttl = struct.pack(b"B", 255) s.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, ttl) - loop = struct.pack(b'B', 1) + loop = struct.pack(b"B", 1) s.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_LOOP, loop) - s.bind(('', _MDNS_PORT)) + s.bind(("", _MDNS_PORT)) return s @@ -659,24 +669,28 @@ class Zeroconf(QuietLogger): try: _value = socket.inet_aton(_MDNS_ADDR) + socket.inet_aton(i) self._listen_socket.setsockopt( - socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, _value) + socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, _value + ) except OSError as e: _errno = e.args[0] if _errno == errno.EADDRINUSE: log.info( - 'Address in use when adding %s to multicast group, ' - 'it is expected to happen on some systems', i, + "Address in use when adding %s to multicast group, " + "it is expected to happen on some systems", + i, ) elif _errno == errno.EADDRNOTAVAIL: log.info( - 'Address not available when adding %s to multicast ' - 'group, it is expected to happen on some systems', i, + "Address not available when adding %s to multicast " + "group, it is expected to happen on some systems", + i, ) continue elif _errno == errno.EINVAL: log.info( - 'Interface of %s does not support multicast, ' - 'it is expected in WSL', i + "Interface of %s does not support multicast, " + "it is expected in WSL", + i, ) continue @@ -685,7 +699,8 @@ class Zeroconf(QuietLogger): respond_socket = new_socket() respond_socket.setsockopt( - socket.IPPROTO_IP, socket.IP_MULTICAST_IF, socket.inet_aton(i)) + socket.IPPROTO_IP, socket.IP_MULTICAST_IF, socket.inet_aton(i) + ) self._respond_sockets.append(respond_socket) @@ -728,7 +743,7 @@ class Zeroconf(QuietLogger): self.listeners.remove(listener) self.notify_all() except Exception as e: # pylint: disable=broad-except - log.exception('Unknown error, possibly benign: %r', e) + log.exception("Unknown error, possibly benign: %r", e) def update_record(self, now, rec): """Used to notify listeners of new information that has updated @@ -747,7 +762,7 @@ class Zeroconf(QuietLogger): def send(self, out): """Sends an outgoing packet.""" packet = out.packet() - log.debug('Sending %r (%d bytes) as %r...', out, len(packet), packet) + log.debug("Sending %r (%d bytes) as %r...", out, len(packet), packet) for s in self._respond_sockets: if self._GLOBAL_DONE: return @@ -759,8 +774,9 @@ class Zeroconf(QuietLogger): else: if bytes_sent != len(packet): self.log_warning_once( - '!!! sent %d out of %d bytes to %r' % ( - bytes_sent, len(packet), s)) + "!!! sent %d out of %d bytes to %r" + % (bytes_sent, len(packet), s) + ) def close(self): """Ends the background threads, and prevent this instance from diff --git a/pylintrc b/pylintrc index 00ffdc9f9a..8f2e9a7359 100644 --- a/pylintrc +++ b/pylintrc @@ -3,6 +3,7 @@ reports=no ignore=api_pb2.py disable= + format, missing-docstring, fixme, unused-argument, diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000000..7a75060c8e --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,3 @@ +[tool.black] +target-version = ["py36", "py37", "py38"] +exclude = 'generated' diff --git a/requirements_test.txt b/requirements_test.txt index d7f8bf50af..10c838a424 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,8 +1,10 @@ pylint==2.7.2 flake8==3.8.4 +black==20.8b1 pillow>4.0.0 cryptography>=2.0.0,<4 pexpect==4.8.0 +pre-commit # Unit tests pytest==6.2.2 diff --git a/script/api_protobuf/api_options_pb2.py b/script/api_protobuf/api_options_pb2.py index e690a2c5d7..f5297c062c 100644 --- a/script/api_protobuf/api_options_pb2.py +++ b/script/api_protobuf/api_options_pb2.py @@ -2,12 +2,14 @@ # source: api_options.proto import sys -_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) + +_b = sys.version_info[0] < 3 and (lambda x: x) or (lambda x: x.encode("latin1")) from google.protobuf.internal import enum_type_wrapper from google.protobuf import descriptor as _descriptor from google.protobuf import message as _message from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database + # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -17,37 +19,38 @@ from google.protobuf import descriptor_pb2 as google_dot_protobuf_dot_descriptor DESCRIPTOR = _descriptor.FileDescriptor( - name='api_options.proto', - package='', - syntax='proto2', - serialized_options=None, - serialized_pb=_b('\n\x11\x61pi_options.proto\x1a google/protobuf/descriptor.proto\"\x06\n\x04void*F\n\rAPISourceType\x12\x0f\n\x0bSOURCE_BOTH\x10\x00\x12\x11\n\rSOURCE_SERVER\x10\x01\x12\x11\n\rSOURCE_CLIENT\x10\x02:E\n\x16needs_setup_connection\x12\x1e.google.protobuf.MethodOptions\x18\x8e\x08 \x01(\x08:\x04true:C\n\x14needs_authentication\x12\x1e.google.protobuf.MethodOptions\x18\x8f\x08 \x01(\x08:\x04true:/\n\x02id\x12\x1f.google.protobuf.MessageOptions\x18\x8c\x08 \x01(\r:\x01\x30:M\n\x06source\x12\x1f.google.protobuf.MessageOptions\x18\x8d\x08 \x01(\x0e\x32\x0e.APISourceType:\x0bSOURCE_BOTH:/\n\x05ifdef\x12\x1f.google.protobuf.MessageOptions\x18\x8e\x08 \x01(\t:3\n\x03log\x12\x1f.google.protobuf.MessageOptions\x18\x8f\x08 \x01(\x08:\x04true:9\n\x08no_delay\x12\x1f.google.protobuf.MessageOptions\x18\x90\x08 \x01(\x08:\x05\x66\x61lse') - , - dependencies=[google_dot_protobuf_dot_descriptor__pb2.DESCRIPTOR,]) + name="api_options.proto", + package="", + syntax="proto2", + serialized_options=None, + serialized_pb=_b( + '\n\x11\x61pi_options.proto\x1a google/protobuf/descriptor.proto"\x06\n\x04void*F\n\rAPISourceType\x12\x0f\n\x0bSOURCE_BOTH\x10\x00\x12\x11\n\rSOURCE_SERVER\x10\x01\x12\x11\n\rSOURCE_CLIENT\x10\x02:E\n\x16needs_setup_connection\x12\x1e.google.protobuf.MethodOptions\x18\x8e\x08 \x01(\x08:\x04true:C\n\x14needs_authentication\x12\x1e.google.protobuf.MethodOptions\x18\x8f\x08 \x01(\x08:\x04true:/\n\x02id\x12\x1f.google.protobuf.MessageOptions\x18\x8c\x08 \x01(\r:\x01\x30:M\n\x06source\x12\x1f.google.protobuf.MessageOptions\x18\x8d\x08 \x01(\x0e\x32\x0e.APISourceType:\x0bSOURCE_BOTH:/\n\x05ifdef\x12\x1f.google.protobuf.MessageOptions\x18\x8e\x08 \x01(\t:3\n\x03log\x12\x1f.google.protobuf.MessageOptions\x18\x8f\x08 \x01(\x08:\x04true:9\n\x08no_delay\x12\x1f.google.protobuf.MessageOptions\x18\x90\x08 \x01(\x08:\x05\x66\x61lse' + ), + dependencies=[ + google_dot_protobuf_dot_descriptor__pb2.DESCRIPTOR, + ], +) _APISOURCETYPE = _descriptor.EnumDescriptor( - name='APISourceType', - full_name='APISourceType', - filename=None, - file=DESCRIPTOR, - values=[ - _descriptor.EnumValueDescriptor( - name='SOURCE_BOTH', index=0, number=0, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='SOURCE_SERVER', index=1, number=1, - serialized_options=None, - type=None), - _descriptor.EnumValueDescriptor( - name='SOURCE_CLIENT', index=2, number=2, - serialized_options=None, - type=None), - ], - containing_type=None, - serialized_options=None, - serialized_start=63, - serialized_end=133, + name="APISourceType", + full_name="APISourceType", + filename=None, + file=DESCRIPTOR, + values=[ + _descriptor.EnumValueDescriptor( + name="SOURCE_BOTH", index=0, number=0, serialized_options=None, type=None + ), + _descriptor.EnumValueDescriptor( + name="SOURCE_SERVER", index=1, number=1, serialized_options=None, type=None + ), + _descriptor.EnumValueDescriptor( + name="SOURCE_CLIENT", index=2, number=2, serialized_options=None, type=None + ), + ], + containing_type=None, + serialized_options=None, + serialized_start=63, + serialized_end=133, ) _sym_db.RegisterEnumDescriptor(_APISOURCETYPE) @@ -58,105 +61,186 @@ SOURCE_CLIENT = 2 NEEDS_SETUP_CONNECTION_FIELD_NUMBER = 1038 needs_setup_connection = _descriptor.FieldDescriptor( - name='needs_setup_connection', full_name='needs_setup_connection', index=0, - number=1038, type=8, cpp_type=7, label=1, - has_default_value=True, default_value=True, - message_type=None, enum_type=None, containing_type=None, - is_extension=True, extension_scope=None, - serialized_options=None, file=DESCRIPTOR) + name="needs_setup_connection", + full_name="needs_setup_connection", + index=0, + number=1038, + type=8, + cpp_type=7, + label=1, + has_default_value=True, + default_value=True, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=True, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, +) NEEDS_AUTHENTICATION_FIELD_NUMBER = 1039 needs_authentication = _descriptor.FieldDescriptor( - name='needs_authentication', full_name='needs_authentication', index=1, - number=1039, type=8, cpp_type=7, label=1, - has_default_value=True, default_value=True, - message_type=None, enum_type=None, containing_type=None, - is_extension=True, extension_scope=None, - serialized_options=None, file=DESCRIPTOR) + name="needs_authentication", + full_name="needs_authentication", + index=1, + number=1039, + type=8, + cpp_type=7, + label=1, + has_default_value=True, + default_value=True, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=True, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, +) ID_FIELD_NUMBER = 1036 id = _descriptor.FieldDescriptor( - name='id', full_name='id', index=2, - number=1036, type=13, cpp_type=3, label=1, - has_default_value=True, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=True, extension_scope=None, - serialized_options=None, file=DESCRIPTOR) + name="id", + full_name="id", + index=2, + number=1036, + type=13, + cpp_type=3, + label=1, + has_default_value=True, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=True, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, +) SOURCE_FIELD_NUMBER = 1037 source = _descriptor.FieldDescriptor( - name='source', full_name='source', index=3, - number=1037, type=14, cpp_type=8, label=1, - has_default_value=True, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=True, extension_scope=None, - serialized_options=None, file=DESCRIPTOR) + name="source", + full_name="source", + index=3, + number=1037, + type=14, + cpp_type=8, + label=1, + has_default_value=True, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=True, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, +) IFDEF_FIELD_NUMBER = 1038 ifdef = _descriptor.FieldDescriptor( - name='ifdef', full_name='ifdef', index=4, - number=1038, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=True, extension_scope=None, - serialized_options=None, file=DESCRIPTOR) + name="ifdef", + full_name="ifdef", + index=4, + number=1038, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=_b("").decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=True, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, +) LOG_FIELD_NUMBER = 1039 log = _descriptor.FieldDescriptor( - name='log', full_name='log', index=5, - number=1039, type=8, cpp_type=7, label=1, - has_default_value=True, default_value=True, - message_type=None, enum_type=None, containing_type=None, - is_extension=True, extension_scope=None, - serialized_options=None, file=DESCRIPTOR) + name="log", + full_name="log", + index=5, + number=1039, + type=8, + cpp_type=7, + label=1, + has_default_value=True, + default_value=True, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=True, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, +) NO_DELAY_FIELD_NUMBER = 1040 no_delay = _descriptor.FieldDescriptor( - name='no_delay', full_name='no_delay', index=6, - number=1040, type=8, cpp_type=7, label=1, - has_default_value=True, default_value=False, - message_type=None, enum_type=None, containing_type=None, - is_extension=True, extension_scope=None, - serialized_options=None, file=DESCRIPTOR) + name="no_delay", + full_name="no_delay", + index=6, + number=1040, + type=8, + cpp_type=7, + label=1, + has_default_value=True, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=True, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, +) _VOID = _descriptor.Descriptor( - name='void', - full_name='void', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto2', - extension_ranges=[], - oneofs=[ - ], - serialized_start=55, - serialized_end=61, + name="void", + full_name="void", + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto2", + extension_ranges=[], + oneofs=[], + serialized_start=55, + serialized_end=61, ) -DESCRIPTOR.message_types_by_name['void'] = _VOID -DESCRIPTOR.enum_types_by_name['APISourceType'] = _APISOURCETYPE -DESCRIPTOR.extensions_by_name['needs_setup_connection'] = needs_setup_connection -DESCRIPTOR.extensions_by_name['needs_authentication'] = needs_authentication -DESCRIPTOR.extensions_by_name['id'] = id -DESCRIPTOR.extensions_by_name['source'] = source -DESCRIPTOR.extensions_by_name['ifdef'] = ifdef -DESCRIPTOR.extensions_by_name['log'] = log -DESCRIPTOR.extensions_by_name['no_delay'] = no_delay +DESCRIPTOR.message_types_by_name["void"] = _VOID +DESCRIPTOR.enum_types_by_name["APISourceType"] = _APISOURCETYPE +DESCRIPTOR.extensions_by_name["needs_setup_connection"] = needs_setup_connection +DESCRIPTOR.extensions_by_name["needs_authentication"] = needs_authentication +DESCRIPTOR.extensions_by_name["id"] = id +DESCRIPTOR.extensions_by_name["source"] = source +DESCRIPTOR.extensions_by_name["ifdef"] = ifdef +DESCRIPTOR.extensions_by_name["log"] = log +DESCRIPTOR.extensions_by_name["no_delay"] = no_delay _sym_db.RegisterFileDescriptor(DESCRIPTOR) -void = _reflection.GeneratedProtocolMessageType('void', (_message.Message,), dict( - DESCRIPTOR = _VOID, - __module__ = 'api_options_pb2' - # @@protoc_insertion_point(class_scope:void) - )) +void = _reflection.GeneratedProtocolMessageType( + "void", + (_message.Message,), + dict( + DESCRIPTOR=_VOID, + __module__="api_options_pb2" + # @@protoc_insertion_point(class_scope:void) + ), +) _sym_db.RegisterMessage(void) -google_dot_protobuf_dot_descriptor__pb2.MethodOptions.RegisterExtension(needs_setup_connection) -google_dot_protobuf_dot_descriptor__pb2.MethodOptions.RegisterExtension(needs_authentication) +google_dot_protobuf_dot_descriptor__pb2.MethodOptions.RegisterExtension( + needs_setup_connection +) +google_dot_protobuf_dot_descriptor__pb2.MethodOptions.RegisterExtension( + needs_authentication +) google_dot_protobuf_dot_descriptor__pb2.MessageOptions.RegisterExtension(id) source.enum_type = _APISOURCETYPE google_dot_protobuf_dot_descriptor__pb2.MessageOptions.RegisterExtension(source) diff --git a/script/api_protobuf/api_protobuf.py b/script/api_protobuf/api_protobuf.py index e78eff76c7..f86145df2f 100644 --- a/script/api_protobuf/api_protobuf.py +++ b/script/api_protobuf/api_protobuf.py @@ -27,39 +27,39 @@ from subprocess import call import api_options_pb2 as pb import google.protobuf.descriptor_pb2 as descriptor -file_header = '// This file was automatically generated with a tool.\n' -file_header += '// See scripts/api_protobuf/api_protobuf.py\n' +file_header = "// This file was automatically generated with a tool.\n" +file_header += "// See scripts/api_protobuf/api_protobuf.py\n" cwd = Path(__file__).resolve().parent -root = cwd.parent.parent / 'esphome' / 'components' / 'api' -prot = root / 'api.protoc' -call(['protoc', '-o', str(prot), '-I', str(root), 'api.proto']) +root = cwd.parent.parent / "esphome" / "components" / "api" +prot = root / "api.protoc" +call(["protoc", "-o", str(prot), "-I", str(root), "api.proto"]) content = prot.read_bytes() d = descriptor.FileDescriptorSet.FromString(content) -def indent_list(text, padding=' '): +def indent_list(text, padding=" "): return [padding + line for line in text.splitlines()] -def indent(text, padding=' '): - return '\n'.join(indent_list(text, padding)) +def indent(text, padding=" "): + return "\n".join(indent_list(text, padding)) def camel_to_snake(name): # https://stackoverflow.com/a/1176023 - s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name) - return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower() + s1 = re.sub("(.)([A-Z][a-z]+)", r"\1_\2", name) + return re.sub("([a-z0-9])([A-Z])", r"\1_\2", s1).lower() -class TypeInfo(): +class TypeInfo: def __init__(self, field): self._field = field @property def default_value(self): - return '' + return "" @property def name(self): @@ -87,11 +87,11 @@ class TypeInfo(): @property def reference_type(self): - return f'{self.cpp_type} ' + return f"{self.cpp_type} " @property def const_reference_type(self): - return f'{self.cpp_type} ' + return f"{self.cpp_type} " @property def public_content(self) -> str: @@ -103,18 +103,20 @@ class TypeInfo(): @property def class_member(self) -> str: - return f'{self.cpp_type} {self.field_name}{{{self.default_value}}}; // NOLINT' + return f"{self.cpp_type} {self.field_name}{{{self.default_value}}}; // NOLINT" @property def decode_varint_content(self) -> str: content = self.decode_varint if content is None: return None - return dedent(f'''\ + return dedent( + f"""\ case {self.number}: {{ this->{self.field_name} = {content}; return true; - }}''') + }}""" + ) decode_varint = None @@ -123,11 +125,13 @@ class TypeInfo(): content = self.decode_length if content is None: return None - return dedent(f'''\ + return dedent( + f"""\ case {self.number}: {{ this->{self.field_name} = {content}; return true; - }}''') + }}""" + ) decode_length = None @@ -136,11 +140,13 @@ class TypeInfo(): content = self.decode_32bit if content is None: return None - return dedent(f'''\ + return dedent( + f"""\ case {self.number}: {{ this->{self.field_name} = {content}; return true; - }}''') + }}""" + ) decode_32bit = None @@ -149,24 +155,26 @@ class TypeInfo(): content = self.decode_64bit if content is None: return None - return dedent(f'''\ + return dedent( + f"""\ case {self.number}: {{ this->{self.field_name} = {content}; return true; - }}''') + }}""" + ) decode_64bit = None @property def encode_content(self): - return f'buffer.{self.encode_func}({self.number}, this->{self.field_name});' + return f"buffer.{self.encode_func}({self.number}, this->{self.field_name});" encode_func = None @property def dump_content(self): o = f'out.append(" {self.name}: ");\n' - o += self.dump(f'this->{self.field_name}') + '\n' + o += self.dump(f"this->{self.field_name}") + "\n" o += f'out.append("\\n");\n' return o @@ -186,115 +194,115 @@ def register_type(name): @register_type(1) class DoubleType(TypeInfo): - cpp_type = 'double' - default_value = '0.0' - decode_64bit = 'value.as_double()' - encode_func = 'encode_double' + cpp_type = "double" + default_value = "0.0" + decode_64bit = "value.as_double()" + encode_func = "encode_double" def dump(self, name): o = f'sprintf(buffer, "%g", {name});\n' - o += f'out.append(buffer);' + o += f"out.append(buffer);" return o @register_type(2) class FloatType(TypeInfo): - cpp_type = 'float' - default_value = '0.0f' - decode_32bit = 'value.as_float()' - encode_func = 'encode_float' + cpp_type = "float" + default_value = "0.0f" + decode_32bit = "value.as_float()" + encode_func = "encode_float" def dump(self, name): o = f'sprintf(buffer, "%g", {name});\n' - o += f'out.append(buffer);' + o += f"out.append(buffer);" return o @register_type(3) class Int64Type(TypeInfo): - cpp_type = 'int64_t' - default_value = '0' - decode_varint = 'value.as_int64()' - encode_func = 'encode_int64' + cpp_type = "int64_t" + default_value = "0" + decode_varint = "value.as_int64()" + encode_func = "encode_int64" def dump(self, name): o = f'sprintf(buffer, "%ll", {name});\n' - o += f'out.append(buffer);' + o += f"out.append(buffer);" return o @register_type(4) class UInt64Type(TypeInfo): - cpp_type = 'uint64_t' - default_value = '0' - decode_varint = 'value.as_uint64()' - encode_func = 'encode_uint64' + cpp_type = "uint64_t" + default_value = "0" + decode_varint = "value.as_uint64()" + encode_func = "encode_uint64" def dump(self, name): o = f'sprintf(buffer, "%ull", {name});\n' - o += f'out.append(buffer);' + o += f"out.append(buffer);" return o @register_type(5) class Int32Type(TypeInfo): - cpp_type = 'int32_t' - default_value = '0' - decode_varint = 'value.as_int32()' - encode_func = 'encode_int32' + cpp_type = "int32_t" + default_value = "0" + decode_varint = "value.as_int32()" + encode_func = "encode_int32" def dump(self, name): o = f'sprintf(buffer, "%d", {name});\n' - o += f'out.append(buffer);' + o += f"out.append(buffer);" return o @register_type(6) class Fixed64Type(TypeInfo): - cpp_type = 'uint64_t' - default_value = '0' - decode_64bit = 'value.as_fixed64()' - encode_func = 'encode_fixed64' + cpp_type = "uint64_t" + default_value = "0" + decode_64bit = "value.as_fixed64()" + encode_func = "encode_fixed64" def dump(self, name): o = f'sprintf(buffer, "%ull", {name});\n' - o += f'out.append(buffer);' + o += f"out.append(buffer);" return o @register_type(7) class Fixed32Type(TypeInfo): - cpp_type = 'uint32_t' - default_value = '0' - decode_32bit = 'value.as_fixed32()' - encode_func = 'encode_fixed32' + cpp_type = "uint32_t" + default_value = "0" + decode_32bit = "value.as_fixed32()" + encode_func = "encode_fixed32" def dump(self, name): o = f'sprintf(buffer, "%u", {name});\n' - o += f'out.append(buffer);' + o += f"out.append(buffer);" return o @register_type(8) class BoolType(TypeInfo): - cpp_type = 'bool' - default_value = 'false' - decode_varint = 'value.as_bool()' - encode_func = 'encode_bool' + cpp_type = "bool" + default_value = "false" + decode_varint = "value.as_bool()" + encode_func = "encode_bool" def dump(self, name): - o = f'out.append(YESNO({name}));' + o = f"out.append(YESNO({name}));" return o @register_type(9) class StringType(TypeInfo): - cpp_type = 'std::string' - default_value = '' - reference_type = 'std::string &' - const_reference_type = 'const std::string &' - decode_length = 'value.as_string()' - encode_func = 'encode_string' + cpp_type = "std::string" + default_value = "" + reference_type = "std::string &" + const_reference_type = "const std::string &" + decode_length = "value.as_string()" + encode_func = "encode_string" def dump(self, name): o = f'out.append("\'").append({name}).append("\'");' @@ -307,37 +315,37 @@ class MessageType(TypeInfo): def cpp_type(self): return self._field.type_name[1:] - default_value = '' + default_value = "" @property def reference_type(self): - return f'{self.cpp_type} &' + return f"{self.cpp_type} &" @property def const_reference_type(self): - return f'const {self.cpp_type} &' + return f"const {self.cpp_type} &" @property def encode_func(self): - return f'encode_message<{self.cpp_type}>' + return f"encode_message<{self.cpp_type}>" @property def decode_length(self): - return f'value.as_message<{self.cpp_type}>()' + return f"value.as_message<{self.cpp_type}>()" def dump(self, name): - o = f'{name}.dump_to(out);' + o = f"{name}.dump_to(out);" return o @register_type(12) class BytesType(TypeInfo): - cpp_type = 'std::string' - default_value = '' - reference_type = 'std::string &' - const_reference_type = 'const std::string &' - decode_length = 'value.as_string()' - encode_func = 'encode_string' + cpp_type = "std::string" + default_value = "" + reference_type = "std::string &" + const_reference_type = "const std::string &" + decode_length = "value.as_string()" + encode_func = "encode_string" def dump(self, name): o = f'out.append("\'").append({name}).append("\'");' @@ -346,14 +354,14 @@ class BytesType(TypeInfo): @register_type(13) class UInt32Type(TypeInfo): - cpp_type = 'uint32_t' - default_value = '0' - decode_varint = 'value.as_uint32()' - encode_func = 'encode_uint32' + cpp_type = "uint32_t" + default_value = "0" + decode_varint = "value.as_uint32()" + encode_func = "encode_uint32" def dump(self, name): o = f'sprintf(buffer, "%u", {name});\n' - o += f'out.append(buffer);' + o += f"out.append(buffer);" return o @@ -361,72 +369,72 @@ class UInt32Type(TypeInfo): class EnumType(TypeInfo): @property def cpp_type(self): - return f'enums::{self._field.type_name[1:]}' + return f"enums::{self._field.type_name[1:]}" @property def decode_varint(self): - return f'value.as_enum<{self.cpp_type}>()' + return f"value.as_enum<{self.cpp_type}>()" - default_value = '' + default_value = "" @property def encode_func(self): - return f'encode_enum<{self.cpp_type}>' + return f"encode_enum<{self.cpp_type}>" def dump(self, name): - o = f'out.append(proto_enum_to_string<{self.cpp_type}>({name}));' + o = f"out.append(proto_enum_to_string<{self.cpp_type}>({name}));" return o @register_type(15) class SFixed32Type(TypeInfo): - cpp_type = 'int32_t' - default_value = '0' - decode_32bit = 'value.as_sfixed32()' - encode_func = 'encode_sfixed32' + cpp_type = "int32_t" + default_value = "0" + decode_32bit = "value.as_sfixed32()" + encode_func = "encode_sfixed32" def dump(self, name): o = f'sprintf(buffer, "%d", {name});\n' - o += f'out.append(buffer);' + o += f"out.append(buffer);" return o @register_type(16) class SFixed64Type(TypeInfo): - cpp_type = 'int64_t' - default_value = '0' - decode_64bit = 'value.as_sfixed64()' - encode_func = 'encode_sfixed64' + cpp_type = "int64_t" + default_value = "0" + decode_64bit = "value.as_sfixed64()" + encode_func = "encode_sfixed64" def dump(self, name): o = f'sprintf(buffer, "%ll", {name});\n' - o += f'out.append(buffer);' + o += f"out.append(buffer);" return o @register_type(17) class SInt32Type(TypeInfo): - cpp_type = 'int32_t' - default_value = '0' - decode_varint = 'value.as_sint32()' - encode_func = 'encode_sint32' + cpp_type = "int32_t" + default_value = "0" + decode_varint = "value.as_sint32()" + encode_func = "encode_sint32" def dump(self, name): o = f'sprintf(buffer, "%d", {name});\n' - o += f'out.append(buffer);' + o += f"out.append(buffer);" return o @register_type(18) class SInt64Type(TypeInfo): - cpp_type = 'int64_t' - default_value = '0' - decode_varint = 'value.as_sint64()' - encode_func = 'encode_sin64' + cpp_type = "int64_t" + default_value = "0" + decode_varint = "value.as_sint64()" + encode_func = "encode_sin64" def dump(self): o = f'sprintf(buffer, "%ll", {name});\n' - o += f'out.append(buffer);' + o += f"out.append(buffer);" return o @@ -437,59 +445,67 @@ class RepeatedTypeInfo(TypeInfo): @property def cpp_type(self): - return f'std::vector<{self._ti.cpp_type}>' + return f"std::vector<{self._ti.cpp_type}>" @property def reference_type(self): - return f'{self.cpp_type} &' + return f"{self.cpp_type} &" @property def const_reference_type(self): - return f'const {self.cpp_type} &' + return f"const {self.cpp_type} &" @property def decode_varint_content(self) -> str: content = self._ti.decode_varint if content is None: return None - return dedent(f'''\ + return dedent( + f"""\ case {self.number}: {{ this->{self.field_name}.push_back({content}); return true; - }}''') + }}""" + ) @property def decode_length_content(self) -> str: content = self._ti.decode_length if content is None: return None - return dedent(f'''\ + return dedent( + f"""\ case {self.number}: {{ this->{self.field_name}.push_back({content}); return true; - }}''') + }}""" + ) @property def decode_32bit_content(self) -> str: content = self._ti.decode_32bit if content is None: return None - return dedent(f'''\ + return dedent( + f"""\ case {self.number}: {{ this->{self.field_name}.push_back({content}); return true; - }}''') + }}""" + ) @property def decode_64bit_content(self) -> str: content = self._ti.decode_64bit if content is None: return None - return dedent(f'''\ + return dedent( + f"""\ case {self.number}: {{ this->{self.field_name}.push_back({content}); return true; - }}''') + }}""" + ) @property def _ti_is_bool(self): @@ -507,9 +523,9 @@ class RepeatedTypeInfo(TypeInfo): def dump_content(self): o = f'for (const auto {"" if self._ti_is_bool else "&"}it : this->{self.field_name}) {{\n' o += f' out.append(" {self.name}: ");\n' - o += indent(self._ti.dump('it')) + '\n' + o += indent(self._ti.dump("it")) + "\n" o += f' out.append("\\n");\n' - o += f'}}\n' + o += f"}}\n" return o @@ -517,8 +533,8 @@ def build_enum_type(desc): name = desc.name out = f"enum {name} : uint32_t {{\n" for v in desc.value: - out += f' {v.name} = {v.number},\n' - out += '};\n' + out += f" {v.name} = {v.number},\n" + out += "};\n" cpp = f"template<>\n" cpp += f"const char *proto_enum_to_string(enums::{name} value) {{\n" @@ -526,8 +542,8 @@ def build_enum_type(desc): for v in desc.value: cpp += f' case enums::{v.name}: return "{v.name}";\n' cpp += f' default: return "UNKNOWN";\n' - cpp += f' }}\n' - cpp += f'}}\n' + cpp += f" }}\n" + cpp += f"}}\n" return out, cpp @@ -562,80 +578,80 @@ def build_message_type(desc): if ti.dump_content: dump.append(ti.dump_content) - cpp = '' + cpp = "" if decode_varint: - decode_varint.append('default:\n return false;') - o = f'bool {desc.name}::decode_varint(uint32_t field_id, ProtoVarInt value) {{\n' - o += ' switch (field_id) {\n' - o += indent("\n".join(decode_varint), ' ') + '\n' - o += ' }\n' - o += '}\n' + decode_varint.append("default:\n return false;") + o = f"bool {desc.name}::decode_varint(uint32_t field_id, ProtoVarInt value) {{\n" + o += " switch (field_id) {\n" + o += indent("\n".join(decode_varint), " ") + "\n" + o += " }\n" + o += "}\n" cpp += o - prot = 'bool decode_varint(uint32_t field_id, ProtoVarInt value) override;' + prot = "bool decode_varint(uint32_t field_id, ProtoVarInt value) override;" protected_content.insert(0, prot) if decode_length: - decode_length.append('default:\n return false;') - o = f'bool {desc.name}::decode_length(uint32_t field_id, ProtoLengthDelimited value) {{\n' - o += ' switch (field_id) {\n' - o += indent("\n".join(decode_length), ' ') + '\n' - o += ' }\n' - o += '}\n' + decode_length.append("default:\n return false;") + o = f"bool {desc.name}::decode_length(uint32_t field_id, ProtoLengthDelimited value) {{\n" + o += " switch (field_id) {\n" + o += indent("\n".join(decode_length), " ") + "\n" + o += " }\n" + o += "}\n" cpp += o - prot = 'bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;' + prot = "bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;" protected_content.insert(0, prot) if decode_32bit: - decode_32bit.append('default:\n return false;') - o = f'bool {desc.name}::decode_32bit(uint32_t field_id, Proto32Bit value) {{\n' - o += ' switch (field_id) {\n' - o += indent("\n".join(decode_32bit), ' ') + '\n' - o += ' }\n' - o += '}\n' + decode_32bit.append("default:\n return false;") + o = f"bool {desc.name}::decode_32bit(uint32_t field_id, Proto32Bit value) {{\n" + o += " switch (field_id) {\n" + o += indent("\n".join(decode_32bit), " ") + "\n" + o += " }\n" + o += "}\n" cpp += o - prot = 'bool decode_32bit(uint32_t field_id, Proto32Bit value) override;' + prot = "bool decode_32bit(uint32_t field_id, Proto32Bit value) override;" protected_content.insert(0, prot) if decode_64bit: - decode_64bit.append('default:\n return false;') - o = f'bool {desc.name}::decode_64bit(uint32_t field_id, Proto64bit value) {{\n' - o += ' switch (field_id) {\n' - o += indent("\n".join(decode_64bit), ' ') + '\n' - o += ' }\n' - o += '}\n' + decode_64bit.append("default:\n return false;") + o = f"bool {desc.name}::decode_64bit(uint32_t field_id, Proto64bit value) {{\n" + o += " switch (field_id) {\n" + o += indent("\n".join(decode_64bit), " ") + "\n" + o += " }\n" + o += "}\n" cpp += o - prot = 'bool decode_64bit(uint32_t field_id, Proto64bit value) override;' + prot = "bool decode_64bit(uint32_t field_id, Proto64bit value) override;" protected_content.insert(0, prot) o = f"void {desc.name}::encode(ProtoWriteBuffer buffer) const {{\n" - o += indent('\n'.join(encode)) + '\n' - o += '}\n' + o += indent("\n".join(encode)) + "\n" + o += "}\n" cpp += o - prot = 'void encode(ProtoWriteBuffer buffer) const override;' + prot = "void encode(ProtoWriteBuffer buffer) const override;" public_content.append(prot) o = f"void {desc.name}::dump_to(std::string &out) const {{\n" if dump: o += f" char buffer[64];\n" o += f' out.append("{desc.name} {{\\n");\n' - o += indent('\n'.join(dump)) + '\n' + o += indent("\n".join(dump)) + "\n" o += f' out.append("}}");\n' else: o += f' out.append("{desc.name} {{}}");\n' - o += '}\n' + o += "}\n" cpp += o - prot = 'void dump_to(std::string &out) const override;' + prot = "void dump_to(std::string &out) const override;" public_content.append(prot) out = f"class {desc.name} : public ProtoMessage {{\n" - out += ' public:\n' - out += indent('\n'.join(public_content)) + '\n' - out += ' protected:\n' - out += indent('\n'.join(protected_content)) + '\n' + out += " public:\n" + out += indent("\n".join(public_content)) + "\n" + out += " protected:\n" + out += indent("\n".join(protected_content)) + "\n" out += "};\n" return out, cpp file = d.file[0] content = file_header -content += '''\ +content += """\ #pragma once #include "proto.h" @@ -643,26 +659,26 @@ content += '''\ namespace esphome { namespace api { -''' +""" cpp = file_header -cpp += '''\ +cpp += """\ #include "api_pb2.h" #include "esphome/core/log.h" namespace esphome { namespace api { -''' +""" -content += 'namespace enums {\n\n' +content += "namespace enums {\n\n" for enum in file.enum_type: s, c = build_enum_type(enum) content += s cpp += c -content += '\n} // namespace enums\n\n' +content += "\n} // namespace enums\n\n" mt = file.message_type @@ -671,21 +687,21 @@ for m in mt: content += s cpp += c -content += '''\ +content += """\ } // namespace api } // namespace esphome -''' -cpp += '''\ +""" +cpp += """\ } // namespace api } // namespace esphome -''' +""" -with open(root / 'api_pb2.h', 'w') as f: +with open(root / "api_pb2.h", "w") as f: f.write(content) -with open(root / 'api_pb2.cpp', 'w') as f: +with open(root / "api_pb2.cpp", "w") as f: f.write(cpp) SOURCE_BOTH = 0 @@ -694,7 +710,7 @@ SOURCE_CLIENT = 2 RECEIVE_CASES = {} -class_name = 'APIServerConnectionBase' +class_name = "APIServerConnectionBase" ifdefs = {} @@ -716,50 +732,50 @@ def build_service_message_type(mt): ifdef = get_opt(mt, pb.ifdef) log = get_opt(mt, pb.log, True) nodelay = get_opt(mt, pb.no_delay, False) - hout = '' - cout = '' + hout = "" + cout = "" if ifdef is not None: ifdefs[str(mt.name)] = ifdef - hout += f'#ifdef {ifdef}\n' - cout += f'#ifdef {ifdef}\n' + hout += f"#ifdef {ifdef}\n" + cout += f"#ifdef {ifdef}\n" if source in (SOURCE_BOTH, SOURCE_SERVER): # Generate send - func = f'send_{snake}' - hout += f'bool {func}(const {mt.name} &msg);\n' - cout += f'bool {class_name}::{func}(const {mt.name} &msg) {{\n' + func = f"send_{snake}" + hout += f"bool {func}(const {mt.name} &msg);\n" + cout += f"bool {class_name}::{func}(const {mt.name} &msg) {{\n" if log: cout += f' ESP_LOGVV(TAG, "{func}: %s", msg.dump().c_str());\n' # cout += f' this->set_nodelay({str(nodelay).lower()});\n' - cout += f' return this->send_message_<{mt.name}>(msg, {id_});\n' - cout += f'}}\n' + cout += f" return this->send_message_<{mt.name}>(msg, {id_});\n" + cout += f"}}\n" if source in (SOURCE_BOTH, SOURCE_CLIENT): # Generate receive - func = f'on_{snake}' - hout += f'virtual void {func}(const {mt.name} &value){{}};\n' - case = '' + func = f"on_{snake}" + hout += f"virtual void {func}(const {mt.name} &value){{}};\n" + case = "" if ifdef is not None: - case += f'#ifdef {ifdef}\n' - case += f'{mt.name} msg;\n' - case += f'msg.decode(msg_data, msg_size);\n' + case += f"#ifdef {ifdef}\n" + case += f"{mt.name} msg;\n" + case += f"msg.decode(msg_data, msg_size);\n" if log: case += f'ESP_LOGVV(TAG, "{func}: %s", msg.dump().c_str());\n' - case += f'this->{func}(msg);\n' + case += f"this->{func}(msg);\n" if ifdef is not None: - case += f'#endif\n' - case += 'break;' + case += f"#endif\n" + case += "break;" RECEIVE_CASES[id_] = case if ifdef is not None: - hout += f'#endif\n' - cout += f'#endif\n' + hout += f"#endif\n" + cout += f"#endif\n" return hout, cout hpp = file_header -hpp += '''\ +hpp += """\ #pragma once #include "api_pb2.h" @@ -768,10 +784,10 @@ hpp += '''\ namespace esphome { namespace api { -''' +""" cpp = file_header -cpp += '''\ +cpp += """\ #include "api_pb2_service.h" #include "esphome/core/log.h" @@ -780,113 +796,113 @@ namespace api { static const char *TAG = "api.service"; -''' +""" -hpp += f'class {class_name} : public ProtoService {{\n' -hpp += ' public:\n' +hpp += f"class {class_name} : public ProtoService {{\n" +hpp += " public:\n" for mt in file.message_type: obj = build_service_message_type(mt) if obj is None: continue hout, cout = obj - hpp += indent(hout) + '\n' + hpp += indent(hout) + "\n" cpp += cout cases = list(RECEIVE_CASES.items()) cases.sort() -hpp += ' protected:\n' -hpp += f' bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override;\n' -out = f'bool {class_name}::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) {{\n' -out += f' switch(msg_type) {{\n' +hpp += " protected:\n" +hpp += f" bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override;\n" +out = f"bool {class_name}::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) {{\n" +out += f" switch(msg_type) {{\n" for i, case in cases: - c = f'case {i}: {{\n' - c += indent(case) + '\n' - c += f'}}' - out += indent(c, ' ') + '\n' -out += ' default: \n' -out += ' return false;\n' -out += ' }\n' -out += ' return true;\n' -out += '}\n' + c = f"case {i}: {{\n" + c += indent(case) + "\n" + c += f"}}" + out += indent(c, " ") + "\n" +out += " default: \n" +out += " return false;\n" +out += " }\n" +out += " return true;\n" +out += "}\n" cpp += out -hpp += '};\n' +hpp += "};\n" serv = file.service[0] -class_name = 'APIServerConnection' -hpp += '\n' -hpp += f'class {class_name} : public {class_name}Base {{\n' -hpp += ' public:\n' -hpp_protected = '' -cpp += '\n' +class_name = "APIServerConnection" +hpp += "\n" +hpp += f"class {class_name} : public {class_name}Base {{\n" +hpp += " public:\n" +hpp_protected = "" +cpp += "\n" m = serv.method[0] for m in serv.method: func = m.name inp = m.input_type[1:] ret = m.output_type[1:] - is_void = ret == 'void' + is_void = ret == "void" snake = camel_to_snake(inp) - on_func = f'on_{snake}' + on_func = f"on_{snake}" needs_conn = get_opt(m, pb.needs_setup_connection, True) needs_auth = get_opt(m, pb.needs_authentication, True) ifdef = ifdefs.get(inp, None) if ifdef is not None: - hpp += f'#ifdef {ifdef}\n' - hpp_protected += f'#ifdef {ifdef}\n' - cpp += f'#ifdef {ifdef}\n' + hpp += f"#ifdef {ifdef}\n" + hpp_protected += f"#ifdef {ifdef}\n" + cpp += f"#ifdef {ifdef}\n" - hpp_protected += f' void {on_func}(const {inp} &msg) override;\n' - hpp += f' virtual {ret} {func}(const {inp} &msg) = 0;\n' - cpp += f'void {class_name}::{on_func}(const {inp} &msg) {{\n' - body = '' + hpp_protected += f" void {on_func}(const {inp} &msg) override;\n" + hpp += f" virtual {ret} {func}(const {inp} &msg) = 0;\n" + cpp += f"void {class_name}::{on_func}(const {inp} &msg) {{\n" + body = "" if needs_conn: - body += 'if (!this->is_connection_setup()) {\n' - body += ' this->on_no_setup_connection();\n' - body += ' return;\n' - body += '}\n' + body += "if (!this->is_connection_setup()) {\n" + body += " this->on_no_setup_connection();\n" + body += " return;\n" + body += "}\n" if needs_auth: - body += 'if (!this->is_authenticated()) {\n' - body += ' this->on_unauthenticated_access();\n' - body += ' return;\n' - body += '}\n' + body += "if (!this->is_authenticated()) {\n" + body += " this->on_unauthenticated_access();\n" + body += " return;\n" + body += "}\n" if is_void: - body += f'this->{func}(msg);\n' + body += f"this->{func}(msg);\n" else: - body += f'{ret} ret = this->{func}(msg);\n' + body += f"{ret} ret = this->{func}(msg);\n" ret_snake = camel_to_snake(ret) - body += f'if (!this->send_{ret_snake}(ret)) {{\n' - body += f' this->on_fatal_error();\n' - body += '}\n' - cpp += indent(body) + '\n' + '}\n' + body += f"if (!this->send_{ret_snake}(ret)) {{\n" + body += f" this->on_fatal_error();\n" + body += "}\n" + cpp += indent(body) + "\n" + "}\n" if ifdef is not None: - hpp += f'#endif\n' - hpp_protected += f'#endif\n' - cpp += f'#endif\n' + hpp += f"#endif\n" + hpp_protected += f"#endif\n" + cpp += f"#endif\n" -hpp += ' protected:\n' +hpp += " protected:\n" hpp += hpp_protected -hpp += '};\n' +hpp += "};\n" -hpp += '''\ +hpp += """\ } // namespace api } // namespace esphome -''' -cpp += '''\ +""" +cpp += """\ } // namespace api } // namespace esphome -''' +""" -with open(root / 'api_pb2_service.h', 'w') as f: +with open(root / "api_pb2_service.h", "w") as f: f.write(hpp) -with open(root / 'api_pb2_service.cpp', 'w') as f: +with open(root / "api_pb2_service.cpp", "w") as f: f.write(cpp) prot.unlink() diff --git a/script/build_codeowners.py b/script/build_codeowners.py index f21e9ca2a5..a1e8d69046 100755 --- a/script/build_codeowners.py +++ b/script/build_codeowners.py @@ -9,13 +9,14 @@ from esphome.config import get_component, get_platform from esphome.core import CORE parser = argparse.ArgumentParser() -parser.add_argument('--check', help="Check if the CODEOWNERS file is up to date.", - action='store_true') +parser.add_argument( + "--check", help="Check if the CODEOWNERS file is up to date.", action="store_true" +) args = parser.parse_args() # The root directory of the repo root = Path(__file__).parent.parent -components_dir = root / 'esphome' / 'components' +components_dir = root / "esphome" / "components" BASE = """ # This file is generated by script/build_codeowners.py @@ -43,16 +44,18 @@ codeowners = defaultdict(list) for path in components_dir.iterdir(): if not path.is_dir(): continue - if not (path / '__init__.py').is_file(): + if not (path / "__init__.py").is_file(): continue name = path.name comp = get_component(name) if comp is None: - print(f'Cannot find component {name}. Make sure current path is pip installed ESPHome') + print( + f"Cannot find component {name}. Make sure current path is pip installed ESPHome" + ) sys.exit(1) - codeowners[f'esphome/components/{name}/*'].extend(comp.codeowners) + codeowners[f"esphome/components/{name}/*"].extend(comp.codeowners) for platform_path in path.iterdir(): platform_name = platform_path.stem @@ -62,15 +65,17 @@ for path in components_dir.iterdir(): if platform_path.is_dir(): # Sub foldered platforms get their own line - if not (platform_path / '__init__.py').is_file(): + if not (platform_path / "__init__.py").is_file(): continue - codeowners[f'esphome/components/{name}/{platform_name}/*'].extend(platform.codeowners) + codeowners[f"esphome/components/{name}/{platform_name}/*"].extend( + platform.codeowners + ) continue # Non-subfoldered platforms add to codeowners at component level - if not platform_path.is_file() or platform_path.name == '__init__.py': + if not platform_path.is_file() or platform_path.name == "__init__.py": continue - codeowners[f'esphome/components/{name}/*'].extend(platform.codeowners) + codeowners[f"esphome/components/{name}/*"].extend(platform.codeowners) for path, owners in sorted(codeowners.items()): @@ -78,16 +83,18 @@ for path, owners in sorted(codeowners.items()): if not owners: continue for owner in owners: - if not owner.startswith('@'): - print(f"Codeowner {owner} for integration {path} must start with an '@' symbol!") + if not owner.startswith("@"): + print( + f"Codeowner {owner} for integration {path} must start with an '@' symbol!" + ) sys.exit(1) parts.append(f"{path} {' '.join(owners)}") # End newline -parts.append('') -content = '\n'.join(parts) -codeowners_file = root / 'CODEOWNERS' +parts.append("") +content = "\n".join(parts) +codeowners_file = root / "CODEOWNERS" if args.check: if codeowners_file.read_text() != content: diff --git a/script/build_compile_commands.py b/script/build_compile_commands.py index f0fc48ad98..4ac14f08b4 100755 --- a/script/build_compile_commands.py +++ b/script/build_compile_commands.py @@ -12,5 +12,5 @@ def main(): print("Done.") -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/script/bump-docker-base-version.py b/script/bump-docker-base-version.py index 178643cea6..765a330ce4 100755 --- a/script/bump-docker-base-version.py +++ b/script/bump-docker-base-version.py @@ -17,36 +17,32 @@ def sub(path, pattern, repl, expected_count=1): def write_version(version: str): for p in [ - ".github/workflows/ci-docker.yml", - ".github/workflows/release-dev.yml", - ".github/workflows/release.yml" + ".github/workflows/ci-docker.yml", + ".github/workflows/release-dev.yml", + ".github/workflows/release.yml", ]: - sub( - p, - r'base_version=".*"', - f'base_version="{version}"' - ) + sub(p, r'base_version=".*"', f'base_version="{version}"') sub( "docker/Dockerfile", r"ARG BUILD_FROM=esphome/esphome-base-amd64:.*", - f"ARG BUILD_FROM=esphome/esphome-base-amd64:{version}" + f"ARG BUILD_FROM=esphome/esphome-base-amd64:{version}", ) sub( "docker/Dockerfile.dev", r"FROM esphome/esphome-base-amd64:.*", - f"FROM esphome/esphome-base-amd64:{version}" + f"FROM esphome/esphome-base-amd64:{version}", ) sub( "docker/Dockerfile.lint", r"FROM esphome/esphome-lint-base:.*", - f"FROM esphome/esphome-lint-base:{version}" + f"FROM esphome/esphome-lint-base:{version}", ) def main(): parser = argparse.ArgumentParser() - parser.add_argument('new_version', type=str) + parser.add_argument("new_version", type=str) args = parser.parse_args() version = args.new_version diff --git a/script/bump-version.py b/script/bump-version.py index 56062ac5cf..54e8c95753 100755 --- a/script/bump-version.py +++ b/script/bump-version.py @@ -16,30 +16,27 @@ class Version: dev: bool = False def __str__(self): - return f'{self.major}.{self.minor}.{self.full_patch}' + return f"{self.major}.{self.minor}.{self.full_patch}" @property def full_patch(self): - res = f'{self.patch}' + res = f"{self.patch}" if self.beta > 0: - res += f'b{self.beta}' + res += f"b{self.beta}" if self.dev: - res += '-dev' + res += "-dev" return res @classmethod def parse(cls, value): - match = re.match(r'(\d+).(\d+).(\d+)(b\d+)?(-dev)?', value) + match = re.match(r"(\d+).(\d+).(\d+)(b\d+)?(-dev)?", value) assert match is not None major = int(match[1]) minor = int(match[2]) patch = int(match[3]) beta = int(match[4][1:]) if match[4] else 0 dev = bool(match[5]) - return Version( - major=major, minor=minor, patch=patch, - beta=beta, dev=dev - ) + return Version(major=major, minor=minor, patch=patch, beta=beta, dev=dev) def sub(path, pattern, repl, expected_count=1): @@ -54,25 +51,21 @@ def sub(path, pattern, repl, expected_count=1): def write_version(version: Version): sub( - 'esphome/const.py', - r"^MAJOR_VERSION = \d+$", - f"MAJOR_VERSION = {version.major}" + "esphome/const.py", r"^MAJOR_VERSION = \d+$", f"MAJOR_VERSION = {version.major}" ) sub( - 'esphome/const.py', - r"^MINOR_VERSION = \d+$", - f"MINOR_VERSION = {version.minor}" + "esphome/const.py", r"^MINOR_VERSION = \d+$", f"MINOR_VERSION = {version.minor}" ) sub( - 'esphome/const.py', + "esphome/const.py", r"^PATCH_VERSION = .*$", - f"PATCH_VERSION = '{version.full_patch}'" + f"PATCH_VERSION = '{version.full_patch}'", ) def main(): parser = argparse.ArgumentParser() - parser.add_argument('new_version', type=str) + parser.add_argument("new_version", type=str) args = parser.parse_args() version = Version.parse(args.new_version) diff --git a/script/ci-custom.py b/script/ci-custom.py index ab2beadf85..8ebf6eb245 100755 --- a/script/ci-custom.py +++ b/script/ci-custom.py @@ -14,6 +14,7 @@ import argparse sys.path.append(os.path.dirname(__file__)) from helpers import git_ls_files, filter_changed + def find_all(a_str, sub): if not a_str.find(sub): # Optimization: If str is not in whole text, then do not try @@ -30,18 +31,21 @@ def find_all(a_str, sub): parser = argparse.ArgumentParser() -parser.add_argument('files', nargs='*', default=[], - help='files to be processed (regex on path)') -parser.add_argument('-c', '--changed', action='store_true', - help='Only run on changed files') -parser.add_argument('--print-slowest', action='store_true', - help='Print the slowest checks') +parser.add_argument( + "files", nargs="*", default=[], help="files to be processed (regex on path)" +) +parser.add_argument( + "-c", "--changed", action="store_true", help="Only run on changed files" +) +parser.add_argument( + "--print-slowest", action="store_true", help="Print the slowest checks" +) args = parser.parse_args() EXECUTABLE_BIT = git_ls_files() files = list(EXECUTABLE_BIT.keys()) # Match against re -file_name_re = re.compile('|'.join(args.files)) +file_name_re = re.compile("|".join(args.files)) files = [p for p in files if file_name_re.search(p)] if args.changed: @@ -49,11 +53,32 @@ if args.changed: files.sort() -file_types = ('.h', '.c', '.cpp', '.tcc', '.yaml', '.yml', '.ini', '.txt', '.ico', '.svg', - '.py', '.html', '.js', '.md', '.sh', '.css', '.proto', '.conf', '.cfg', - '.woff', '.woff2', '') -cpp_include = ('*.h', '*.c', '*.cpp', '*.tcc') -ignore_types = ('.ico', '.woff', '.woff2', '') +file_types = ( + ".h", + ".c", + ".cpp", + ".tcc", + ".yaml", + ".yml", + ".ini", + ".txt", + ".ico", + ".svg", + ".py", + ".html", + ".js", + ".md", + ".sh", + ".css", + ".proto", + ".conf", + ".cfg", + ".woff", + ".woff2", + "", +) +cpp_include = ("*.h", "*.c", "*.cpp", "*.tcc") +ignore_types = (".ico", ".woff", ".woff2", "") LINT_FILE_CHECKS = [] LINT_CONTENT_CHECKS = [] @@ -61,9 +86,9 @@ LINT_POST_CHECKS = [] def run_check(lint_obj, fname, *args): - include = lint_obj['include'] - exclude = lint_obj['exclude'] - func = lint_obj['func'] + include = lint_obj["include"] + exclude = lint_obj["exclude"] + func = lint_obj["func"] if include is not None: for incl in include: if fnmatch.fnmatch(fname, incl): @@ -85,21 +110,24 @@ def run_checks(lints, fname, *args): print(f"Check {lint['func'].__name__} on file {fname} failed:") raise duration = time.process_time() - start - lint.setdefault('durations', []).append(duration) + lint.setdefault("durations", []).append(duration) def _add_check(checks, func, include=None, exclude=None): - checks.append({ - 'include': include, - 'exclude': exclude or [], - 'func': func, - }) + checks.append( + { + "include": include, + "exclude": exclude or [], + "func": func, + } + ) def lint_file_check(**kwargs): def decorator(func): _add_check(LINT_FILE_CHECKS, func, **kwargs) return func + return decorator @@ -107,6 +135,7 @@ def lint_content_check(**kwargs): def decorator(func): _add_check(LINT_CONTENT_CHECKS, func, **kwargs) return func + return decorator @@ -116,7 +145,7 @@ def lint_post_check(func): def lint_re_check(regex, **kwargs): - flags = kwargs.pop('flags', re.MULTILINE) + flags = kwargs.pop("flags", re.MULTILINE) prog = re.compile(regex, flags) decor = lint_content_check(**kwargs) @@ -125,18 +154,19 @@ def lint_re_check(regex, **kwargs): def new_func(fname, content): errors = [] for match in prog.finditer(content): - if 'NOLINT' in match.group(0): + if "NOLINT" in match.group(0): continue lineno = content.count("\n", 0, match.start()) + 1 - substr = content[:match.start()] - col = len(substr) - substr.rfind('\n') + substr = content[: match.start()] + col = len(substr) - substr.rfind("\n") err = func(fname, match) if err is None: continue - errors.append((lineno, col+1, err)) + errors.append((lineno, col + 1, err)) return errors return decor(new_func) + return decorator @@ -152,73 +182,99 @@ def lint_content_find_check(find, **kwargs): errors = [] for line, col in find_all(content, find_): err = func(fname) - errors.append((line+1, col+1, err)) + errors.append((line + 1, col + 1, err)) return errors + return decor(new_func) + return decorator -@lint_file_check(include=['*.ino']) +@lint_file_check(include=["*.ino"]) def lint_ino(fname): return "This file extension (.ino) is not allowed. Please use either .cpp or .h" -@lint_file_check(exclude=[f'*{f}' for f in file_types] + [ - '.clang-*', '.dockerignore', '.editorconfig', '*.gitignore', 'LICENSE', 'pylintrc', - 'MANIFEST.in', 'docker/Dockerfile*', 'docker/rootfs/*', 'script/*', -]) +@lint_file_check( + exclude=[f"*{f}" for f in file_types] + + [ + ".clang-*", + ".dockerignore", + ".editorconfig", + "*.gitignore", + "LICENSE", + "pylintrc", + "MANIFEST.in", + "docker/Dockerfile*", + "docker/rootfs/*", + "script/*", + ] +) def lint_ext_check(fname): - return "This file extension is not a registered file type. If this is an error, please " \ - "update the script/ci-custom.py script." + return ( + "This file extension is not a registered file type. If this is an error, please " + "update the script/ci-custom.py script." + ) -@lint_file_check(exclude=[ - 'docker/rootfs/*', 'script/*', 'setup.py' -]) +@lint_file_check(exclude=["docker/rootfs/*", "script/*", "setup.py"]) def lint_executable_bit(fname): ex = EXECUTABLE_BIT[fname] if ex != 100644: - return 'File has invalid executable bit {}. If running from a windows machine please ' \ - 'see disabling executable bit in git.'.format(ex) + return ( + "File has invalid executable bit {}. If running from a windows machine please " + "see disabling executable bit in git.".format(ex) + ) return None -@lint_content_find_check('\t', exclude=[ - 'esphome/dashboard/static/ace.js', 'esphome/dashboard/static/ext-searchbox.js', -]) +@lint_content_find_check( + "\t", + exclude=[ + "esphome/dashboard/static/ace.js", + "esphome/dashboard/static/ext-searchbox.js", + ], +) def lint_tabs(fname): return "File contains tab character. Please convert tabs to spaces." -@lint_content_find_check('\r') +@lint_content_find_check("\r") def lint_newline(fname): return "File contains windows newline. Please set your editor to unix newline mode." -@lint_content_check(exclude=['*.svg']) +@lint_content_check(exclude=["*.svg"]) def lint_end_newline(fname, content): - if content and not content.endswith('\n'): + if content and not content.endswith("\n"): return "File does not end with a newline, please add an empty line at the end of the file." return None -CPP_RE_EOL = r'\s*?(?://.*?)?$' +CPP_RE_EOL = r"\s*?(?://.*?)?$" def highlight(s): - return f'\033[36m{s}\033[0m' + return f"\033[36m{s}\033[0m" -@lint_re_check(r'^#define\s+([a-zA-Z0-9_]+)\s+([0-9bx]+)' + CPP_RE_EOL, - include=cpp_include, exclude=['esphome/core/log.h']) +@lint_re_check( + r"^#define\s+([a-zA-Z0-9_]+)\s+([0-9bx]+)" + CPP_RE_EOL, + include=cpp_include, + exclude=["esphome/core/log.h"], +) def lint_no_defines(fname, match): - s = highlight('static const uint8_t {} = {};'.format(match.group(1), match.group(2))) - return ("#define macros for integer constants are not allowed, please use " - "{} style instead (replace uint8_t with the appropriate " - "datatype). See also Google style guide.".format(s)) + s = highlight( + "static const uint8_t {} = {};".format(match.group(1), match.group(2)) + ) + return ( + "#define macros for integer constants are not allowed, please use " + "{} style instead (replace uint8_t with the appropriate " + "datatype). See also Google style guide.".format(s) + ) -@lint_re_check(r'^\s*delay\((\d+)\);' + CPP_RE_EOL, include=cpp_include) +@lint_re_check(r"^\s*delay\((\d+)\);" + CPP_RE_EOL, include=cpp_include) def lint_no_long_delays(fname, match): duration_ms = int(match.group(1)) if duration_ms < 50: @@ -232,7 +288,7 @@ def lint_no_long_delays(fname, match): ) -@lint_content_check(include=['esphome/const.py']) +@lint_content_check(include=["esphome/const.py"]) def lint_const_ordered(fname, content): """Lint that value in const.py are ordered. @@ -240,54 +296,67 @@ def lint_const_ordered(fname, content): """ lines = content.splitlines() errors = [] - for start in ['CONF_', 'ICON_', 'UNIT_']: - matching = [(i+1, line) for i, line in enumerate(lines) if line.startswith(start)] - ordered = list(sorted(matching, key=lambda x: x[1].replace('_', ' '))) + for start in ["CONF_", "ICON_", "UNIT_"]: + matching = [ + (i + 1, line) for i, line in enumerate(lines) if line.startswith(start) + ] + ordered = list(sorted(matching, key=lambda x: x[1].replace("_", " "))) ordered = [(mi, ol) for (mi, _), (_, ol) in zip(matching, ordered)] for (mi, ml), (oi, ol) in zip(matching, ordered): if ml == ol: continue target = next(i for i, l in ordered if l == ml) target_text = next(l for i, l in matching if target == i) - errors.append((mi, 1, - f"Constant {highlight(ml)} is not ordered, please make sure all " - f"constants are ordered. See line {mi} (should go to line {target}, " - f"{target_text})")) + errors.append( + ( + mi, + 1, + f"Constant {highlight(ml)} is not ordered, please make sure all " + f"constants are ordered. See line {mi} (should go to line {target}, " + f"{target_text})", + ) + ) return errors -@lint_re_check(r'^\s*CONF_([A-Z_0-9a-z]+)\s+=\s+[\'"](.*?)[\'"]\s*?$', include=['*.py']) +@lint_re_check(r'^\s*CONF_([A-Z_0-9a-z]+)\s+=\s+[\'"](.*?)[\'"]\s*?$', include=["*.py"]) def lint_conf_matches(fname, match): const = match.group(1) value = match.group(2) const_norm = const.lower() - value_norm = value.replace('.', '_') + value_norm = value.replace(".", "_") if const_norm == value_norm: return None - return ("Constant {} does not match value {}! Please make sure the constant's name matches its " - "value!" - "".format(highlight('CONF_' + const), highlight(value))) + return ( + "Constant {} does not match value {}! Please make sure the constant's name matches its " + "value!" + "".format(highlight("CONF_" + const), highlight(value)) + ) CONF_RE = r'^(CONF_[a-zA-Z0-9_]+)\s*=\s*[\'"].*?[\'"]\s*?$' -with codecs.open('esphome/const.py', 'r', encoding='utf-8') as f_handle: +with codecs.open("esphome/const.py", "r", encoding="utf-8") as f_handle: constants_content = f_handle.read() CONSTANTS = [m.group(1) for m in re.finditer(CONF_RE, constants_content, re.MULTILINE)] CONSTANTS_USES = collections.defaultdict(list) -@lint_re_check(CONF_RE, include=['*.py'], exclude=['esphome/const.py']) +@lint_re_check(CONF_RE, include=["*.py"], exclude=["esphome/const.py"]) def lint_conf_from_const_py(fname, match): name = match.group(1) if name not in CONSTANTS: CONSTANTS_USES[name].append(fname) return None - return ("Constant {} has already been defined in const.py - please import the constant from " - "const.py directly.".format(highlight(name))) + return ( + "Constant {} has already been defined in const.py - please import the constant from " + "const.py directly.".format(highlight(name)) + ) -RAW_PIN_ACCESS_RE = r'^\s(pinMode|digitalWrite|digitalRead)\((.*)->get_pin\(\),\s*([^)]+).*\)' +RAW_PIN_ACCESS_RE = ( + r"^\s(pinMode|digitalWrite|digitalRead)\((.*)->get_pin\(\),\s*([^)]+).*\)" +) @lint_re_check(RAW_PIN_ACCESS_RE, include=cpp_include) @@ -296,33 +365,49 @@ def lint_no_raw_pin_access(fname, match): pin = match.group(2) mode = match.group(3) new_func = { - 'pinMode': 'pin_mode', - 'digitalWrite': 'digital_write', - 'digitalRead': 'digital_read', + "pinMode": "pin_mode", + "digitalWrite": "digital_write", + "digitalRead": "digital_read", }[func] - new_code = highlight(f'{pin}->{new_func}({mode})') - return (f"Don't use raw {func} calls. Instead, use the `->{new_func}` function: {new_code}") + new_code = highlight(f"{pin}->{new_func}({mode})") + return f"Don't use raw {func} calls. Instead, use the `->{new_func}` function: {new_code}" # Functions from Arduino framework that are forbidden to use directly ARDUINO_FORBIDDEN = [ - 'digitalWrite', 'digitalRead', 'pinMode', - 'shiftOut', 'shiftIn', - 'radians', 'degrees', - 'interrupts', 'noInterrupts', - 'lowByte', 'highByte', - 'bitRead', 'bitSet', 'bitClear', 'bitWrite', - 'bit', 'analogRead', 'analogWrite', - 'pulseIn', 'pulseInLong', - 'tone', + "digitalWrite", + "digitalRead", + "pinMode", + "shiftOut", + "shiftIn", + "radians", + "degrees", + "interrupts", + "noInterrupts", + "lowByte", + "highByte", + "bitRead", + "bitSet", + "bitClear", + "bitWrite", + "bit", + "analogRead", + "analogWrite", + "pulseIn", + "pulseInLong", + "tone", ] -ARDUINO_FORBIDDEN_RE = r'[^\w\d](' + r'|'.join(ARDUINO_FORBIDDEN) + r')\(.*' +ARDUINO_FORBIDDEN_RE = r"[^\w\d](" + r"|".join(ARDUINO_FORBIDDEN) + r")\(.*" -@lint_re_check(ARDUINO_FORBIDDEN_RE, include=cpp_include, exclude=[ - 'esphome/components/mqtt/custom_mqtt_device.h', - 'esphome/core/esphal.*', -]) +@lint_re_check( + ARDUINO_FORBIDDEN_RE, + include=cpp_include, + exclude=[ + "esphome/components/mqtt/custom_mqtt_device.h", + "esphome/core/esphal.*", + ], +) def lint_no_arduino_framework_functions(fname, match): nolint = highlight("// NOLINT") return ( @@ -334,9 +419,13 @@ def lint_no_arduino_framework_functions(fname, match): ) -@lint_re_check(r'[^\w\d]byte\s+[\w\d]+\s*=', include=cpp_include, exclude={ - 'esphome/components/tuya/tuya.h', -}) +@lint_re_check( + r"[^\w\d]byte\s+[\w\d]+\s*=", + include=cpp_include, + exclude={ + "esphome/components/tuya/tuya.h", + }, +) def lint_no_byte_datatype(fname, match): return ( f"The datatype {highlight('byte')} is not allowed to be used in ESPHome. " @@ -350,112 +439,143 @@ def lint_constants_usage(): for constant, uses in CONSTANTS_USES.items(): if len(uses) < 4: continue - errors.append("Constant {} is defined in {} files. Please move all definitions of the " - "constant to const.py (Uses: {})" - "".format(highlight(constant), len(uses), ', '.join(uses))) + errors.append( + "Constant {} is defined in {} files. Please move all definitions of the " + "constant to const.py (Uses: {})" + "".format(highlight(constant), len(uses), ", ".join(uses)) + ) return errors def relative_cpp_search_text(fname, content): - parts = fname.split('/') + parts = fname.split("/") integration = parts[2] return f'#include "esphome/components/{integration}' -@lint_content_find_check(relative_cpp_search_text, include=['esphome/components/*.cpp']) +@lint_content_find_check(relative_cpp_search_text, include=["esphome/components/*.cpp"]) def lint_relative_cpp_import(fname): - return ("Component contains absolute import - Components must always use " - "relative imports.\n" - "Change:\n" - ' #include "esphome/components/abc/abc.h"\n' - 'to:\n' - ' #include "abc.h"\n\n') + return ( + "Component contains absolute import - Components must always use " + "relative imports.\n" + "Change:\n" + ' #include "esphome/components/abc/abc.h"\n' + "to:\n" + ' #include "abc.h"\n\n' + ) def relative_py_search_text(fname, content): - parts = fname.split('/') + parts = fname.split("/") integration = parts[2] - return f'esphome.components.{integration}' + return f"esphome.components.{integration}" -@lint_content_find_check(relative_py_search_text, include=['esphome/components/*.py'], - exclude=['esphome/components/web_server/__init__.py']) +@lint_content_find_check( + relative_py_search_text, + include=["esphome/components/*.py"], + exclude=["esphome/components/web_server/__init__.py"], +) def lint_relative_py_import(fname): - return ("Component contains absolute import - Components must always use " - "relative imports within the integration.\n" - "Change:\n" - ' from esphome.components.abc import abc_ns"\n' - 'to:\n' - ' from . import abc_ns\n\n') + return ( + "Component contains absolute import - Components must always use " + "relative imports within the integration.\n" + "Change:\n" + ' from esphome.components.abc import abc_ns"\n' + "to:\n" + " from . import abc_ns\n\n" + ) -@lint_content_check(include=['esphome/components/*.h', 'esphome/components/*.cpp', - 'esphome/components/*.tcc']) +@lint_content_check( + include=[ + "esphome/components/*.h", + "esphome/components/*.cpp", + "esphome/components/*.tcc", + ] +) def lint_namespace(fname, content): - expected_name = re.match(r'^esphome/components/([^/]+)/.*', - fname.replace(os.path.sep, '/')).group(1) - search = f'namespace {expected_name}' + expected_name = re.match( + r"^esphome/components/([^/]+)/.*", fname.replace(os.path.sep, "/") + ).group(1) + search = f"namespace {expected_name}" if search in content: return None - return 'Invalid namespace found in C++ file. All integration C++ files should put all ' \ - 'functions in a separate namespace that matches the integration\'s name. ' \ - 'Please make sure the file contains {}'.format(highlight(search)) + return ( + "Invalid namespace found in C++ file. All integration C++ files should put all " + "functions in a separate namespace that matches the integration's name. " + "Please make sure the file contains {}".format(highlight(search)) + ) -@lint_content_find_check('"esphome.h"', include=cpp_include, exclude=['tests/custom.h']) +@lint_content_find_check('"esphome.h"', include=cpp_include, exclude=["tests/custom.h"]) def lint_esphome_h(fname): - return ("File contains reference to 'esphome.h' - This file is " - "auto-generated and should only be used for *custom* " - "components. Please replace with references to the direct files.") + return ( + "File contains reference to 'esphome.h' - This file is " + "auto-generated and should only be used for *custom* " + "components. Please replace with references to the direct files." + ) -@lint_content_check(include=['*.h']) +@lint_content_check(include=["*.h"]) def lint_pragma_once(fname, content): - if '#pragma once' not in content: - return ("Header file contains no 'pragma once' header guard. Please add a " - "'#pragma once' line at the top of the file.") + if "#pragma once" not in content: + return ( + "Header file contains no 'pragma once' header guard. Please add a " + "'#pragma once' line at the top of the file." + ) return None -@lint_re_check(r'(whitelist|blacklist|slave)', - exclude=['script/ci-custom.py'], flags=re.IGNORECASE | re.MULTILINE) +@lint_re_check( + r"(whitelist|blacklist|slave)", + exclude=["script/ci-custom.py"], + flags=re.IGNORECASE | re.MULTILINE, +) def lint_inclusive_language(fname, match): # From https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=49decddd39e5f6132ccd7d9fdc3d7c470b0061bb - return ("Avoid the use of whitelist/blacklist/slave.\n" - "Recommended replacements for 'master / slave' are:\n" - " '{primary,main} / {secondary,replica,subordinate}\n" - " '{initiator,requester} / {target,responder}'\n" - " '{controller,host} / {device,worker,proxy}'\n" - " 'leader / follower'\n" - " 'director / performer'\n" - "\n" - "Recommended replacements for 'blacklist/whitelist' are:\n" - " 'denylist / allowlist'\n" - " 'blocklist / passlist'") + return ( + "Avoid the use of whitelist/blacklist/slave.\n" + "Recommended replacements for 'master / slave' are:\n" + " '{primary,main} / {secondary,replica,subordinate}\n" + " '{initiator,requester} / {target,responder}'\n" + " '{controller,host} / {device,worker,proxy}'\n" + " 'leader / follower'\n" + " 'director / performer'\n" + "\n" + "Recommended replacements for 'blacklist/whitelist' are:\n" + " 'denylist / allowlist'\n" + " 'blocklist / passlist'" + ) - -@lint_content_find_check('ESP_LOG', include=['*.h', '*.tcc'], exclude=[ - 'esphome/components/binary_sensor/binary_sensor.h', - 'esphome/components/cover/cover.h', - 'esphome/components/display/display_buffer.h', - 'esphome/components/i2c/i2c.h', - 'esphome/components/mqtt/mqtt_component.h', - 'esphome/components/output/binary_output.h', - 'esphome/components/output/float_output.h', - 'esphome/components/sensor/sensor.h', - 'esphome/components/stepper/stepper.h', - 'esphome/components/switch/switch.h', - 'esphome/components/text_sensor/text_sensor.h', - 'esphome/components/climate/climate.h', - 'esphome/core/component.h', - 'esphome/core/esphal.h', - 'esphome/core/log.h', - 'tests/custom.h', -]) +@lint_content_find_check( + "ESP_LOG", + include=["*.h", "*.tcc"], + exclude=[ + "esphome/components/binary_sensor/binary_sensor.h", + "esphome/components/cover/cover.h", + "esphome/components/display/display_buffer.h", + "esphome/components/i2c/i2c.h", + "esphome/components/mqtt/mqtt_component.h", + "esphome/components/output/binary_output.h", + "esphome/components/output/float_output.h", + "esphome/components/sensor/sensor.h", + "esphome/components/stepper/stepper.h", + "esphome/components/switch/switch.h", + "esphome/components/text_sensor/text_sensor.h", + "esphome/components/climate/climate.h", + "esphome/core/component.h", + "esphome/core/esphal.h", + "esphome/core/log.h", + "tests/custom.h", + ], +) def lint_log_in_header(fname): - return ('Found reference to ESP_LOG in header file. Using ESP_LOG* in header files ' - 'is currently not possible - please move the definition to a source file (.cpp)') + return ( + "Found reference to ESP_LOG in header file. Using ESP_LOG* in header files " + "is currently not possible - please move the definition to a source file (.cpp)" + ) errors = collections.defaultdict(list) @@ -488,14 +608,17 @@ for fname in files: if ext in ignore_types: continue try: - with codecs.open(fname, 'r', encoding='utf-8') as f_handle: + with codecs.open(fname, "r", encoding="utf-8") as f_handle: content = f_handle.read() except UnicodeDecodeError: - add_errors(fname, "File is not readable as UTF-8. Please set your editor to UTF-8 mode.") + add_errors( + fname, + "File is not readable as UTF-8. Please set your editor to UTF-8 mode.", + ) continue run_checks(LINT_CONTENT_CHECKS, fname, fname, content) -run_checks(LINT_POST_CHECKS, 'POST') +run_checks(LINT_POST_CHECKS, "POST") for f, errs in sorted(errors.items()): print(f"\033[0;32m************* File \033[1;32m{f}\033[0m") @@ -506,8 +629,8 @@ for f, errs in sorted(errors.items()): if args.print_slowest: lint_times = [] for lint in LINT_FILE_CHECKS + LINT_CONTENT_CHECKS + LINT_POST_CHECKS: - durations = lint.get('durations', []) - lint_times.append((sum(durations), len(durations), lint['func'].__name__)) + durations = lint.get("durations", []) + lint_times.append((sum(durations), len(durations), lint["func"].__name__)) lint_times.sort(key=lambda x: -x[0]) for i in range(min(len(lint_times), 10)): dur, invocations, name = lint_times[i] diff --git a/script/helpers.py b/script/helpers.py index e0aaee8711..5b1b7ba918 100644 --- a/script/helpers.py +++ b/script/helpers.py @@ -5,15 +5,15 @@ import re import subprocess import sys -root_path = os.path.abspath(os.path.normpath(os.path.join(__file__, '..', '..'))) -basepath = os.path.join(root_path, 'esphome') -temp_header_file = os.path.join(root_path, '.temp-clang-tidy.cpp') +root_path = os.path.abspath(os.path.normpath(os.path.join(__file__, "..", ".."))) +basepath = os.path.join(root_path, "esphome") +temp_header_file = os.path.join(root_path, ".temp-clang-tidy.cpp") def shlex_quote(s): if not s: return "''" - if re.search(r'[^\w@%+=:,./-]', s) is None: + if re.search(r"[^\w@%+=:,./-]", s) is None: return s return "'" + s.replace("'", "'\"'\"'") + "'" @@ -24,63 +24,71 @@ def build_all_include(): # Otherwise header-only integrations would not be tested by clang-tidy headers = [] for path in walk_files(basepath): - filetypes = ('.h',) + filetypes = (".h",) ext = os.path.splitext(path)[1] if ext in filetypes: path = os.path.relpath(path, root_path) - include_p = path.replace(os.path.sep, '/') + include_p = path.replace(os.path.sep, "/") headers.append(f'#include "{include_p}"') headers.sort() - headers.append('') - content = '\n'.join(headers) - with codecs.open(temp_header_file, 'w', encoding='utf-8') as f: + headers.append("") + content = "\n".join(headers) + with codecs.open(temp_header_file, "w", encoding="utf-8") as f: f.write(content) def build_compile_commands(): - gcc_flags_json = os.path.join(root_path, '.gcc-flags.json') + gcc_flags_json = os.path.join(root_path, ".gcc-flags.json") if not os.path.isfile(gcc_flags_json): print("Could not find {} file which is required for clang-tidy.") - print('Please run "pio init --ide atom" in the root esphome folder to generate that file.') + print( + 'Please run "pio init --ide atom" in the root esphome folder to generate that file.' + ) sys.exit(1) - with codecs.open(gcc_flags_json, 'r', encoding='utf-8') as f: + with codecs.open(gcc_flags_json, "r", encoding="utf-8") as f: gcc_flags = json.load(f) - exec_path = gcc_flags['execPath'] - include_paths = gcc_flags['gccIncludePaths'].split(',') - includes = [f'-I{p}' for p in include_paths] - cpp_flags = gcc_flags['gccDefaultCppFlags'].split(' ') - defines = [flag for flag in cpp_flags if flag.startswith('-D')] + exec_path = gcc_flags["execPath"] + include_paths = gcc_flags["gccIncludePaths"].split(",") + includes = [f"-I{p}" for p in include_paths] + cpp_flags = gcc_flags["gccDefaultCppFlags"].split(" ") + defines = [flag for flag in cpp_flags if flag.startswith("-D")] command = [exec_path] command.extend(includes) command.extend(defines) - command.append('-std=gnu++11') - command.append('-Wall') - command.append('-Wno-delete-non-virtual-dtor') - command.append('-Wno-unused-variable') - command.append('-Wunreachable-code') + command.append("-std=gnu++11") + command.append("-Wall") + command.append("-Wno-delete-non-virtual-dtor") + command.append("-Wno-unused-variable") + command.append("-Wunreachable-code") source_files = [] for path in walk_files(basepath): - filetypes = ('.cpp',) + filetypes = (".cpp",) ext = os.path.splitext(path)[1] if ext in filetypes: source_files.append(os.path.abspath(path)) source_files.append(temp_header_file) source_files.sort() - compile_commands = [{ - 'directory': root_path, - 'command': ' '.join(shlex_quote(x) for x in (command + ['-o', p + '.o', '-c', p])), - 'file': p - } for p in source_files] - compile_commands_json = os.path.join(root_path, 'compile_commands.json') + compile_commands = [ + { + "directory": root_path, + "command": " ".join( + shlex_quote(x) for x in (command + ["-o", p + ".o", "-c", p]) + ), + "file": p, + } + for p in source_files + ] + compile_commands_json = os.path.join(root_path, "compile_commands.json") if os.path.isfile(compile_commands_json): - with codecs.open(compile_commands_json, 'r', encoding='utf-8') as f: + with codecs.open(compile_commands_json, "r", encoding="utf-8") as f: try: if json.load(f) == compile_commands: return + # pylint: disable=bare-except except: pass - with codecs.open(compile_commands_json, 'w', encoding='utf-8') as f: + with codecs.open(compile_commands_json, "w", encoding="utf-8") as f: json.dump(compile_commands, f, indent=2) @@ -93,7 +101,13 @@ def walk_files(path): def get_output(*args): proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) output, err = proc.communicate() - return output.decode('utf-8') + return output.decode("utf-8") + + +def get_err(*args): + proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + output, err = proc.communicate() + return err.decode("utf-8") def splitlines_no_ends(string): @@ -101,18 +115,19 @@ def splitlines_no_ends(string): def changed_files(): - check_remotes = ['upstream', 'origin'] - check_remotes.extend(splitlines_no_ends(get_output('git', 'remote'))) + check_remotes = ["upstream", "origin"] + check_remotes.extend(splitlines_no_ends(get_output("git", "remote"))) for remote in check_remotes: - command = ['git', 'merge-base', f'refs/remotes/{remote}/dev', 'HEAD'] + command = ["git", "merge-base", f"refs/remotes/{remote}/dev", "HEAD"] try: merge_base = splitlines_no_ends(get_output(*command))[0] break + # pylint: disable=bare-except except: pass else: raise ValueError("Git not configured") - command = ['git', 'diff', merge_base, '--name-only'] + command = ["git", "diff", merge_base, "--name-only"] changed = splitlines_no_ends(get_output(*command)) changed = [os.path.relpath(f, os.getcwd()) for f in changed] changed.sort() @@ -131,10 +146,8 @@ def filter_changed(files): def git_ls_files(): - command = ['git', 'ls-files', '-s'] + command = ["git", "ls-files", "-s"] proc = subprocess.Popen(command, stdout=subprocess.PIPE) output, err = proc.communicate() - lines = [x.split() for x in output.decode('utf-8').splitlines()] - return { - s[3].strip(): int(s[0]) for s in lines - } + lines = [x.split() for x in output.decode("utf-8").splitlines()] + return {s[3].strip(): int(s[0]) for s in lines} diff --git a/script/lint-python b/script/lint-python index 4915115262..41885b9672 100755 --- a/script/lint-python +++ b/script/lint-python @@ -1,15 +1,14 @@ #!/usr/bin/env python3 from __future__ import print_function +from helpers import get_output, get_err, git_ls_files, filter_changed import argparse -import collections import os import re import sys sys.path.append(os.path.dirname(__file__)) -from helpers import get_output, git_ls_files, filter_changed curfile = None @@ -22,26 +21,28 @@ def print_error(file, lineno, msg): print("\033[0;32m************* File \033[1;32m{}\033[0m".format(file)) curfile = file - print(u'{}:{} - {}'.format(file, lineno, msg)) + print("{}:{} - {}".format(file, lineno, msg)) def main(): parser = argparse.ArgumentParser() - parser.add_argument('files', nargs='*', default=[], - help='files to be processed (regex on path)') - parser.add_argument('-c', '--changed', action='store_true', - help='Only run on changed files') + parser.add_argument( + "files", nargs="*", default=[], help="files to be processed (regex on path)" + ) + parser.add_argument( + "-c", "--changed", action="store_true", help="Only run on changed files" + ) args = parser.parse_args() files = [] for path in git_ls_files(): - filetypes = ('.py',) + filetypes = (".py",) ext = os.path.splitext(path)[1] - if ext in filetypes and path.startswith('esphome'): + if ext in filetypes and path.startswith("esphome"): path = os.path.relpath(path, os.getcwd()) files.append(path) # Match against re - file_name_re = re.compile('|'.join(args.files)) + file_name_re = re.compile("|".join(args.files)) files = [p for p in files if file_name_re.search(p)] if args.changed: @@ -52,34 +53,45 @@ def main(): sys.exit(0) errors = 0 - cmd = ['flake8'] + files + + cmd = ["black", "--verbose", "--check"] + files + print("Running black...") + log = get_err(*cmd) + for line in log.splitlines(): + WOULD_REFORMAT = "would reformat" + if line.startswith(WOULD_REFORMAT): + file_ = line[len(WOULD_REFORMAT) + 1 :] + print_error(file_, None, "Please format this file with the black formatter") + errors += 1 + + cmd = ["flake8"] + files print("Running flake8...") log = get_output(*cmd) for line in log.splitlines(): - line = line.split(':', 4) + line = line.split(":", 4) if len(line) < 4: continue file_ = line[0] linno = line[1] - msg = (':'.join(line[3:])).strip() + msg = (":".join(line[3:])).strip() print_error(file_, linno, msg) errors += 1 - cmd = ['pylint', '-f', 'parseable', '--persistent=n'] + files + cmd = ["pylint", "-f", "parseable", "--persistent=n"] + files print("Running pylint...") log = get_output(*cmd) for line in log.splitlines(): - line = line.split(':', 3) + line = line.split(":", 3) if len(line) < 3: continue file_ = line[0] linno = line[1] - msg = (':'.join(line[2:])).strip() + msg = (":".join(line[2:])).strip() print_error(file_, linno, msg) errors += 1 sys.exit(errors) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/script/setup b/script/setup index d70a44ee49..199b46891d 100755 --- a/script/setup +++ b/script/setup @@ -6,3 +6,5 @@ set -e cd "$(dirname "$0")/.." pip3 install -r requirements.txt -r requirements_test.txt pip3 install -e . + +pre-commit install diff --git a/setup.cfg b/setup.cfg index 32a60839a5..755cef47c0 100644 --- a/setup.cfg +++ b/setup.cfg @@ -17,6 +17,45 @@ Topic :: Home Automation [flake8] max-line-length = 120 +# Following 4 for black compatibility +# E501: line too long +# W503: Line break occurred before a binary operator +# E203: Whitespace before ':' +# D202 No blank lines allowed after function docstring + +# TODO fix flake8 +# D100 Missing docstring in public module +# D101 Missing docstring in public class +# D102 Missing docstring in public method +# D103 Missing docstring in public function +# D104 Missing docstring in public package +# D105 Missing docstring in magic method +# D107 Missing docstring in __init__ +# D200 One-line docstring should fit on one line with quotes +# D205 1 blank line required between summary line and description +# D209 Multi-line docstring closing quotes should be on a separate line +# D400 First line should end with a period +# D401 First line should be in imperative mood + +ignore = + E501, + W503, + E203, + D202, + + D100, + D101, + D102, + D103, + D104, + D105, + D107, + D200, + D205, + D209, + D400, + D401, + exclude = api_pb2.py [bdist_wheel] diff --git a/tests/component_tests/binary_sensor/test_binary_sensor.py b/tests/component_tests/binary_sensor/test_binary_sensor.py index 72c0dc1cde..8da93a476e 100644 --- a/tests/component_tests/binary_sensor/test_binary_sensor.py +++ b/tests/component_tests/binary_sensor/test_binary_sensor.py @@ -1,4 +1,4 @@ -""" Tests for the binary sensor component """ +"""Tests for the binary sensor component.""" def test_binary_sensor_is_setup(generate_main): @@ -8,7 +8,9 @@ def test_binary_sensor_is_setup(generate_main): # Given # When - main_cpp = generate_main("tests/component_tests/binary_sensor/test_binary_sensor.yaml") + main_cpp = generate_main( + "tests/component_tests/binary_sensor/test_binary_sensor.yaml" + ) # Then assert "new gpio::GPIOBinarySensor();" in main_cpp @@ -22,10 +24,12 @@ def test_binary_sensor_sets_mandatory_fields(generate_main): # Given # When - main_cpp = generate_main("tests/component_tests/binary_sensor/test_binary_sensor.yaml") + main_cpp = generate_main( + "tests/component_tests/binary_sensor/test_binary_sensor.yaml" + ) # Then - assert "bs_1->set_name(\"test bs1\");" in main_cpp + assert 'bs_1->set_name("test bs1");' in main_cpp assert "bs_1->set_pin(new GPIOPin" in main_cpp @@ -36,7 +40,9 @@ def test_binary_sensor_config_value_internal_set(generate_main): # Given # When - main_cpp = generate_main("tests/component_tests/binary_sensor/test_binary_sensor.yaml") + main_cpp = generate_main( + "tests/component_tests/binary_sensor/test_binary_sensor.yaml" + ) # Then assert "bs_1->set_internal(true);" in main_cpp diff --git a/tests/component_tests/sensor/test_sensor.py b/tests/component_tests/sensor/test_sensor.py index e82a024005..35ce1f4e11 100644 --- a/tests/component_tests/sensor/test_sensor.py +++ b/tests/component_tests/sensor/test_sensor.py @@ -1,4 +1,4 @@ -""" Tests for the sensor component """ +"""Tests for the sensor component.""" def test_sensor_device_class_set(generate_main): @@ -11,4 +11,4 @@ def test_sensor_device_class_set(generate_main): main_cpp = generate_main("tests/component_tests/sensor/test_sensor.yaml") # Then - assert "s_1->set_device_class(\"voltage\");" in main_cpp + assert 's_1->set_device_class("voltage");' in main_cpp diff --git a/tests/unit_tests/conftest.py b/tests/unit_tests/conftest.py index adef39a0b3..41d0f3dadb 100644 --- a/tests/unit_tests/conftest.py +++ b/tests/unit_tests/conftest.py @@ -27,4 +27,3 @@ def fixture_path() -> Path: Location of all fixture files. """ return here / "fixtures" - diff --git a/tests/unit_tests/strategies.py b/tests/unit_tests/strategies.py index f4763f047f..4bc0482f5f 100644 --- a/tests/unit_tests/strategies.py +++ b/tests/unit_tests/strategies.py @@ -12,4 +12,6 @@ def mac_addr_strings(): This consists of six strings representing integers [0..255], without zero-padding, joined by dots. """ - return st.builds("{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}".format, *(6 * [st.integers(0, 255)])) + return st.builds( + "{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}".format, *(6 * [st.integers(0, 255)]) + ) diff --git a/tests/unit_tests/test_codegen.py b/tests/unit_tests/test_codegen.py index 931e191de6..9f402465fa 100644 --- a/tests/unit_tests/test_codegen.py +++ b/tests/unit_tests/test_codegen.py @@ -4,23 +4,75 @@ from esphome import codegen as cg # Test interface remains the same. -@pytest.mark.parametrize("attr", ( - # from cpp_generator - "Expression", "RawExpression", "RawStatement", "TemplateArguments", - "StructInitializer", "ArrayInitializer", "safe_exp", "Statement", "LineComment", - "progmem_array", "statement", "variable", "Pvariable", "new_Pvariable", - "add", "add_global", "add_library", "add_build_flag", "add_define", - "get_variable", "get_variable_with_full_id", "process_lambda", "is_template", "templatable", "MockObj", - "MockObjClass", - # from cpp_helpers - "gpio_pin_expression", "register_component", "build_registry_entry", - "build_registry_list", "extract_registry_entry_config", "register_parented", - "global_ns", "void", "nullptr", "float_", "double", "bool_", "int_", "std_ns", "std_string", - "std_vector", "uint8", "uint16", "uint32", "int32", "const_char_ptr", "NAN", - "esphome_ns", "App", "Nameable", "Component", "ComponentPtr", - # from cpp_types - "PollingComponent", "Application", "optional", "arduino_json_ns", "JsonObject", - "JsonObjectRef", "JsonObjectConstRef", "Controller", "GPIOPin" -)) +@pytest.mark.parametrize( + "attr", + ( + # from cpp_generator + "Expression", + "RawExpression", + "RawStatement", + "TemplateArguments", + "StructInitializer", + "ArrayInitializer", + "safe_exp", + "Statement", + "LineComment", + "progmem_array", + "statement", + "variable", + "Pvariable", + "new_Pvariable", + "add", + "add_global", + "add_library", + "add_build_flag", + "add_define", + "get_variable", + "get_variable_with_full_id", + "process_lambda", + "is_template", + "templatable", + "MockObj", + "MockObjClass", + # from cpp_helpers + "gpio_pin_expression", + "register_component", + "build_registry_entry", + "build_registry_list", + "extract_registry_entry_config", + "register_parented", + "global_ns", + "void", + "nullptr", + "float_", + "double", + "bool_", + "int_", + "std_ns", + "std_string", + "std_vector", + "uint8", + "uint16", + "uint32", + "int32", + "const_char_ptr", + "NAN", + "esphome_ns", + "App", + "Nameable", + "Component", + "ComponentPtr", + # from cpp_types + "PollingComponent", + "Application", + "optional", + "arduino_json_ns", + "JsonObject", + "JsonObjectRef", + "JsonObjectConstRef", + "Controller", + "GPIOPin", + ), +) def test_exists(attr): assert hasattr(cg, attr) diff --git a/tests/unit_tests/test_config_validation.py b/tests/unit_tests/test_config_validation.py index 846df71a94..949d4251ee 100644 --- a/tests/unit_tests/test_config_validation.py +++ b/tests/unit_tests/test_config_validation.py @@ -2,7 +2,7 @@ import pytest import string from hypothesis import given, example -from hypothesis.strategies import one_of, text, integers, booleans, builds +from hypothesis.strategies import one_of, text, integers, builds from esphome import config_validation from esphome.config_validation import Invalid @@ -24,7 +24,7 @@ def test_alphanumeric__valid(value): @pytest.mark.parametrize("value", ("£23", "Foo!")) def test_alphanumeric__invalid(value): with pytest.raises(Invalid): - actual = config_validation.alphanumeric(value) + config_validation.alphanumeric(value) @given(value=text(alphabet=string.ascii_lowercase + string.digits + "_-")) @@ -34,9 +34,7 @@ def test_valid_name__valid(value): assert actual == value -@pytest.mark.parametrize("value", ( - "foo bar", "FooBar", "foo::bar" -)) +@pytest.mark.parametrize("value", ("foo bar", "FooBar", "foo::bar")) def test_valid_name__invalid(value): with pytest.raises(Invalid): config_validation.valid_name(value) @@ -49,9 +47,7 @@ def test_string__valid(value): assert actual == str(value) -@pytest.mark.parametrize("value", ( - {}, [], True, False, None -)) +@pytest.mark.parametrize("value", ({}, [], True, False, None)) def test_string__invalid(value): with pytest.raises(Invalid): config_validation.string(value) @@ -83,23 +79,17 @@ def test_icon__invalid(): config_validation.icon("foo") -@pytest.mark.parametrize("value", ( - "True", "YES", "on", "enAblE", True -)) +@pytest.mark.parametrize("value", ("True", "YES", "on", "enAblE", True)) def test_boolean__valid_true(value): assert config_validation.boolean(value) is True -@pytest.mark.parametrize("value", ( - "False", "NO", "off", "disAblE", False -)) +@pytest.mark.parametrize("value", ("False", "NO", "off", "disAblE", False)) def test_boolean__valid_false(value): assert config_validation.boolean(value) is False -@pytest.mark.parametrize("value", ( - None, 1, 0, "foo" -)) +@pytest.mark.parametrize("value", (None, 1, 0, "foo")) def test_boolean__invalid(value): with pytest.raises(Invalid, match="Expected boolean value"): config_validation.boolean(value) diff --git a/tests/unit_tests/test_core.py b/tests/unit_tests/test_core.py index 27b64ec3d5..fd3f171275 100644 --- a/tests/unit_tests/test_core.py +++ b/tests/unit_tests/test_core.py @@ -8,13 +8,16 @@ from esphome import core, const class TestHexInt: - @pytest.mark.parametrize("value, expected", ( - (1, "0x01"), - (255, "0xFF"), - (128, "0x80"), - (256, "0x100"), - (-1, "-0x01"), # TODO: this currently fails - )) + @pytest.mark.parametrize( + "value, expected", + ( + (1, "0x01"), + (255, "0xFF"), + (128, "0x80"), + (256, "0x100"), + (-1, "-0x01"), # TODO: this currently fails + ), + ) def test_str(self, value, expected): target = core.HexInt(value) @@ -68,18 +71,14 @@ class TestMACAddress: assert actual.text == "0xDEADBEEF00FFULL" -@pytest.mark.parametrize("value", ( - 1, 2, -1, 0, 1.0, -1.0, 42.0009, -42.0009 -)) +@pytest.mark.parametrize("value", (1, 2, -1, 0, 1.0, -1.0, 42.0009, -42.0009)) def test_is_approximately_integer__in_range(value): actual = core.is_approximately_integer(value) assert actual is True -@pytest.mark.parametrize("value", ( - 42.01, -42.01, 1.5 -)) +@pytest.mark.parametrize("value", (42.01, -42.01, 1.5)) def test_is_approximately_integer__not_in_range(value): actual = core.is_approximately_integer(value) @@ -87,26 +86,29 @@ def test_is_approximately_integer__not_in_range(value): class TestTimePeriod: - @pytest.mark.parametrize("kwargs, expected", ( - ({}, {}), - ({"microseconds": 1}, {"microseconds": 1}), - ({"microseconds": 1.0001}, {"microseconds": 1}), - ({"milliseconds": 2}, {"milliseconds": 2}), - ({"milliseconds": 2.0001}, {"milliseconds": 2}), - ({"milliseconds": 2.01}, {"milliseconds": 2, "microseconds": 10}), - ({"seconds": 3}, {"seconds": 3}), - ({"seconds": 3.0001}, {"seconds": 3}), - ({"seconds": 3.01}, {"seconds": 3, "milliseconds": 10}), - ({"minutes": 4}, {"minutes": 4}), - ({"minutes": 4.0001}, {"minutes": 4}), - ({"minutes": 4.1}, {"minutes": 4, "seconds": 6}), - ({"hours": 5}, {"hours": 5}), - ({"hours": 5.0001}, {"hours": 5}), - ({"hours": 5.1}, {"hours": 5, "minutes": 6}), - ({"days": 6}, {"days": 6}), - ({"days": 6.0001}, {"days": 6}), - ({"days": 6.1}, {"days": 6, "hours": 2, "minutes": 24}), - )) + @pytest.mark.parametrize( + "kwargs, expected", + ( + ({}, {}), + ({"microseconds": 1}, {"microseconds": 1}), + ({"microseconds": 1.0001}, {"microseconds": 1}), + ({"milliseconds": 2}, {"milliseconds": 2}), + ({"milliseconds": 2.0001}, {"milliseconds": 2}), + ({"milliseconds": 2.01}, {"milliseconds": 2, "microseconds": 10}), + ({"seconds": 3}, {"seconds": 3}), + ({"seconds": 3.0001}, {"seconds": 3}), + ({"seconds": 3.01}, {"seconds": 3, "milliseconds": 10}), + ({"minutes": 4}, {"minutes": 4}), + ({"minutes": 4.0001}, {"minutes": 4}), + ({"minutes": 4.1}, {"minutes": 4, "seconds": 6}), + ({"hours": 5}, {"hours": 5}), + ({"hours": 5.0001}, {"hours": 5}), + ({"hours": 5.1}, {"hours": 5, "minutes": 6}), + ({"days": 6}, {"days": 6}), + ({"days": 6.0001}, {"days": 6}), + ({"days": 6.1}, {"days": 6, "hours": 2, "minutes": 24}), + ), + ) def test_init(self, kwargs, expected): target = core.TimePeriod(**kwargs) @@ -118,26 +120,29 @@ class TestTimePeriod: with pytest.raises(ValueError, match="Maximum precision is microseconds"): core.TimePeriod(microseconds=1.1) - @pytest.mark.parametrize("kwargs, expected", ( - ({}, "0s"), - ({"microseconds": 1}, "1us"), - ({"microseconds": 1.0001}, "1us"), - ({"milliseconds": 2}, "2ms"), - ({"milliseconds": 2.0001}, "2ms"), - ({"milliseconds": 2.01}, "2010us"), - ({"seconds": 3}, "3s"), - ({"seconds": 3.0001}, "3s"), - ({"seconds": 3.01}, "3010ms"), - ({"minutes": 4}, "4min"), - ({"minutes": 4.0001}, "4min"), - ({"minutes": 4.1}, "246s"), - ({"hours": 5}, "5h"), - ({"hours": 5.0001}, "5h"), - ({"hours": 5.1}, "306min"), - ({"days": 6}, "6d"), - ({"days": 6.0001}, "6d"), - ({"days": 6.1}, "8784min"), - )) + @pytest.mark.parametrize( + "kwargs, expected", + ( + ({}, "0s"), + ({"microseconds": 1}, "1us"), + ({"microseconds": 1.0001}, "1us"), + ({"milliseconds": 2}, "2ms"), + ({"milliseconds": 2.0001}, "2ms"), + ({"milliseconds": 2.01}, "2010us"), + ({"seconds": 3}, "3s"), + ({"seconds": 3.0001}, "3s"), + ({"seconds": 3.01}, "3010ms"), + ({"minutes": 4}, "4min"), + ({"minutes": 4.0001}, "4min"), + ({"minutes": 4.1}, "246s"), + ({"hours": 5}, "5h"), + ({"hours": 5.0001}, "5h"), + ({"hours": 5.1}, "306min"), + ({"days": 6}, "6d"), + ({"days": 6.0001}, "6d"), + ({"days": 6.1}, "8784min"), + ), + ) def test_str(self, kwargs, expected): target = core.TimePeriod(**kwargs) @@ -145,61 +150,59 @@ class TestTimePeriod: assert actual == expected - @pytest.mark.parametrize("comparison, other, expected", ( - ("__eq__", core.TimePeriod(microseconds=900), False), - ("__eq__", core.TimePeriod(milliseconds=1), True), - ("__eq__", core.TimePeriod(microseconds=1100), False), - ("__eq__", 1000, NotImplemented), - ("__eq__", "1000", NotImplemented), - ("__eq__", True, NotImplemented), - ("__eq__", object(), NotImplemented), - ("__eq__", None, NotImplemented), - - ("__ne__", core.TimePeriod(microseconds=900), True), - ("__ne__", core.TimePeriod(milliseconds=1), False), - ("__ne__", core.TimePeriod(microseconds=1100), True), - ("__ne__", 1000, NotImplemented), - ("__ne__", "1000", NotImplemented), - ("__ne__", True, NotImplemented), - ("__ne__", object(), NotImplemented), - ("__ne__", None, NotImplemented), - - ("__lt__", core.TimePeriod(microseconds=900), False), - ("__lt__", core.TimePeriod(milliseconds=1), False), - ("__lt__", core.TimePeriod(microseconds=1100), True), - ("__lt__", 1000, NotImplemented), - ("__lt__", "1000", NotImplemented), - ("__lt__", True, NotImplemented), - ("__lt__", object(), NotImplemented), - ("__lt__", None, NotImplemented), - - ("__gt__", core.TimePeriod(microseconds=900), True), - ("__gt__", core.TimePeriod(milliseconds=1), False), - ("__gt__", core.TimePeriod(microseconds=1100), False), - ("__gt__", 1000, NotImplemented), - ("__gt__", "1000", NotImplemented), - ("__gt__", True, NotImplemented), - ("__gt__", object(), NotImplemented), - ("__gt__", None, NotImplemented), - - ("__le__", core.TimePeriod(microseconds=900), False), - ("__le__", core.TimePeriod(milliseconds=1), True), - ("__le__", core.TimePeriod(microseconds=1100), True), - ("__le__", 1000, NotImplemented), - ("__le__", "1000", NotImplemented), - ("__le__", True, NotImplemented), - ("__le__", object(), NotImplemented), - ("__le__", None, NotImplemented), - - ("__ge__", core.TimePeriod(microseconds=900), True), - ("__ge__", core.TimePeriod(milliseconds=1), True), - ("__ge__", core.TimePeriod(microseconds=1100), False), - ("__ge__", 1000, NotImplemented), - ("__ge__", "1000", NotImplemented), - ("__ge__", True, NotImplemented), - ("__ge__", object(), NotImplemented), - ("__ge__", None, NotImplemented), - )) + @pytest.mark.parametrize( + "comparison, other, expected", + ( + ("__eq__", core.TimePeriod(microseconds=900), False), + ("__eq__", core.TimePeriod(milliseconds=1), True), + ("__eq__", core.TimePeriod(microseconds=1100), False), + ("__eq__", 1000, NotImplemented), + ("__eq__", "1000", NotImplemented), + ("__eq__", True, NotImplemented), + ("__eq__", object(), NotImplemented), + ("__eq__", None, NotImplemented), + ("__ne__", core.TimePeriod(microseconds=900), True), + ("__ne__", core.TimePeriod(milliseconds=1), False), + ("__ne__", core.TimePeriod(microseconds=1100), True), + ("__ne__", 1000, NotImplemented), + ("__ne__", "1000", NotImplemented), + ("__ne__", True, NotImplemented), + ("__ne__", object(), NotImplemented), + ("__ne__", None, NotImplemented), + ("__lt__", core.TimePeriod(microseconds=900), False), + ("__lt__", core.TimePeriod(milliseconds=1), False), + ("__lt__", core.TimePeriod(microseconds=1100), True), + ("__lt__", 1000, NotImplemented), + ("__lt__", "1000", NotImplemented), + ("__lt__", True, NotImplemented), + ("__lt__", object(), NotImplemented), + ("__lt__", None, NotImplemented), + ("__gt__", core.TimePeriod(microseconds=900), True), + ("__gt__", core.TimePeriod(milliseconds=1), False), + ("__gt__", core.TimePeriod(microseconds=1100), False), + ("__gt__", 1000, NotImplemented), + ("__gt__", "1000", NotImplemented), + ("__gt__", True, NotImplemented), + ("__gt__", object(), NotImplemented), + ("__gt__", None, NotImplemented), + ("__le__", core.TimePeriod(microseconds=900), False), + ("__le__", core.TimePeriod(milliseconds=1), True), + ("__le__", core.TimePeriod(microseconds=1100), True), + ("__le__", 1000, NotImplemented), + ("__le__", "1000", NotImplemented), + ("__le__", True, NotImplemented), + ("__le__", object(), NotImplemented), + ("__le__", None, NotImplemented), + ("__ge__", core.TimePeriod(microseconds=900), True), + ("__ge__", core.TimePeriod(milliseconds=1), True), + ("__ge__", core.TimePeriod(microseconds=1100), False), + ("__ge__", 1000, NotImplemented), + ("__ge__", "1000", NotImplemented), + ("__ge__", True, NotImplemented), + ("__ge__", object(), NotImplemented), + ("__ge__", None, NotImplemented), + ), + ) def test_comparison(self, comparison, other, expected): target = core.TimePeriod(microseconds=1000) @@ -238,19 +241,19 @@ class TestLambda: "it.strftime(64, 0, ", "my_font", "", - ", TextAlign::TOP_CENTER, \"%H:%M:%S\", ", + ', TextAlign::TOP_CENTER, "%H:%M:%S", ', "esptime", ".", "now());\nit.printf(64, 16, ", "my_font2", "", - ", TextAlign::TOP_CENTER, \"%.1f°C (%.1f%%)\", ", + ', TextAlign::TOP_CENTER, "%.1f°C (%.1f%%)", ', "office_tmp", ".", "state, ", "office_hmd", ".", - "state);\n \nint x = 4; " + "state);\n \nint x = 4; ", ] def test_requires_ids(self): @@ -296,24 +299,33 @@ class TestID: def target(self): return core.ID(None, is_declaration=True, type="binary_sensor::Example") - @pytest.mark.parametrize("id, is_manual, expected", ( - ("foo", None, True), - (None, None, False), - ("foo", True, True), - ("foo", False, False), - (None, True, True), - )) + @pytest.mark.parametrize( + "id, is_manual, expected", + ( + ("foo", None, True), + (None, None, False), + ("foo", True, True), + ("foo", False, False), + (None, True, True), + ), + ) def test_init__resolve_is_manual(self, id, is_manual, expected): target = core.ID(id, is_manual=is_manual) assert target.is_manual == expected - @pytest.mark.parametrize("registered_ids, expected", ( - ([], "binary_sensor_example"), - (["binary_sensor_example"], "binary_sensor_example_2"), - (["foo"], "binary_sensor_example"), - (["binary_sensor_example", "foo", "binary_sensor_example_2"], "binary_sensor_example_3"), - )) + @pytest.mark.parametrize( + "registered_ids, expected", + ( + ([], "binary_sensor_example"), + (["binary_sensor_example"], "binary_sensor_example_2"), + (["foo"], "binary_sensor_example"), + ( + ["binary_sensor_example", "foo", "binary_sensor_example_2"], + "binary_sensor_example_3", + ), + ), + ) def test_resolve(self, target, registered_ids, expected): actual = target.resolve(registered_ids) @@ -326,18 +338,23 @@ class TestID: actual = target.copy() assert actual is not target - assert all(getattr(actual, n) == getattr(target, n) - for n in ("id", "is_declaration", "type", "is_manual")) + assert all( + getattr(actual, n) == getattr(target, n) + for n in ("id", "is_declaration", "type", "is_manual") + ) - @pytest.mark.parametrize("comparison, other, expected", ( - ("__eq__", core.ID(id="foo"), True), - ("__eq__", core.ID(id="bar"), False), - ("__eq__", 1000, NotImplemented), - ("__eq__", "1000", NotImplemented), - ("__eq__", True, NotImplemented), - ("__eq__", object(), NotImplemented), - ("__eq__", None, NotImplemented), - )) + @pytest.mark.parametrize( + "comparison, other, expected", + ( + ("__eq__", core.ID(id="foo"), True), + ("__eq__", core.ID(id="bar"), False), + ("__eq__", 1000, NotImplemented), + ("__eq__", "1000", NotImplemented), + ("__eq__", True, NotImplemented), + ("__eq__", object(), NotImplemented), + ("__eq__", None, NotImplemented), + ), + ) def test_comparison(self, comparison, other, expected): target = core.ID(id="foo") @@ -384,14 +401,17 @@ class TestDocumentRange: class TestDefine: - @pytest.mark.parametrize("name, value, prop, expected", ( - ("ANSWER", None, "as_build_flag", "-DANSWER"), - ("ANSWER", None, "as_macro", "#define ANSWER"), - ("ANSWER", None, "as_tuple", ("ANSWER", None)), - ("ANSWER", 42, "as_build_flag", "-DANSWER=42"), - ("ANSWER", 42, "as_macro", "#define ANSWER 42"), - ("ANSWER", 42, "as_tuple", ("ANSWER", 42)), - )) + @pytest.mark.parametrize( + "name, value, prop, expected", + ( + ("ANSWER", None, "as_build_flag", "-DANSWER"), + ("ANSWER", None, "as_macro", "#define ANSWER"), + ("ANSWER", None, "as_tuple", ("ANSWER", None)), + ("ANSWER", 42, "as_build_flag", "-DANSWER=42"), + ("ANSWER", 42, "as_macro", "#define ANSWER 42"), + ("ANSWER", 42, "as_tuple", ("ANSWER", 42)), + ), + ) def test_properties(self, name, value, prop, expected): target = core.Define(name, value) @@ -399,18 +419,21 @@ class TestDefine: assert actual == expected - @pytest.mark.parametrize("comparison, other, expected", ( - ("__eq__", core.Define(name="FOO", value=42), True), - ("__eq__", core.Define(name="FOO", value=13), False), - ("__eq__", core.Define(name="FOO"), False), - ("__eq__", core.Define(name="BAR", value=42), False), - ("__eq__", core.Define(name="BAR"), False), - ("__eq__", 1000, NotImplemented), - ("__eq__", "1000", NotImplemented), - ("__eq__", True, NotImplemented), - ("__eq__", object(), NotImplemented), - ("__eq__", None, NotImplemented), - )) + @pytest.mark.parametrize( + "comparison, other, expected", + ( + ("__eq__", core.Define(name="FOO", value=42), True), + ("__eq__", core.Define(name="FOO", value=13), False), + ("__eq__", core.Define(name="FOO"), False), + ("__eq__", core.Define(name="BAR", value=42), False), + ("__eq__", core.Define(name="BAR"), False), + ("__eq__", 1000, NotImplemented), + ("__eq__", "1000", NotImplemented), + ("__eq__", True, NotImplemented), + ("__eq__", object(), NotImplemented), + ("__eq__", None, NotImplemented), + ), + ) def test_comparison(self, comparison, other, expected): target = core.Define(name="FOO", value=42) @@ -420,12 +443,15 @@ class TestDefine: class TestLibrary: - @pytest.mark.parametrize("name, value, prop, expected", ( - ("mylib", None, "as_lib_dep", "mylib"), - ("mylib", None, "as_tuple", ("mylib", None)), - ("mylib", "1.2.3", "as_lib_dep", "mylib@1.2.3"), - ("mylib", "1.2.3", "as_tuple", ("mylib", "1.2.3")), - )) + @pytest.mark.parametrize( + "name, value, prop, expected", + ( + ("mylib", None, "as_lib_dep", "mylib"), + ("mylib", None, "as_tuple", ("mylib", None)), + ("mylib", "1.2.3", "as_lib_dep", "mylib@1.2.3"), + ("mylib", "1.2.3", "as_tuple", ("mylib", "1.2.3")), + ), + ) def test_properties(self, name, value, prop, expected): target = core.Library(name, value) @@ -433,16 +459,19 @@ class TestLibrary: assert actual == expected - @pytest.mark.parametrize("comparison, other, expected", ( - ("__eq__", core.Library(name="libfoo", version="1.2.3"), True), - ("__eq__", core.Library(name="libfoo", version="1.2.4"), False), - ("__eq__", core.Library(name="libbar", version="1.2.3"), False), - ("__eq__", 1000, NotImplemented), - ("__eq__", "1000", NotImplemented), - ("__eq__", True, NotImplemented), - ("__eq__", object(), NotImplemented), - ("__eq__", None, NotImplemented), - )) + @pytest.mark.parametrize( + "comparison, other, expected", + ( + ("__eq__", core.Library(name="libfoo", version="1.2.3"), True), + ("__eq__", core.Library(name="libfoo", version="1.2.4"), False), + ("__eq__", core.Library(name="libbar", version="1.2.3"), False), + ("__eq__", 1000, NotImplemented), + ("__eq__", "1000", NotImplemented), + ("__eq__", True, NotImplemented), + ("__eq__", object(), NotImplemented), + ("__eq__", None, NotImplemented), + ), + ) def test_comparison(self, comparison, other, expected): target = core.Library(name="libfoo", version="1.2.3") diff --git a/tests/unit_tests/test_cpp_generator.py b/tests/unit_tests/test_cpp_generator.py index b130124b54..5a8087ffa9 100644 --- a/tests/unit_tests/test_cpp_generator.py +++ b/tests/unit_tests/test_cpp_generator.py @@ -9,18 +9,18 @@ from esphome import cpp_types as ct class TestExpressions: - @pytest.mark.parametrize("target, expected", ( - (cg.RawExpression("foo && bar"), "foo && bar"), - - (cg.AssignmentExpression(None, None, "foo", "bar", None), 'foo = "bar"'), - (cg.AssignmentExpression(ct.float_, "*", "foo", 1, None), 'float *foo = 1'), - (cg.AssignmentExpression(ct.float_, "", "foo", 1, None), 'float foo = 1'), - - (cg.VariableDeclarationExpression(ct.int32, "*", "foo"), "int32_t *foo"), - (cg.VariableDeclarationExpression(ct.int32, "", "foo"), "int32_t foo"), - - (cg.ParameterExpression(ct.std_string, "foo"), "std::string foo"), - )) + @pytest.mark.parametrize( + "target, expected", + ( + (cg.RawExpression("foo && bar"), "foo && bar"), + (cg.AssignmentExpression(None, None, "foo", "bar", None), 'foo = "bar"'), + (cg.AssignmentExpression(ct.float_, "*", "foo", 1, None), "float *foo = 1"), + (cg.AssignmentExpression(ct.float_, "", "foo", 1, None), "float foo = 1"), + (cg.VariableDeclarationExpression(ct.int32, "*", "foo"), "int32_t *foo"), + (cg.VariableDeclarationExpression(ct.int32, "", "foo"), "int32_t foo"), + (cg.ParameterExpression(ct.std_string, "foo"), "std::string foo"), + ), + ) def test_str__simple(self, target: cg.Expression, expected: str): actual = str(target) @@ -67,10 +67,7 @@ class TestTemplateArguments: class TestCallExpression: def test_str__no_template_args(self): - target = cg.CallExpression( - cg.RawExpression("my_function"), - 1, "2", False - ) + target = cg.CallExpression(cg.RawExpression("my_function"), 1, "2", False) actual = str(target) @@ -80,7 +77,9 @@ class TestCallExpression: target = cg.CallExpression( cg.RawExpression("my_function"), cg.TemplateArguments(int, float), - 1, "2", False + 1, + "2", + False, ) actual = str(target) @@ -100,36 +99,32 @@ class TestStructInitializer: actual = str(target) - assert actual == 'foo::MyStruct{\n' \ - ' .state = "on",\n' \ - ' .min_length = 1,\n' \ - ' .max_length = 5,\n' \ - '}' + assert ( + actual == "foo::MyStruct{\n" + ' .state = "on",\n' + " .min_length = 1,\n" + " .max_length = 5,\n" + "}" + ) class TestArrayInitializer: def test_str__empty(self): - target = cg.ArrayInitializer( - None, None - ) + target = cg.ArrayInitializer(None, None) actual = str(target) assert actual == "{}" def test_str__not_multiline(self): - target = cg.ArrayInitializer( - 1, 2, 3, 4 - ) + target = cg.ArrayInitializer(1, 2, 3, 4) actual = str(target) assert actual == "{1, 2, 3, 4}" def test_str__multiline(self): - target = cg.ArrayInitializer( - 1, 2, 3, 4, multiline=True - ) + target = cg.ArrayInitializer(1, 2, 3, 4, multiline=True) actual = str(target) @@ -169,7 +164,7 @@ class TestLambdaExpression: def test_str__with_return(self): target = cg.LambdaExpression( - ("return (foo == 5) && (bar < 10));", ), + ("return (foo == 5) && (bar < 10));",), cg.ParameterListExpression((int, "foo"), (float, "bar")), "=", bool, @@ -185,27 +180,26 @@ class TestLambdaExpression: class TestLiterals: - @pytest.mark.parametrize("target, expected", ( - (cg.StringLiteral("foo"), '"foo"'), - - (cg.IntLiteral(0), "0"), - (cg.IntLiteral(42), "42"), - (cg.IntLiteral(4304967295), "4304967295ULL"), - (cg.IntLiteral(2150483647), "2150483647UL"), - (cg.IntLiteral(-2150083647), "-2150083647LL"), - - (cg.BoolLiteral(True), "true"), - (cg.BoolLiteral(False), "false"), - - (cg.HexIntLiteral(0), "0x00"), - (cg.HexIntLiteral(42), "0x2A"), - (cg.HexIntLiteral(682), "0x2AA"), - - (cg.FloatLiteral(0.0), "0.0f"), - (cg.FloatLiteral(4.2), "4.2f"), - (cg.FloatLiteral(1.23456789), "1.23456789f"), - (cg.FloatLiteral(math.nan), "NAN"), - )) + @pytest.mark.parametrize( + "target, expected", + ( + (cg.StringLiteral("foo"), '"foo"'), + (cg.IntLiteral(0), "0"), + (cg.IntLiteral(42), "42"), + (cg.IntLiteral(4304967295), "4304967295ULL"), + (cg.IntLiteral(2150483647), "2150483647UL"), + (cg.IntLiteral(-2150083647), "-2150083647LL"), + (cg.BoolLiteral(True), "true"), + (cg.BoolLiteral(False), "false"), + (cg.HexIntLiteral(0), "0x00"), + (cg.HexIntLiteral(42), "0x2A"), + (cg.HexIntLiteral(682), "0x2AA"), + (cg.FloatLiteral(0.0), "0.0f"), + (cg.FloatLiteral(4.2), "4.2f"), + (cg.FloatLiteral(1.23456789), "1.23456789f"), + (cg.FloatLiteral(math.nan), "NAN"), + ), + ) def test_str__simple(self, target: cg.Literal, expected: str): actual = str(target) @@ -216,7 +210,9 @@ FAKE_ENUM_VALUE = cg.EnumValue() FAKE_ENUM_VALUE.enum_value = "foo" -@pytest.mark.parametrize("obj, expected_type", ( +@pytest.mark.parametrize( + "obj, expected_type", + ( (cg.RawExpression("foo"), cg.RawExpression), (FAKE_ENUM_VALUE, cg.StringLiteral), (True, cg.BoolLiteral), @@ -230,49 +226,59 @@ FAKE_ENUM_VALUE.enum_value = "foo" (cg.TimePeriodMinutes(minutes=42), cg.IntLiteral), ((1, 2, 3), cg.ArrayInitializer), ([1, 2, 3], cg.ArrayInitializer), -)) + ), +) def test_safe_exp__allowed_values(obj, expected_type): actual = cg.safe_exp(obj) assert isinstance(actual, expected_type) -@pytest.mark.parametrize("obj, expected_type", ( +@pytest.mark.parametrize( + "obj, expected_type", + ( (bool, ct.bool_), (int, ct.int32), (float, ct.float_), -)) + ), +) def test_safe_exp__allowed_types(obj, expected_type): actual = cg.safe_exp(obj) assert actual is expected_type -@pytest.mark.parametrize("obj, expected_error", ( +@pytest.mark.parametrize( + "obj, expected_error", + ( (cg.ID("foo"), "Object foo is an ID."), ((x for x in "foo"), r"Object <.*> is a coroutine."), (None, "Object is not an expression"), -)) + ), +) def test_safe_exp__invalid_values(obj, expected_error): with pytest.raises(ValueError, match=expected_error): cg.safe_exp(obj) class TestStatements: - @pytest.mark.parametrize("target, expected", ( - (cg.RawStatement("foo && bar"), "foo && bar"), - - (cg.ExpressionStatement("foo"), '"foo";'), - (cg.ExpressionStatement(42), '42;'), - - (cg.LineComment("The point of foo is..."), "// The point of foo is..."), - (cg.LineComment("Help help\nI'm being repressed"), "// Help help\n// I'm being repressed"), - + @pytest.mark.parametrize( + "target, expected", ( - cg.ProgmemAssignmentExpression(ct.uint16, "foo", "bar", None), - 'static const uint16_t foo[] PROGMEM = "bar"' - ) - )) + (cg.RawStatement("foo && bar"), "foo && bar"), + (cg.ExpressionStatement("foo"), '"foo";'), + (cg.ExpressionStatement(42), "42;"), + (cg.LineComment("The point of foo is..."), "// The point of foo is..."), + ( + cg.LineComment("Help help\nI'm being repressed"), + "// Help help\n// I'm being repressed", + ), + ( + cg.ProgmemAssignmentExpression(ct.uint16, "foo", "bar", None), + 'static const uint16_t foo[] PROGMEM = "bar"', + ), + ), + ) def test_str__simple(self, target: cg.Statement, expected: str): actual = str(target) diff --git a/tests/unit_tests/test_cpp_helpers.py b/tests/unit_tests/test_cpp_helpers.py index d8f32e7a51..c6f37f6b5d 100644 --- a/tests/unit_tests/test_cpp_helpers.py +++ b/tests/unit_tests/test_cpp_helpers.py @@ -15,11 +15,9 @@ def test_gpio_pin_expression__conf_is_none(monkeypatch): def test_gpio_pin_expression__new_pin(monkeypatch): - target = ch.gpio_pin_expression({ - const.CONF_NUMBER: 42, - const.CONF_MODE: "input", - const.CONF_INVERTED: False - }) + target = ch.gpio_pin_expression( + {const.CONF_NUMBER: 42, const.CONF_MODE: "input", const.CONF_INVERTED: False} + ) actual = next(target) @@ -71,10 +69,13 @@ def test_register_component__with_setup_priority(monkeypatch): add_mock = Mock() monkeypatch.setattr(ch, "add", add_mock) - target = ch.register_component(var, { - const.CONF_SETUP_PRIORITY: "123", - const.CONF_UPDATE_INTERVAL: "456", - }) + target = ch.register_component( + var, + { + const.CONF_SETUP_PRIORITY: "123", + const.CONF_UPDATE_INTERVAL: "456", + }, + ) actual = next(target) diff --git a/tests/unit_tests/test_helpers.py b/tests/unit_tests/test_helpers.py index 6e89a05bc2..00a6b08133 100644 --- a/tests/unit_tests/test_helpers.py +++ b/tests/unit_tests/test_helpers.py @@ -6,69 +6,89 @@ from hypothesis.provisional import ip_addresses from esphome import helpers -@pytest.mark.parametrize("preferred_string, current_strings, expected", ( - ("foo", [], "foo"), - # TODO: Should this actually start at 1? - ("foo", ["foo"], "foo_2"), - ("foo", ("foo",), "foo_2"), - ("foo", ("foo", "foo_2"), "foo_3"), - ("foo", ("foo", "foo_2", "foo_2"), "foo_3"), -)) +@pytest.mark.parametrize( + "preferred_string, current_strings, expected", + ( + ("foo", [], "foo"), + # TODO: Should this actually start at 1? + ("foo", ["foo"], "foo_2"), + ("foo", ("foo",), "foo_2"), + ("foo", ("foo", "foo_2"), "foo_3"), + ("foo", ("foo", "foo_2", "foo_2"), "foo_3"), + ), +) def test_ensure_unique_string(preferred_string, current_strings, expected): actual = helpers.ensure_unique_string(preferred_string, current_strings) assert actual == expected -@pytest.mark.parametrize("text, expected", ( - ("foo", "foo"), - ("foo\nbar", "foo\nbar"), - ("foo\nbar\neek", "foo\n bar\neek"), -)) +@pytest.mark.parametrize( + "text, expected", + ( + ("foo", "foo"), + ("foo\nbar", "foo\nbar"), + ("foo\nbar\neek", "foo\n bar\neek"), + ), +) def test_indent_all_but_first_and_last(text, expected): actual = helpers.indent_all_but_first_and_last(text) assert actual == expected -@pytest.mark.parametrize("text, expected", ( - ("foo", [" foo"]), - ("foo\nbar", [" foo", " bar"]), - ("foo\nbar\neek", [" foo", " bar", " eek"]), -)) +@pytest.mark.parametrize( + "text, expected", + ( + ("foo", [" foo"]), + ("foo\nbar", [" foo", " bar"]), + ("foo\nbar\neek", [" foo", " bar", " eek"]), + ), +) def test_indent_list(text, expected): actual = helpers.indent_list(text) assert actual == expected -@pytest.mark.parametrize("text, expected", ( - ("foo", " foo"), - ("foo\nbar", " foo\n bar"), - ("foo\nbar\neek", " foo\n bar\n eek"), -)) +@pytest.mark.parametrize( + "text, expected", + ( + ("foo", " foo"), + ("foo\nbar", " foo\n bar"), + ("foo\nbar\neek", " foo\n bar\n eek"), + ), +) def test_indent(text, expected): actual = helpers.indent(text) assert actual == expected -@pytest.mark.parametrize("string, expected", ( - ("foo", '"foo"'), - ("foo\nbar", '"foo\\012bar"'), - ("foo\\bar", '"foo\\134bar"'), - ('foo "bar"', '"foo \\042bar\\042"'), - ('foo 🐍', '"foo \\360\\237\\220\\215"'), -)) +@pytest.mark.parametrize( + "string, expected", + ( + ("foo", '"foo"'), + ("foo\nbar", '"foo\\012bar"'), + ("foo\\bar", '"foo\\134bar"'), + ('foo "bar"', '"foo \\042bar\\042"'), + ("foo 🐍", '"foo \\360\\237\\220\\215"'), + ), +) def test_cpp_string_escape(string, expected): actual = helpers.cpp_string_escape(string) assert actual == expected -@pytest.mark.parametrize("host", ( - "127.0.0", "localhost", "127.0.0.b", -)) +@pytest.mark.parametrize( + "host", + ( + "127.0.0", + "localhost", + "127.0.0.b", + ), +) def test_is_ip_address__invalid(host): actual = helpers.is_ip_address(host) @@ -82,13 +102,16 @@ def test_is_ip_address__valid(value): assert actual is True -@pytest.mark.parametrize("var, value, default, expected", ( - ("FOO", None, False, False), - ("FOO", None, True, True), - ("FOO", "", False, False), - ("FOO", "Yes", False, True), - ("FOO", "123", False, True), -)) +@pytest.mark.parametrize( + "var, value, default, expected", + ( + ("FOO", None, False, False), + ("FOO", None, True, True), + ("FOO", "", False, False), + ("FOO", "Yes", False, True), + ("FOO", "123", False, True), + ), +) def test_get_bool_env(monkeypatch, var, value, default, expected): if value is None: monkeypatch.delenv(var, raising=False) @@ -100,10 +123,7 @@ def test_get_bool_env(monkeypatch, var, value, default, expected): assert actual == expected -@pytest.mark.parametrize("value, expected", ( - (None, False), - ("Yes", True) -)) +@pytest.mark.parametrize("value, expected", ((None, False), ("Yes", True))) def test_is_hassio(monkeypatch, value, expected): if value is None: monkeypatch.delenv("ESPHOME_IS_HASSIO", raising=False) @@ -185,20 +205,23 @@ class Test_copy_file_if_changed: assert src.read_text() == dst.read_text() -@pytest.mark.parametrize("file1, file2, expected", ( - # Same file - ("file-a.txt", "file-a.txt", True), - # Different files, different size - ("file-a.txt", "file-b_1.txt", False), - # Different files, same size - ("file-a.txt", "file-c.txt", False), - # Same files - ("file-b_1.txt", "file-b_2.txt", True), - # Not a file - ("file-a.txt", "", False), - # File doesn't exist - ("file-a.txt", "file-d.txt", False), -)) +@pytest.mark.parametrize( + "file1, file2, expected", + ( + # Same file + ("file-a.txt", "file-a.txt", True), + # Different files, different size + ("file-a.txt", "file-b_1.txt", False), + # Different files, same size + ("file-a.txt", "file-c.txt", False), + # Same files + ("file-b_1.txt", "file-b_2.txt", True), + # Not a file + ("file-a.txt", "", False), + # File doesn't exist + ("file-a.txt", "file-d.txt", False), + ), +) def test_file_compare(fixture_path, file1, file2, expected): path1 = fixture_path / "helpers" / file1 path2 = fixture_path / "helpers" / file2 diff --git a/tests/unit_tests/test_pins.py b/tests/unit_tests/test_pins.py index 7d68181add..6bc6f4d766 100644 --- a/tests/unit_tests/test_pins.py +++ b/tests/unit_tests/test_pins.py @@ -15,12 +15,12 @@ from esphome import pins MOCK_ESP8266_BOARD_ID = "_mock_esp8266" -MOCK_ESP8266_PINS = {'X0': 16, 'X1': 5, 'X2': 4, 'LED': 2} +MOCK_ESP8266_PINS = {"X0": 16, "X1": 5, "X2": 4, "LED": 2} MOCK_ESP8266_BOARD_ALIAS_ID = "_mock_esp8266_alias" MOCK_ESP8266_FLASH_SIZE = pins.FLASH_SIZE_2_MB MOCK_ESP32_BOARD_ID = "_mock_esp32" -MOCK_ESP32_PINS = {'Y0': 12, 'Y1': 8, 'Y2': 3, 'LED': 9, "A0": 8} +MOCK_ESP32_PINS = {"Y0": 12, "Y1": 8, "Y2": 3, "LED": 9, "A0": 8} MOCK_ESP32_BOARD_ALIAS_ID = "_mock_esp32_alias" UNKNOWN_PLATFORM = "STM32" @@ -68,10 +68,13 @@ def core_esp32(core): class Test_lookup_pin: - @pytest.mark.parametrize("value, expected", ( + @pytest.mark.parametrize( + "value, expected", + ( ("X1", 5), ("MOSI", 13), - )) + ), + ) def test_valid_esp8266_pin(self, core_esp8266, value, expected): actual = pins._lookup_pin(value) @@ -84,11 +87,14 @@ class Test_lookup_pin: assert actual == 4 - @pytest.mark.parametrize("value, expected", ( + @pytest.mark.parametrize( + "value, expected", + ( ("Y1", 8), ("A0", 8), ("MOSI", 23), - )) + ), + ) def test_valid_esp32_pin(self, core_esp32, value, expected): actual = pins._lookup_pin(value) @@ -102,7 +108,9 @@ class Test_lookup_pin: assert actual == 3 def test_invalid_pin(self, core_esp8266): - with pytest.raises(Invalid, match="Cannot resolve pin name 'X42' for board _mock_esp8266."): + with pytest.raises( + Invalid, match="Cannot resolve pin name 'X42' for board _mock_esp8266." + ): pins._lookup_pin("X42") def test_unsupported_platform(self, core): @@ -113,13 +121,16 @@ class Test_lookup_pin: class Test_translate_pin: - @pytest.mark.parametrize("value, expected", ( + @pytest.mark.parametrize( + "value, expected", + ( (2, 2), ("3", 3), ("GPIO4", 4), ("TX", 1), ("Y0", 12), - )) + ), + ) def test_valid_values(self, core_esp32, value, expected): actual = pins._translate_pin(value) @@ -137,7 +148,9 @@ class Test_validate_gpio_pin: assert actual == 22 - @pytest.mark.parametrize("value, match", ( + @pytest.mark.parametrize( + "value, match", + ( (-1, "ESP32: Invalid pin number: -1"), (40, "ESP32: Invalid pin number: 40"), (6, "This pin cannot be used on ESP32s and"), @@ -150,7 +163,8 @@ class Test_validate_gpio_pin: (29, "The pin GPIO29 is not usable on ESP32s"), (30, "The pin GPIO30 is not usable on ESP32s"), (31, "The pin GPIO31 is not usable on ESP32s"), - )) + ), + ) def test_esp32_invalid_pin(self, core_esp32, value, match): with pytest.raises(Invalid, match=match): pins.validate_gpio_pin(value) @@ -168,14 +182,17 @@ class Test_validate_gpio_pin: assert actual == 12 - @pytest.mark.parametrize("value, match", ( + @pytest.mark.parametrize( + "value, match", + ( (-1, "ESP8266: Invalid pin number: -1"), (18, "ESP8266: Invalid pin number: 18"), (6, "This pin cannot be used on ESP8266s and"), (7, "This pin cannot be used on ESP8266s and"), (8, "This pin cannot be used on ESP8266s and"), (11, "This pin cannot be used on ESP8266s and"), - )) + ), + ) def test_esp8266_invalid_pin(self, core_esp8266, value, match): with pytest.raises(Invalid, match=match): pins.validate_gpio_pin(value) @@ -196,18 +213,19 @@ class Test_validate_gpio_pin: class Test_input_pin: - @pytest.mark.parametrize("value, expected", ( - ("X0", 16), - )) + @pytest.mark.parametrize("value, expected", (("X0", 16),)) def test_valid_esp8266_values(self, core_esp8266, value, expected): actual = pins.input_pin(value) assert actual == expected - @pytest.mark.parametrize("value, expected", ( + @pytest.mark.parametrize( + "value, expected", + ( ("Y0", 12), (17, 17), - )) + ), + ) def test_valid_esp32_values(self, core_esp32, value, expected): actual = pins.input_pin(value) @@ -226,18 +244,19 @@ class Test_input_pin: class Test_input_pullup_pin: - @pytest.mark.parametrize("value, expected", ( - ("X0", 16), - )) + @pytest.mark.parametrize("value, expected", (("X0", 16),)) def test_valid_esp8266_values(self, core_esp8266, value, expected): actual = pins.input_pullup_pin(value) assert actual == expected - @pytest.mark.parametrize("value, expected", ( + @pytest.mark.parametrize( + "value, expected", + ( ("Y0", 12), (17, 17), - )) + ), + ) def test_valid_esp32_values(self, core_esp32, value, expected): actual = pins.input_pullup_pin(value) @@ -256,18 +275,19 @@ class Test_input_pullup_pin: class Test_output_pin: - @pytest.mark.parametrize("value, expected", ( - ("X0", 16), - )) + @pytest.mark.parametrize("value, expected", (("X0", 16),)) def test_valid_esp8266_values(self, core_esp8266, value, expected): actual = pins.output_pin(value) assert actual == expected - @pytest.mark.parametrize("value, expected", ( + @pytest.mark.parametrize( + "value, expected", + ( ("Y0", 12), (17, 17), - )) + ), + ) def test_valid_esp32_values(self, core_esp32, value, expected): actual = pins.output_pin(value) @@ -291,18 +311,19 @@ class Test_output_pin: class Test_analog_pin: - @pytest.mark.parametrize("value, expected", ( - (17, 17), - )) + @pytest.mark.parametrize("value, expected", ((17, 17),)) def test_valid_esp8266_values(self, core_esp8266, value, expected): actual = pins.analog_pin(value) assert actual == expected - @pytest.mark.parametrize("value, expected", ( + @pytest.mark.parametrize( + "value, expected", + ( (32, 32), (39, 39), - )) + ), + ) def test_valid_esp32_values(self, core_esp32, value, expected): actual = pins.analog_pin(value) diff --git a/tests/unit_tests/test_wizard.py b/tests/unit_tests/test_wizard.py index 2baff80edd..6c952608d4 100644 --- a/tests/unit_tests/test_wizard.py +++ b/tests/unit_tests/test_wizard.py @@ -1,4 +1,4 @@ -""" Tests for the wizard.py file """ +"""Tests for the wizard.py file.""" import esphome.wizard as wz import pytest @@ -14,7 +14,7 @@ def default_config(): "board": "test_board", "ssid": "test_ssid", "psk": "test_psk", - "password": "" + "password": "", } @@ -35,13 +35,13 @@ def test_sanitize_quotes_replaces_with_escaped_char(): The sanitize_quotes function should replace double quotes with their escaped equivalents """ # Given - input_str = "\"key\": \"value\"" + input_str = '"key": "value"' # When output_str = wz.sanitize_double_quotes(input_str) # Then - assert output_str == "\\\"key\\\": \\\"value\\\"" + assert output_str == '\\"key\\": \\"value\\"' def test_config_file_fallback_ap_includes_descriptive_name(default_config): @@ -55,7 +55,7 @@ def test_config_file_fallback_ap_includes_descriptive_name(default_config): config = wz.wizard_file(**default_config) # Then - assert f"ssid: \"Test Node Fallback Hotspot\"" in config + assert 'ssid: "Test Node Fallback Hotspot"' in config def test_config_file_fallback_ap_name_less_than_32_chars(default_config): @@ -70,7 +70,7 @@ def test_config_file_fallback_ap_name_less_than_32_chars(default_config): config = wz.wizard_file(**default_config) # Then - assert f"ssid: \"A Very Long Name For This Node\"" in config + assert 'ssid: "A Very Long Name For This Node"' in config def test_config_file_should_include_ota(default_config): @@ -115,7 +115,9 @@ def test_wizard_write_sets_platform(default_config, tmp_path, monkeypatch): assert f"platform: {default_config['platform']}" in generated_config -def test_wizard_write_defaults_platform_from_board_esp8266(default_config, tmp_path, monkeypatch): +def test_wizard_write_defaults_platform_from_board_esp8266( + default_config, tmp_path, monkeypatch +): """ If the platform is not explicitly set, use "ESP8266" if the board is one of the ESP8266 boards """ @@ -133,7 +135,9 @@ def test_wizard_write_defaults_platform_from_board_esp8266(default_config, tmp_p assert "platform: ESP8266" in generated_config -def test_wizard_write_defaults_platform_from_board_esp32(default_config, tmp_path, monkeypatch): +def test_wizard_write_defaults_platform_from_board_esp32( + default_config, tmp_path, monkeypatch +): """ If the platform is not explicitly set, use "ESP32" if the board is not one of the ESP8266 boards """ @@ -167,7 +171,9 @@ def test_safe_print_step_prints_step_number_and_description(monkeypatch): # Then # Collect arguments to all safe_print() calls (substituting "" for any empty ones) - all_args = [call.args[0] if len(call.args) else "" for call in wz.safe_print.call_args_list] + all_args = [ + call.args[0] if len(call.args) else "" for call in wz.safe_print.call_args_list + ] assert any(step_desc == arg for arg in all_args) assert any(f"STEP {step_num}" in arg for arg in all_args) @@ -212,7 +218,7 @@ def test_strip_accents_removes_diacritics(): """ # Given - input_str = u"Kühne" + input_str = "Kühne" expected_str = "Kuhne" # When @@ -264,7 +270,7 @@ def test_wizard_accepts_default_answers_esp8266(tmpdir, monkeypatch, wizard_answ monkeypatch.setattr("builtins.input", input_mock) monkeypatch.setattr(wz, "safe_print", lambda t=None: 0) monkeypatch.setattr(wz, "sleep", lambda _: 0) - monkeypatch.setattr(wz, "wizard_write", MagicMock()) + monkeypatch.setattr(wz, "wizard_write", MagicMock()) # When retval = wz.wizard(str(config_file)) @@ -286,7 +292,7 @@ def test_wizard_accepts_default_answers_esp32(tmpdir, monkeypatch, wizard_answer monkeypatch.setattr("builtins.input", input_mock) monkeypatch.setattr(wz, "safe_print", lambda t=None: 0) monkeypatch.setattr(wz, "sleep", lambda _: 0) - monkeypatch.setattr(wz, "wizard_write", MagicMock()) + monkeypatch.setattr(wz, "wizard_write", MagicMock()) # When retval = wz.wizard(str(config_file)) @@ -306,14 +312,16 @@ def test_wizard_offers_better_node_name(tmpdir, monkeypatch, wizard_answers): # Given wizard_answers[0] = "Küche #2" expected_name = "kuche_2" - monkeypatch.setattr(wz, "default_input", MagicMock(side_effect=lambda _, default: default)) + monkeypatch.setattr( + wz, "default_input", MagicMock(side_effect=lambda _, default: default) + ) config_file = tmpdir.join("test.yaml") input_mock = MagicMock(side_effect=wizard_answers) monkeypatch.setattr("builtins.input", input_mock) monkeypatch.setattr(wz, "safe_print", lambda t=None: 0) monkeypatch.setattr(wz, "sleep", lambda _: 0) - monkeypatch.setattr(wz, "wizard_write", MagicMock()) + monkeypatch.setattr(wz, "wizard_write", MagicMock()) # When retval = wz.wizard(str(config_file)) @@ -336,7 +344,7 @@ def test_wizard_requires_correct_platform(tmpdir, monkeypatch, wizard_answers): monkeypatch.setattr("builtins.input", input_mock) monkeypatch.setattr(wz, "safe_print", lambda t=None: 0) monkeypatch.setattr(wz, "sleep", lambda _: 0) - monkeypatch.setattr(wz, "wizard_write", MagicMock()) + monkeypatch.setattr(wz, "wizard_write", MagicMock()) # When retval = wz.wizard(str(config_file)) @@ -358,7 +366,7 @@ def test_wizard_requires_correct_board(tmpdir, monkeypatch, wizard_answers): monkeypatch.setattr("builtins.input", input_mock) monkeypatch.setattr(wz, "safe_print", lambda t=None: 0) monkeypatch.setattr(wz, "sleep", lambda _: 0) - monkeypatch.setattr(wz, "wizard_write", MagicMock()) + monkeypatch.setattr(wz, "wizard_write", MagicMock()) # When retval = wz.wizard(str(config_file)) @@ -380,7 +388,7 @@ def test_wizard_requires_valid_ssid(tmpdir, monkeypatch, wizard_answers): monkeypatch.setattr("builtins.input", input_mock) monkeypatch.setattr(wz, "safe_print", lambda t=None: 0) monkeypatch.setattr(wz, "sleep", lambda _: 0) - monkeypatch.setattr(wz, "wizard_write", MagicMock()) + monkeypatch.setattr(wz, "wizard_write", MagicMock()) # When retval = wz.wizard(str(config_file)) From 570ec36fe310fa36a5133124ba33c58f5761a098 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 8 Mar 2021 08:23:54 +1300 Subject: [PATCH 078/111] MCP23XXX Refactor (#1560) * Refactor MCP23XXX classes to consolidate shared code * Update test mcp23xxx pin schemas --- CODEOWNERS | 9 +- esphome/components/mcp23008/__init__.py | 62 ++------- esphome/components/mcp23008/mcp23008.cpp | 69 ++-------- esphome/components/mcp23008/mcp23008.h | 58 +-------- esphome/components/mcp23017/__init__.py | 62 ++------- esphome/components/mcp23017/mcp23017.cpp | 74 ++--------- esphome/components/mcp23017/mcp23017.h | 71 +---------- esphome/components/mcp23s08/__init__.py | 57 ++------- esphome/components/mcp23s08/mcp23s08.cpp | 100 +++------------ esphome/components/mcp23s08/mcp23s08.h | 54 +------- esphome/components/mcp23s17/__init__.py | 57 ++------- esphome/components/mcp23s17/mcp23s17.cpp | 84 ++----------- esphome/components/mcp23s17/mcp23s17.h | 68 +--------- esphome/components/mcp23x08_base/__init__.py | 8 ++ .../mcp23x08_base/mcp23x08_base.cpp | 89 +++++++++++++ .../components/mcp23x08_base/mcp23x08_base.h | 39 ++++++ esphome/components/mcp23x17_base/__init__.py | 8 ++ .../mcp23x17_base/mcp23x17_base.cpp | 93 ++++++++++++++ .../components/mcp23x17_base/mcp23x17_base.h | 52 ++++++++ esphome/components/mcp23xxx_base/__init__.py | 118 ++++++++++++++++++ .../mcp23xxx_base/mcp23xxx_base.cpp | 21 ++++ .../components/mcp23xxx_base/mcp23xxx_base.h | 55 ++++++++ esphome/const.py | 1 + tests/test1.yaml | 30 +++-- tests/test3.yaml | 4 +- 25 files changed, 603 insertions(+), 740 deletions(-) create mode 100644 esphome/components/mcp23x08_base/__init__.py create mode 100644 esphome/components/mcp23x08_base/mcp23x08_base.cpp create mode 100644 esphome/components/mcp23x08_base/mcp23x08_base.h create mode 100644 esphome/components/mcp23x17_base/__init__.py create mode 100644 esphome/components/mcp23x17_base/mcp23x17_base.cpp create mode 100644 esphome/components/mcp23x17_base/mcp23x17_base.h create mode 100644 esphome/components/mcp23xxx_base/__init__.py create mode 100644 esphome/components/mcp23xxx_base/mcp23xxx_base.cpp create mode 100644 esphome/components/mcp23xxx_base/mcp23xxx_base.h diff --git a/CODEOWNERS b/CODEOWNERS index 0f22c8ace9..4fdfb21569 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -46,8 +46,13 @@ esphome/components/ledc/* @OttoWinter esphome/components/light/* @esphome/core esphome/components/logger/* @esphome/core esphome/components/max7219digit/* @rspaargaren -esphome/components/mcp23s08/* @SenexCrenshaw -esphome/components/mcp23s17/* @SenexCrenshaw +esphome/components/mcp23008/* @jesserockz +esphome/components/mcp23017/* @jesserockz +esphome/components/mcp23s08/* @SenexCrenshaw @jesserockz +esphome/components/mcp23s17/* @SenexCrenshaw @jesserockz +esphome/components/mcp23x08_base/* @jesserockz +esphome/components/mcp23x17_base/* @jesserockz +esphome/components/mcp23xxx_base/* @jesserockz esphome/components/mcp2515/* @danielschramm @mvturnho esphome/components/mcp9808/* @k7hpn esphome/components/network/* @esphome/core diff --git a/esphome/components/mcp23008/__init__.py b/esphome/components/mcp23008/__init__.py index 4736765e64..ae43e9ff0a 100644 --- a/esphome/components/mcp23008/__init__.py +++ b/esphome/components/mcp23008/__init__.py @@ -1,76 +1,28 @@ import esphome.codegen as cg import esphome.config_validation as cv -from esphome import pins -from esphome.components import i2c -from esphome.const import ( - CONF_ID, - CONF_NUMBER, - CONF_MODE, - CONF_INVERTED, - CONF_OPEN_DRAIN_INTERRUPT, -) +from esphome.components import i2c, mcp23x08_base, mcp23xxx_base +from esphome.const import CONF_ID +AUTO_LOAD = ["mcp23x08_base"] +CODEOWNERS = ["@jesserockz"] DEPENDENCIES = ["i2c"] MULTI_CONF = True mcp23008_ns = cg.esphome_ns.namespace("mcp23008") -MCP23008GPIOMode = mcp23008_ns.enum("MCP23008GPIOMode") -MCP23008_GPIO_MODES = { - "INPUT": MCP23008GPIOMode.MCP23008_INPUT, - "INPUT_PULLUP": MCP23008GPIOMode.MCP23008_INPUT_PULLUP, - "OUTPUT": MCP23008GPIOMode.MCP23008_OUTPUT, -} -MCP23008 = mcp23008_ns.class_("MCP23008", cg.Component, i2c.I2CDevice) -MCP23008GPIOPin = mcp23008_ns.class_("MCP23008GPIOPin", cg.GPIOPin) +MCP23008 = mcp23008_ns.class_("MCP23008", mcp23x08_base.MCP23X08Base, i2c.I2CDevice) CONFIG_SCHEMA = ( cv.Schema( { cv.Required(CONF_ID): cv.declare_id(MCP23008), - cv.Optional(CONF_OPEN_DRAIN_INTERRUPT, default=False): cv.boolean, } ) - .extend(cv.COMPONENT_SCHEMA) + .extend(mcp23xxx_base.MCP23XXX_CONFIG_SCHEMA) .extend(i2c.i2c_device_schema(0x20)) ) def to_code(config): - var = cg.new_Pvariable(config[CONF_ID]) - yield cg.register_component(var, config) + var = yield mcp23xxx_base.register_mcp23xxx(config) yield i2c.register_i2c_device(var, config) - cg.add(var.set_open_drain_ints(config[CONF_OPEN_DRAIN_INTERRUPT])) - - -CONF_MCP23008 = "mcp23008" -MCP23008_OUTPUT_PIN_SCHEMA = cv.Schema( - { - cv.Required(CONF_MCP23008): cv.use_id(MCP23008), - cv.Required(CONF_NUMBER): cv.int_, - cv.Optional(CONF_MODE, default="OUTPUT"): cv.enum( - MCP23008_GPIO_MODES, upper=True - ), - cv.Optional(CONF_INVERTED, default=False): cv.boolean, - } -) -MCP23008_INPUT_PIN_SCHEMA = cv.Schema( - { - cv.Required(CONF_MCP23008): cv.use_id(MCP23008), - cv.Required(CONF_NUMBER): cv.int_, - cv.Optional(CONF_MODE, default="INPUT"): cv.enum( - MCP23008_GPIO_MODES, upper=True - ), - cv.Optional(CONF_INVERTED, default=False): cv.boolean, - } -) - - -@pins.PIN_SCHEMA_REGISTRY.register( - CONF_MCP23008, (MCP23008_OUTPUT_PIN_SCHEMA, MCP23008_INPUT_PIN_SCHEMA) -) -def mcp23008_pin_to_code(config): - parent = yield cg.get_variable(config[CONF_MCP23008]) - yield MCP23008GPIOPin.new( - parent, config[CONF_NUMBER], config[CONF_MODE], config[CONF_INVERTED] - ) diff --git a/esphome/components/mcp23008/mcp23008.cpp b/esphome/components/mcp23008/mcp23008.cpp index 22c7322458..eb66bfc7d7 100644 --- a/esphome/components/mcp23008/mcp23008.cpp +++ b/esphome/components/mcp23008/mcp23008.cpp @@ -9,85 +9,32 @@ static const char *TAG = "mcp23008"; void MCP23008::setup() { ESP_LOGCONFIG(TAG, "Setting up MCP23008..."); uint8_t iocon; - if (!this->read_reg_(MCP23008_IOCON, &iocon)) { + if (!this->read_reg(mcp23x08_base::MCP23X08_IOCON, &iocon)) { this->mark_failed(); return; } if (this->open_drain_ints_) { // enable open-drain interrupt pins, 3.3V-safe - this->write_reg_(MCP23008_IOCON, 0x04); + this->write_reg(mcp23x08_base::MCP23X08_IOCON, 0x04); } } -bool MCP23008::digital_read(uint8_t pin) { - uint8_t bit = pin % 8; - uint8_t reg_addr = MCP23008_GPIO; - uint8_t value = 0; - this->read_reg_(reg_addr, &value); - return value & (1 << bit); -} -void MCP23008::digital_write(uint8_t pin, bool value) { - uint8_t reg_addr = MCP23008_OLAT; - this->update_reg_(pin, value, reg_addr); -} -void MCP23008::pin_mode(uint8_t pin, uint8_t mode) { - uint8_t iodir = MCP23008_IODIR; - uint8_t gppu = MCP23008_GPPU; - switch (mode) { - case MCP23008_INPUT: - this->update_reg_(pin, true, iodir); - break; - case MCP23008_INPUT_PULLUP: - this->update_reg_(pin, true, iodir); - this->update_reg_(pin, true, gppu); - break; - case MCP23008_OUTPUT: - this->update_reg_(pin, false, iodir); - break; - default: - break; - } -} -float MCP23008::get_setup_priority() const { return setup_priority::HARDWARE; } -bool MCP23008::read_reg_(uint8_t reg, uint8_t *value) { + +void MCP23008::dump_config() { ESP_LOGCONFIG(TAG, "MCP23008:"); } + +bool MCP23008::read_reg(uint8_t reg, uint8_t *value) { if (this->is_failed()) return false; return this->read_byte(reg, value); } -bool MCP23008::write_reg_(uint8_t reg, uint8_t value) { + +bool MCP23008::write_reg(uint8_t reg, uint8_t value) { if (this->is_failed()) return false; return this->write_byte(reg, value); } -void MCP23008::update_reg_(uint8_t pin, bool pin_value, uint8_t reg_addr) { - uint8_t bit = pin % 8; - uint8_t reg_value = 0; - if (reg_addr == MCP23008_OLAT) { - reg_value = this->olat_; - } else { - this->read_reg_(reg_addr, ®_value); - } - - if (pin_value) - reg_value |= 1 << bit; - else - reg_value &= ~(1 << bit); - - this->write_reg_(reg_addr, reg_value); - - if (reg_addr == MCP23008_OLAT) { - this->olat_ = reg_value; - } -} - -MCP23008GPIOPin::MCP23008GPIOPin(MCP23008 *parent, uint8_t pin, uint8_t mode, bool inverted) - : GPIOPin(pin, mode, inverted), parent_(parent) {} -void MCP23008GPIOPin::setup() { this->pin_mode(this->mode_); } -void MCP23008GPIOPin::pin_mode(uint8_t mode) { this->parent_->pin_mode(this->pin_, mode); } -bool MCP23008GPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) != this->inverted_; } -void MCP23008GPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value != this->inverted_); } } // namespace mcp23008 } // namespace esphome diff --git a/esphome/components/mcp23008/mcp23008.h b/esphome/components/mcp23008/mcp23008.h index e30a924dde..42c8e497fa 100644 --- a/esphome/components/mcp23008/mcp23008.h +++ b/esphome/components/mcp23008/mcp23008.h @@ -1,71 +1,23 @@ #pragma once #include "esphome/core/component.h" +#include "esphome/components/mcp23x08_base/mcp23x08_base.h" #include "esphome/core/esphal.h" #include "esphome/components/i2c/i2c.h" namespace esphome { namespace mcp23008 { -/// Modes for MCP23008 pins -enum MCP23008GPIOMode : uint8_t { - MCP23008_INPUT = INPUT, // 0x00 - MCP23008_INPUT_PULLUP = INPUT_PULLUP, // 0x02 - MCP23008_OUTPUT = OUTPUT // 0x01 -}; - -enum MCP23008GPIORegisters { - // A side - MCP23008_IODIR = 0x00, - MCP23008_IPOL = 0x01, - MCP23008_GPINTEN = 0x02, - MCP23008_DEFVAL = 0x03, - MCP23008_INTCON = 0x04, - MCP23008_IOCON = 0x05, - MCP23008_GPPU = 0x06, - MCP23008_INTF = 0x07, - MCP23008_INTCAP = 0x08, - MCP23008_GPIO = 0x09, - MCP23008_OLAT = 0x0A, -}; - -class MCP23008 : public Component, public i2c::I2CDevice { +class MCP23008 : public mcp23x08_base::MCP23X08Base, public i2c::I2CDevice { public: MCP23008() = default; void setup() override; - - bool digital_read(uint8_t pin); - void digital_write(uint8_t pin, bool value); - void pin_mode(uint8_t pin, uint8_t mode); - - void set_open_drain_ints(const bool value) { open_drain_ints_ = value; } - - float get_setup_priority() const override; + void dump_config() override; protected: - // read a given register - bool read_reg_(uint8_t reg, uint8_t *value); - // write a value to a given register - bool write_reg_(uint8_t reg, uint8_t value); - // update registers with given pin value. - void update_reg_(uint8_t pin, bool pin_value, uint8_t reg_a); - - uint8_t olat_{0x00}; - bool open_drain_ints_; -}; - -class MCP23008GPIOPin : public GPIOPin { - public: - MCP23008GPIOPin(MCP23008 *parent, uint8_t pin, uint8_t mode, bool inverted = false); - - void setup() override; - void pin_mode(uint8_t mode) override; - bool digital_read() override; - void digital_write(bool value) override; - - protected: - MCP23008 *parent_; + bool read_reg(uint8_t reg, uint8_t *value) override; + bool write_reg(uint8_t reg, uint8_t value) override; }; } // namespace mcp23008 diff --git a/esphome/components/mcp23017/__init__.py b/esphome/components/mcp23017/__init__.py index dae7a6fead..a86133e232 100644 --- a/esphome/components/mcp23017/__init__.py +++ b/esphome/components/mcp23017/__init__.py @@ -1,76 +1,28 @@ import esphome.codegen as cg import esphome.config_validation as cv -from esphome import pins -from esphome.components import i2c -from esphome.const import ( - CONF_ID, - CONF_NUMBER, - CONF_MODE, - CONF_INVERTED, - CONF_OPEN_DRAIN_INTERRUPT, -) +from esphome.components import i2c, mcp23x17_base, mcp23xxx_base +from esphome.const import CONF_ID +AUTO_LOAD = ["mcp23x17_base"] +CODEOWNERS = ["@jesserockz"] DEPENDENCIES = ["i2c"] MULTI_CONF = True mcp23017_ns = cg.esphome_ns.namespace("mcp23017") -MCP23017GPIOMode = mcp23017_ns.enum("MCP23017GPIOMode") -MCP23017_GPIO_MODES = { - "INPUT": MCP23017GPIOMode.MCP23017_INPUT, - "INPUT_PULLUP": MCP23017GPIOMode.MCP23017_INPUT_PULLUP, - "OUTPUT": MCP23017GPIOMode.MCP23017_OUTPUT, -} -MCP23017 = mcp23017_ns.class_("MCP23017", cg.Component, i2c.I2CDevice) -MCP23017GPIOPin = mcp23017_ns.class_("MCP23017GPIOPin", cg.GPIOPin) +MCP23017 = mcp23017_ns.class_("MCP23017", mcp23x17_base.MCP23X17Base, i2c.I2CDevice) CONFIG_SCHEMA = ( cv.Schema( { cv.Required(CONF_ID): cv.declare_id(MCP23017), - cv.Optional(CONF_OPEN_DRAIN_INTERRUPT, default=False): cv.boolean, } ) - .extend(cv.COMPONENT_SCHEMA) + .extend(mcp23xxx_base.MCP23XXX_CONFIG_SCHEMA) .extend(i2c.i2c_device_schema(0x20)) ) def to_code(config): - var = cg.new_Pvariable(config[CONF_ID]) - yield cg.register_component(var, config) + var = yield mcp23xxx_base.register_mcp23xxx(config) yield i2c.register_i2c_device(var, config) - cg.add(var.set_open_drain_ints(config[CONF_OPEN_DRAIN_INTERRUPT])) - - -CONF_MCP23017 = "mcp23017" -MCP23017_OUTPUT_PIN_SCHEMA = cv.Schema( - { - cv.Required(CONF_MCP23017): cv.use_id(MCP23017), - cv.Required(CONF_NUMBER): cv.int_, - cv.Optional(CONF_MODE, default="OUTPUT"): cv.enum( - MCP23017_GPIO_MODES, upper=True - ), - cv.Optional(CONF_INVERTED, default=False): cv.boolean, - } -) -MCP23017_INPUT_PIN_SCHEMA = cv.Schema( - { - cv.Required(CONF_MCP23017): cv.use_id(MCP23017), - cv.Required(CONF_NUMBER): cv.int_, - cv.Optional(CONF_MODE, default="INPUT"): cv.enum( - MCP23017_GPIO_MODES, upper=True - ), - cv.Optional(CONF_INVERTED, default=False): cv.boolean, - } -) - - -@pins.PIN_SCHEMA_REGISTRY.register( - CONF_MCP23017, (MCP23017_OUTPUT_PIN_SCHEMA, MCP23017_INPUT_PIN_SCHEMA) -) -def mcp23017_pin_to_code(config): - parent = yield cg.get_variable(config[CONF_MCP23017]) - yield MCP23017GPIOPin.new( - parent, config[CONF_NUMBER], config[CONF_MODE], config[CONF_INVERTED] - ) diff --git a/esphome/components/mcp23017/mcp23017.cpp b/esphome/components/mcp23017/mcp23017.cpp index ec972668ef..0523b14d5a 100644 --- a/esphome/components/mcp23017/mcp23017.cpp +++ b/esphome/components/mcp23017/mcp23017.cpp @@ -9,90 +9,32 @@ static const char *TAG = "mcp23017"; void MCP23017::setup() { ESP_LOGCONFIG(TAG, "Setting up MCP23017..."); uint8_t iocon; - if (!this->read_reg_(MCP23017_IOCONA, &iocon)) { + if (!this->read_reg(mcp23x17_base::MCP23X17_IOCONA, &iocon)) { this->mark_failed(); return; } if (this->open_drain_ints_) { // enable open-drain interrupt pins, 3.3V-safe - this->write_reg_(MCP23017_IOCONA, 0x04); - this->write_reg_(MCP23017_IOCONB, 0x04); + this->write_reg(mcp23x17_base::MCP23X17_IOCONA, 0x04); + this->write_reg(mcp23x17_base::MCP23X17_IOCONB, 0x04); } } -bool MCP23017::digital_read(uint8_t pin) { - uint8_t bit = pin % 8; - uint8_t reg_addr = pin < 8 ? MCP23017_GPIOA : MCP23017_GPIOB; - uint8_t value = 0; - this->read_reg_(reg_addr, &value); - return value & (1 << bit); -} -void MCP23017::digital_write(uint8_t pin, bool value) { - uint8_t reg_addr = pin < 8 ? MCP23017_OLATA : MCP23017_OLATB; - this->update_reg_(pin, value, reg_addr); -} -void MCP23017::pin_mode(uint8_t pin, uint8_t mode) { - uint8_t iodir = pin < 8 ? MCP23017_IODIRA : MCP23017_IODIRB; - uint8_t gppu = pin < 8 ? MCP23017_GPPUA : MCP23017_GPPUB; - switch (mode) { - case MCP23017_INPUT: - this->update_reg_(pin, true, iodir); - break; - case MCP23017_INPUT_PULLUP: - this->update_reg_(pin, true, iodir); - this->update_reg_(pin, true, gppu); - break; - case MCP23017_OUTPUT: - this->update_reg_(pin, false, iodir); - break; - default: - break; - } -} -float MCP23017::get_setup_priority() const { return setup_priority::IO; } -bool MCP23017::read_reg_(uint8_t reg, uint8_t *value) { + +void MCP23017::dump_config() { ESP_LOGCONFIG(TAG, "MCP23017:"); } + +bool MCP23017::read_reg(uint8_t reg, uint8_t *value) { if (this->is_failed()) return false; return this->read_byte(reg, value); } -bool MCP23017::write_reg_(uint8_t reg, uint8_t value) { +bool MCP23017::write_reg(uint8_t reg, uint8_t value) { if (this->is_failed()) return false; return this->write_byte(reg, value); } -void MCP23017::update_reg_(uint8_t pin, bool pin_value, uint8_t reg_addr) { - uint8_t bit = pin % 8; - uint8_t reg_value = 0; - if (reg_addr == MCP23017_OLATA) { - reg_value = this->olat_a_; - } else if (reg_addr == MCP23017_OLATB) { - reg_value = this->olat_b_; - } else { - this->read_reg_(reg_addr, ®_value); - } - - if (pin_value) - reg_value |= 1 << bit; - else - reg_value &= ~(1 << bit); - - this->write_reg_(reg_addr, reg_value); - - if (reg_addr == MCP23017_OLATA) { - this->olat_a_ = reg_value; - } else if (reg_addr == MCP23017_OLATB) { - this->olat_b_ = reg_value; - } -} - -MCP23017GPIOPin::MCP23017GPIOPin(MCP23017 *parent, uint8_t pin, uint8_t mode, bool inverted) - : GPIOPin(pin, mode, inverted), parent_(parent) {} -void MCP23017GPIOPin::setup() { this->pin_mode(this->mode_); } -void MCP23017GPIOPin::pin_mode(uint8_t mode) { this->parent_->pin_mode(this->pin_, mode); } -bool MCP23017GPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) != this->inverted_; } -void MCP23017GPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value != this->inverted_); } } // namespace mcp23017 } // namespace esphome diff --git a/esphome/components/mcp23017/mcp23017.h b/esphome/components/mcp23017/mcp23017.h index 5656dcc58d..fd9086a492 100644 --- a/esphome/components/mcp23017/mcp23017.h +++ b/esphome/components/mcp23017/mcp23017.h @@ -1,84 +1,23 @@ #pragma once #include "esphome/core/component.h" +#include "esphome/components/mcp23x17_base/mcp23x17_base.h" #include "esphome/core/esphal.h" #include "esphome/components/i2c/i2c.h" namespace esphome { namespace mcp23017 { -/// Modes for MCP23017 pins -enum MCP23017GPIOMode : uint8_t { - MCP23017_INPUT = INPUT, // 0x00 - MCP23017_INPUT_PULLUP = INPUT_PULLUP, // 0x02 - MCP23017_OUTPUT = OUTPUT // 0x01 -}; - -enum MCP23017GPIORegisters { - // A side - MCP23017_IODIRA = 0x00, - MCP23017_IPOLA = 0x02, - MCP23017_GPINTENA = 0x04, - MCP23017_DEFVALA = 0x06, - MCP23017_INTCONA = 0x08, - MCP23017_IOCONA = 0x0A, - MCP23017_GPPUA = 0x0C, - MCP23017_INTFA = 0x0E, - MCP23017_INTCAPA = 0x10, - MCP23017_GPIOA = 0x12, - MCP23017_OLATA = 0x14, - // B side - MCP23017_IODIRB = 0x01, - MCP23017_IPOLB = 0x03, - MCP23017_GPINTENB = 0x05, - MCP23017_DEFVALB = 0x07, - MCP23017_INTCONB = 0x09, - MCP23017_IOCONB = 0x0B, - MCP23017_GPPUB = 0x0D, - MCP23017_INTFB = 0x0F, - MCP23017_INTCAPB = 0x11, - MCP23017_GPIOB = 0x13, - MCP23017_OLATB = 0x15, -}; - -class MCP23017 : public Component, public i2c::I2CDevice { +class MCP23017 : public mcp23x17_base::MCP23X17Base, public i2c::I2CDevice { public: MCP23017() = default; void setup() override; - - bool digital_read(uint8_t pin); - void digital_write(uint8_t pin, bool value); - void pin_mode(uint8_t pin, uint8_t mode); - - void set_open_drain_ints(const bool value) { open_drain_ints_ = value; } - - float get_setup_priority() const override; + void dump_config() override; protected: - // read a given register - bool read_reg_(uint8_t reg, uint8_t *value); - // write a value to a given register - bool write_reg_(uint8_t reg, uint8_t value); - // update registers with given pin value. - void update_reg_(uint8_t pin, bool pin_value, uint8_t reg_a); - - uint8_t olat_a_{0x00}; - uint8_t olat_b_{0x00}; - bool open_drain_ints_; -}; - -class MCP23017GPIOPin : public GPIOPin { - public: - MCP23017GPIOPin(MCP23017 *parent, uint8_t pin, uint8_t mode, bool inverted = false); - - void setup() override; - void pin_mode(uint8_t mode) override; - bool digital_read() override; - void digital_write(bool value) override; - - protected: - MCP23017 *parent_; + bool read_reg(uint8_t reg, uint8_t *value) override; + bool write_reg(uint8_t reg, uint8_t value) override; }; } // namespace mcp23017 diff --git a/esphome/components/mcp23s08/__init__.py b/esphome/components/mcp23s08/__init__.py index b0e047b6ba..cfeb65de9b 100644 --- a/esphome/components/mcp23s08/__init__.py +++ b/esphome/components/mcp23s08/__init__.py @@ -1,26 +1,18 @@ import esphome.codegen as cg import esphome.config_validation as cv -from esphome import pins -from esphome.components import spi -from esphome.const import CONF_ID, CONF_NUMBER, CONF_MODE, CONF_INVERTED - -CODEOWNERS = ["@SenexCrenshaw"] +from esphome.components import spi, mcp23x08_base, mcp23xxx_base +from esphome.const import CONF_ID +AUTO_LOAD = ["mcp23x08_base"] +CODEOWNERS = ["@SenexCrenshaw", "@jesserockz"] DEPENDENCIES = ["spi"] MULTI_CONF = True CONF_DEVICEADDRESS = "deviceaddress" mcp23S08_ns = cg.esphome_ns.namespace("mcp23s08") -mcp23S08GPIOMode = mcp23S08_ns.enum("MCP23S08GPIOMode") -mcp23S08_GPIO_MODES = { - "INPUT": mcp23S08GPIOMode.MCP23S08_INPUT, - "INPUT_PULLUP": mcp23S08GPIOMode.MCP23S08_INPUT_PULLUP, - "OUTPUT": mcp23S08GPIOMode.MCP23S08_OUTPUT, -} -mcp23S08 = mcp23S08_ns.class_("MCP23S08", cg.Component, spi.SPIDevice) -mcp23S08GPIOPin = mcp23S08_ns.class_("MCP23S08GPIOPin", cg.GPIOPin) +mcp23S08 = mcp23S08_ns.class_("MCP23S08", mcp23x08_base.MCP23X08Base, spi.SPIDevice) CONFIG_SCHEMA = ( cv.Schema( @@ -29,47 +21,12 @@ CONFIG_SCHEMA = ( cv.Optional(CONF_DEVICEADDRESS, default=0): cv.uint8_t, } ) - .extend(cv.COMPONENT_SCHEMA) + .extend(mcp23xxx_base.MCP23XXX_CONFIG_SCHEMA) .extend(spi.spi_device_schema()) ) def to_code(config): - var = cg.new_Pvariable(config[CONF_ID]) + var = yield mcp23xxx_base.register_mcp23xxx(config) cg.add(var.set_device_address(config[CONF_DEVICEADDRESS])) - yield cg.register_component(var, config) yield spi.register_spi_device(var, config) - - -CONF_MCP23S08 = "mcp23s08" - -mcp23S08_OUTPUT_PIN_SCHEMA = cv.Schema( - { - cv.GenerateID(CONF_MCP23S08): cv.use_id(mcp23S08), - cv.Required(CONF_NUMBER): cv.int_, - cv.Optional(CONF_MODE, default="OUTPUT"): cv.enum( - mcp23S08_GPIO_MODES, upper=True - ), - cv.Optional(CONF_INVERTED, default=False): cv.boolean, - } -) -mcp23S08_INPUT_PIN_SCHEMA = cv.Schema( - { - cv.GenerateID(CONF_MCP23S08): cv.use_id(mcp23S08), - cv.Required(CONF_NUMBER): cv.int_range(0, 7), - cv.Optional(CONF_MODE, default="INPUT"): cv.enum( - mcp23S08_GPIO_MODES, upper=True - ), - cv.Optional(CONF_INVERTED, default=False): cv.boolean, - } -) - - -@pins.PIN_SCHEMA_REGISTRY.register( - CONF_MCP23S08, (mcp23S08_OUTPUT_PIN_SCHEMA, mcp23S08_INPUT_PIN_SCHEMA) -) -def mcp23S08_pin_to_code(config): - parent = yield cg.get_variable(config[CONF_MCP23S08]) - yield mcp23S08GPIOPin.new( - parent, config[CONF_NUMBER], config[CONF_MODE], config[CONF_INVERTED] - ) diff --git a/esphome/components/mcp23s08/mcp23s08.cpp b/esphome/components/mcp23s08/mcp23s08.cpp index 07e1808485..3bb03cb225 100644 --- a/esphome/components/mcp23s08/mcp23s08.cpp +++ b/esphome/components/mcp23s08/mcp23s08.cpp @@ -15,14 +15,18 @@ void MCP23S08::set_device_address(uint8_t device_addr) { void MCP23S08::setup() { ESP_LOGCONFIG(TAG, "Setting up MCP23S08..."); this->spi_setup(); - this->enable(); - this->transfer_byte(MCP23S08_IODIR); - this->transfer_byte(0xFF); - for (uint8_t i = 0; i < MCP23S08_OLAT; i++) { - this->transfer_byte(0x00); - } + this->enable(); + uint8_t cmd = 0b01000000; + this->transfer_byte(cmd); + this->transfer_byte(mcp23x08_base::MCP23X08_IOCON); + this->transfer_byte(0b00011000); // Enable HAEN pins for addressing this->disable(); + + if (this->open_drain_ints_) { + // enable open-drain interrupt pins, 3.3V-safe + this->write_reg(mcp23x08_base::MCP23X08_IOCON, 0x04); + } } void MCP23S08::dump_config() { @@ -30,76 +34,6 @@ void MCP23S08::dump_config() { LOG_PIN(" CS Pin: ", this->cs_); } -float MCP23S08::get_setup_priority() const { return setup_priority::HARDWARE; } - -bool MCP23S08::digital_read(uint8_t pin) { - if (pin > 7) { - return false; - } - uint8_t bit = pin % 8; - uint8_t reg_addr = MCP23S08_GPIO; - uint8_t value = 0; - this->read_reg(reg_addr, &value); - return value & (1 << bit); -} - -void MCP23S08::digital_write(uint8_t pin, bool value) { - if (pin > 7) { - return; - } - uint8_t reg_addr = MCP23S08_OLAT; - this->update_reg(pin, value, reg_addr); -} - -void MCP23S08::pin_mode(uint8_t pin, uint8_t mode) { - uint8_t iodir = MCP23S08_IODIR; - uint8_t gppu = MCP23S08_GPPU; - switch (mode) { - case MCP23S08_INPUT: - this->update_reg(pin, true, iodir); - break; - case MCP23S08_INPUT_PULLUP: - this->update_reg(pin, true, iodir); - this->update_reg(pin, true, gppu); - break; - case MCP23S08_OUTPUT: - this->update_reg(pin, false, iodir); - break; - default: - break; - } -} - -void MCP23S08::update_reg(uint8_t pin, bool pin_value, uint8_t reg_addr) { - uint8_t bit = pin % 8; - uint8_t reg_value = 0; - if (reg_addr == MCP23S08_OLAT) { - reg_value = this->olat_; - } else { - this->read_reg(reg_addr, ®_value); - } - - if (pin_value) - reg_value |= 1 << bit; - else - reg_value &= ~(1 << bit); - - this->write_reg(reg_addr, reg_value); - - if (reg_addr == MCP23S08_OLAT) { - this->olat_ = reg_value; - } -} - -bool MCP23S08::write_reg(uint8_t reg, uint8_t value) { - this->enable(); - this->transfer_byte(this->device_opcode_); - this->transfer_byte(reg); - this->transfer_byte(value); - this->disable(); - return true; -} - bool MCP23S08::read_reg(uint8_t reg, uint8_t *value) { uint8_t data; this->enable(); @@ -110,12 +44,14 @@ bool MCP23S08::read_reg(uint8_t reg, uint8_t *value) { return true; } -MCP23S08GPIOPin::MCP23S08GPIOPin(MCP23S08 *parent, uint8_t pin, uint8_t mode, bool inverted) - : GPIOPin(pin, mode, inverted), parent_(parent) {} -void MCP23S08GPIOPin::setup() { this->pin_mode(this->mode_); } -void MCP23S08GPIOPin::pin_mode(uint8_t mode) { this->parent_->pin_mode(this->pin_, mode); } -bool MCP23S08GPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) != this->inverted_; } -void MCP23S08GPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value != this->inverted_); } +bool MCP23S08::write_reg(uint8_t reg, uint8_t value) { + this->enable(); + this->transfer_byte(this->device_opcode_); + this->transfer_byte(reg); + this->transfer_byte(value); + this->disable(); + return true; +} } // namespace mcp23s08 } // namespace esphome diff --git a/esphome/components/mcp23s08/mcp23s08.h b/esphome/components/mcp23s08/mcp23s08.h index a90f89ba23..4ca02c54fc 100644 --- a/esphome/components/mcp23s08/mcp23s08.h +++ b/esphome/components/mcp23s08/mcp23s08.h @@ -1,35 +1,14 @@ #pragma once #include "esphome/core/component.h" +#include "esphome/components/mcp23x08_base/mcp23x08_base.h" #include "esphome/core/esphal.h" #include "esphome/components/spi/spi.h" namespace esphome { namespace mcp23s08 { -/// Modes for MCP23S08 pins -enum MCP23S08GPIOMode : uint8_t { - MCP23S08_INPUT = INPUT, // 0x00 - MCP23S08_INPUT_PULLUP = INPUT_PULLUP, // 0x02 - MCP23S08_OUTPUT = OUTPUT // 0x01 -}; - -enum MCP23S08GPIORegisters { - // A side - MCP23S08_IODIR = 0x00, - MCP23S08_IPOL = 0x01, - MCP23S08_GPINTEN = 0x02, - MCP23S08_DEFVAL = 0x03, - MCP23S08_INTCON = 0x04, - MCP23S08_IOCON = 0x05, - MCP23S08_GPPU = 0x06, - MCP23S08_INTF = 0x07, - MCP23S08_INTCAP = 0x08, - MCP23S08_GPIO = 0x09, - MCP23S08_OLAT = 0x0A, -}; - -class MCP23S08 : public Component, +class MCP23S08 : public mcp23x08_base::MCP23X08Base, public spi::SPIDevice { public: @@ -37,37 +16,14 @@ class MCP23S08 : public Component, void setup() override; void dump_config() override; - bool digital_read(uint8_t pin); - void digital_write(uint8_t pin, bool value); - void pin_mode(uint8_t pin, uint8_t mode); void set_device_address(uint8_t device_addr); - float get_setup_priority() const override; - - // read a given register - bool read_reg(uint8_t reg, uint8_t *value); - // write a value to a given register - bool write_reg(uint8_t reg, uint8_t value); - // update registers with given pin value. - void update_reg(uint8_t pin, bool pin_value, uint8_t reg_a); - protected: + bool read_reg(uint8_t reg, uint8_t *value) override; + bool write_reg(uint8_t reg, uint8_t value) override; + uint8_t device_opcode_ = 0x40; - uint8_t olat_{0x00}; -}; - -class MCP23S08GPIOPin : public GPIOPin { - public: - MCP23S08GPIOPin(MCP23S08 *parent, uint8_t pin, uint8_t mode, bool inverted = false); - - void setup() override; - void pin_mode(uint8_t mode) override; - bool digital_read() override; - void digital_write(bool value) override; - - protected: - MCP23S08 *parent_; }; } // namespace mcp23s08 diff --git a/esphome/components/mcp23s17/__init__.py b/esphome/components/mcp23s17/__init__.py index 85f22874a6..c38bd2617a 100644 --- a/esphome/components/mcp23s17/__init__.py +++ b/esphome/components/mcp23s17/__init__.py @@ -1,26 +1,18 @@ import esphome.codegen as cg import esphome.config_validation as cv -from esphome import pins -from esphome.components import spi -from esphome.const import CONF_ID, CONF_NUMBER, CONF_MODE, CONF_INVERTED - -CODEOWNERS = ["@SenexCrenshaw"] +from esphome.components import spi, mcp23x17_base, mcp23xxx_base +from esphome.const import CONF_ID +AUTO_LOAD = ["mcp23x17_base"] +CODEOWNERS = ["@SenexCrenshaw", "@jesserockz"] DEPENDENCIES = ["spi"] MULTI_CONF = True CONF_DEVICEADDRESS = "deviceaddress" mcp23S17_ns = cg.esphome_ns.namespace("mcp23s17") -mcp23S17GPIOMode = mcp23S17_ns.enum("MCP23S17GPIOMode") -mcp23S17_GPIO_MODES = { - "INPUT": mcp23S17GPIOMode.MCP23S17_INPUT, - "INPUT_PULLUP": mcp23S17GPIOMode.MCP23S17_INPUT_PULLUP, - "OUTPUT": mcp23S17GPIOMode.MCP23S17_OUTPUT, -} -mcp23S17 = mcp23S17_ns.class_("MCP23S17", cg.Component, spi.SPIDevice) -mcp23S17GPIOPin = mcp23S17_ns.class_("MCP23S17GPIOPin", cg.GPIOPin) +mcp23S17 = mcp23S17_ns.class_("MCP23S17", mcp23x17_base.MCP23X17Base, spi.SPIDevice) CONFIG_SCHEMA = ( cv.Schema( @@ -29,47 +21,12 @@ CONFIG_SCHEMA = ( cv.Optional(CONF_DEVICEADDRESS, default=0): cv.uint8_t, } ) - .extend(cv.COMPONENT_SCHEMA) + .extend(mcp23xxx_base.MCP23XXX_CONFIG_SCHEMA) .extend(spi.spi_device_schema()) ) def to_code(config): - var = cg.new_Pvariable(config[CONF_ID]) + var = yield mcp23xxx_base.register_mcp23xxx(config) cg.add(var.set_device_address(config[CONF_DEVICEADDRESS])) - yield cg.register_component(var, config) yield spi.register_spi_device(var, config) - - -CONF_MCP23S17 = "mcp23s17" - -mcp23S17_OUTPUT_PIN_SCHEMA = cv.Schema( - { - cv.GenerateID(CONF_MCP23S17): cv.use_id(mcp23S17), - cv.Required(CONF_NUMBER): cv.int_, - cv.Optional(CONF_MODE, default="OUTPUT"): cv.enum( - mcp23S17_GPIO_MODES, upper=True - ), - cv.Optional(CONF_INVERTED, default=False): cv.boolean, - } -) -mcp23S17_INPUT_PIN_SCHEMA = cv.Schema( - { - cv.Required(CONF_MCP23S17): cv.use_id(mcp23S17), - cv.Required(CONF_NUMBER): cv.int_range(0, 15), - cv.Optional(CONF_MODE, default="INPUT"): cv.enum( - mcp23S17_GPIO_MODES, upper=True - ), - cv.Optional(CONF_INVERTED, default=False): cv.boolean, - } -) - - -@pins.PIN_SCHEMA_REGISTRY.register( - CONF_MCP23S17, (mcp23S17_OUTPUT_PIN_SCHEMA, mcp23S17_INPUT_PIN_SCHEMA) -) -def mcp23S17_pin_to_code(config): - parent = yield cg.get_variable(config[CONF_MCP23S17]) - yield mcp23S17GPIOPin.new( - parent, config[CONF_NUMBER], config[CONF_MODE], config[CONF_INVERTED] - ) diff --git a/esphome/components/mcp23s17/mcp23s17.cpp b/esphome/components/mcp23s17/mcp23s17.cpp index 30e4f63953..7c2cfcd526 100644 --- a/esphome/components/mcp23s17/mcp23s17.cpp +++ b/esphome/components/mcp23s17/mcp23s17.cpp @@ -19,17 +19,15 @@ void MCP23S17::setup() { this->enable(); uint8_t cmd = 0b01000000; this->transfer_byte(cmd); - this->transfer_byte(0x18); - this->transfer_byte(0x0A); - this->transfer_byte(this->device_opcode_); - this->transfer_byte(0); - this->transfer_byte(0xFF); - this->transfer_byte(0xFF); - - for (uint8_t i = 0; i < 20; i++) { - this->transfer_byte(0); - } + this->transfer_byte(mcp23x17_base::MCP23X17_IOCONA); + this->transfer_byte(0b00011000); // Enable HAEN pins for addressing this->disable(); + + if (this->open_drain_ints_) { + // enable open-drain interrupt pins, 3.3V-safe + this->write_reg(mcp23x17_base::MCP23X17_IOCONA, 0x04); + this->write_reg(mcp23x17_base::MCP23X17_IOCONB, 0x04); + } } void MCP23S17::dump_config() { @@ -37,65 +35,6 @@ void MCP23S17::dump_config() { LOG_PIN(" CS Pin: ", this->cs_); } -float MCP23S17::get_setup_priority() const { return setup_priority::HARDWARE; } - -bool MCP23S17::digital_read(uint8_t pin) { - uint8_t bit = pin % 8; - uint8_t reg_addr = pin < 8 ? MCP23S17_GPIOA : MCP23S17_GPIOB; - uint8_t value = 0; - this->read_reg(reg_addr, &value); - return value & (1 << bit); -} - -void MCP23S17::digital_write(uint8_t pin, bool value) { - uint8_t reg_addr = pin < 8 ? MCP23S17_OLATA : MCP23S17_OLATB; - this->update_reg(pin, value, reg_addr); -} - -void MCP23S17::pin_mode(uint8_t pin, uint8_t mode) { - uint8_t iodir = pin < 8 ? MCP23S17_IODIRA : MCP23S17_IODIRB; - uint8_t gppu = pin < 8 ? MCP23S17_GPPUA : MCP23S17_GPPUB; - switch (mode) { - case MCP23S17_INPUT: - this->update_reg(pin, true, iodir); - break; - case MCP23S17_INPUT_PULLUP: - this->update_reg(pin, true, iodir); - this->update_reg(pin, true, gppu); - break; - case MCP23S17_OUTPUT: - this->update_reg(pin, false, iodir); - break; - default: - break; - } -} - -void MCP23S17::update_reg(uint8_t pin, bool pin_value, uint8_t reg_addr) { - uint8_t bit = pin % 8; - uint8_t reg_value = 0; - if (reg_addr == MCP23S17_OLATA) { - reg_value = this->olat_a_; - } else if (reg_addr == MCP23S17_OLATB) { - reg_value = this->olat_b_; - } else { - this->read_reg(reg_addr, ®_value); - } - - if (pin_value) - reg_value |= 1 << bit; - else - reg_value &= ~(1 << bit); - - this->write_reg(reg_addr, reg_value); - - if (reg_addr == MCP23S17_OLATA) { - this->olat_a_ = reg_value; - } else if (reg_addr == MCP23S17_OLATB) { - this->olat_b_ = reg_value; - } -} - bool MCP23S17::read_reg(uint8_t reg, uint8_t *value) { this->enable(); this->transfer_byte(this->device_opcode_ | 1); @@ -115,12 +54,5 @@ bool MCP23S17::write_reg(uint8_t reg, uint8_t value) { return true; } -MCP23S17GPIOPin::MCP23S17GPIOPin(MCP23S17 *parent, uint8_t pin, uint8_t mode, bool inverted) - : GPIOPin(pin, mode, inverted), parent_(parent) {} -void MCP23S17GPIOPin::setup() { this->pin_mode(this->mode_); } -void MCP23S17GPIOPin::pin_mode(uint8_t mode) { this->parent_->pin_mode(this->pin_, mode); } -bool MCP23S17GPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) != this->inverted_; } -void MCP23S17GPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value != this->inverted_); } - } // namespace mcp23s17 } // namespace esphome diff --git a/esphome/components/mcp23s17/mcp23s17.h b/esphome/components/mcp23s17/mcp23s17.h index 8e27ff447f..1ced144c23 100644 --- a/esphome/components/mcp23s17/mcp23s17.h +++ b/esphome/components/mcp23s17/mcp23s17.h @@ -1,47 +1,14 @@ #pragma once #include "esphome/core/component.h" +#include "esphome/components/mcp23x17_base/mcp23x17_base.h" #include "esphome/core/esphal.h" #include "esphome/components/spi/spi.h" namespace esphome { namespace mcp23s17 { -/// Modes for MCP23S17 pins -enum MCP23S17GPIOMode : uint8_t { - MCP23S17_INPUT = INPUT, // 0x00 - MCP23S17_INPUT_PULLUP = INPUT_PULLUP, // 0x02 - MCP23S17_OUTPUT = OUTPUT // 0x01 -}; - -enum MCP23S17GPIORegisters { - // A side - MCP23S17_IODIRA = 0x00, - MCP23S17_IPOLA = 0x02, - MCP23S17_GPINTENA = 0x04, - MCP23S17_DEFVALA = 0x06, - MCP23S17_INTCONA = 0x08, - MCP23S17_IOCONA = 0x0A, - MCP23S17_GPPUA = 0x0C, - MCP23S17_INTFA = 0x0E, - MCP23S17_INTCAPA = 0x10, - MCP23S17_GPIOA = 0x12, - MCP23S17_OLATA = 0x14, - // B side - MCP23S17_IODIRB = 0x01, - MCP23S17_IPOLB = 0x03, - MCP23S17_GPINTENB = 0x05, - MCP23S17_DEFVALB = 0x07, - MCP23S17_INTCONB = 0x09, - MCP23S17_IOCONB = 0x0B, - MCP23S17_GPPUB = 0x0D, - MCP23S17_INTFB = 0x0F, - MCP23S17_INTCAPB = 0x11, - MCP23S17_GPIOB = 0x13, - MCP23S17_OLATB = 0x15, -}; - -class MCP23S17 : public Component, +class MCP23S17 : public mcp23x17_base::MCP23X17Base, public spi::SPIDevice { public: @@ -51,36 +18,11 @@ class MCP23S17 : public Component, void dump_config() override; void set_device_address(uint8_t device_addr); - bool digital_read(uint8_t pin); - void digital_write(uint8_t pin, bool value); - void pin_mode(uint8_t pin, uint8_t mode); - - float get_setup_priority() const override; - - // read a given register - bool read_reg(uint8_t reg, uint8_t *value); - // write a value to a given register - bool write_reg(uint8_t reg, uint8_t value); - // update registers with given pin value. - void update_reg(uint8_t pin, bool pin_value, uint8_t reg_a); - protected: + bool read_reg(uint8_t reg, uint8_t *value) override; + bool write_reg(uint8_t reg, uint8_t value) override; + uint8_t device_opcode_ = 0x40; - uint8_t olat_a_{0x00}; - uint8_t olat_b_{0x00}; -}; - -class MCP23S17GPIOPin : public GPIOPin { - public: - MCP23S17GPIOPin(MCP23S17 *parent, uint8_t pin, uint8_t mode, bool inverted = false); - - void setup() override; - void pin_mode(uint8_t mode) override; - bool digital_read() override; - void digital_write(bool value) override; - - protected: - MCP23S17 *parent_; }; } // namespace mcp23s17 diff --git a/esphome/components/mcp23x08_base/__init__.py b/esphome/components/mcp23x08_base/__init__.py new file mode 100644 index 0000000000..ba44917202 --- /dev/null +++ b/esphome/components/mcp23x08_base/__init__.py @@ -0,0 +1,8 @@ +import esphome.codegen as cg +from esphome.components import mcp23xxx_base + +AUTO_LOAD = ["mcp23xxx_base"] +CODEOWNERS = ["@jesserockz"] + +mcp23x08_base_ns = cg.esphome_ns.namespace("mcp23x08_base") +MCP23X08Base = mcp23x08_base_ns.class_("MCP23X08Base", mcp23xxx_base.MCP23XXXBase) diff --git a/esphome/components/mcp23x08_base/mcp23x08_base.cpp b/esphome/components/mcp23x08_base/mcp23x08_base.cpp new file mode 100644 index 0000000000..c14e0020dd --- /dev/null +++ b/esphome/components/mcp23x08_base/mcp23x08_base.cpp @@ -0,0 +1,89 @@ +#include "mcp23x08_base.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace mcp23x08_base { + +static const char *TAG = "mcp23x08_base"; + +bool MCP23X08Base::digital_read(uint8_t pin) { + uint8_t bit = pin % 8; + uint8_t reg_addr = mcp23x08_base::MCP23X08_GPIO; + uint8_t value = 0; + this->read_reg(reg_addr, &value); + return value & (1 << bit); +} + +void MCP23X08Base::digital_write(uint8_t pin, bool value) { + uint8_t reg_addr = mcp23x08_base::MCP23X08_OLAT; + this->update_reg(pin, value, reg_addr); +} + +void MCP23X08Base::pin_mode(uint8_t pin, uint8_t mode) { + uint8_t iodir = mcp23x08_base::MCP23X08_IODIR; + uint8_t gppu = mcp23x08_base::MCP23X08_GPPU; + switch (mode) { + case mcp23xxx_base::MCP23XXX_INPUT: + this->update_reg(pin, true, iodir); + break; + case mcp23xxx_base::MCP23XXX_INPUT_PULLUP: + this->update_reg(pin, true, iodir); + this->update_reg(pin, true, gppu); + break; + case mcp23xxx_base::MCP23XXX_OUTPUT: + this->update_reg(pin, false, iodir); + break; + default: + break; + } +} + +void MCP23X08Base::pin_interrupt_mode(uint8_t pin, mcp23xxx_base::MCP23XXXInterruptMode interrupt_mode) { + uint8_t gpinten = mcp23x08_base::MCP23X08_GPINTEN; + uint8_t intcon = mcp23x08_base::MCP23X08_INTCON; + uint8_t defval = mcp23x08_base::MCP23X08_DEFVAL; + + switch (interrupt_mode) { + case mcp23xxx_base::MCP23XXX_CHANGE: + this->update_reg(pin, true, gpinten); + this->update_reg(pin, false, intcon); + break; + case mcp23xxx_base::MCP23XXX_RISING: + this->update_reg(pin, true, gpinten); + this->update_reg(pin, true, intcon); + this->update_reg(pin, true, defval); + break; + case mcp23xxx_base::MCP23XXX_FALLING: + this->update_reg(pin, true, gpinten); + this->update_reg(pin, true, intcon); + this->update_reg(pin, false, defval); + break; + case mcp23xxx_base::MCP23XXX_NO_INTERRUPT: + this->update_reg(pin, false, gpinten); + break; + } +} + +void MCP23X08Base::update_reg(uint8_t pin, bool pin_value, uint8_t reg_addr) { + uint8_t bit = pin % 8; + uint8_t reg_value = 0; + if (reg_addr == mcp23x08_base::MCP23X08_OLAT) { + reg_value = this->olat_; + } else { + this->read_reg(reg_addr, ®_value); + } + + if (pin_value) + reg_value |= 1 << bit; + else + reg_value &= ~(1 << bit); + + this->write_reg(reg_addr, reg_value); + + if (reg_addr == mcp23x08_base::MCP23X08_OLAT) { + this->olat_ = reg_value; + } +} + +} // namespace mcp23x08_base +} // namespace esphome diff --git a/esphome/components/mcp23x08_base/mcp23x08_base.h b/esphome/components/mcp23x08_base/mcp23x08_base.h new file mode 100644 index 0000000000..5e2c1a047f --- /dev/null +++ b/esphome/components/mcp23x08_base/mcp23x08_base.h @@ -0,0 +1,39 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/mcp23xxx_base/mcp23xxx_base.h" +#include "esphome/core/esphal.h" + +namespace esphome { +namespace mcp23x08_base { + +enum MCP23S08GPIORegisters { + // A side + MCP23X08_IODIR = 0x00, + MCP23X08_IPOL = 0x01, + MCP23X08_GPINTEN = 0x02, + MCP23X08_DEFVAL = 0x03, + MCP23X08_INTCON = 0x04, + MCP23X08_IOCON = 0x05, + MCP23X08_GPPU = 0x06, + MCP23X08_INTF = 0x07, + MCP23X08_INTCAP = 0x08, + MCP23X08_GPIO = 0x09, + MCP23X08_OLAT = 0x0A, +}; + +class MCP23X08Base : public mcp23xxx_base::MCP23XXXBase { + public: + bool digital_read(uint8_t pin) override; + void digital_write(uint8_t pin, bool value) override; + void pin_mode(uint8_t pin, uint8_t mode) override; + void pin_interrupt_mode(uint8_t pin, mcp23xxx_base::MCP23XXXInterruptMode interrupt_mode) override; + + protected: + void update_reg(uint8_t pin, bool pin_value, uint8_t reg_a) override; + + uint8_t olat_{0x00}; +}; + +} // namespace mcp23x08_base +} // namespace esphome diff --git a/esphome/components/mcp23x17_base/__init__.py b/esphome/components/mcp23x17_base/__init__.py new file mode 100644 index 0000000000..97e0b3823d --- /dev/null +++ b/esphome/components/mcp23x17_base/__init__.py @@ -0,0 +1,8 @@ +import esphome.codegen as cg +from esphome.components import mcp23xxx_base + +AUTO_LOAD = ["mcp23xxx_base"] +CODEOWNERS = ["@jesserockz"] + +mcp23x17_base_ns = cg.esphome_ns.namespace("mcp23x17_base") +MCP23X17Base = mcp23x17_base_ns.class_("MCP23X17Base", mcp23xxx_base.MCP23XXXBase) diff --git a/esphome/components/mcp23x17_base/mcp23x17_base.cpp b/esphome/components/mcp23x17_base/mcp23x17_base.cpp new file mode 100644 index 0000000000..18f3ba7c6d --- /dev/null +++ b/esphome/components/mcp23x17_base/mcp23x17_base.cpp @@ -0,0 +1,93 @@ +#include "mcp23x17_base.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace mcp23x17_base { + +static const char *TAG = "mcp23x17_base"; + +bool MCP23X17Base::digital_read(uint8_t pin) { + uint8_t bit = pin % 8; + uint8_t reg_addr = pin < 8 ? mcp23x17_base::MCP23X17_GPIOA : mcp23x17_base::MCP23X17_GPIOB; + uint8_t value = 0; + this->read_reg(reg_addr, &value); + return value & (1 << bit); +} + +void MCP23X17Base::digital_write(uint8_t pin, bool value) { + uint8_t reg_addr = pin < 8 ? mcp23x17_base::MCP23X17_OLATA : mcp23x17_base::MCP23X17_OLATB; + this->update_reg(pin, value, reg_addr); +} + +void MCP23X17Base::pin_mode(uint8_t pin, uint8_t mode) { + uint8_t iodir = pin < 8 ? mcp23x17_base::MCP23X17_IODIRA : mcp23x17_base::MCP23X17_IODIRB; + uint8_t gppu = pin < 8 ? mcp23x17_base::MCP23X17_GPPUA : mcp23x17_base::MCP23X17_GPPUB; + switch (mode) { + case mcp23xxx_base::MCP23XXX_INPUT: + this->update_reg(pin, true, iodir); + break; + case mcp23xxx_base::MCP23XXX_INPUT_PULLUP: + this->update_reg(pin, true, iodir); + this->update_reg(pin, true, gppu); + break; + case mcp23xxx_base::MCP23XXX_OUTPUT: + this->update_reg(pin, false, iodir); + break; + default: + break; + } +} + +void MCP23X17Base::pin_interrupt_mode(uint8_t pin, mcp23xxx_base::MCP23XXXInterruptMode interrupt_mode) { + uint8_t gpinten = pin < 8 ? mcp23x17_base::MCP23X17_GPINTENA : mcp23x17_base::MCP23X17_GPINTENB; + uint8_t intcon = pin < 8 ? mcp23x17_base::MCP23X17_INTCONA : mcp23x17_base::MCP23X17_INTCONB; + uint8_t defval = pin < 8 ? mcp23x17_base::MCP23X17_DEFVALA : mcp23x17_base::MCP23X17_DEFVALB; + + switch (interrupt_mode) { + case mcp23xxx_base::MCP23XXX_CHANGE: + this->update_reg(pin, true, gpinten); + this->update_reg(pin, false, intcon); + break; + case mcp23xxx_base::MCP23XXX_RISING: + this->update_reg(pin, true, gpinten); + this->update_reg(pin, true, intcon); + this->update_reg(pin, true, defval); + break; + case mcp23xxx_base::MCP23XXX_FALLING: + this->update_reg(pin, true, gpinten); + this->update_reg(pin, true, intcon); + this->update_reg(pin, false, defval); + break; + case mcp23xxx_base::MCP23XXX_NO_INTERRUPT: + this->update_reg(pin, false, gpinten); + break; + } +} + +void MCP23X17Base::update_reg(uint8_t pin, bool pin_value, uint8_t reg_addr) { + uint8_t bit = pin % 8; + uint8_t reg_value = 0; + if (reg_addr == mcp23x17_base::MCP23X17_OLATA) { + reg_value = this->olat_a_; + } else if (reg_addr == mcp23x17_base::MCP23X17_OLATB) { + reg_value = this->olat_b_; + } else { + this->read_reg(reg_addr, ®_value); + } + + if (pin_value) + reg_value |= 1 << bit; + else + reg_value &= ~(1 << bit); + + this->write_reg(reg_addr, reg_value); + + if (reg_addr == mcp23x17_base::MCP23X17_OLATA) { + this->olat_a_ = reg_value; + } else if (reg_addr == mcp23x17_base::MCP23X17_OLATB) { + this->olat_b_ = reg_value; + } +} + +} // namespace mcp23x17_base +} // namespace esphome diff --git a/esphome/components/mcp23x17_base/mcp23x17_base.h b/esphome/components/mcp23x17_base/mcp23x17_base.h new file mode 100644 index 0000000000..1bbcb97041 --- /dev/null +++ b/esphome/components/mcp23x17_base/mcp23x17_base.h @@ -0,0 +1,52 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/mcp23xxx_base/mcp23xxx_base.h" +#include "esphome/core/esphal.h" + +namespace esphome { +namespace mcp23x17_base { + +enum MCP23X17GPIORegisters { + // A side + MCP23X17_IODIRA = 0x00, + MCP23X17_IPOLA = 0x02, + MCP23X17_GPINTENA = 0x04, + MCP23X17_DEFVALA = 0x06, + MCP23X17_INTCONA = 0x08, + MCP23X17_IOCONA = 0x0A, + MCP23X17_GPPUA = 0x0C, + MCP23X17_INTFA = 0x0E, + MCP23X17_INTCAPA = 0x10, + MCP23X17_GPIOA = 0x12, + MCP23X17_OLATA = 0x14, + // B side + MCP23X17_IODIRB = 0x01, + MCP23X17_IPOLB = 0x03, + MCP23X17_GPINTENB = 0x05, + MCP23X17_DEFVALB = 0x07, + MCP23X17_INTCONB = 0x09, + MCP23X17_IOCONB = 0x0B, + MCP23X17_GPPUB = 0x0D, + MCP23X17_INTFB = 0x0F, + MCP23X17_INTCAPB = 0x11, + MCP23X17_GPIOB = 0x13, + MCP23X17_OLATB = 0x15, +}; + +class MCP23X17Base : public mcp23xxx_base::MCP23XXXBase { + public: + bool digital_read(uint8_t pin) override; + void digital_write(uint8_t pin, bool value) override; + void pin_mode(uint8_t pin, uint8_t mode) override; + void pin_interrupt_mode(uint8_t pin, mcp23xxx_base::MCP23XXXInterruptMode interrupt_mode) override; + + protected: + void update_reg(uint8_t pin, bool pin_value, uint8_t reg_a) override; + + uint8_t olat_a_{0x00}; + uint8_t olat_b_{0x00}; +}; + +} // namespace mcp23x17_base +} // namespace esphome diff --git a/esphome/components/mcp23xxx_base/__init__.py b/esphome/components/mcp23xxx_base/__init__.py new file mode 100644 index 0000000000..f8ab224193 --- /dev/null +++ b/esphome/components/mcp23xxx_base/__init__.py @@ -0,0 +1,118 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome import pins +from esphome.const import ( + CONF_ID, + CONF_NUMBER, + CONF_MODE, + CONF_INVERTED, + CONF_INTERRUPT, + CONF_OPEN_DRAIN_INTERRUPT, +) +from esphome.core import coroutine + +CODEOWNERS = ["@jesserockz"] + +mcp23xxx_base_ns = cg.esphome_ns.namespace("mcp23xxx_base") +MCP23XXXBase = mcp23xxx_base_ns.class_("MCP23XXXBase", cg.Component) +MCP23XXXGPIOPin = mcp23xxx_base_ns.class_("MCP23XXXGPIOPin", cg.GPIOPin) +MCP23XXXGPIOMode = mcp23xxx_base_ns.enum("MCP23XXXGPIOMode") +MCP23XXXInterruptMode = mcp23xxx_base_ns.enum("MCP23XXXInterruptMode") + +MCP23XXX_INTERRUPT_MODES = { + "NO_INTERRUPT": MCP23XXXInterruptMode.MCP23XXX_NO_INTERRUPT, + "CHANGE": MCP23XXXInterruptMode.MCP23XXX_CHANGE, + "RISING": MCP23XXXInterruptMode.MCP23XXX_RISING, + "FALLING": MCP23XXXInterruptMode.MCP23XXX_FALLING, +} + +MCP23XXX_GPIO_MODES = { + "INPUT": MCP23XXXGPIOMode.MCP23XXX_INPUT, + "INPUT_PULLUP": MCP23XXXGPIOMode.MCP23XXX_INPUT_PULLUP, + "OUTPUT": MCP23XXXGPIOMode.MCP23XXX_OUTPUT, +} + +MCP23XXX_CONFIG_SCHEMA = cv.Schema( + { + cv.Optional(CONF_OPEN_DRAIN_INTERRUPT, default=False): cv.boolean, + } +).extend(cv.COMPONENT_SCHEMA) + + +@coroutine +def register_mcp23xxx(config): + var = cg.new_Pvariable(config[CONF_ID]) + yield cg.register_component(var, config) + cg.add(var.set_open_drain_ints(config[CONF_OPEN_DRAIN_INTERRUPT])) + return var + + +CONF_MCP23XXX = "mcp23xxx" +MCP23XXX_OUTPUT_PIN_SCHEMA = cv.Schema( + { + cv.Required(CONF_MCP23XXX): cv.use_id(MCP23XXXBase), + cv.Required(CONF_NUMBER): cv.int_, + cv.Optional(CONF_MODE, default="OUTPUT"): cv.enum( + MCP23XXX_GPIO_MODES, upper=True + ), + cv.Optional(CONF_INVERTED, default=False): cv.boolean, + cv.Optional(CONF_INTERRUPT, default="NO_INTERRUPT"): cv.enum( + MCP23XXX_INTERRUPT_MODES, upper=True + ), + } +) +MCP23XXX_INPUT_PIN_SCHEMA = cv.Schema( + { + cv.Required(CONF_MCP23XXX): cv.use_id(MCP23XXXBase), + cv.Required(CONF_NUMBER): cv.int_, + cv.Optional(CONF_MODE, default="INPUT"): cv.enum( + MCP23XXX_GPIO_MODES, upper=True + ), + cv.Optional(CONF_INVERTED, default=False): cv.boolean, + cv.Optional(CONF_INTERRUPT, default="NO_INTERRUPT"): cv.enum( + MCP23XXX_INTERRUPT_MODES, upper=True + ), + } +) + + +@pins.PIN_SCHEMA_REGISTRY.register( + CONF_MCP23XXX, (MCP23XXX_OUTPUT_PIN_SCHEMA, MCP23XXX_INPUT_PIN_SCHEMA) +) +def mcp23xxx_pin_to_code(config): + parent = yield cg.get_variable(config[CONF_MCP23XXX]) + yield MCP23XXXGPIOPin.new( + parent, + config[CONF_NUMBER], + config[CONF_MODE], + config[CONF_INVERTED], + config[CONF_INTERRUPT], + ) + + +# BEGIN Removed pin schemas below to show error in configuration +# TODO remove in 1.19.0 + +for id in ["mcp23008", "mcp23s08", "mcp23017", "mcp23s17"]: + PIN_SCHEMA = cv.Schema( + { + cv.Required(id): cv.invalid( + f"'{id}:' has been removed from the pin schema in 1.17.0, please use 'mcp23xxx:'" + ), + cv.Required(CONF_NUMBER): cv.int_, + cv.Optional(CONF_MODE, default="INPUT"): cv.enum( + MCP23XXX_GPIO_MODES, upper=True + ), + cv.Optional(CONF_INVERTED, default=False): cv.boolean, + cv.Optional(CONF_INTERRUPT, default="NO_INTERRUPT"): cv.enum( + MCP23XXX_INTERRUPT_MODES, upper=True + ), + } + ) + + @pins.PIN_SCHEMA_REGISTRY.register(id, (PIN_SCHEMA, PIN_SCHEMA)) + def pin_to_code(config): + pass + + +# END Removed pin schemas diff --git a/esphome/components/mcp23xxx_base/mcp23xxx_base.cpp b/esphome/components/mcp23xxx_base/mcp23xxx_base.cpp new file mode 100644 index 0000000000..37c55fceaf --- /dev/null +++ b/esphome/components/mcp23xxx_base/mcp23xxx_base.cpp @@ -0,0 +1,21 @@ +#include "mcp23xxx_base.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace mcp23xxx_base { + +float MCP23XXXBase::get_setup_priority() const { return setup_priority::IO; } + +MCP23XXXGPIOPin::MCP23XXXGPIOPin(MCP23XXXBase *parent, uint8_t pin, uint8_t mode, bool inverted, + MCP23XXXInterruptMode interrupt_mode) + : GPIOPin(pin, mode, inverted), parent_(parent), interrupt_mode_(interrupt_mode) {} +void MCP23XXXGPIOPin::setup() { this->pin_mode(this->mode_); } +void MCP23XXXGPIOPin::pin_mode(uint8_t mode) { + this->parent_->pin_mode(this->pin_, mode); + this->parent_->pin_interrupt_mode(this->pin_, this->interrupt_mode_); +} +bool MCP23XXXGPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) != this->inverted_; } +void MCP23XXXGPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value != this->inverted_); } + +} // namespace mcp23xxx_base +} // namespace esphome diff --git a/esphome/components/mcp23xxx_base/mcp23xxx_base.h b/esphome/components/mcp23xxx_base/mcp23xxx_base.h new file mode 100644 index 0000000000..bf01320264 --- /dev/null +++ b/esphome/components/mcp23xxx_base/mcp23xxx_base.h @@ -0,0 +1,55 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/core/esphal.h" + +namespace esphome { +namespace mcp23xxx_base { + +enum MCP23XXXInterruptMode : uint8_t { MCP23XXX_NO_INTERRUPT = 0, MCP23XXX_CHANGE, MCP23XXX_RISING, MCP23XXX_FALLING }; + +/// Modes for MCP23XXX pins +enum MCP23XXXGPIOMode : uint8_t { + MCP23XXX_INPUT = INPUT, // 0x00 + MCP23XXX_INPUT_PULLUP = INPUT_PULLUP, // 0x02 + MCP23XXX_OUTPUT = OUTPUT // 0x01 +}; + +class MCP23XXXBase : public Component { + public: + virtual bool digital_read(uint8_t pin); + virtual void digital_write(uint8_t pin, bool value); + virtual void pin_mode(uint8_t pin, uint8_t mode); + virtual void pin_interrupt_mode(uint8_t pin, MCP23XXXInterruptMode interrupt_mode); + + void set_open_drain_ints(const bool value) { this->open_drain_ints_ = value; } + float get_setup_priority() const override; + + protected: + // read a given register + virtual bool read_reg(uint8_t reg, uint8_t *value); + // write a value to a given register + virtual bool write_reg(uint8_t reg, uint8_t value); + // update registers with given pin value. + virtual void update_reg(uint8_t pin, bool pin_value, uint8_t reg_a); + + bool open_drain_ints_; +}; + +class MCP23XXXGPIOPin : public GPIOPin { + public: + MCP23XXXGPIOPin(MCP23XXXBase *parent, uint8_t pin, uint8_t mode, bool inverted = false, + MCP23XXXInterruptMode interrupt_mode = MCP23XXX_NO_INTERRUPT); + + void setup() override; + void pin_mode(uint8_t mode) override; + bool digital_read() override; + void digital_write(bool value) override; + + protected: + MCP23XXXBase *parent_; + MCP23XXXInterruptMode interrupt_mode_; +}; + +} // namespace mcp23xxx_base +} // namespace esphome diff --git a/esphome/const.py b/esphome/const.py index 6bb842c22b..af5961be88 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -255,6 +255,7 @@ CONF_INTENSITY = "intensity" CONF_INTERLOCK = "interlock" CONF_INTERNAL = "internal" CONF_INTERNAL_FILTER = "internal_filter" +CONF_INTERRUPT = "interrupt" CONF_INTERVAL = "interval" CONF_INVALID_COOLDOWN = "invalid_cooldown" CONF_INVERT = "invert" diff --git a/tests/test1.yaml b/tests/test1.yaml index 4dd3173d47..7651505c9a 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -879,7 +879,7 @@ binary_sensor: - platform: gpio name: 'MCP23S08 Pin #1' pin: - mcp23s08: mcp23s08_hub + mcp23xxx: mcp23s08_hub # Use pin number 1 number: 1 # One of INPUT or INPUT_PULLUP @@ -888,12 +888,22 @@ binary_sensor: - platform: gpio name: 'MCP23S17 Pin #1' pin: - mcp23s17: mcp23s17_hub + mcp23xxx: mcp23s17_hub # Use pin number 1 number: 1 # One of INPUT or INPUT_PULLUP mode: INPUT_PULLUP inverted: False + - platform: gpio + name: 'MCP23S17 Pin #1 with interrupt' + pin: + mcp23xxx: mcp23s17_hub + # Use pin number 1 + number: 1 + # One of INPUT or INPUT_PULLUP + mode: INPUT_PULLUP + inverted: False + interrupt: FALLING - platform: gpio pin: GPIO9 name: 'Living Room Window' @@ -1012,14 +1022,14 @@ binary_sensor: - platform: gpio name: 'MCP21 binary sensor' pin: - mcp23017: mcp23017_hub + mcp23xxx: mcp23017_hub number: 1 mode: INPUT inverted: True - platform: gpio name: 'MCP22 binary sensor' pin: - mcp23008: mcp23008_hub + mcp23xxx: mcp23008_hub number: 7 mode: INPUT_PULLUP inverted: False @@ -1176,14 +1186,14 @@ output: - platform: gpio id: id22 pin: - mcp23017: mcp23017_hub + mcp23xxx: mcp23017_hub number: 0 mode: OUTPUT inverted: False - platform: gpio id: id23 pin: - mcp23008: mcp23008_hub + mcp23xxx: mcp23008_hub number: 0 mode: OUTPUT inverted: False @@ -1459,7 +1469,7 @@ switch: - platform: gpio name: 'MCP23S08 Pin #0' pin: - mcp23s08: mcp23s08_hub + mcp23xxx: mcp23s08_hub # Use pin number 0 number: 0 mode: OUTPUT @@ -1467,7 +1477,7 @@ switch: - platform: gpio name: 'MCP23S17 Pin #0' pin: - mcp23s17: mcp23s17_hub + mcp23xxx: mcp23s17_hub # Use pin number 0 number: 1 mode: OUTPUT @@ -1755,10 +1765,10 @@ display: it.print("1234"); - platform: tm1637 clk_pin: - mcp23017: mcp23017_hub + mcp23xxx: mcp23017_hub number: 1 dio_pin: - mcp23017: mcp23017_hub + mcp23xxx: mcp23017_hub number: 2 intensity: 3 lambda: |- diff --git a/tests/test3.yaml b/tests/test3.yaml index 30578b451d..f4efe1067d 100644 --- a/tests/test3.yaml +++ b/tests/test3.yaml @@ -560,14 +560,14 @@ switch: - platform: gpio id: gpio_switch1 pin: - mcp23017: mcp23017_hub + mcp23xxx: mcp23017_hub number: 0 mode: OUTPUT interlock: &interlock [gpio_switch1, gpio_switch2, gpio_switch3] - platform: gpio id: gpio_switch2 pin: - mcp23008: mcp23008_hub + mcp23xxx: mcp23008_hub number: 0 mode: OUTPUT interlock: *interlock From d5cf4b7eace9c02d8344aaea7644e81c1dc988dc Mon Sep 17 00:00:00 2001 From: Guillermo Ruffino Date: Sun, 7 Mar 2021 19:59:32 -0300 Subject: [PATCH 079/111] Improve error checking: too many component id candidates (#1570) * add error too many candidates * Improve error checking of ids --- esphome/__main__.py | 2 ++ esphome/config.py | 43 ++++++++++++++++++++++++++++++++++++------- tests/test1.yaml | 43 ++++++++++++++++++++++++++++++------------- tests/test3.yaml | 5 ----- 4 files changed, 68 insertions(+), 25 deletions(-) diff --git a/esphome/__main__.py b/esphome/__main__.py index 2acd16cf95..20cb44d11c 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -320,6 +320,8 @@ def command_config(args, config): def command_vscode(args): from esphome import vscode + logging.disable(logging.INFO) + logging.disable(logging.WARNING) CORE.config_path = args.configuration[0] vscode.read_config(args) diff --git a/esphome/config.py b/esphome/config.py index 995861fa6e..3317196965 100644 --- a/esphome/config.py +++ b/esphome/config.py @@ -282,12 +282,19 @@ class Config(OrderedDict): if item_index in data: doc_range = [x for x in data.keys() if x == item_index][0].esp_range data = data[item_index] - except (KeyError, IndexError, TypeError): + except (KeyError, IndexError, TypeError, AttributeError): return doc_range if isinstance(data, core.ID): data = data.id if isinstance(data, ESPHomeDataBase) and data.esp_range is not None: doc_range = data.esp_range + elif isinstance(data, dict): + platform_item = data.get("platform") + if ( + isinstance(platform_item, ESPHomeDataBase) + and platform_item.esp_range is not None + ): + doc_range = platform_item.esp_range return doc_range @@ -359,7 +366,7 @@ def do_id_pass(result): # type: (Config) -> None if id.id is not None: # manually declared match = next((v[0] for v in declare_ids if v[0].id == id.id), None) - if match is None: + if match is None or not match.is_manual: # No declared ID with this name import difflib @@ -369,7 +376,7 @@ def do_id_pass(result): # type: (Config) -> None ) # Find candidates matches = difflib.get_close_matches( - id.id, [v[0].id for v in declare_ids] + id.id, [v[0].id for v in declare_ids if v[0].is_manual] ) if matches: matches_s = ", ".join(f'"{x}"' for x in matches) @@ -389,15 +396,37 @@ def do_id_pass(result): # type: (Config) -> None ) if id.id is None and id.type is not None: + matches = [] for v in declare_ids: if v[0] is None or not isinstance(v[0].type, MockObjClass): continue inherits = v[0].type.inherits_from(id.type) if inherits: - id.id = v[0].id - break - else: - result.add_str_error(f"Couldn't resolve ID for type '{id.type}'", path) + matches.append(v[0]) + + if len(matches) == 0: + result.add_str_error( + f"Couldn't find any component that can be used for '{id.type}'. Are you missing a hub declaration?", + path, + ) + elif len(matches) == 1: + id.id = matches[0].id + elif len(matches) > 1: + if str(id.type) == "time::RealTimeClock": + id.id = matches[0].id + else: + manual_declared_count = sum(1 for m in matches if m.is_manual) + if manual_declared_count > 0: + ids = ", ".join([f"'{m.id}'" for m in matches if m.is_manual]) + result.add_str_error( + f"Too many candidates found for '{path[-1]}' type '{id.type}' {'Some are' if manual_declared_count > 1 else 'One is'} {ids}", + path, + ) + else: + result.add_str_error( + f"Too many candidates found for '{path[-1]}' type '{id.type}' You must assign an explicit ID to the parent component you want to use.", + path, + ) def recursive_check_replaceme(value): diff --git a/tests/test1.yaml b/tests/test1.yaml index 7651505c9a..f5c3ab9b57 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -144,9 +144,13 @@ mqtt: - uart.write: id: uart0 data: Hello World - - uart.write: [0x00, 0x20, 0x30] - - uart.write: !lambda |- - return {}; + - uart.write: + id: uart0 + data: [0x00, 0x20, 0x30] + - uart.write: + id: uart0 + data: !lambda |- + return {}; i2c: sda: 21 @@ -469,7 +473,7 @@ sensor: name: 'HLW8012 Power' id: hlw8012_power energy: - name: "HLW8012 Energy" + name: 'HLW8012 Energy' id: hlw8012_energy update_interval: 15s current_resistor: 0.001 ohm @@ -583,6 +587,7 @@ sensor: reference_resistance: '430 Ω' rtd_nominal_resistance: '100 Ω' - platform: mhz19 + uart_id: uart0 co2: name: 'MH-Z19 CO2 Value' temperature: @@ -648,25 +653,27 @@ sensor: name: Pulse Width pin: GPIO12 - platform: senseair + uart_id: uart0 co2: name: 'SenseAir CO2 Value' update_interval: 15s - platform: sm300d2 + uart_id: uart0 co2: - name: "SM300D2 CO2 Value" + name: 'SM300D2 CO2 Value' formaldehyde: - name: "SM300D2 Formaldehyde Value" + name: 'SM300D2 Formaldehyde Value' tvoc: - name: "SM300D2 TVOC Value" + name: 'SM300D2 TVOC Value' pm_2_5: - name: "SM300D2 PM2.5 Value" + name: 'SM300D2 PM2.5 Value' pm_10_0: - name: "SM300D2 PM10 Value" + name: 'SM300D2 PM10 Value' temperature: - name: "SM300D2 Temperature Value" + name: 'SM300D2 Temperature Value' humidity: - name: "SM300D2 Humidity Value" - update_interval: 60s + name: 'SM300D2 Humidity Value' + update_interval: 60s - platform: sht3xd temperature: name: 'Living Room Temperature 8' @@ -785,6 +792,7 @@ sensor: root["key"] = id(the_sensor).state; root["greeting"] = "Hello World"; - platform: sds011 + uart_id: uart0 pm_2_5: name: 'SDS011 PM2.5' pm_10_0: @@ -834,6 +842,7 @@ sensor: name: 'AQI' calculation_type: 'CAQI' - platform: teleinfo + uart_id: uart0 tags: - tag_name: 'HCHC' sensor: @@ -1007,6 +1016,7 @@ binary_sensor: id: gpio_19 frequency: !lambda 'return 500.0;' - platform: pn532 + pn532_id: pn532_bs uid: 74-10-37-94 name: 'PN532 NFC Tag' - platform: rdm6300 @@ -1518,7 +1528,7 @@ switch: turn_on_action: remote_transmitter.transmit_samsung36: address: 0x0400 - command: 0x000E00FF + command: 0x000E00FF - platform: template name: Sony turn_on_action: @@ -1653,12 +1663,15 @@ switch: id: my_switch state: !lambda 'return false;' - platform: uart + uart_id: uart0 name: 'UART String Output' data: 'DataToSend' - platform: uart + uart_id: uart0 name: 'UART Bytes Output' data: [0xDE, 0xAD, 0xBE, 0xEF] - platform: uart + uart_id: uart0 name: 'UART Recurring Output' data: [0xDE, 0xAD, 0xBE, 0xEF] send_every: 1s @@ -1774,6 +1787,7 @@ display: lambda: |- it.print("1234"); - platform: nextion + uart_id: uart0 lambda: |- it.set_component_value("gauge", 50); it.set_component_text("textview", "Hello World!"); @@ -1901,6 +1915,7 @@ status_led: pin: GPIO2 pn532_spi: + id: pn532_bs cs_pin: GPIO23 update_interval: 1s on_tag: @@ -1913,6 +1928,7 @@ pn532_spi: pn532_i2c: rdm6300: + uart_id: uart0 rc522_spi: cs_pin: GPIO23 @@ -1928,6 +1944,7 @@ rc522_i2c: ESP_LOGD("main", "Found tag %s", x.c_str()); gps: + uart_id: uart0 time: - platform: sntp diff --git a/tests/test3.yaml b/tests/test3.yaml index f4efe1067d..ee95e9d35f 100644 --- a/tests/test3.yaml +++ b/tests/test3.yaml @@ -197,10 +197,6 @@ uart: rx_pin: GPIO3 baud_rate: 115200 - - id: adalight_uart - rx_pin: GPIO3 - baud_rate: 115200 - ota: safe_mode: True port: 3286 @@ -814,7 +810,6 @@ light: effects: - wled: - adalight: - uart_id: adalight_uart - e131: universe: 1 - platform: hbridge From 1e227e8051a288c8c46b17b755a4da8a4aec3893 Mon Sep 17 00:00:00 2001 From: Guillermo Ruffino Date: Sun, 7 Mar 2021 21:05:08 -0300 Subject: [PATCH 080/111] Schema dump (#1564) * schema dump idea accept boolean or anything default accept null also for full dicts added some common validators more simple validators support multi_conf better handle automations updates updates handle lists removed not needed class move to own folder generalized for automations lists, etc updates updates clean up clean up fix automations made comment optional basic docs support added more docs fixes docs handling updates updates fix components parent updates updates updates Fix inkplate 6 registration updates Disable logging for vscode add on better handle buses keep extended order as in CONFIGs updates updates updates disable comments moved to scripts/build_jsonschema added configurable decorators path handling fix handle list_schema fixes and cleanup add jschema_extractor to maybe updates lint no schema in git add generated loggers list * lint --- esphome/automation.py | 12 + esphome/components/canbus/__init__.py | 12 +- esphome/components/inkplate6/display.py | 4 +- esphome/components/mcp2515/canbus.py | 2 +- esphome/components/remote_base/__init__.py | 20 +- esphome/config_validation.py | 5 + esphome/const.py | 1 - esphome/jsonschema.py | 90 +++ esphome/voluptuous_schema.py | 2 + script/build_jsonschema.py | 708 +++++++++++++++++++++ setup.py | 69 +- tests/component_tests/conftest.py | 4 +- 12 files changed, 879 insertions(+), 50 deletions(-) create mode 100644 esphome/jsonschema.py create mode 100644 script/build_jsonschema.py diff --git a/esphome/automation.py b/esphome/automation.py index 63e4ce0372..eb6cb02532 100644 --- a/esphome/automation.py +++ b/esphome/automation.py @@ -11,6 +11,7 @@ from esphome.const import ( CONF_TIME, ) from esphome.core import coroutine +from esphome.jsonschema import jschema_extractor from esphome.util import Registry @@ -21,7 +22,12 @@ def maybe_simple_id(*validators): def maybe_conf(conf, *validators): validator = cv.All(*validators) + @jschema_extractor("maybe") def validate(value): + # pylint: disable=comparison-with-callable + if value == jschema_extractor: + return validator + if isinstance(value, dict): return validator(value) with cv.remove_prepend_path([conf]): @@ -103,7 +109,13 @@ def validate_automation(extra_schema=None, extra_validators=None, single=False): # This should only happen with invalid configs, but let's have a nice error message. return [schema(value)] + @jschema_extractor("automation") def validator(value): + # hack to get the schema + # pylint: disable=comparison-with-callable + if value == jschema_extractor: + return schema + value = validator_(value) if extra_validators is not None: value = cv.Schema([extra_validators])(value) diff --git a/esphome/components/canbus/__init__.py b/esphome/components/canbus/__init__.py index 28501c7a85..1cbebb07da 100644 --- a/esphome/components/canbus/__init__.py +++ b/esphome/components/canbus/__init__.py @@ -12,7 +12,6 @@ CONF_USE_EXTENDED_ID = "use_extended_id" CONF_CANBUS_ID = "canbus_id" CONF_BIT_RATE = "bit_rate" CONF_ON_FRAME = "on_frame" -CONF_CANBUS_SEND = "canbus.send" def validate_id(id_value, id_ext): @@ -59,7 +58,7 @@ CAN_SPEEDS = { "1000KBPS": CanSpeed.CAN_1000KBPS, } -CONFIG_SCHEMA = cv.Schema( +CANBUS_SCHEMA = cv.Schema( { cv.GenerateID(): cv.declare_id(CanbusComponent), cv.Required(CONF_CAN_ID): cv.int_range(min=0, max=0x1FFFFFFF), @@ -70,6 +69,13 @@ CONFIG_SCHEMA = cv.Schema( 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, + 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, + } + ), } ), } @@ -104,7 +110,7 @@ def register_canbus(var, config): # Actions @automation.register_action( - CONF_CANBUS_SEND, + "canbus.send", canbus_ns.class_("CanbusSendAction", automation.Action), cv.maybe_simple_value( { diff --git a/esphome/components/inkplate6/display.py b/esphome/components/inkplate6/display.py index cd2b5ac51b..323936d2c4 100644 --- a/esphome/components/inkplate6/display.py +++ b/esphome/components/inkplate6/display.py @@ -87,7 +87,9 @@ CONFIG_SCHEMA = cv.All( CONF_DISPLAY_DATA_7_PIN, default=27 ): pins.internal_gpio_output_pin_schema, } - ).extend(cv.polling_component_schema("5s").extend(i2c.i2c_device_schema(0x48))), + ) + .extend(cv.polling_component_schema("5s")) + .extend(i2c.i2c_device_schema(0x48)), cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA), ) diff --git a/esphome/components/mcp2515/canbus.py b/esphome/components/mcp2515/canbus.py index 68e28d45b8..0fc679d17a 100644 --- a/esphome/components/mcp2515/canbus.py +++ b/esphome/components/mcp2515/canbus.py @@ -26,7 +26,7 @@ MCP_MODE = { "LISTENONLY": McpMode.CANCTRL_REQOP_LISTENONLY, } -CONFIG_SCHEMA = canbus.CONFIG_SCHEMA.extend( +CONFIG_SCHEMA = canbus.CANBUS_SCHEMA.extend( { cv.GenerateID(): cv.declare_id(mcp2515), cv.Optional(CONF_CLOCK, default="8MHZ"): cv.enum(CAN_CLOCK, upper=True), diff --git a/esphome/components/remote_base/__init__.py b/esphome/components/remote_base/__init__.py index 7e81daa1e1..96579c05bb 100644 --- a/esphome/components/remote_base/__init__.py +++ b/esphome/components/remote_base/__init__.py @@ -29,6 +29,7 @@ from esphome.const import ( CONF_RC_CODE_2, ) from esphome.core import coroutine +from esphome.jsonschema import jschema_extractor from esphome.util import Registry, SimpleRegistry AUTO_LOAD = ["binary_sensor"] @@ -123,13 +124,16 @@ def validate_repeat(value): return validate_repeat({CONF_TIMES: value}) +BASE_REMOTE_TRANSMITTER_SCHEMA = cv.Schema( + { + cv.GenerateID(CONF_TRANSMITTER_ID): cv.use_id(RemoteTransmitterBase), + cv.Optional(CONF_REPEAT): validate_repeat, + } +) + + def register_action(name, type_, schema): - validator = templatize(schema).extend( - { - cv.GenerateID(CONF_TRANSMITTER_ID): cv.use_id(RemoteTransmitterBase), - cv.Optional(CONF_REPEAT): validate_repeat, - } - ) + validator = templatize(schema).extend(BASE_REMOTE_TRANSMITTER_SCHEMA) registerer = automation.register_action( f"remote_transmitter.transmit_{name}", type_, validator ) @@ -190,11 +194,15 @@ def validate_dumpers(value): def validate_triggers(base_schema): assert isinstance(base_schema, cv.Schema) + @jschema_extractor("triggers") def validator(config): added_keys = {} for key, (_, valid) in TRIGGER_REGISTRY.items(): added_keys[cv.Optional(key)] = valid new_schema = base_schema.extend(added_keys) + # pylint: disable=comparison-with-callable + if config == jschema_extractor: + return new_schema return new_schema(config) return validator diff --git a/esphome/config_validation.py b/esphome/config_validation.py index 46cc1fad50..4a65c59379 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -46,6 +46,7 @@ from esphome.core import ( TimePeriodMinutes, ) from esphome.helpers import list_starts_with, add_class_to_obj +from esphome.jsonschema import jschema_composite, jschema_registry, jschema_typed from esphome.voluptuous_schema import _Schema from esphome.yaml_util import make_data_base @@ -306,6 +307,7 @@ def boolean(value): ) +@jschema_composite def ensure_list(*validators): """Validate this configuration option to be a list. @@ -1341,6 +1343,7 @@ def extract_keys(schema): return keys +@jschema_typed def typed_schema(schemas, **kwargs): """Create a schema that has a key to distinguish between schemas""" key = kwargs.pop("key", CONF_TYPE) @@ -1442,6 +1445,7 @@ def validate_registry_entry(name, registry): ) ignore_keys = extract_keys(base_schema) + @jschema_registry(registry) def validator(value): if isinstance(value, str): value = {value: {}} @@ -1488,6 +1492,7 @@ def validate_registry(name, registry): return ensure_list(validate_registry_entry(name, registry)) +@jschema_composite def maybe_simple_value(*validators, **kwargs): key = kwargs.pop("key", CONF_VALUE) validator = All(*validators) diff --git a/esphome/const.py b/esphome/const.py index af5961be88..5d735698bc 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -180,7 +180,6 @@ CONF_ENERGY = "energy" CONF_ENTITY_ID = "entity_id" CONF_ESP8266_RESTORE_FROM_FLASH = "esp8266_restore_from_flash" CONF_ESPHOME = "esphome" -CONF_ESPHOME_CORE_VERSION = "esphome_core_version" CONF_EVENT = "event" CONF_EXPIRE_AFTER = "expire_after" CONF_EXTERNAL_VCC = "external_vcc" diff --git a/esphome/jsonschema.py b/esphome/jsonschema.py new file mode 100644 index 0000000000..25d85ed58f --- /dev/null +++ b/esphome/jsonschema.py @@ -0,0 +1,90 @@ +# These are a helper decorators to help get schema from some +# components which uses volutuous in a way where validation +# is hidden in local functions + +# These decorators should not modify at all what the functions +# originally do. +# +# However there is a property to further disable decorator +# impat. +# +# This is set to true by script/build_jsonschema.py +# only, so data is collected (again functionality is not modified) +EnableJsonSchemaCollect = False + +extended_schemas = {} +list_schemas = {} +registry_schemas = {} +hidden_schemas = {} +typed_schemas = {} + + +def jschema_extractor(validator_name): + if EnableJsonSchemaCollect: + + def decorator(func): + hidden_schemas[str(func)] = validator_name + return func + + return decorator + + def dummy(f): + return f + + return dummy + + +def jschema_extended(func): + if EnableJsonSchemaCollect: + + def decorate(*args, **kwargs): + ret = func(*args, **kwargs) + assert len(args) == 2 + extended_schemas[str(ret)] = args + return ret + + return decorate + + return func + + +def jschema_composite(func): + if EnableJsonSchemaCollect: + + def decorate(*args, **kwargs): + ret = func(*args, **kwargs) + # args length might be 2, but 2nd is always validator + list_schemas[str(ret)] = args + return ret + + return decorate + + return func + + +def jschema_registry(registry): + if EnableJsonSchemaCollect: + + def decorator(func): + registry_schemas[str(func)] = registry + return func + + return decorator + + def dummy(f): + return f + + return dummy + + +def jschema_typed(func): + if EnableJsonSchemaCollect: + + def decorate(*args, **kwargs): + ret = func(*args, **kwargs) + typed_schemas[str(ret)] = (args, kwargs) + return ret + + return decorate + + return func diff --git a/esphome/voluptuous_schema.py b/esphome/voluptuous_schema.py index 86c20510aa..0fdae423cf 100644 --- a/esphome/voluptuous_schema.py +++ b/esphome/voluptuous_schema.py @@ -2,6 +2,7 @@ import difflib import itertools import voluptuous as vol +from esphome.jsonschema import jschema_extended class ExtraKeysInvalid(vol.Invalid): @@ -202,6 +203,7 @@ class _Schema(vol.Schema): self._extra_schemas.append(validator) return self + @jschema_extended # pylint: disable=signature-differs def extend(self, *schemas, **kwargs): extra = kwargs.pop("extra", None) diff --git a/script/build_jsonschema.py b/script/build_jsonschema.py new file mode 100644 index 0000000000..e179c95541 --- /dev/null +++ b/script/build_jsonschema.py @@ -0,0 +1,708 @@ +#!/usr/bin/env python3 + +import json +import argparse +import os +import re +from pathlib import Path +import voluptuous as vol + +# NOTE: Cannot import other esphome components globally as a modification in jsonschema +# is needed before modules are loaded +import esphome.jsonschema as ejs + +ejs.EnableJsonSchemaCollect = True + +DUMP_COMMENTS = False + +JSC_ACTION = "automation.ACTION_REGISTRY" +JSC_ALLOF = "allOf" +JSC_ANYOF = "anyOf" +JSC_COMMENT = "$comment" +JSC_CONDITION = "automation.CONDITION_REGISTRY" +JSC_DESCRIPTION = "description" +JSC_ONEOF = "oneOf" +JSC_PROPERTIES = "properties" +JSC_REF = "$ref" +SIMPLE_AUTOMATION = "simple_automation" + +schema_names = {} +schema_registry = {} +components = {} +modules = {} +registries = [] +pending_refs = [] + +definitions = {} +base_props = {} + + +parser = argparse.ArgumentParser() +parser.add_argument( + "--output", default="esphome.json", help="Output filename", type=os.path.abspath +) + +args = parser.parse_args() + + +def get_ref(definition): + return {JSC_REF: "#/definitions/" + definition} + + +def is_ref(jschema): + return isinstance(jschema, dict) and JSC_REF in jschema + + +def unref(jschema): + return definitions[jschema[JSC_REF][len("#/definitions/") :]] + + +def add_definition_array_or_single_object(ref): + return {JSC_ANYOF: [{"type": "array", "items": ref}, ref]} + + +def add_core(): + from esphome.core_config import CONFIG_SCHEMA + + base_props["esphome"] = get_jschema("esphome", CONFIG_SCHEMA.schema) + + +def add_buses(): + # uart + from esphome.components.uart import UART_DEVICE_SCHEMA + + get_jschema("uart_bus", UART_DEVICE_SCHEMA) + + # spi + from esphome.components.spi import spi_device_schema + + get_jschema("spi_bus", spi_device_schema(False)) + + # i2c + from esphome.components.i2c import i2c_device_schema + + get_jschema("i2c_bus", i2c_device_schema(None)) + + +def add_registries(): + for domain, module in modules.items(): + add_module_registries(domain, module) + + +def add_module_registries(domain, module): + from esphome.util import Registry + + for c in dir(module): + m = getattr(module, c) + if isinstance(m, Registry): + add_registry(domain + "." + c, m) + + +def add_registry(registry_name, registry): + validators = [] + registries.append((registry, registry_name)) + for name in registry.keys(): + schema = get_jschema(str(name), registry[name].schema, create_return_ref=False) + if not schema: + schema = {"type": "string"} + o_schema = {"type": "object", JSC_PROPERTIES: {name: schema}} + validators.append(o_schema) + definitions[registry_name] = {JSC_ANYOF: validators} + + +def get_registry_ref(registry): + # we don't know yet + ref = {JSC_REF: "pending"} + pending_refs.append((ref, registry)) + return ref + + +def solve_pending_refs(): + for ref, registry in pending_refs: + for registry_match, name in registries: + if registry == registry_match: + ref[JSC_REF] = "#/definitions/" + name + + +def add_module_schemas(name, module): + import esphome.config_validation as cv + + for c in dir(module): + v = getattr(module, c) + if isinstance(v, cv.Schema): + get_jschema(name + "." + c, v) + + +def get_dirs(): + from esphome.config import CORE_COMPONENTS_PATH + + dir_names = [ + d + for d in os.listdir(CORE_COMPONENTS_PATH) + if not d.startswith("__") + and os.path.isdir(os.path.join(CORE_COMPONENTS_PATH, d)) + ] + return dir_names + + +def get_logger_tags(): + from esphome.config import CORE_COMPONENTS_PATH + import glob + + pattern = re.compile(r'^static const char(\*\s|\s\*)TAG = "(\w.*)";', re.MULTILINE) + tags = [ + "app", + "component", + "esphal", + "helpers", + "preferences", + "scheduler", + "api.service", + ] + for x in os.walk(CORE_COMPONENTS_PATH): + for y in glob.glob(os.path.join(x[0], "*.cpp")): + with open(y, "r") as file: + data = file.read() + match = pattern.search(data) + if match: + tags.append(match.group(2)) + return tags + + +def load_components(): + import esphome.config_validation as cv + from esphome.config import get_component + + modules["cv"] = cv + from esphome import automation + + modules["automation"] = automation + + for domain in get_dirs(): + components[domain] = get_component(domain) + modules[domain] = components[domain].module + + +def add_components(): + from esphome.config import get_platform + + for domain, c in components.items(): + if c.is_platform_component: + # this is a platform_component, e.g. binary_sensor + platform_schema = [ + { + "type": "object", + "properties": {"platform": {"type": "string"}}, + } + ] + if domain != "output" and domain != "display": + # output bases are either FLOAT or BINARY so don't add common base for this + # display bases are either simple or FULL so don't add common base for this + platform_schema = [ + {"$ref": f"#/definitions/{domain}.{domain.upper()}_SCHEMA"} + ] + platform_schema + + base_props[domain] = {"type": "array", "items": {"allOf": platform_schema}} + + add_module_registries(domain, c.module) + add_module_schemas(domain, c.module) + + # need first to iterate all platforms then iteate components + # a platform component can have other components as properties, + # e.g. climate components usually have a temperature sensor + + for domain, c in components.items(): + if (c.config_schema is not None) or c.is_platform_component: + if c.is_platform_component: + platform_schema = base_props[domain]["items"]["allOf"] + for platform in get_dirs(): + p = get_platform(domain, platform) + if p is not None: + # this is a platform element, e.g. + # - platform: gpio + schema = get_jschema( + domain + "-" + platform, + p.config_schema, + create_return_ref=False, + ) + if ( + schema + ): # for invalid schemas, None is returned thus is deprecated + platform_schema.append( + { + "if": { + JSC_PROPERTIES: { + "platform": {"const": platform} + } + }, + "then": schema, + } + ) + + elif c.config_schema is not None: + # adds root components which are not platforms, e.g. api: logger: + if c.is_multi_conf: + schema = get_jschema(domain, c.config_schema) + schema = add_definition_array_or_single_object(schema) + else: + schema = get_jschema(domain, c.config_schema, False) + base_props[domain] = schema + + +def get_automation_schema(name, vschema): + from esphome.automation import AUTOMATION_SCHEMA + + # ensure SIMPLE_AUTOMATION + if SIMPLE_AUTOMATION not in definitions: + simple_automation = add_definition_array_or_single_object(get_ref(JSC_ACTION)) + simple_automation[JSC_ANYOF].append( + get_jschema(AUTOMATION_SCHEMA.__module__, AUTOMATION_SCHEMA) + ) + + definitions[schema_names[str(AUTOMATION_SCHEMA)]][JSC_PROPERTIES][ + "then" + ] = add_definition_array_or_single_object(get_ref(JSC_ACTION)) + definitions[SIMPLE_AUTOMATION] = simple_automation + + extra_vschema = None + if AUTOMATION_SCHEMA == ejs.extended_schemas[str(vschema)][0]: + extra_vschema = ejs.extended_schemas[str(vschema)][1] + + if not extra_vschema: + return get_ref(SIMPLE_AUTOMATION) + + # add then property + extra_jschema = get_jschema(name, extra_vschema, False) + + if is_ref(extra_jschema): + return extra_jschema + + if not JSC_PROPERTIES in extra_jschema: + # these are interval: and exposure_notifications, featuring automations a component + extra_jschema[JSC_ALLOF][0][JSC_PROPERTIES][ + "then" + ] = add_definition_array_or_single_object(get_ref(JSC_ACTION)) + ref = create_ref(name, extra_vschema, extra_jschema) + return add_definition_array_or_single_object(ref) + + # automations can be either + # * a single action, + # * an array of action, + # * an object with automation's schema and a then key + # with again a single action or an array of actions + + extra_jschema[JSC_PROPERTIES]["then"] = add_definition_array_or_single_object( + get_ref(JSC_ACTION) + ) + jschema = add_definition_array_or_single_object(get_ref(JSC_ACTION)) + jschema[JSC_ANYOF].append(extra_jschema) + + return create_ref(name, extra_vschema, jschema) + + +def get_entry(parent_key, vschema): + from esphome.voluptuous_schema import _Schema as schema_type + + entry = {} + # annotate schema validator info + if DUMP_COMMENTS: + entry[JSC_COMMENT] = "entry: " + parent_key + "/" + str(vschema) + + if isinstance(vschema, list): + ref = get_jschema(parent_key + "[]", vschema[0]) + entry = {"type": "array", "items": ref} + elif isinstance(vschema, schema_type) and hasattr(vschema, "schema"): + entry = get_jschema(parent_key, vschema, False) + elif hasattr(vschema, "validators"): + entry = get_jschema(parent_key, vschema, False) + elif vschema in schema_registry: + entry = schema_registry[vschema].copy() + elif str(vschema) in ejs.registry_schemas: + entry = get_registry_ref(ejs.registry_schemas[str(vschema)]) + elif str(vschema) in ejs.list_schemas: + ref = get_jschema(parent_key, ejs.list_schemas[str(vschema)][0]) + entry = {JSC_ANYOF: [ref, {"type": "array", "items": ref}]} + + elif str(vschema) in ejs.typed_schemas: + schema_types = [{"type": "object", "properties": {"type": {"type": "string"}}}] + entry = {"allOf": schema_types} + for schema_key, vschema_type in ejs.typed_schemas[str(vschema)][0][0].items(): + schema_types.append( + { + "if": {"properties": {"type": {"const": schema_key}}}, + "then": get_jschema(f"{parent_key}-{schema_key}", vschema_type), + } + ) + + elif str(vschema) in ejs.hidden_schemas: + # get the schema from the automation schema + type = ejs.hidden_schemas[str(vschema)] + inner_vschema = vschema(ejs.jschema_extractor) + if type == "automation": + entry = get_automation_schema(parent_key, inner_vschema) + elif type == "maybe": + entry = get_jschema(parent_key, inner_vschema) + else: + raise ValueError("Unknown extracted schema type") + elif str(vschema).startswith(" str: CORE.config_path = path From 2c0fe49b86f5b1ede27e76f3ef68f5f03af22d65 Mon Sep 17 00:00:00 2001 From: Derek Hageman Date: Sun, 7 Mar 2021 23:25:49 -0700 Subject: [PATCH 081/111] Inkplate 6 Optimizations (#1592) --- esphome/components/inkplate6/inkplate.cpp | 185 +++++++++++----------- 1 file changed, 89 insertions(+), 96 deletions(-) diff --git a/esphome/components/inkplate6/inkplate.cpp b/esphome/components/inkplate6/inkplate.cpp index 3b17ba8f52..d60f97c36d 100644 --- a/esphome/components/inkplate6/inkplate.cpp +++ b/esphome/components/inkplate6/inkplate.cpp @@ -204,12 +204,10 @@ void Inkplate6::fill(Color color) { if (this->greyscale_) { uint8_t fill = ((color.red * 2126 / 10000) + (color.green * 7152 / 10000) + (color.blue * 722 / 10000)) >> 5; - for (uint32_t i = 0; i < this->get_buffer_length_(); i++) - this->buffer_[i] = (fill << 4) | fill; + memset(this->buffer_, (fill << 4) | fill, this->get_buffer_length_()); } else { uint8_t fill = color.is_on() ? 0x00 : 0xFF; - for (uint32_t i = 0; i < this->get_buffer_length_(); i++) - this->partial_buffer_[i] = fill; + memset(this->partial_buffer_, fill, this->get_buffer_length_()); } ESP_LOGV(TAG, "Fill finished (%lums)", millis() - start_time); @@ -233,15 +231,12 @@ void Inkplate6::display1b_() { ESP_LOGV(TAG, "Display1b called"); unsigned long start_time = millis(); - for (int i = 0; i < this->get_buffer_length_(); i++) { - this->buffer_[i] &= this->partial_buffer_[i]; - this->buffer_[i] |= this->partial_buffer_[i]; - } + memcpy(this->buffer_, this->partial_buffer_, this->get_buffer_length_()); - uint16_t pos; uint32_t send; uint8_t data; uint8_t buffer_value; + const uint8_t *buffer_ptr; eink_on_(); clean_fast_(0, 1); clean_fast_(1, 5); @@ -252,75 +247,72 @@ void Inkplate6::display1b_() { clean_fast_(2, 1); clean_fast_(0, 11); + uint32_t clock = (1 << this->cl_pin_->get_pin()); ESP_LOGV(TAG, "Display1b start loops (%lums)", millis() - start_time); for (int k = 0; k < 3; k++) { - pos = this->get_buffer_length_() - 1; + buffer_ptr = &this->buffer_[this->get_buffer_length_() - 1]; vscan_start_(); for (int i = 0; i < this->get_height_internal(); i++) { - buffer_value = this->buffer_[pos]; + buffer_value = *(buffer_ptr--); data = LUTB[(buffer_value >> 4) & 0x0F]; send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | (((data & B11100000) >> 5) << 25); hscan_start_(send); data = LUTB[buffer_value & 0x0F]; send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | - (((data & B11100000) >> 5) << 25); - GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin()); - GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin()); - pos--; + (((data & B11100000) >> 5) << 25) | clock; + GPIO.out_w1ts = send; + GPIO.out_w1tc = send; - for (int j = 0; j < (this->get_width_internal() / 8) - 1; j++) { - buffer_value = this->buffer_[pos]; + for (int j = 0, jm = (this->get_width_internal() / 8) - 1; j < jm; j++) { + buffer_value = *(buffer_ptr--); data = LUTB[(buffer_value >> 4) & 0x0F]; send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | - (((data & B11100000) >> 5) << 25); - GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin()); - GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin()); + (((data & B11100000) >> 5) << 25) | clock; + GPIO.out_w1ts = send; + GPIO.out_w1tc = send; data = LUTB[buffer_value & 0x0F]; send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | - (((data & B11100000) >> 5) << 25); - GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin()); - GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin()); - pos--; + (((data & B11100000) >> 5) << 25) | clock; + GPIO.out_w1ts = send; + GPIO.out_w1tc = send; } - GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin()); - GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin()); + GPIO.out_w1ts = send; + GPIO.out_w1tc = get_data_pin_mask_() | clock; vscan_end_(); } delayMicroseconds(230); } ESP_LOGV(TAG, "Display1b first loop x %d (%lums)", 3, millis() - start_time); - pos = this->get_buffer_length_() - 1; + buffer_ptr = &this->buffer_[this->get_buffer_length_() - 1]; vscan_start_(); for (int i = 0; i < this->get_height_internal(); i++) { - buffer_value = this->buffer_[pos]; + buffer_value = *(buffer_ptr--); data = LUT2[(buffer_value >> 4) & 0x0F]; send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | (((data & B11100000) >> 5) << 25); hscan_start_(send); data = LUT2[buffer_value & 0x0F]; send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | - (((data & B11100000) >> 5) << 25); - GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin()); - GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin()); - pos--; - for (int j = 0; j < (this->get_width_internal() / 8) - 1; j++) { - buffer_value = this->buffer_[pos]; + (((data & B11100000) >> 5) << 25) | clock; + GPIO.out_w1ts = send; + GPIO.out_w1tc = send; + for (int j = 0, jm = (this->get_width_internal() / 8) - 1; j < jm; j++) { + buffer_value = *(buffer_ptr--); data = LUT2[(buffer_value >> 4) & 0x0F]; send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | - (((data & B11100000) >> 5) << 25); - GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin()); - GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin()); + (((data & B11100000) >> 5) << 25) | clock; + GPIO.out_w1ts = send; + GPIO.out_w1tc = send; data = LUT2[buffer_value & 0x0F]; send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | - (((data & B11100000) >> 5) << 25); - GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin()); - GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin()); - pos--; + (((data & B11100000) >> 5) << 25) | clock; + GPIO.out_w1ts = send; + GPIO.out_w1tc = send; } - GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin()); - GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin()); + GPIO.out_w1ts = send; + GPIO.out_w1tc = get_data_pin_mask_() | clock; vscan_end_(); } delayMicroseconds(230); @@ -328,21 +320,21 @@ void Inkplate6::display1b_() { vscan_start_(); for (int i = 0; i < this->get_height_internal(); i++) { - buffer_value = this->buffer_[pos]; data = 0b00000000; send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | (((data & B11100000) >> 5) << 25); hscan_start_(send); - GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin()); - GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin()); + send |= clock; + GPIO.out_w1ts = send; + GPIO.out_w1tc = send; for (int j = 0; j < (this->get_width_internal() / 8) - 1; j++) { - GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin()); - GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin()); - GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin()); - GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin()); + GPIO.out_w1ts = send; + GPIO.out_w1tc = send; + GPIO.out_w1ts = send; + GPIO.out_w1tc = send; } - GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin()); - GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin()); + GPIO.out_w1ts = clock; + GPIO.out_w1tc = get_data_pin_mask_() | clock; vscan_end_(); } delayMicroseconds(230); @@ -368,8 +360,9 @@ void Inkplate6::display3b_() { clean_fast_(2, 1); clean_fast_(0, 11); + uint32_t clock = (1 << this->cl_pin_->get_pin()); for (int k = 0; k < 8; k++) { - uint32_t pos = this->get_buffer_length_() - 1; + const uint8_t *buffer_ptr = &this->buffer_[this->get_buffer_length_() - 1]; uint32_t send; uint8_t pix1; uint8_t pix2; @@ -380,10 +373,10 @@ void Inkplate6::display3b_() { vscan_start_(); for (int i = 0; i < this->get_height_internal(); i++) { - pix1 = this->buffer_[pos--]; - pix2 = this->buffer_[pos--]; - pix3 = this->buffer_[pos--]; - pix4 = this->buffer_[pos--]; + pix1 = (*buffer_ptr--); + pix2 = (*buffer_ptr--); + pix3 = (*buffer_ptr--); + pix4 = (*buffer_ptr--); pixel = (waveform3Bit[pix1 & 0x07][k] << 6) | (waveform3Bit[(pix1 >> 4) & 0x07][k] << 4) | (waveform3Bit[pix2 & 0x07][k] << 2) | (waveform3Bit[(pix2 >> 4) & 0x07][k] << 0); pixel2 = (waveform3Bit[pix3 & 0x07][k] << 6) | (waveform3Bit[(pix3 >> 4) & 0x07][k] << 4) | @@ -393,32 +386,32 @@ void Inkplate6::display3b_() { (((pixel & B11100000) >> 5) << 25); hscan_start_(send); send = ((pixel2 & B00000011) << 4) | (((pixel2 & B00001100) >> 2) << 18) | (((pixel2 & B00010000) >> 4) << 23) | - (((pixel2 & B11100000) >> 5) << 25); - GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin()); - GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin()); + (((pixel2 & B11100000) >> 5) << 25) | clock; + GPIO.out_w1ts = send; + GPIO.out_w1tc = send; - for (int j = 0; j < (this->get_width_internal() / 8) - 1; j++) { - pix1 = this->buffer_[pos--]; - pix2 = this->buffer_[pos--]; - pix3 = this->buffer_[pos--]; - pix4 = this->buffer_[pos--]; + for (int j = 0, jm = (this->get_width_internal() / 8) - 1; j < jm; j++) { + pix1 = (*buffer_ptr--); + pix2 = (*buffer_ptr--); + pix3 = (*buffer_ptr--); + pix4 = (*buffer_ptr--); pixel = (waveform3Bit[pix1 & 0x07][k] << 6) | (waveform3Bit[(pix1 >> 4) & 0x07][k] << 4) | (waveform3Bit[pix2 & 0x07][k] << 2) | (waveform3Bit[(pix2 >> 4) & 0x07][k] << 0); pixel2 = (waveform3Bit[pix3 & 0x07][k] << 6) | (waveform3Bit[(pix3 >> 4) & 0x07][k] << 4) | (waveform3Bit[pix4 & 0x07][k] << 2) | (waveform3Bit[(pix4 >> 4) & 0x07][k] << 0); send = ((pixel & B00000011) << 4) | (((pixel & B00001100) >> 2) << 18) | (((pixel & B00010000) >> 4) << 23) | - (((pixel & B11100000) >> 5) << 25); - GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin()); - GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin()); + (((pixel & B11100000) >> 5) << 25) | clock; + GPIO.out_w1ts = send; + GPIO.out_w1tc = send; send = ((pixel2 & B00000011) << 4) | (((pixel2 & B00001100) >> 2) << 18) | (((pixel2 & B00010000) >> 4) << 23) | - (((pixel2 & B11100000) >> 5) << 25); - GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin()); - GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin()); + (((pixel2 & B11100000) >> 5) << 25) | clock; + GPIO.out_w1ts = send; + GPIO.out_w1tc = send; } - GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin()); - GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin()); + GPIO.out_w1ts = send; + GPIO.out_w1tc = get_data_pin_mask_() | clock; vscan_end_(); } delayMicroseconds(230); @@ -445,8 +438,8 @@ bool Inkplate6::partial_update_() { uint8_t diffw, diffb; uint32_t n = (this->get_buffer_length_() * 2) - 1; - for (int i = 0; i < this->get_height_internal(); i++) { - for (int j = 0; j < (this->get_width_internal() / 8); j++) { + for (int i = 0, im = this->get_height_internal(); i < im; i++) { + for (int j = 0, jm = (this->get_width_internal() / 8); j < jm; j++) { diffw = (this->buffer_[pos] ^ this->partial_buffer_[pos]) & ~(this->partial_buffer_[pos]); diffb = (this->buffer_[pos] ^ this->partial_buffer_[pos]) & this->partial_buffer_[pos]; pos--; @@ -457,23 +450,24 @@ bool Inkplate6::partial_update_() { ESP_LOGV(TAG, "Partial update buffer built after (%lums)", millis() - start_time); eink_on_(); + uint32_t clock = (1 << this->cl_pin_->get_pin()); for (int k = 0; k < 5; k++) { vscan_start_(); - n = (this->get_buffer_length_() * 2) - 1; + const uint8_t *data_ptr = &this->partial_buffer_2_[(this->get_buffer_length_() * 2) - 1]; for (int i = 0; i < this->get_height_internal(); i++) { - data = this->partial_buffer_2_[n--]; + data = *(data_ptr--); send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | (((data & B11100000) >> 5) << 25); hscan_start_(send); - for (int j = 0; j < (this->get_width_internal() / 4) - 1; j++) { - data = this->partial_buffer_2_[n--]; + for (int j = 0, jm = (this->get_width_internal() / 4) - 1; j < jm; j++) { + data = *(data_ptr--); send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | - (((data & B11100000) >> 5) << 25); - GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin()); - GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin()); + (((data & B11100000) >> 5) << 25) | clock; + GPIO.out_w1ts = send; + GPIO.out_w1tc = send; } - GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin()); - GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin()); + GPIO.out_w1ts = send; + GPIO.out_w1tc = get_data_pin_mask_() | clock; vscan_end_(); } delayMicroseconds(230); @@ -484,9 +478,7 @@ bool Inkplate6::partial_update_() { vscan_start_(); eink_off_(); - for (int i = 0; i < this->get_buffer_length_(); i++) { - this->buffer_[i] = this->partial_buffer_[i]; - } + memcpy(this->buffer_, this->partial_buffer_, this->get_buffer_length_()); ESP_LOGV(TAG, "Partial update finished (%lums)", millis() - start_time); return true; } @@ -567,21 +559,22 @@ void Inkplate6::clean_fast_(uint8_t c, uint8_t rep) { uint32_t send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) | (((data & B11100000) >> 5) << 25); + uint32_t clock = (1 << this->cl_pin_->get_pin()); for (int k = 0; k < rep; k++) { vscan_start_(); for (int i = 0; i < this->get_height_internal(); i++) { hscan_start_(send); - GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin()); - GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin()); - for (int j = 0; j < this->get_width_internal() / 8; j++) { - GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin()); - GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin()); - GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin()); - GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin()); + GPIO.out_w1ts = send | clock; + GPIO.out_w1tc = clock; + for (int j = 0, jm = this->get_width_internal() / 8; j < jm; j++) { + GPIO.out_w1ts = clock; + GPIO.out_w1tc = clock; + GPIO.out_w1ts = clock; + GPIO.out_w1tc = clock; } - GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin()); - GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin()); + GPIO.out_w1ts = clock; + GPIO.out_w1tc = get_data_pin_mask_() | clock; vscan_end_(); } delayMicroseconds(230); From 5e5f8d5f9b4b88dc6718ae6c4f53411e200543b0 Mon Sep 17 00:00:00 2001 From: Guillermo Ruffino Date: Mon, 8 Mar 2021 22:53:20 -0300 Subject: [PATCH 082/111] schema-dump-pins (#1596) * schema dump idea accept boolean or anything default accept null also for full dicts added some common validators more simple validators support multi_conf better handle automations updates updates handle lists removed not needed class move to own folder generalized for automations lists, etc updates updates clean up clean up fix automations made comment optional basic docs support added more docs fixes docs handling updates updates fix components parent updates updates updates Fix inkplate 6 registration updates Disable logging for vscode add on better handle buses keep extended order as in CONFIGs updates updates updates disable comments moved to scripts/build_jsonschema added configurable decorators path handling fix handle list_schema fixes and cleanup add jschema_extractor to maybe updates lint no schema in git add generated loggers list * lint * support pin schema --- esphome/jsonschema.py | 19 ++++++++-------- script/build_jsonschema.py | 44 +++++++++++++++++++++++++++++++++----- 2 files changed, 49 insertions(+), 14 deletions(-) diff --git a/esphome/jsonschema.py b/esphome/jsonschema.py index 25d85ed58f..12929dc602 100644 --- a/esphome/jsonschema.py +++ b/esphome/jsonschema.py @@ -1,13 +1,14 @@ -# These are a helper decorators to help get schema from some -# components which uses volutuous in a way where validation -# is hidden in local functions +"""Helpers to retrieve schema from voluptuous validators. + +These are a helper decorators to help get schema from some +components which uses volutuous in a way where validation +is hidden in local functions +These decorators should not modify at all what the functions +originally do. +However there is a property to further disable decorator +impact.""" + -# These decorators should not modify at all what the functions -# originally do. -# -# However there is a property to further disable decorator -# impat. -# # This is set to true by script/build_jsonschema.py # only, so data is collected (again functionality is not modified) EnableJsonSchemaCollect = False diff --git a/script/build_jsonschema.py b/script/build_jsonschema.py index e179c95541..2c9534b861 100644 --- a/script/build_jsonschema.py +++ b/script/build_jsonschema.py @@ -195,7 +195,7 @@ def add_components(): "properties": {"platform": {"type": "string"}}, } ] - if domain != "output" and domain != "display": + if domain not in ("output", "display"): # output bases are either FLOAT or BINARY so don't add common base for this # display bases are either simple or FULL so don't add common base for this platform_schema = [ @@ -601,6 +601,42 @@ def convert_schema(path, vschema, un_extend=True): return output +def add_pin_schema(): + from esphome import pins + + add_module_schemas("PIN", pins) + + +def add_pin_registry(): + from esphome import pins + + pin_registry = pins.PIN_SCHEMA_REGISTRY + assert len(pin_registry) > 0 + # Here are schemas for pcf8574, mcp23xxx and other port expanders which add + # gpio registers + # ESPHome validates pins schemas if it founds a key in the pin configuration. + # This key is added to a required in jsonschema, and all options are part of a + # oneOf section, so only one is selected. Also internal schema adds number as required. + + for mode in ("INPUT", "OUTPUT"): + schema_name = f"PIN.GPIO_FULL_{mode}_PIN_SCHEMA" + internal = definitions[schema_name] + definitions[schema_name]["additionalItems"] = False + definitions[f"PIN.{mode}_INTERNAL"] = internal + schemas = [get_ref(f"PIN.{mode}_INTERNAL")] + schemas[0]["required"] = ["number"] + # accept string and object, for internal shorthand pin IO: + definitions[schema_name] = {"oneOf": schemas, "type": ["string", "object"]} + + for k, v in pin_registry.items(): + pin_jschema = get_jschema( + f"PIN.{mode}_" + k, v[1][0 if mode == "OUTPUT" else 1] + ) + if unref(pin_jschema): + pin_jschema["required"] = [k] + schemas.append(pin_jschema) + + def dump_schema(): import esphome.config_validation as cv @@ -669,10 +705,7 @@ def dump_schema(): add_module_schemas("CONFIG", cv) get_jschema("POLLING_COMPONENT", cv.polling_component_schema("60s")) - add_module_schemas("PIN", pins) - # fix short shothand pin IO: - definitions["PIN.GPIO_FULL_INPUT_PIN_SCHEMA"]["type"] = ["string", "object"] - definitions["PIN.GPIO_FULL_OUTPUT_PIN_SCHEMA"]["type"] = ["string", "object"] + add_pin_schema() add_module_schemas("REMOTE_BASE", remote_base) add_module_schemas("AUTOMATION", automation) @@ -699,6 +732,7 @@ def dump_schema(): add_components() add_registries() # need second pass, e.g. climate.pid.autotune + add_pin_registry() solve_pending_refs() write_file_if_changed(file_path, json.dumps(output)) From 2a12caa955f6597c93a563dd549b0390891e86e3 Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Wed, 10 Mar 2021 21:17:50 -0800 Subject: [PATCH 083/111] change lcd clear() to clear the buffer (#1600) Co-authored-by: Samuel Sieb --- esphome/components/lcd_base/lcd_display.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/esphome/components/lcd_base/lcd_display.cpp b/esphome/components/lcd_base/lcd_display.cpp index 25ac143817..50bd818cdb 100644 --- a/esphome/components/lcd_base/lcd_display.cpp +++ b/esphome/components/lcd_base/lcd_display.cpp @@ -104,9 +104,7 @@ void HOT LCDDisplay::display() { } } void LCDDisplay::update() { - for (uint8_t i = 0; i < this->rows_ * this->columns_; i++) - this->buffer_[i] = ' '; - + this->clear(); this->call_writer(); this->display(); } @@ -149,9 +147,8 @@ void LCDDisplay::printf(const char *format, ...) { this->print(0, 0, buffer); } void LCDDisplay::clear() { - // clear display, also sets DDRAM address to 0 (home) - this->command_(LCD_DISPLAY_COMMAND_CLEAR_DISPLAY); - delay(2); + for (uint8_t i = 0; i < this->rows_ * this->columns_; i++) + this->buffer_[i] = ' '; } #ifdef USE_TIME void LCDDisplay::strftime(uint8_t column, uint8_t row, const char *format, time::ESPTime time) { From c9ee513fa8713c594b61405108ca81ffc813bffb Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Wed, 10 Mar 2021 21:26:55 -0800 Subject: [PATCH 084/111] PN532 - don't read extra page and fix size (#1565) Co-authored-by: Samuel Sieb --- esphome/components/pn532/pn532_mifare_ultralight.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/esphome/components/pn532/pn532_mifare_ultralight.cpp b/esphome/components/pn532/pn532_mifare_ultralight.cpp index bd62806c1e..09d94c42b1 100644 --- a/esphome/components/pn532/pn532_mifare_ultralight.cpp +++ b/esphome/components/pn532/pn532_mifare_ultralight.cpp @@ -17,12 +17,12 @@ nfc::NfcTag *PN532::read_mifare_ultralight_tag_(std::vector &uid) { if (!this->find_mifare_ultralight_ndef_(message_length, message_start_index)) { return new nfc::NfcTag(uid, nfc::NFC_FORUM_TYPE_2); } + ESP_LOGVV(TAG, "message length: %d, start: %d", message_length, message_start_index); if (message_length == 0) { return new nfc::NfcTag(uid, nfc::NFC_FORUM_TYPE_2); } std::vector data; - uint8_t index = 0; for (uint8_t page = nfc::MIFARE_ULTRALIGHT_DATA_START_PAGE; page < nfc::MIFARE_ULTRALIGHT_MAX_PAGE; page++) { std::vector page_data; if (!this->read_mifare_ultralight_page_(page, page_data)) { @@ -31,13 +31,12 @@ nfc::NfcTag *PN532::read_mifare_ultralight_tag_(std::vector &uid) { } data.insert(data.end(), page_data.begin(), page_data.end()); - if (index >= (message_length + message_start_index)) + if (data.size() >= (message_length + message_start_index)) break; - - index += page_data.size(); } data.erase(data.begin(), data.begin() + message_start_index); + data.erase(data.begin() + message_length, data.end()); return new nfc::NfcTag(uid, nfc::NFC_FORUM_TYPE_2, data); } From cd1353ae54ff83a663614c0599893ca346aa9e4a Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Sat, 13 Mar 2021 08:42:37 +1300 Subject: [PATCH 085/111] Update PULL_REQUEST_TEMPLATE.md --- .github/PULL_REQUEST_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index fbdcf367ad..0d77eee7aa 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -7,7 +7,7 @@ Quick description - [ ] Bugfix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) -- [ ] Configuration change (this will require users to update their yaml configuraiton files to keep working) +- [ ] Configuration change (this will require users to update their yaml configuration files to keep working) **Related issue or feature (if applicable):** fixes From 28a72fa56be2c4c1ca7068577bad2785fff6af01 Mon Sep 17 00:00:00 2001 From: Massimiliano Ravelli Date: Fri, 12 Mar 2021 23:58:43 +0100 Subject: [PATCH 086/111] Fixed component_tests config (#1608) --- script/component_test | 9 +++++++++ script/fulltest | 1 + tests/component_tests/conftest.py | 8 ++++++++ 3 files changed, 18 insertions(+) create mode 100755 script/component_test diff --git a/script/component_test b/script/component_test new file mode 100755 index 0000000000..549c68fb25 --- /dev/null +++ b/script/component_test @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +set -x + +pytest tests/component_tests diff --git a/script/fulltest b/script/fulltest index 795482281a..a605beebfe 100755 --- a/script/fulltest +++ b/script/fulltest @@ -10,4 +10,5 @@ script/ci-custom.py script/lint-python script/lint-cpp script/unit_test +script/component_test script/test diff --git a/tests/component_tests/conftest.py b/tests/component_tests/conftest.py index 1676e3f0d4..aa564ed7b1 100644 --- a/tests/component_tests/conftest.py +++ b/tests/component_tests/conftest.py @@ -1,5 +1,13 @@ """Fixtures for component tests.""" +import sys +from pathlib import Path + +# Add package root to python path +here = Path(__file__).parent +package_root = here.parent.parent +sys.path.insert(0, package_root.as_posix()) + import pytest from esphome.core import CORE From 2e7c1d73451d09156a749974478fa9889716ef78 Mon Sep 17 00:00:00 2001 From: Alex Date: Sun, 14 Mar 2021 10:45:01 +1300 Subject: [PATCH 087/111] Added receive for Fujitsu ACs (#1577) --- esphome/components/fujitsu_general/climate.py | 2 +- .../fujitsu_general/fujitsu_general.cpp | 434 ++++++++++++------ .../fujitsu_general/fujitsu_general.h | 24 +- 3 files changed, 317 insertions(+), 143 deletions(-) diff --git a/esphome/components/fujitsu_general/climate.py b/esphome/components/fujitsu_general/climate.py index 64319eff20..d58049e3f2 100644 --- a/esphome/components/fujitsu_general/climate.py +++ b/esphome/components/fujitsu_general/climate.py @@ -10,7 +10,7 @@ FujitsuGeneralClimate = fujitsu_general_ns.class_( "FujitsuGeneralClimate", climate_ir.ClimateIR ) -CONFIG_SCHEMA = climate_ir.CLIMATE_IR_SCHEMA.extend( +CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend( { cv.GenerateID(): cv.declare_id(FujitsuGeneralClimate), } diff --git a/esphome/components/fujitsu_general/fujitsu_general.cpp b/esphome/components/fujitsu_general/fujitsu_general.cpp index f611464248..75ee3f708b 100644 --- a/esphome/components/fujitsu_general/fujitsu_general.cpp +++ b/esphome/components/fujitsu_general/fujitsu_general.cpp @@ -3,224 +3,207 @@ namespace esphome { namespace fujitsu_general { -static const char *TAG = "fujitsu_general.climate"; +// bytes' bits are reversed for fujitsu, so nibbles are ordered 1, 0, 3, 2, 5, 4, etc... -// Control packet -const uint16_t FUJITSU_GENERAL_STATE_LENGTH = 16; +#define SET_NIBBLE(message, nibble, value) (message[nibble / 2] |= (value & 0b00001111) << ((nibble % 2) ? 0 : 4)) +#define GET_NIBBLE(message, nibble) ((message[nibble / 2] >> ((nibble % 2) ? 0 : 4)) & 0b00001111) -const uint8_t FUJITSU_GENERAL_BASE_BYTE0 = 0x14; -const uint8_t FUJITSU_GENERAL_BASE_BYTE1 = 0x63; -const uint8_t FUJITSU_GENERAL_BASE_BYTE2 = 0x00; -const uint8_t FUJITSU_GENERAL_BASE_BYTE3 = 0x10; -const uint8_t FUJITSU_GENERAL_BASE_BYTE4 = 0x10; -const uint8_t FUJITSU_GENERAL_BASE_BYTE5 = 0xFE; -const uint8_t FUJITSU_GENERAL_BASE_BYTE6 = 0x09; -const uint8_t FUJITSU_GENERAL_BASE_BYTE7 = 0x30; +static const char* TAG = "fujitsu_general.climate"; -// Temperature and POWER ON -const uint8_t FUJITSU_GENERAL_POWER_ON_MASK_BYTE8 = 0b00000001; -const uint8_t FUJITSU_GENERAL_BASE_BYTE8 = 0x40; +// Common header +const uint8_t FUJITSU_GENERAL_COMMON_LENGTH = 6; +const uint8_t FUJITSU_GENERAL_COMMON_BYTE0 = 0x14; +const uint8_t FUJITSU_GENERAL_COMMON_BYTE1 = 0x63; +const uint8_t FUJITSU_GENERAL_COMMON_BYTE2 = 0x00; +const uint8_t FUJITSU_GENERAL_COMMON_BYTE3 = 0x10; +const uint8_t FUJITSU_GENERAL_COMMON_BYTE4 = 0x10; +const uint8_t FUJITSU_GENERAL_MESSAGE_TYPE_BYTE = 5; + +// State message - temp & fan etc. +const uint8_t FUJITSU_GENERAL_STATE_MESSAGE_LENGTH = 16; +const uint8_t FUJITSU_GENERAL_MESSAGE_TYPE_STATE = 0xFE; + +// Util messages - off & eco etc. +const uint8_t FUJITSU_GENERAL_UTIL_MESSAGE_LENGTH = 7; +const uint8_t FUJITSU_GENERAL_MESSAGE_TYPE_OFF = 0x02; +const uint8_t FUJITSU_GENERAL_MESSAGE_TYPE_ECONOMY = 0x09; +const uint8_t FUJITSU_GENERAL_MESSAGE_TYPE_NUDGE = 0x6C; + +// State header +const uint8_t FUJITSU_GENERAL_STATE_HEADER_BYTE0 = 0x09; +const uint8_t FUJITSU_GENERAL_STATE_HEADER_BYTE1 = 0x30; + +// State footer +const uint8_t FUJITSU_GENERAL_STATE_FOOTER_BYTE0 = 0x20; + +// Temperature +const uint8_t FUJITSU_GENERAL_TEMPERATURE_NIBBLE = 16; + +// Power on +const uint8_t FUJITSU_GENERAL_POWER_ON_NIBBLE = 17; +const uint8_t FUJITSU_GENERAL_POWER_ON = 0x01; +const uint8_t FUJITSU_GENERAL_POWER_OFF = 0x00; // Mode -const uint8_t FUJITSU_GENERAL_MODE_AUTO_BYTE9 = 0x00; -const uint8_t FUJITSU_GENERAL_MODE_HEAT_BYTE9 = 0x04; -const uint8_t FUJITSU_GENERAL_MODE_COOL_BYTE9 = 0x01; -const uint8_t FUJITSU_GENERAL_MODE_DRY_BYTE9 = 0x02; -const uint8_t FUJITSU_GENERAL_MODE_FAN_BYTE9 = 0x03; -const uint8_t FUJITSU_GENERAL_MODE_10C_BYTE9 = 0x0B; -const uint8_t FUJITSU_GENERAL_BASE_BYTE9 = 0x01; +const uint8_t FUJITSU_GENERAL_MODE_NIBBLE = 19; +const uint8_t FUJITSU_GENERAL_MODE_AUTO = 0x00; +const uint8_t FUJITSU_GENERAL_MODE_HEAT = 0x04; +const uint8_t FUJITSU_GENERAL_MODE_COOL = 0x01; +const uint8_t FUJITSU_GENERAL_MODE_DRY = 0x02; +const uint8_t FUJITSU_GENERAL_MODE_FAN = 0x03; +// const uint8_t FUJITSU_GENERAL_MODE_10C = 0x0B; -// Fan speed and swing -const uint8_t FUJITSU_GENERAL_FAN_AUTO_BYTE10 = 0x00; -const uint8_t FUJITSU_GENERAL_FAN_HIGH_BYTE10 = 0x01; -const uint8_t FUJITSU_GENERAL_FAN_MEDIUM_BYTE10 = 0x02; -const uint8_t FUJITSU_GENERAL_FAN_LOW_BYTE10 = 0x03; -const uint8_t FUJITSU_GENERAL_FAN_SILENT_BYTE10 = 0x04; -const uint8_t FUJITSU_GENERAL_SWING_NONE_BYTE10 = 0x00; -const uint8_t FUJITSU_GENERAL_SWING_VERTICAL_BYTE10 = 0x01; -const uint8_t FUJITSU_GENERAL_SWING_HORIZONTAL_BYTE10 = 0x02; -const uint8_t FUJITSU_GENERAL_SWING_BOTH_BYTE10 = 0x03; -const uint8_t FUJITSU_GENERAL_BASE_BYTE10 = 0x00; +// Swing +const uint8_t FUJITSU_GENERAL_FAN_NIBBLE = 20; +const uint8_t FUJITSU_GENERAL_FAN_AUTO = 0x00; +const uint8_t FUJITSU_GENERAL_FAN_HIGH = 0x01; +const uint8_t FUJITSU_GENERAL_FAN_MEDIUM = 0x02; +const uint8_t FUJITSU_GENERAL_FAN_LOW = 0x03; +const uint8_t FUJITSU_GENERAL_FAN_SILENT = 0x04; -const uint8_t FUJITSU_GENERAL_BASE_BYTE11 = 0x00; -const uint8_t FUJITSU_GENERAL_BASE_BYTE12 = 0x00; -const uint8_t FUJITSU_GENERAL_BASE_BYTE13 = 0x00; +// Fan speed +const uint8_t FUJITSU_GENERAL_SWING_NIBBLE = 21; +const uint8_t FUJITSU_GENERAL_SWING_NONE = 0x00; +const uint8_t FUJITSU_GENERAL_SWING_VERTICAL = 0x01; +const uint8_t FUJITSU_GENERAL_SWING_HORIZONTAL = 0x02; +const uint8_t FUJITSU_GENERAL_SWING_BOTH = 0x03; -// Outdoor Unit Low Noise -const uint8_t FUJITSU_GENERAL_OUTDOOR_UNIT_LOW_NOISE_BYTE14 = 0xA0; -const uint8_t FUJITSU_GENERAL_BASE_BYTE14 = 0x20; - -// CRC -const uint8_t FUJITSU_GENERAL_BASE_BYTE15 = 0x6F; - -// Power off packet is specific -const uint16_t FUJITSU_GENERAL_OFF_LENGTH = 7; - -const uint8_t FUJITSU_GENERAL_OFF_BYTE0 = FUJITSU_GENERAL_BASE_BYTE0; -const uint8_t FUJITSU_GENERAL_OFF_BYTE1 = FUJITSU_GENERAL_BASE_BYTE1; -const uint8_t FUJITSU_GENERAL_OFF_BYTE2 = FUJITSU_GENERAL_BASE_BYTE2; -const uint8_t FUJITSU_GENERAL_OFF_BYTE3 = FUJITSU_GENERAL_BASE_BYTE3; -const uint8_t FUJITSU_GENERAL_OFF_BYTE4 = FUJITSU_GENERAL_BASE_BYTE4; -const uint8_t FUJITSU_GENERAL_OFF_BYTE5 = 0x02; -const uint8_t FUJITSU_GENERAL_OFF_BYTE6 = 0xFD; - -const uint8_t FUJITSU_GENERAL_TEMP_MAX = 30; // Celsius -const uint8_t FUJITSU_GENERAL_TEMP_MIN = 16; // Celsius +// TODO Outdoor Unit Low Noise +// const uint8_t FUJITSU_GENERAL_OUTDOOR_UNIT_LOW_NOISE_BYTE14 = 0xA0; +// const uint8_t FUJITSU_GENERAL_STATE_BYTE14 = 0x20; const uint16_t FUJITSU_GENERAL_HEADER_MARK = 3300; const uint16_t FUJITSU_GENERAL_HEADER_SPACE = 1600; + const uint16_t FUJITSU_GENERAL_BIT_MARK = 420; const uint16_t FUJITSU_GENERAL_ONE_SPACE = 1200; const uint16_t FUJITSU_GENERAL_ZERO_SPACE = 420; + const uint16_t FUJITSU_GENERAL_TRL_MARK = 420; const uint16_t FUJITSU_GENERAL_TRL_SPACE = 8000; const uint32_t FUJITSU_GENERAL_CARRIER_FREQUENCY = 38000; -FujitsuGeneralClimate::FujitsuGeneralClimate() - : ClimateIR( - FUJITSU_GENERAL_TEMP_MIN, FUJITSU_GENERAL_TEMP_MAX, 1.0f, true, true, - {climate::CLIMATE_FAN_AUTO, climate::CLIMATE_FAN_LOW, climate::CLIMATE_FAN_MEDIUM, climate::CLIMATE_FAN_HIGH}, - {climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_VERTICAL, climate::CLIMATE_SWING_HORIZONTAL, - climate::CLIMATE_SWING_BOTH}) {} - void FujitsuGeneralClimate::transmit_state() { if (this->mode == climate::CLIMATE_MODE_OFF) { this->transmit_off_(); return; } - uint8_t remote_state[FUJITSU_GENERAL_STATE_LENGTH] = {0}; - remote_state[0] = FUJITSU_GENERAL_BASE_BYTE0; - remote_state[1] = FUJITSU_GENERAL_BASE_BYTE1; - remote_state[2] = FUJITSU_GENERAL_BASE_BYTE2; - remote_state[3] = FUJITSU_GENERAL_BASE_BYTE3; - remote_state[4] = FUJITSU_GENERAL_BASE_BYTE4; - remote_state[5] = FUJITSU_GENERAL_BASE_BYTE5; - remote_state[6] = FUJITSU_GENERAL_BASE_BYTE6; - remote_state[7] = FUJITSU_GENERAL_BASE_BYTE7; - remote_state[8] = FUJITSU_GENERAL_BASE_BYTE8; - remote_state[9] = FUJITSU_GENERAL_BASE_BYTE9; - remote_state[10] = FUJITSU_GENERAL_BASE_BYTE10; - remote_state[11] = FUJITSU_GENERAL_BASE_BYTE11; - remote_state[12] = FUJITSU_GENERAL_BASE_BYTE12; - remote_state[13] = FUJITSU_GENERAL_BASE_BYTE13; - remote_state[14] = FUJITSU_GENERAL_BASE_BYTE14; - remote_state[15] = FUJITSU_GENERAL_BASE_BYTE15; + ESP_LOGV(TAG, "Transmit state"); + + uint8_t remote_state[FUJITSU_GENERAL_STATE_MESSAGE_LENGTH] = {0}; + + // Common message header + remote_state[0] = FUJITSU_GENERAL_COMMON_BYTE0; + remote_state[1] = FUJITSU_GENERAL_COMMON_BYTE1; + remote_state[2] = FUJITSU_GENERAL_COMMON_BYTE2; + remote_state[3] = FUJITSU_GENERAL_COMMON_BYTE3; + remote_state[4] = FUJITSU_GENERAL_COMMON_BYTE4; + remote_state[5] = FUJITSU_GENERAL_MESSAGE_TYPE_STATE; + remote_state[6] = FUJITSU_GENERAL_STATE_HEADER_BYTE0; + remote_state[7] = FUJITSU_GENERAL_STATE_HEADER_BYTE1; + + // unknown, does not appear to change with any remote settings + remote_state[14] = FUJITSU_GENERAL_STATE_FOOTER_BYTE0; // Set temperature - auto safecelsius = + uint8_t temperature_clamped = (uint8_t) roundf(clamp(this->target_temperature, FUJITSU_GENERAL_TEMP_MIN, FUJITSU_GENERAL_TEMP_MAX)); - remote_state[8] = (byte) safecelsius - 16; - remote_state[8] = remote_state[8] << 4; + uint8_t temperature_offset = temperature_clamped - FUJITSU_GENERAL_TEMP_MIN; + SET_NIBBLE(remote_state, FUJITSU_GENERAL_TEMPERATURE_NIBBLE, temperature_offset); - // If not powered - set power on flag + // Set power on if (!this->power_) { - remote_state[8] = (byte) remote_state[8] | FUJITSU_GENERAL_POWER_ON_MASK_BYTE8; + SET_NIBBLE(remote_state, FUJITSU_GENERAL_POWER_ON_NIBBLE, FUJITSU_GENERAL_POWER_ON); } // Set mode switch (this->mode) { case climate::CLIMATE_MODE_COOL: - remote_state[9] = FUJITSU_GENERAL_MODE_COOL_BYTE9; + SET_NIBBLE(remote_state, FUJITSU_GENERAL_MODE_NIBBLE, FUJITSU_GENERAL_MODE_COOL); break; case climate::CLIMATE_MODE_HEAT: - remote_state[9] = FUJITSU_GENERAL_MODE_HEAT_BYTE9; + SET_NIBBLE(remote_state, FUJITSU_GENERAL_MODE_NIBBLE, FUJITSU_GENERAL_MODE_HEAT); break; case climate::CLIMATE_MODE_DRY: - remote_state[9] = FUJITSU_GENERAL_MODE_DRY_BYTE9; + SET_NIBBLE(remote_state, FUJITSU_GENERAL_MODE_NIBBLE, FUJITSU_GENERAL_MODE_DRY); break; case climate::CLIMATE_MODE_FAN_ONLY: - remote_state[9] = FUJITSU_GENERAL_MODE_FAN_BYTE9; + SET_NIBBLE(remote_state, FUJITSU_GENERAL_MODE_NIBBLE, FUJITSU_GENERAL_MODE_FAN); break; case climate::CLIMATE_MODE_AUTO: default: - remote_state[9] = FUJITSU_GENERAL_MODE_AUTO_BYTE9; + SET_NIBBLE(remote_state, FUJITSU_GENERAL_MODE_NIBBLE, FUJITSU_GENERAL_MODE_AUTO); break; - // TODO: CLIMATE_MODE_10C are missing in esphome + // TODO: CLIMATE_MODE_10C is missing from esphome } // Set fan switch (this->fan_mode) { case climate::CLIMATE_FAN_HIGH: - remote_state[10] = FUJITSU_GENERAL_FAN_HIGH_BYTE10; + SET_NIBBLE(remote_state, FUJITSU_GENERAL_FAN_NIBBLE, FUJITSU_GENERAL_FAN_HIGH); break; case climate::CLIMATE_FAN_MEDIUM: - remote_state[10] = FUJITSU_GENERAL_FAN_MEDIUM_BYTE10; + SET_NIBBLE(remote_state, FUJITSU_GENERAL_FAN_NIBBLE, FUJITSU_GENERAL_FAN_MEDIUM); break; case climate::CLIMATE_FAN_LOW: - remote_state[10] = FUJITSU_GENERAL_FAN_LOW_BYTE10; + SET_NIBBLE(remote_state, FUJITSU_GENERAL_FAN_NIBBLE, FUJITSU_GENERAL_FAN_LOW); break; case climate::CLIMATE_FAN_AUTO: default: - remote_state[10] = FUJITSU_GENERAL_FAN_AUTO_BYTE10; + SET_NIBBLE(remote_state, FUJITSU_GENERAL_FAN_NIBBLE, FUJITSU_GENERAL_FAN_AUTO); break; + // TODO Quiet / Silent } // Set swing switch (this->swing_mode) { case climate::CLIMATE_SWING_VERTICAL: - remote_state[10] = (byte) remote_state[10] | (FUJITSU_GENERAL_SWING_VERTICAL_BYTE10 << 4); + SET_NIBBLE(remote_state, FUJITSU_GENERAL_SWING_NIBBLE, FUJITSU_GENERAL_SWING_VERTICAL); break; case climate::CLIMATE_SWING_HORIZONTAL: - remote_state[10] = (byte) remote_state[10] | (FUJITSU_GENERAL_SWING_HORIZONTAL_BYTE10 << 4); + SET_NIBBLE(remote_state, FUJITSU_GENERAL_SWING_NIBBLE, FUJITSU_GENERAL_SWING_HORIZONTAL); break; case climate::CLIMATE_SWING_BOTH: - remote_state[10] = (byte) remote_state[10] | (FUJITSU_GENERAL_SWING_BOTH_BYTE10 << 4); + SET_NIBBLE(remote_state, FUJITSU_GENERAL_SWING_NIBBLE, FUJITSU_GENERAL_SWING_BOTH); break; case climate::CLIMATE_SWING_OFF: default: - remote_state[10] = (byte) remote_state[10] | (FUJITSU_GENERAL_SWING_NONE_BYTE10 << 4); + SET_NIBBLE(remote_state, FUJITSU_GENERAL_SWING_NIBBLE, FUJITSU_GENERAL_SWING_NONE); break; } // TODO: missing support for outdoor unit low noise // remote_state[14] = (byte) remote_state[14] | FUJITSU_GENERAL_OUTDOOR_UNIT_LOW_NOISE_BYTE14; - // CRC - remote_state[15] = 0; - for (int i = 7; i < 15; i++) { - remote_state[15] += (byte) remote_state[i]; // Addiction - } - remote_state[15] = 0x100 - remote_state[15]; // mod 256 + remote_state[FUJITSU_GENERAL_STATE_MESSAGE_LENGTH - 1] = this->checksum_state_(remote_state); - auto transmit = this->transmitter_->transmit(); - auto data = transmit.get_data(); - - data->set_carrier_frequency(FUJITSU_GENERAL_CARRIER_FREQUENCY); - - // Header - data->mark(FUJITSU_GENERAL_HEADER_MARK); - data->space(FUJITSU_GENERAL_HEADER_SPACE); - // Data - for (uint8_t i : remote_state) { - // Send all Bits from Byte Data in Reverse Order - for (uint8_t mask = 00000001; mask > 0; mask <<= 1) { // iterate through bit mask - data->mark(FUJITSU_GENERAL_BIT_MARK); - bool bit = i & mask; - data->space(bit ? FUJITSU_GENERAL_ONE_SPACE : FUJITSU_GENERAL_ZERO_SPACE); - // Next bits - } - } - // Footer - data->mark(FUJITSU_GENERAL_TRL_MARK); - data->space(FUJITSU_GENERAL_TRL_SPACE); - - transmit.perform(); + this->transmit_(remote_state, FUJITSU_GENERAL_STATE_MESSAGE_LENGTH); this->power_ = true; } void FujitsuGeneralClimate::transmit_off_() { - uint8_t remote_state[FUJITSU_GENERAL_OFF_LENGTH] = {0}; + ESP_LOGV(TAG, "Transmit off"); - remote_state[0] = FUJITSU_GENERAL_OFF_BYTE0; - remote_state[1] = FUJITSU_GENERAL_OFF_BYTE1; - remote_state[2] = FUJITSU_GENERAL_OFF_BYTE2; - remote_state[3] = FUJITSU_GENERAL_OFF_BYTE3; - remote_state[4] = FUJITSU_GENERAL_OFF_BYTE4; - remote_state[5] = FUJITSU_GENERAL_OFF_BYTE5; - remote_state[6] = FUJITSU_GENERAL_OFF_BYTE6; + uint8_t remote_state[FUJITSU_GENERAL_UTIL_MESSAGE_LENGTH] = {0}; + + remote_state[0] = FUJITSU_GENERAL_COMMON_BYTE0; + remote_state[1] = FUJITSU_GENERAL_COMMON_BYTE1; + remote_state[2] = FUJITSU_GENERAL_COMMON_BYTE2; + remote_state[3] = FUJITSU_GENERAL_COMMON_BYTE3; + remote_state[4] = FUJITSU_GENERAL_COMMON_BYTE4; + remote_state[5] = FUJITSU_GENERAL_MESSAGE_TYPE_OFF; + remote_state[6] = this->checksum_util_(remote_state); + + this->transmit_(remote_state, FUJITSU_GENERAL_UTIL_MESSAGE_LENGTH); + + this->power_ = false; +} + +void FujitsuGeneralClimate::transmit_(uint8_t const* message, uint8_t length) { + ESP_LOGV(TAG, "Transmit message length %d", length); auto transmit = this->transmitter_->transmit(); auto data = transmit.get_data(); @@ -232,22 +215,191 @@ void FujitsuGeneralClimate::transmit_off_() { data->space(FUJITSU_GENERAL_HEADER_SPACE); // Data - for (uint8_t i : remote_state) { - // Send all Bits from Byte Data in Reverse Order - for (uint8_t mask = 00000001; mask > 0; mask <<= 1) { // iterate through bit mask + for (uint8_t i = 0; i < length; ++i) { + const uint8_t byte = message[i]; + for (uint8_t mask = 0b00000001; mask > 0; mask <<= 1) { // write from right to left data->mark(FUJITSU_GENERAL_BIT_MARK); - bool bit = i & mask; + bool bit = byte & mask; data->space(bit ? FUJITSU_GENERAL_ONE_SPACE : FUJITSU_GENERAL_ZERO_SPACE); - // Next bits } } + // Footer data->mark(FUJITSU_GENERAL_TRL_MARK); data->space(FUJITSU_GENERAL_TRL_SPACE); transmit.perform(); +} - this->power_ = false; +uint8_t FujitsuGeneralClimate::checksum_state_(uint8_t const* message) { + uint8_t checksum = 0; + for (uint8_t i = 7; i < FUJITSU_GENERAL_STATE_MESSAGE_LENGTH - 1; ++i) { + checksum += message[i]; + } + return 256 - checksum; +} + +uint8_t FujitsuGeneralClimate::checksum_util_(uint8_t const* message) { return 255 - message[5]; } + +bool FujitsuGeneralClimate::on_receive(remote_base::RemoteReceiveData data) { + ESP_LOGV(TAG, "Received IR message"); + + // Validate header + if (!data.expect_item(FUJITSU_GENERAL_HEADER_MARK, FUJITSU_GENERAL_HEADER_SPACE)) { + ESP_LOGV(TAG, "Header fail"); + return false; + } + + uint8_t recv_message[FUJITSU_GENERAL_STATE_MESSAGE_LENGTH] = {0}; + + // Read header + for (uint8_t byte = 0; byte < FUJITSU_GENERAL_COMMON_LENGTH; ++byte) { + // Read bit + for (uint8_t bit = 0; bit < 8; ++bit) { + if (data.expect_item(FUJITSU_GENERAL_BIT_MARK, FUJITSU_GENERAL_ONE_SPACE)) { + recv_message[byte] |= 1 << bit; // read from right to left + } else if (!data.expect_item(FUJITSU_GENERAL_BIT_MARK, FUJITSU_GENERAL_ZERO_SPACE)) { + ESP_LOGV(TAG, "Byte %d bit %d fail", byte, bit); + return false; + } + } + } + + const uint8_t recv_message_type = recv_message[FUJITSU_GENERAL_MESSAGE_TYPE_BYTE]; + uint8_t recv_message_length; + + switch (recv_message_type) { + case FUJITSU_GENERAL_MESSAGE_TYPE_STATE: + ESP_LOGV(TAG, "Received state message"); + recv_message_length = FUJITSU_GENERAL_STATE_MESSAGE_LENGTH; + break; + case FUJITSU_GENERAL_MESSAGE_TYPE_OFF: + case FUJITSU_GENERAL_MESSAGE_TYPE_ECONOMY: + case FUJITSU_GENERAL_MESSAGE_TYPE_NUDGE: + ESP_LOGV(TAG, "Received util message"); + recv_message_length = FUJITSU_GENERAL_UTIL_MESSAGE_LENGTH; + break; + default: + ESP_LOGV(TAG, "Unknown message type %X", recv_message_type); + return false; + } + + // Read message body + for (uint8_t byte = FUJITSU_GENERAL_COMMON_LENGTH; byte < recv_message_length; ++byte) { + for (uint8_t bit = 0; bit < 8; ++bit) { + if (data.expect_item(FUJITSU_GENERAL_BIT_MARK, FUJITSU_GENERAL_ONE_SPACE)) { + recv_message[byte] |= 1 << bit; // read from right to left + } else if (!data.expect_item(FUJITSU_GENERAL_BIT_MARK, FUJITSU_GENERAL_ZERO_SPACE)) { + ESP_LOGV(TAG, "Byte %d bit %d fail", byte, bit); + return false; + } + } + } + + // Validate footer + if (!data.expect_mark(FUJITSU_GENERAL_BIT_MARK)) { + ESP_LOGV(TAG, "Footer fail"); + return false; + } + + for (uint8_t byte = 0; byte < recv_message_length; ++byte) { + ESP_LOGVV(TAG, "%02X", recv_message[byte]); + } + + const uint8_t recv_checksum = recv_message[recv_message_length - 1]; + uint8_t calculated_checksum; + if (recv_message_type == FUJITSU_GENERAL_MESSAGE_TYPE_STATE) { + calculated_checksum = this->checksum_state_(recv_message); + } else { + calculated_checksum = this->checksum_util_(recv_message); + } + + if (recv_checksum != calculated_checksum) { + ESP_LOGV(TAG, "Checksum fail - expected %X - got %X", calculated_checksum, recv_checksum); + return false; + } + + if (recv_message_type == FUJITSU_GENERAL_MESSAGE_TYPE_STATE) { + const uint8_t recv_tempertature = GET_NIBBLE(recv_message, FUJITSU_GENERAL_TEMPERATURE_NIBBLE); + const uint8_t offset_temperature = recv_tempertature + FUJITSU_GENERAL_TEMP_MIN; + this->target_temperature = offset_temperature; + ESP_LOGV(TAG, "Received temperature %d", offset_temperature); + + const uint8_t recv_mode = GET_NIBBLE(recv_message, FUJITSU_GENERAL_MODE_NIBBLE); + ESP_LOGV(TAG, "Received mode %X", recv_mode); + switch (recv_mode) { + case FUJITSU_GENERAL_MODE_COOL: + this->mode = climate::CLIMATE_MODE_COOL; + break; + case FUJITSU_GENERAL_MODE_HEAT: + this->mode = climate::CLIMATE_MODE_HEAT; + break; + case FUJITSU_GENERAL_MODE_DRY: + this->mode = climate::CLIMATE_MODE_DRY; + break; + case FUJITSU_GENERAL_MODE_FAN: + this->mode = climate::CLIMATE_MODE_FAN_ONLY; + break; + case FUJITSU_GENERAL_MODE_AUTO: + default: + // TODO: CLIMATE_MODE_10C is missing from esphome + this->mode = climate::CLIMATE_MODE_AUTO; + break; + } + + const uint8_t recv_fan_mode = GET_NIBBLE(recv_message, FUJITSU_GENERAL_FAN_NIBBLE); + ESP_LOGV(TAG, "Received fan mode %X", recv_fan_mode); + switch (recv_fan_mode) { + // TODO No Quiet / Silent in ESPH + case FUJITSU_GENERAL_FAN_SILENT: + case FUJITSU_GENERAL_FAN_LOW: + this->fan_mode = climate::CLIMATE_FAN_LOW; + break; + case FUJITSU_GENERAL_FAN_MEDIUM: + this->fan_mode = climate::CLIMATE_FAN_MEDIUM; + break; + case FUJITSU_GENERAL_FAN_HIGH: + this->fan_mode = climate::CLIMATE_FAN_HIGH; + break; + case FUJITSU_GENERAL_FAN_AUTO: + default: + this->fan_mode = climate::CLIMATE_FAN_AUTO; + break; + } + + const uint8_t recv_swing_mode = GET_NIBBLE(recv_message, FUJITSU_GENERAL_SWING_NIBBLE); + ESP_LOGV(TAG, "Received swing mode %X", recv_swing_mode); + switch (recv_swing_mode) { + case FUJITSU_GENERAL_SWING_VERTICAL: + this->swing_mode = climate::CLIMATE_SWING_VERTICAL; + break; + case FUJITSU_GENERAL_SWING_HORIZONTAL: + this->swing_mode = climate::CLIMATE_SWING_HORIZONTAL; + break; + case FUJITSU_GENERAL_SWING_BOTH: + this->swing_mode = climate::CLIMATE_SWING_BOTH; + break; + case FUJITSU_GENERAL_SWING_NONE: + default: + this->swing_mode = climate::CLIMATE_SWING_OFF; + } + + this->power_ = true; + } + + else if (recv_message_type == FUJITSU_GENERAL_MESSAGE_TYPE_OFF) { + ESP_LOGV(TAG, "Received off message"); + this->mode = climate::CLIMATE_MODE_OFF; + this->power_ = false; + } + + else { + ESP_LOGV(TAG, "Received unsupprted message type %X", recv_message_type); + return false; + } + + this->publish_state(); + return true; } } // namespace fujitsu_general diff --git a/esphome/components/fujitsu_general/fujitsu_general.h b/esphome/components/fujitsu_general/fujitsu_general.h index 80db81a167..8154d7a1d2 100644 --- a/esphome/components/fujitsu_general/fujitsu_general.h +++ b/esphome/components/fujitsu_general/fujitsu_general.h @@ -1,5 +1,6 @@ #pragma once +#include "esphome/core/log.h" #include "esphome/core/component.h" #include "esphome/core/automation.h" #include "esphome/components/climate_ir/climate_ir.h" @@ -7,9 +8,17 @@ namespace esphome { namespace fujitsu_general { +const uint8_t FUJITSU_GENERAL_TEMP_MIN = 16; // Celsius // TODO 16 for heating, 18 for cooling, unsupported in ESPH +const uint8_t FUJITSU_GENERAL_TEMP_MAX = 30; // Celsius + class FujitsuGeneralClimate : public climate_ir::ClimateIR { public: - FujitsuGeneralClimate(); + FujitsuGeneralClimate() + : ClimateIR(FUJITSU_GENERAL_TEMP_MIN, FUJITSU_GENERAL_TEMP_MAX, 1.0f, true, true, + {climate::CLIMATE_FAN_AUTO, climate::CLIMATE_FAN_LOW, climate::CLIMATE_FAN_MEDIUM, + climate::CLIMATE_FAN_HIGH}, + {climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_VERTICAL, climate::CLIMATE_SWING_HORIZONTAL, + climate::CLIMATE_SWING_BOTH}) {} protected: /// Transmit via IR the state of this climate controller. @@ -17,6 +26,19 @@ class FujitsuGeneralClimate : public climate_ir::ClimateIR { /// Transmit via IR power off command. void transmit_off_(); + /// Parse incomming message + bool on_receive(remote_base::RemoteReceiveData data) override; + + /// Transmit message as IR pulses + void transmit_(uint8_t const* message, uint8_t length); + + /// Calculate checksum for a state message + uint8_t checksum_state_(uint8_t const* message); + + /// Calculate cecksum for a util message + uint8_t checksum_util_(uint8_t const* message); + + // true if currently on - fujitsus transmit an on flag on when the remote moves from off to on bool power_{false}; }; From 848a5f1680f5878a0d5b52bfabf2e535e2a0b4e1 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Sun, 14 Mar 2021 13:27:16 +1300 Subject: [PATCH 088/111] Change COLOR_ON to be 255 values instead of 1 (#1594) --- esphome/components/display/display_buffer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/display/display_buffer.cpp b/esphome/components/display/display_buffer.cpp index 8b65ce72e1..994997e6e0 100644 --- a/esphome/components/display/display_buffer.cpp +++ b/esphome/components/display/display_buffer.cpp @@ -9,7 +9,7 @@ namespace display { static const char *TAG = "display"; const Color COLOR_OFF(0, 0, 0, 0); -const Color COLOR_ON(1, 1, 1, 1); +const Color COLOR_ON(255, 255, 255, 255); void DisplayBuffer::init_internal_(uint32_t buffer_length) { this->buffer_ = new uint8_t[buffer_length]; From 08998caabc7d592ef07bb1bdc7386ae4e697b4a0 Mon Sep 17 00:00:00 2001 From: WeekendWarrior1 Date: Sun, 14 Mar 2021 11:29:31 +1100 Subject: [PATCH 089/111] a4988 wait 1ms when coming out of sleep (#1597) --- esphome/components/a4988/a4988.cpp | 6 ++++++ esphome/components/a4988/a4988.h | 1 + 2 files changed, 7 insertions(+) diff --git a/esphome/components/a4988/a4988.cpp b/esphome/components/a4988/a4988.cpp index 99b677a9ab..4100563412 100644 --- a/esphome/components/a4988/a4988.cpp +++ b/esphome/components/a4988/a4988.cpp @@ -11,6 +11,7 @@ void A4988::setup() { if (this->sleep_pin_ != nullptr) { this->sleep_pin_->setup(); this->sleep_pin_->digital_write(false); + this->sleep_pin_state_ = false; } this->step_pin_->setup(); this->step_pin_->digital_write(false); @@ -27,7 +28,12 @@ void A4988::dump_config() { void A4988::loop() { bool at_target = this->has_reached_target(); if (this->sleep_pin_ != nullptr) { + bool sleep_rising_edge = !sleep_pin_state_ & !at_target; this->sleep_pin_->digital_write(!at_target); + this->sleep_pin_state_ = !at_target; + if (sleep_rising_edge) { + delayMicroseconds(1000); + } } if (at_target) { this->high_freq_.stop(); diff --git a/esphome/components/a4988/a4988.h b/esphome/components/a4988/a4988.h index 10fb5e0015..5be0f3ce69 100644 --- a/esphome/components/a4988/a4988.h +++ b/esphome/components/a4988/a4988.h @@ -21,6 +21,7 @@ class A4988 : public stepper::Stepper, public Component { GPIOPin *step_pin_; GPIOPin *dir_pin_; GPIOPin *sleep_pin_{nullptr}; + bool sleep_pin_state_; HighFrequencyLoopRequester high_freq_; }; From 7708b81ef5b84d3c80454a14a083ec3234d2df51 Mon Sep 17 00:00:00 2001 From: Jim Ekman Date: Wed, 17 Mar 2021 14:40:02 +0100 Subject: [PATCH 090/111] Support fan speed levels (#1541) * Add fan speed percentage support to the API * Add float fan speed percentage * Add percentage support to automation and configuration * Update Tuya fan * Fix pylint warning * Update API to use speed levels instead of percentage * Use speed levels * Fix type warnings * MQTT component now converts between speed levels and enums * Webserver now supports speed_level * Update prometheus * Remove low/medium/high settings from speed fan * Remove unused enum * Configurable speed levels for speed fan * Remove unused import * Rename speed_level->speed and speed_levels->speed_count * Rename supported_speed_levels -> supported_speed_count in API and FanTraits Field id stays the same in the protocol, so the change is not breaking for aioesphome. --- esphome/components/api/api.proto | 10 +++-- esphome/components/api/api_connection.cpp | 22 ++++++++--- esphome/components/api/api_pb2.cpp | 39 ++++++++++++++++++++ esphome/components/api/api_pb2.h | 6 ++- esphome/components/binary/fan/binary_fan.cpp | 2 +- esphome/components/fan/__init__.py | 12 +----- esphome/components/fan/automation.h | 2 +- esphome/components/fan/fan_helpers.cpp | 20 ++++++++++ esphome/components/fan/fan_helpers.h | 11 ++++++ esphome/components/fan/fan_state.cpp | 31 +++++++--------- esphome/components/fan/fan_state.h | 19 ++++------ esphome/components/fan/fan_traits.h | 11 ++++-- esphome/components/mqtt/mqtt_fan.cpp | 6 ++- esphome/components/speed/fan/__init__.py | 19 ++++------ esphome/components/speed/fan/speed_fan.cpp | 10 ++--- esphome/components/speed/fan/speed_fan.h | 12 ++---- esphome/components/tuya/fan/tuya_fan.cpp | 16 +++----- esphome/components/web_server/web_server.cpp | 19 +++++++++- esphome/const.py | 1 + esphome/core/helpers.cpp | 7 ++++ esphome/core/helpers.h | 1 + tests/test1.yaml | 5 +-- 22 files changed, 182 insertions(+), 99 deletions(-) create mode 100644 esphome/components/fan/fan_helpers.cpp create mode 100644 esphome/components/fan/fan_helpers.h diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index aaa9477985..ede2cc6205 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -303,6 +303,7 @@ message ListEntitiesFanResponse { bool supports_oscillation = 5; bool supports_speed = 6; bool supports_direction = 7; + int32 supported_speed_count = 8; } enum FanSpeed { FAN_SPEED_LOW = 0; @@ -322,8 +323,9 @@ message FanStateResponse { fixed32 key = 1; bool state = 2; bool oscillating = 3; - FanSpeed speed = 4; + FanSpeed speed = 4 [deprecated = true]; FanDirection direction = 5; + int32 speed_level = 6; } message FanCommandRequest { option (id) = 31; @@ -334,12 +336,14 @@ message FanCommandRequest { fixed32 key = 1; bool has_state = 2; bool state = 3; - bool has_speed = 4; - FanSpeed speed = 5; + bool has_speed = 4 [deprecated = true]; + FanSpeed speed = 5 [deprecated = true]; bool has_oscillating = 6; bool oscillating = 7; bool has_direction = 8; FanDirection direction = 9; + bool has_speed_level = 10; + int32 speed_level = 11; } // ==================== LIGHT ==================== diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index ecbe5b79c6..8098c93781 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -9,6 +9,9 @@ #ifdef USE_HOMEASSISTANT_TIME #include "esphome/components/homeassistant/time/homeassistant_time.h" #endif +#ifdef USE_FAN +#include "esphome/components/fan/fan_helpers.h" +#endif namespace esphome { namespace api { @@ -246,8 +249,10 @@ bool APIConnection::send_fan_state(fan::FanState *fan) { resp.state = fan->state; if (traits.supports_oscillation()) resp.oscillating = fan->oscillating; - if (traits.supports_speed()) - resp.speed = static_cast(fan->speed); + if (traits.supports_speed()) { + resp.speed_level = fan->speed; + resp.speed = static_cast(fan::speed_level_to_enum(fan->speed, traits.supported_speed_count())); + } if (traits.supports_direction()) resp.direction = static_cast(fan->direction); return this->send_fan_state_response(resp); @@ -262,6 +267,7 @@ bool APIConnection::send_fan_info(fan::FanState *fan) { msg.supports_oscillation = traits.supports_oscillation(); msg.supports_speed = traits.supports_speed(); msg.supports_direction = traits.supports_direction(); + msg.supported_speed_count = traits.supported_speed_count(); return this->send_list_entities_fan_response(msg); } void APIConnection::fan_command(const FanCommandRequest &msg) { @@ -269,13 +275,19 @@ void APIConnection::fan_command(const FanCommandRequest &msg) { if (fan == nullptr) return; + auto traits = fan->get_traits(); + auto call = fan->make_call(); if (msg.has_state) call.set_state(msg.state); if (msg.has_oscillating) call.set_oscillating(msg.oscillating); - if (msg.has_speed) - call.set_speed(static_cast(msg.speed)); + if (msg.has_speed_level) { + // Prefer level + call.set_speed(msg.speed_level); + } else if (msg.has_speed) { + call.set_speed(fan::speed_enum_to_level(static_cast(msg.speed), traits.supported_speed_count())); + } if (msg.has_direction) call.set_direction(static_cast(msg.direction)); call.perform(); @@ -590,7 +602,7 @@ HelloResponse APIConnection::hello(const HelloRequest &msg) { HelloResponse resp; resp.api_version_major = 1; - resp.api_version_minor = 3; + resp.api_version_minor = 4; resp.server_info = App.get_name() + " (esphome v" ESPHOME_VERSION ")"; this->connection_state_ = ConnectionState::CONNECTED; return resp; diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index 7a6b55bf91..23538b77bf 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -774,6 +774,10 @@ bool ListEntitiesFanResponse::decode_varint(uint32_t field_id, ProtoVarInt value this->supports_direction = value.as_bool(); return true; } + case 8: { + this->supported_speed_count = value.as_int32(); + return true; + } default: return false; } @@ -814,6 +818,7 @@ void ListEntitiesFanResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(5, this->supports_oscillation); buffer.encode_bool(6, this->supports_speed); buffer.encode_bool(7, this->supports_direction); + buffer.encode_int32(8, this->supported_speed_count); } void ListEntitiesFanResponse::dump_to(std::string &out) const { char buffer[64]; @@ -846,6 +851,11 @@ void ListEntitiesFanResponse::dump_to(std::string &out) const { out.append(" supports_direction: "); out.append(YESNO(this->supports_direction)); out.append("\n"); + + out.append(" supported_speed_count: "); + sprintf(buffer, "%d", this->supported_speed_count); + out.append(buffer); + out.append("\n"); out.append("}"); } bool FanStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { @@ -866,6 +876,10 @@ bool FanStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { this->direction = value.as_enum(); return true; } + case 6: { + this->speed_level = value.as_int32(); + return true; + } default: return false; } @@ -886,6 +900,7 @@ void FanStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(3, this->oscillating); buffer.encode_enum(4, this->speed); buffer.encode_enum(5, this->direction); + buffer.encode_int32(6, this->speed_level); } void FanStateResponse::dump_to(std::string &out) const { char buffer[64]; @@ -910,6 +925,11 @@ void FanStateResponse::dump_to(std::string &out) const { out.append(" direction: "); out.append(proto_enum_to_string(this->direction)); out.append("\n"); + + out.append(" speed_level: "); + sprintf(buffer, "%d", this->speed_level); + out.append(buffer); + out.append("\n"); out.append("}"); } bool FanCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { @@ -946,6 +966,14 @@ bool FanCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { this->direction = value.as_enum(); return true; } + case 10: { + this->has_speed_level = value.as_bool(); + return true; + } + case 11: { + this->speed_level = value.as_int32(); + return true; + } default: return false; } @@ -970,6 +998,8 @@ void FanCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(7, this->oscillating); buffer.encode_bool(8, this->has_direction); buffer.encode_enum(9, this->direction); + buffer.encode_bool(10, this->has_speed_level); + buffer.encode_int32(11, this->speed_level); } void FanCommandRequest::dump_to(std::string &out) const { char buffer[64]; @@ -1010,6 +1040,15 @@ void FanCommandRequest::dump_to(std::string &out) const { out.append(" direction: "); out.append(proto_enum_to_string(this->direction)); out.append("\n"); + + out.append(" has_speed_level: "); + out.append(YESNO(this->has_speed_level)); + out.append("\n"); + + out.append(" speed_level: "); + sprintf(buffer, "%d", this->speed_level); + out.append(buffer); + out.append("\n"); out.append("}"); } bool ListEntitiesLightResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index abee4a11d4..f70ac74a79 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -284,6 +284,7 @@ class ListEntitiesFanResponse : public ProtoMessage { bool supports_oscillation{false}; // NOLINT bool supports_speed{false}; // NOLINT bool supports_direction{false}; // NOLINT + int32_t supported_speed_count{0}; // NOLINT void encode(ProtoWriteBuffer buffer) const override; void dump_to(std::string &out) const override; @@ -299,6 +300,7 @@ class FanStateResponse : public ProtoMessage { bool oscillating{false}; // NOLINT enums::FanSpeed speed{}; // NOLINT enums::FanDirection direction{}; // NOLINT + int32_t speed_level{0}; // NOLINT void encode(ProtoWriteBuffer buffer) const override; void dump_to(std::string &out) const override; @@ -317,6 +319,8 @@ class FanCommandRequest : public ProtoMessage { bool oscillating{false}; // NOLINT bool has_direction{false}; // NOLINT enums::FanDirection direction{}; // NOLINT + bool has_speed_level{false}; // NOLINT + int32_t speed_level{0}; // NOLINT void encode(ProtoWriteBuffer buffer) const override; void dump_to(std::string &out) const override; @@ -401,9 +405,9 @@ class ListEntitiesSensorResponse : public ProtoMessage { std::string unique_id{}; // NOLINT std::string icon{}; // NOLINT std::string unit_of_measurement{}; // NOLINT - std::string device_class{}; // NOLINT int32_t accuracy_decimals{0}; // NOLINT bool force_update{false}; // NOLINT + std::string device_class{}; // NOLINT void encode(ProtoWriteBuffer buffer) const override; void dump_to(std::string &out) const override; diff --git a/esphome/components/binary/fan/binary_fan.cpp b/esphome/components/binary/fan/binary_fan.cpp index 5fd1867e7f..fa6ddc249f 100644 --- a/esphome/components/binary/fan/binary_fan.cpp +++ b/esphome/components/binary/fan/binary_fan.cpp @@ -16,7 +16,7 @@ void binary::BinaryFan::dump_config() { } } void BinaryFan::setup() { - auto traits = fan::FanTraits(this->oscillating_ != nullptr, false, this->direction_ != nullptr); + 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; }); } diff --git a/esphome/components/fan/__init__.py b/esphome/components/fan/__init__.py index 79ed72f496..10e38682e7 100644 --- a/esphome/components/fan/__init__.py +++ b/esphome/components/fan/__init__.py @@ -28,14 +28,6 @@ TurnOnAction = fan_ns.class_("TurnOnAction", automation.Action) TurnOffAction = fan_ns.class_("TurnOffAction", automation.Action) ToggleAction = fan_ns.class_("ToggleAction", automation.Action) -FanSpeed = fan_ns.enum("FanSpeed") -FAN_SPEEDS = { - "OFF": FanSpeed.FAN_SPEED_OFF, - "LOW": FanSpeed.FAN_SPEED_LOW, - "MEDIUM": FanSpeed.FAN_SPEED_MEDIUM, - "HIGH": FanSpeed.FAN_SPEED_HIGH, -} - FAN_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend( { cv.GenerateID(): cv.declare_id(FanState), @@ -128,7 +120,7 @@ def fan_turn_off_to_code(config, action_id, template_arg, args): { cv.Required(CONF_ID): cv.use_id(FanState), cv.Optional(CONF_OSCILLATING): cv.templatable(cv.boolean), - cv.Optional(CONF_SPEED): cv.templatable(cv.enum(FAN_SPEEDS, upper=True)), + cv.Optional(CONF_SPEED): cv.templatable(cv.int_range(1)), } ), ) @@ -139,7 +131,7 @@ def fan_turn_on_to_code(config, action_id, template_arg, args): template_ = yield cg.templatable(config[CONF_OSCILLATING], args, bool) cg.add(var.set_oscillating(template_)) if CONF_SPEED in config: - template_ = yield cg.templatable(config[CONF_SPEED], args, FanSpeed) + template_ = yield cg.templatable(config[CONF_SPEED], args, int) cg.add(var.set_speed(template_)) yield var diff --git a/esphome/components/fan/automation.h b/esphome/components/fan/automation.h index d96ed994e8..25c92075ba 100644 --- a/esphome/components/fan/automation.h +++ b/esphome/components/fan/automation.h @@ -12,7 +12,7 @@ template class TurnOnAction : public Action { explicit TurnOnAction(FanState *state) : state_(state) {} TEMPLATABLE_VALUE(bool, oscillating) - TEMPLATABLE_VALUE(FanSpeed, speed) + TEMPLATABLE_VALUE(int, speed) void play(Ts... x) override { auto call = this->state_->turn_on(); diff --git a/esphome/components/fan/fan_helpers.cpp b/esphome/components/fan/fan_helpers.cpp new file mode 100644 index 0000000000..be16e6bb64 --- /dev/null +++ b/esphome/components/fan/fan_helpers.cpp @@ -0,0 +1,20 @@ +#include +#include "fan_helpers.h" + +namespace esphome { +namespace fan { + +FanSpeed speed_level_to_enum(int speed_level, int supported_speed_levels) { + const auto speed_ratio = static_cast(speed_level) / (supported_speed_levels + 1); + const auto legacy_level = static_cast(clamp(ceilf(speed_ratio * 3), 1, 3)); + return static_cast(legacy_level - 1); +} + +int speed_enum_to_level(FanSpeed speed, int supported_speed_levels) { + const auto enum_level = static_cast(speed) + 1; + const auto speed_level = roundf(enum_level / 3.0f * supported_speed_levels); + return static_cast(speed_level); +} + +} // namespace fan +} // namespace esphome diff --git a/esphome/components/fan/fan_helpers.h b/esphome/components/fan/fan_helpers.h new file mode 100644 index 0000000000..138aa5bca3 --- /dev/null +++ b/esphome/components/fan/fan_helpers.h @@ -0,0 +1,11 @@ +#pragma once +#include "fan_state.h" + +namespace esphome { +namespace fan { + +FanSpeed speed_level_to_enum(int speed_level, int supported_speed_levels); +int speed_enum_to_level(FanSpeed speed, int supported_speed_levels); + +} // namespace fan +} // namespace esphome diff --git a/esphome/components/fan/fan_state.cpp b/esphome/components/fan/fan_state.cpp index ae58b04150..5a3a7ecebc 100644 --- a/esphome/components/fan/fan_state.cpp +++ b/esphome/components/fan/fan_state.cpp @@ -1,4 +1,5 @@ #include "fan_state.h" +#include "fan_helpers.h" #include "esphome/core/log.h" namespace esphome { @@ -20,7 +21,7 @@ FanStateCall FanState::make_call() { return FanStateCall(this); } struct FanStateRTCState { bool state; - FanSpeed speed; + int speed; bool oscillating; FanDirection direction; }; @@ -52,16 +53,8 @@ void FanStateCall::perform() const { this->state_->direction = *this->direction_; } if (this->speed_.has_value()) { - switch (*this->speed_) { - case FAN_SPEED_LOW: - case FAN_SPEED_MEDIUM: - case FAN_SPEED_HIGH: - this->state_->speed = *this->speed_; - break; - default: - // protect from invalid input - break; - } + const int speed_count = this->state_->get_traits().supported_speed_count(); + this->state_->speed = static_cast(clamp(*this->speed_, 1, speed_count)); } FanStateRTCState saved{}; @@ -73,13 +66,15 @@ void FanStateCall::perform() const { this->state_->state_callback_.call(); } -FanStateCall &FanStateCall::set_speed(const char *speed) { - if (strcasecmp(speed, "low") == 0) { - this->set_speed(FAN_SPEED_LOW); - } else if (strcasecmp(speed, "medium") == 0) { - this->set_speed(FAN_SPEED_MEDIUM); - } else if (strcasecmp(speed, "high") == 0) { - this->set_speed(FAN_SPEED_HIGH); + +FanStateCall &FanStateCall::set_speed(const char *legacy_speed) { + const auto supported_speed_count = this->state_->get_traits().supported_speed_count(); + if (strcasecmp(legacy_speed, "low") == 0) { + this->set_speed(fan::speed_enum_to_level(FAN_SPEED_LOW, supported_speed_count)); + } else if (strcasecmp(legacy_speed, "medium") == 0) { + this->set_speed(fan::speed_enum_to_level(FAN_SPEED_MEDIUM, supported_speed_count)); + } else if (strcasecmp(legacy_speed, "high") == 0) { + this->set_speed(fan::speed_enum_to_level(FAN_SPEED_HIGH, supported_speed_count)); } return *this; } diff --git a/esphome/components/fan/fan_state.h b/esphome/components/fan/fan_state.h index 7ab8337e94..a0dda4083a 100644 --- a/esphome/components/fan/fan_state.h +++ b/esphome/components/fan/fan_state.h @@ -3,12 +3,13 @@ #include "esphome/core/component.h" #include "esphome/core/helpers.h" #include "esphome/core/preferences.h" +#include "esphome/core/log.h" #include "fan_traits.h" namespace esphome { namespace fan { -/// Simple enum to represent the speed of a fan. +/// Simple enum to represent the speed of a fan. - DEPRECATED - Will be deleted soon enum FanSpeed { FAN_SPEED_LOW = 0, ///< The fan is running on low speed. FAN_SPEED_MEDIUM = 1, ///< The fan is running on medium speed. @@ -40,15 +41,11 @@ class FanStateCall { this->oscillating_ = oscillating; return *this; } - FanStateCall &set_speed(FanSpeed speed) { + FanStateCall &set_speed(int speed) { this->speed_ = speed; return *this; } - FanStateCall &set_speed(optional speed) { - this->speed_ = speed; - return *this; - } - FanStateCall &set_speed(const char *speed); + FanStateCall &set_speed(const char *legacy_speed); FanStateCall &set_direction(FanDirection direction) { this->direction_ = direction; return *this; @@ -63,8 +60,8 @@ class FanStateCall { protected: FanState *const state_; optional binary_state_; - optional oscillating_{}; - optional speed_{}; + optional oscillating_; + optional speed_; optional direction_{}; }; @@ -86,8 +83,8 @@ class FanState : public Nameable, public Component { bool state{false}; /// The current oscillation state of the fan. bool oscillating{false}; - /// The current fan speed. - FanSpeed speed{FAN_SPEED_HIGH}; + /// The current fan speed level + int speed{}; /// The current direction of the fan FanDirection direction{FAN_DIRECTION_FORWARD}; diff --git a/esphome/components/fan/fan_traits.h b/esphome/components/fan/fan_traits.h index 75663484c5..e69d8e2e53 100644 --- a/esphome/components/fan/fan_traits.h +++ b/esphome/components/fan/fan_traits.h @@ -6,8 +6,8 @@ namespace fan { class FanTraits { public: FanTraits() = default; - FanTraits(bool oscillation, bool speed, bool direction) - : oscillation_(oscillation), speed_(speed), direction_(direction) {} + FanTraits(bool oscillation, bool speed, bool direction, int speed_count) + : oscillation_(oscillation), speed_(speed), direction_(direction), speed_count_(speed_count) {} /// Return if this fan supports oscillation. bool supports_oscillation() const { return this->oscillation_; } @@ -15,8 +15,12 @@ class FanTraits { void set_oscillation(bool oscillation) { this->oscillation_ = oscillation; } /// Return if this fan supports speed modes. bool supports_speed() const { return this->speed_; } - /// Set whether this fan supports speed modes. + /// Set whether this fan supports speed levels. void set_speed(bool speed) { this->speed_ = speed; } + /// Return how many speed levels the fan has + int supported_speed_count() const { return this->speed_count_; } + /// Set how many speed levels this fan has. + void set_supported_speed_count(int speed_count) { this->speed_count_ = speed_count; } /// Return if this fan supports changing direction bool supports_direction() const { return this->direction_; } /// Set whether this fan supports changing direction @@ -26,6 +30,7 @@ class FanTraits { bool oscillation_{false}; bool speed_{false}; bool direction_{false}; + int speed_count_{}; }; } // namespace fan diff --git a/esphome/components/mqtt/mqtt_fan.cpp b/esphome/components/mqtt/mqtt_fan.cpp index f115fe1bac..c020d73105 100644 --- a/esphome/components/mqtt/mqtt_fan.cpp +++ b/esphome/components/mqtt/mqtt_fan.cpp @@ -2,6 +2,7 @@ #include "esphome/core/log.h" #ifdef USE_FAN +#include "esphome/components/fan/fan_helpers.h" namespace esphome { namespace mqtt { @@ -94,9 +95,10 @@ bool MQTTFanComponent::publish_state() { this->state_->oscillating ? "oscillate_on" : "oscillate_off"); failed = failed || !success; } - if (this->state_->get_traits().supports_speed()) { + auto traits = this->state_->get_traits(); + if (traits.supports_speed()) { const char *payload; - switch (this->state_->speed) { + switch (fan::speed_level_to_enum(this->state_->speed, traits.supported_speed_count())) { case FAN_SPEED_LOW: { payload = "low"; break; diff --git a/esphome/components/speed/fan/__init__.py b/esphome/components/speed/fan/__init__.py index a8f306d713..fdb0a9af09 100644 --- a/esphome/components/speed/fan/__init__.py +++ b/esphome/components/speed/fan/__init__.py @@ -7,9 +7,7 @@ from esphome.const import ( CONF_DIRECTION_OUTPUT, CONF_OUTPUT_ID, CONF_SPEED, - CONF_LOW, - CONF_MEDIUM, - CONF_HIGH, + CONF_SPEED_COUNT, ) from .. import speed_ns @@ -21,13 +19,10 @@ CONFIG_SCHEMA = fan.FAN_SCHEMA.extend( cv.Required(CONF_OUTPUT): cv.use_id(output.FloatOutput), cv.Optional(CONF_OSCILLATION_OUTPUT): cv.use_id(output.BinaryOutput), cv.Optional(CONF_DIRECTION_OUTPUT): cv.use_id(output.BinaryOutput), - cv.Optional(CONF_SPEED, default={}): cv.Schema( - { - cv.Optional(CONF_LOW, default=0.33): cv.percentage, - cv.Optional(CONF_MEDIUM, default=0.66): cv.percentage, - cv.Optional(CONF_HIGH, default=1.0): cv.percentage, - } + cv.Optional(CONF_SPEED): cv.invalid( + "Configuring individual speeds is deprecated." ), + cv.Optional(CONF_SPEED_COUNT, default=100): cv.int_range(min=1), } ).extend(cv.COMPONENT_SCHEMA) @@ -35,10 +30,10 @@ CONFIG_SCHEMA = fan.FAN_SCHEMA.extend( def to_code(config): output_ = yield cg.get_variable(config[CONF_OUTPUT]) state = yield fan.create_fan_state(config) - var = cg.new_Pvariable(config[CONF_OUTPUT_ID], state, output_) + var = cg.new_Pvariable( + config[CONF_OUTPUT_ID], state, output_, config[CONF_SPEED_COUNT] + ) yield cg.register_component(var, config) - speeds = config[CONF_SPEED] - cg.add(var.set_speeds(speeds[CONF_LOW], speeds[CONF_MEDIUM], speeds[CONF_HIGH])) if CONF_OSCILLATION_OUTPUT in config: oscillation_output = yield cg.get_variable(config[CONF_OSCILLATION_OUTPUT]) diff --git a/esphome/components/speed/fan/speed_fan.cpp b/esphome/components/speed/fan/speed_fan.cpp index 45117d64c3..0455fa200d 100644 --- a/esphome/components/speed/fan/speed_fan.cpp +++ b/esphome/components/speed/fan/speed_fan.cpp @@ -1,4 +1,5 @@ #include "speed_fan.h" +#include "esphome/components/fan/fan_helpers.h" #include "esphome/core/log.h" namespace esphome { @@ -16,7 +17,7 @@ void SpeedFan::dump_config() { } } void SpeedFan::setup() { - auto traits = fan::FanTraits(this->oscillating_ != nullptr, true, this->direction_ != nullptr); + auto traits = fan::FanTraits(this->oscillating_ != nullptr, true, this->direction_ != nullptr, this->speed_count_); this->fan_->set_traits(traits); this->fan_->add_on_state_callback([this]() { this->next_update_ = true; }); } @@ -29,12 +30,7 @@ void SpeedFan::loop() { { float speed = 0.0f; if (this->fan_->state) { - if (this->fan_->speed == fan::FAN_SPEED_LOW) - speed = this->low_speed_; - else if (this->fan_->speed == fan::FAN_SPEED_MEDIUM) - speed = this->medium_speed_; - else if (this->fan_->speed == fan::FAN_SPEED_HIGH) - speed = this->high_speed_; + speed = static_cast(this->fan_->speed) / static_cast(this->speed_count_); } ESP_LOGD(TAG, "Setting speed: %.2f", speed); this->output_->set_level(speed); diff --git a/esphome/components/speed/fan/speed_fan.h b/esphome/components/speed/fan/speed_fan.h index cce9d07544..6b7fa0b0f2 100644 --- a/esphome/components/speed/fan/speed_fan.h +++ b/esphome/components/speed/fan/speed_fan.h @@ -10,28 +10,22 @@ namespace speed { class SpeedFan : public Component { public: - SpeedFan(fan::FanState *fan, output::FloatOutput *output) : fan_(fan), output_(output) {} + SpeedFan(fan::FanState *fan, output::FloatOutput *output, int speed_count) + : fan_(fan), output_(output), speed_count_(speed_count) {} void setup() override; void loop() override; void dump_config() override; float get_setup_priority() const override; void set_oscillating(output::BinaryOutput *oscillating) { this->oscillating_ = oscillating; } void set_direction(output::BinaryOutput *direction) { this->direction_ = direction; } - void set_speeds(float low, float medium, float high) { - this->low_speed_ = low; - this->medium_speed_ = medium; - this->high_speed_ = high; - } protected: fan::FanState *fan_; output::FloatOutput *output_; output::BinaryOutput *oscillating_{nullptr}; output::BinaryOutput *direction_{nullptr}; - float low_speed_{}; - float medium_speed_{}; - float high_speed_{}; bool next_update_{true}; + int speed_count_{}; }; } // namespace speed diff --git a/esphome/components/tuya/fan/tuya_fan.cpp b/esphome/components/tuya/fan/tuya_fan.cpp index 9850fa65ed..718f292f5b 100644 --- a/esphome/components/tuya/fan/tuya_fan.cpp +++ b/esphome/components/tuya/fan/tuya_fan.cpp @@ -1,4 +1,5 @@ #include "esphome/core/log.h" +#include "esphome/components/fan/fan_helpers.h" #include "tuya_fan.h" namespace esphome { @@ -7,18 +8,18 @@ namespace tuya { static const char *TAG = "tuya.fan"; void TuyaFan::setup() { - auto traits = fan::FanTraits(this->oscillation_id_.has_value(), this->speed_id_.has_value(), false); + auto traits = fan::FanTraits(this->oscillation_id_.has_value(), this->speed_id_.has_value(), false, 3); this->fan_->set_traits(traits); if (this->speed_id_.has_value()) { this->parent_->register_listener(*this->speed_id_, [this](TuyaDatapoint datapoint) { auto call = this->fan_->make_call(); if (datapoint.value_enum == 0x0) - call.set_speed(fan::FAN_SPEED_LOW); + call.set_speed(1); else if (datapoint.value_enum == 0x1) - call.set_speed(fan::FAN_SPEED_MEDIUM); + call.set_speed(2); else if (datapoint.value_enum == 0x2) - call.set_speed(fan::FAN_SPEED_HIGH); + call.set_speed(3); else ESP_LOGCONFIG(TAG, "Speed has invalid value %d", datapoint.value_enum); ESP_LOGD(TAG, "MCU reported speed of: %d", datapoint.value_enum); @@ -75,12 +76,7 @@ void TuyaFan::write_state() { TuyaDatapoint datapoint{}; datapoint.id = *this->speed_id_; datapoint.type = TuyaDatapointType::ENUM; - if (this->fan_->speed == fan::FAN_SPEED_LOW) - datapoint.value_enum = 0; - if (this->fan_->speed == fan::FAN_SPEED_MEDIUM) - datapoint.value_enum = 1; - if (this->fan_->speed == fan::FAN_SPEED_HIGH) - datapoint.value_enum = 2; + datapoint.value_enum = this->fan_->speed - 1; ESP_LOGD(TAG, "Setting speed: %d", datapoint.value_enum); this->parent_->set_datapoint_value(datapoint); } diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index 9e0e881b1b..fbb215ee17 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -12,6 +12,10 @@ #include #endif +#ifdef USE_FAN +#include "esphome/components/fan/fan_helpers.h" +#endif + namespace esphome { namespace web_server { @@ -364,8 +368,10 @@ std::string WebServer::fan_json(fan::FanState *obj) { root["id"] = "fan-" + obj->get_object_id(); root["state"] = obj->state ? "ON" : "OFF"; root["value"] = obj->state; - if (obj->get_traits().supports_speed()) { - switch (obj->speed) { + const auto traits = obj->get_traits(); + if (traits.supports_speed()) { + root["speed_level"] = obj->speed; + switch (fan::speed_level_to_enum(obj->speed, traits.supported_speed_count())) { case fan::FAN_SPEED_LOW: root["speed"] = "low"; break; @@ -400,6 +406,15 @@ void WebServer::handle_fan_request(AsyncWebServerRequest *request, UrlMatch matc String speed = request->getParam("speed")->value(); call.set_speed(speed.c_str()); } + if (request->hasParam("speed_level")) { + String speed_level = request->getParam("speed_level")->value(); + auto val = parse_int(speed_level.c_str()); + if (!val.has_value()) { + ESP_LOGW(TAG, "Can't convert '%s' to number!", speed_level.c_str()); + return; + } + call.set_speed(*val); + } if (request->hasParam("oscillation")) { String speed = request->getParam("oscillation")->value(); auto val = parse_on_off(speed.c_str()); diff --git a/esphome/const.py b/esphome/const.py index 5d735698bc..232449e647 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -483,6 +483,7 @@ CONF_SLEEP_WHEN_DONE = "sleep_when_done" CONF_SONY = "sony" CONF_SPEED = "speed" CONF_SPEED_COMMAND_TOPIC = "speed_command_topic" +CONF_SPEED_COUNT = "speed_count" CONF_SPEED_STATE_TOPIC = "speed_state_topic" CONF_SPI_ID = "spi_id" CONF_SPIKE_REJECTION = "spike_rejection" diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index 3e7472d2fe..db26e62781 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -248,6 +248,13 @@ optional parse_float(const std::string &str) { return {}; return value; } +optional parse_int(const std::string &str) { + char *end; + int value = ::strtol(str.c_str(), &end, 10); + if (end == nullptr || end != str.end().base()) + return {}; + return value; +} uint32_t fnv1_hash(const std::string &str) { uint32_t hash = 2166136261UL; for (char c : str) { diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index 40e53e601e..63706e8a19 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -42,6 +42,7 @@ std::string to_string(float val); std::string to_string(double val); std::string to_string(long double val); optional parse_float(const std::string &str); +optional parse_int(const std::string &str); /// Sanitize the hostname by removing characters that are not in the allowlist and truncating it to 63 chars. std::string sanitize_hostname(const std::string &hostname); diff --git a/tests/test1.yaml b/tests/test1.yaml index f5c3ab9b57..316f31e68a 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -1708,13 +1708,10 @@ fan: direction_output: gpio_26 - platform: speed output: pca_6 + speed_count: 10 name: 'Living Room Fan 2' oscillation_output: gpio_19 direction_output: gpio_26 - speed: - low: 0.45 - medium: 0.75 - high: 1.0 oscillation_state_topic: oscillation/state/topic oscillation_command_topic: oscillation/command/topic speed_state_topic: speed/state/topic From faf577a9dd607ddf66e3b440bd5e6332d9a8410b Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 18 Mar 2021 09:22:48 +1300 Subject: [PATCH 091/111] Add option to suffix name with mac address (#1615) * Add option to suffix name with mac address * Rename and add to test file --- esphome/core/application.h | 8 ++++++-- esphome/core_config.py | 9 ++++++++- tests/test1.yaml | 1 + 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/esphome/core/application.h b/esphome/core/application.h index 3c293e6c8f..e9c8638f60 100644 --- a/esphome/core/application.h +++ b/esphome/core/application.h @@ -37,8 +37,12 @@ namespace esphome { class Application { public: - void pre_setup(const std::string &name, const char *compilation_time) { - this->name_ = name; + void pre_setup(const std::string &name, const char *compilation_time, bool name_add_mac_suffix) { + if (name_add_mac_suffix) { + this->name_ = name + "_" + get_mac_address().substr(6); + } else { + this->name_ = name; + } this->compilation_time_ = compilation_time; global_preferences.begin(); } diff --git a/esphome/core_config.py b/esphome/core_config.py index c0498a8fa9..55d219d86b 100644 --- a/esphome/core_config.py +++ b/esphome/core_config.py @@ -45,6 +45,8 @@ LoopTrigger = cg.esphome_ns.class_( VERSION_REGEX = re.compile(r"^[0-9]+\.[0-9]+\.[0-9]+(?:[ab]\d+)?$") +CONF_NAME_ADD_MAC_SUFFIX = "name_add_mac_suffix" + def validate_board(value): if CORE.is_esp8266: @@ -173,6 +175,7 @@ CONFIG_SCHEMA = cv.Schema( ), cv.Optional(CONF_INCLUDES, default=[]): cv.ensure_list(valid_include), cv.Optional(CONF_LIBRARIES, default=[]): cv.ensure_list(cv.string_strict), + cv.Optional(CONF_NAME_ADD_MAC_SUFFIX, default=False): cv.boolean, cv.Optional("esphome_core_version"): cv.invalid( "The esphome_core_version option has been " "removed in 1.13 - the esphome core source " @@ -289,7 +292,11 @@ def _add_automations(config): def to_code(config): cg.add_global(cg.global_ns.namespace("esphome").using) cg.add( - cg.App.pre_setup(config[CONF_NAME], cg.RawExpression('__DATE__ ", " __TIME__')) + cg.App.pre_setup( + config[CONF_NAME], + cg.RawExpression('__DATE__ ", " __TIME__'), + config[CONF_NAME_ADD_MAC_SUFFIX], + ) ) CORE.add_job(_add_automations, config) diff --git a/tests/test1.yaml b/tests/test1.yaml index 316f31e68a..b50d5eeef7 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -6,6 +6,7 @@ substitutions: esphome: name: test1 + name_add_mac_suffix: true platform: ESP32 board: nodemcu-32s on_boot: From f34c9b33fc5280e09768d1fedc13c6c6478e69c7 Mon Sep 17 00:00:00 2001 From: "Sergey V. DUDANOV" Date: Thu, 18 Mar 2021 00:27:50 +0400 Subject: [PATCH 092/111] Midea climate support (#1328) * Added support for Midea IoT climate devices via UART interface (USB-dongle) * Fixed lint checks * Fixed lint checks * CODEOWNERS update * Clang-format * Clang-format * Add network device notification message support (show WiFi sign on devices) * Make wifi_signal_sensor optional component * Some optimization * Optimizations and code formatting * Fixed lint checks * Fixed lint checks * Fixed sign error * Code changes * Network notify repeat every 10 min * Added log messages * Fixed lint checks * Refactoring: MideaClimate => MideaAC * Using enums instead literals in Midea states * Enum changed to be more correct * Shrink notify frame to 32 bytes * Fixed lint checks * Change notify frame appliance type to common broadcast * Control optimization * Fixed control error * Control command now don't reset others uncontrollable properties of device * Fixed lint checks * Some optimization * on_receive callback give const Frame * Fix control * Fixes * Some minor changes * Fixed lint error * No dependency from wifi_signal sensor for stretched WiFi icon. New option: stretched_icon instead wifi_signal_id. * Fix option name * Added export of outdoor temperature as sensor value * Fixed lint errors * Fixed pylint error * Minor fix * Fix temperature overflow in some cases * Added answer on QueryNetwork command from appliance. Now don't wait for ack on 0x0D command. * Fix lint error * Added humidity setpoint optional sensor * Added boolean options 'swing_horizontal' and 'swing_both' * Added debug frame output * Added debug frame output * Fix lints error * Some debug output optimization * Fix lint check * Some code optimization: adding templates * Fix lint error * Added sensors device classes * Python code reformatted with black formatter * RX frame debug message RX frame debug message now prints before checking * Remove CRC check for receiving frames * Added experimental power usage option * Added power usage option * Fixed lint errors * Major changes. See esp-docs. * Added tests in test4.yaml * Added tests in test1.yaml * Added wifi dependency * Fix test1.yaml * Some fix :) * One more refactoring * One more refactoring * One more refactoring --- CODEOWNERS | 2 + esphome/components/midea_ac/__init__.py | 0 esphome/components/midea_ac/climate.py | 69 ++++++++ esphome/components/midea_ac/midea_climate.cpp | 110 ++++++++++++ esphome/components/midea_ac/midea_climate.h | 47 +++++ esphome/components/midea_ac/midea_frame.cpp | 160 ++++++++++++++++++ esphome/components/midea_ac/midea_frame.h | 137 +++++++++++++++ esphome/components/midea_dongle/__init__.py | 30 ++++ .../components/midea_dongle/midea_dongle.cpp | 98 +++++++++++ .../components/midea_dongle/midea_dongle.h | 56 ++++++ .../components/midea_dongle/midea_frame.cpp | 95 +++++++++++ esphome/components/midea_dongle/midea_frame.h | 104 ++++++++++++ tests/test1.yaml | 17 ++ 13 files changed, 925 insertions(+) create mode 100644 esphome/components/midea_ac/__init__.py create mode 100644 esphome/components/midea_ac/climate.py create mode 100644 esphome/components/midea_ac/midea_climate.cpp create mode 100644 esphome/components/midea_ac/midea_climate.h create mode 100644 esphome/components/midea_ac/midea_frame.cpp create mode 100644 esphome/components/midea_ac/midea_frame.h create mode 100644 esphome/components/midea_dongle/__init__.py create mode 100644 esphome/components/midea_dongle/midea_dongle.cpp create mode 100644 esphome/components/midea_dongle/midea_dongle.h create mode 100644 esphome/components/midea_dongle/midea_frame.cpp create mode 100644 esphome/components/midea_dongle/midea_frame.h diff --git a/CODEOWNERS b/CODEOWNERS index 4fdfb21569..47cd5f59ca 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -55,6 +55,8 @@ esphome/components/mcp23x17_base/* @jesserockz esphome/components/mcp23xxx_base/* @jesserockz esphome/components/mcp2515/* @danielschramm @mvturnho esphome/components/mcp9808/* @k7hpn +esphome/components/midea_ac/* @dudanov +esphome/components/midea_dongle/* @dudanov esphome/components/network/* @esphome/core esphome/components/nfc/* @jesserockz esphome/components/ota/* @esphome/core diff --git a/esphome/components/midea_ac/__init__.py b/esphome/components/midea_ac/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/midea_ac/climate.py b/esphome/components/midea_ac/climate.py new file mode 100644 index 0000000000..94aed91d4c --- /dev/null +++ b/esphome/components/midea_ac/climate.py @@ -0,0 +1,69 @@ +from esphome.components import climate, sensor +import esphome.config_validation as cv +import esphome.codegen as cg +from esphome.const import ( + CONF_ID, + UNIT_CELSIUS, + UNIT_PERCENT, + UNIT_WATT, + ICON_THERMOMETER, + ICON_POWER, + DEVICE_CLASS_POWER, + DEVICE_CLASS_TEMPERATURE, + ICON_WATER_PERCENT, + DEVICE_CLASS_HUMIDITY, +) +from esphome.components.midea_dongle import CONF_MIDEA_DONGLE_ID, MideaDongle + +AUTO_LOAD = ["climate", "sensor", "midea_dongle"] +CODEOWNERS = ["@dudanov"] + +CONF_BEEPER = "beeper" +CONF_SWING_HORIZONTAL = "swing_horizontal" +CONF_SWING_BOTH = "swing_both" +CONF_OUTDOOR_TEMPERATURE = "outdoor_temperature" +CONF_POWER_USAGE = "power_usage" +CONF_HUMIDITY_SETPOINT = "humidity_setpoint" +midea_ac_ns = cg.esphome_ns.namespace("midea_ac") +MideaAC = midea_ac_ns.class_("MideaAC", climate.Climate, cg.Component) + +CONFIG_SCHEMA = cv.All( + climate.CLIMATE_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(MideaAC), + cv.GenerateID(CONF_MIDEA_DONGLE_ID): cv.use_id(MideaDongle), + cv.Optional(CONF_BEEPER, default=False): cv.boolean, + cv.Optional(CONF_SWING_HORIZONTAL, default=False): cv.boolean, + cv.Optional(CONF_SWING_BOTH, default=False): cv.boolean, + cv.Optional(CONF_OUTDOOR_TEMPERATURE): sensor.sensor_schema( + UNIT_CELSIUS, ICON_THERMOMETER, 0, DEVICE_CLASS_TEMPERATURE + ), + cv.Optional(CONF_POWER_USAGE): sensor.sensor_schema( + UNIT_WATT, ICON_POWER, 0, DEVICE_CLASS_POWER + ), + cv.Optional(CONF_HUMIDITY_SETPOINT): sensor.sensor_schema( + UNIT_PERCENT, ICON_WATER_PERCENT, 0, DEVICE_CLASS_HUMIDITY + ), + } + ).extend(cv.COMPONENT_SCHEMA) +) + + +def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + yield cg.register_component(var, config) + yield climate.register_climate(var, config) + paren = yield cg.get_variable(config[CONF_MIDEA_DONGLE_ID]) + cg.add(var.set_midea_dongle_parent(paren)) + cg.add(var.set_beeper_feedback(config[CONF_BEEPER])) + cg.add(var.set_swing_horizontal(config[CONF_SWING_HORIZONTAL])) + cg.add(var.set_swing_both(config[CONF_SWING_BOTH])) + if CONF_OUTDOOR_TEMPERATURE in config: + sens = yield sensor.new_sensor(config[CONF_OUTDOOR_TEMPERATURE]) + cg.add(var.set_outdoor_temperature_sensor(sens)) + if CONF_POWER_USAGE in config: + sens = yield sensor.new_sensor(config[CONF_POWER_USAGE]) + cg.add(var.set_power_sensor(sens)) + if CONF_HUMIDITY_SETPOINT in config: + sens = yield sensor.new_sensor(config[CONF_HUMIDITY_SETPOINT]) + cg.add(var.set_humidity_setpoint_sensor(sens)) diff --git a/esphome/components/midea_ac/midea_climate.cpp b/esphome/components/midea_ac/midea_climate.cpp new file mode 100644 index 0000000000..8a74251696 --- /dev/null +++ b/esphome/components/midea_ac/midea_climate.cpp @@ -0,0 +1,110 @@ +#include "esphome/core/log.h" +#include "midea_climate.h" + +namespace esphome { +namespace midea_ac { + +static const char *TAG = "midea_ac"; + +static void set_sensor(sensor::Sensor *sensor, float value) { + if (sensor != nullptr && (!sensor->has_state() || sensor->get_raw_state() != value)) + sensor->publish_state(value); +} + +template void set_property(T &property, T value, bool &flag) { + if (property != value) { + property = value; + flag = true; + } +} + +void MideaAC::on_frame(const midea_dongle::Frame &frame) { + const auto p = frame.as(); + if (p.has_power_info()) { + set_sensor(this->power_sensor_, p.get_power_usage()); + return; + } else if (!p.has_properties()) { + ESP_LOGW(TAG, "RX: frame has unknown type"); + return; + } + if (p.get_type() == midea_dongle::MideaMessageType::DEVICE_CONTROL) { + ESP_LOGD(TAG, "RX: control frame"); + this->ctrl_request_ = false; + } else { + ESP_LOGD(TAG, "RX: query frame"); + } + if (this->ctrl_request_) + return; + this->cmd_frame_.set_properties(p); // copy properties from response + bool need_publish = false; + set_property(this->mode, p.get_mode(), need_publish); + set_property(this->target_temperature, p.get_target_temp(), need_publish); + set_property(this->current_temperature, p.get_indoor_temp(), need_publish); + set_property(this->fan_mode, p.get_fan_mode(), need_publish); + set_property(this->swing_mode, p.get_swing_mode(), need_publish); + if (need_publish) + this->publish_state(); + set_sensor(this->outdoor_sensor_, p.get_outdoor_temp()); + set_sensor(this->humidity_sensor_, p.get_humidity_setpoint()); +} + +void MideaAC::on_update() { + if (this->ctrl_request_) { + ESP_LOGD(TAG, "TX: control"); + this->parent_->write_frame(this->cmd_frame_); + } else { + ESP_LOGD(TAG, "TX: query"); + if (this->power_sensor_ == nullptr || this->request_num_++ % 32) + this->parent_->write_frame(this->query_frame_); + else + this->parent_->write_frame(this->power_frame_); + } +} + +void MideaAC::control(const climate::ClimateCall &call) { + if (call.get_mode().has_value() && call.get_mode().value() != this->mode) { + this->cmd_frame_.set_mode(call.get_mode().value()); + this->ctrl_request_ = true; + } + if (call.get_target_temperature().has_value() && call.get_target_temperature().value() != this->target_temperature) { + this->cmd_frame_.set_target_temp(call.get_target_temperature().value()); + this->ctrl_request_ = true; + } + if (call.get_fan_mode().has_value() && call.get_fan_mode().value() != this->fan_mode) { + this->cmd_frame_.set_fan_mode(call.get_fan_mode().value()); + this->ctrl_request_ = true; + } + if (call.get_swing_mode().has_value() && call.get_swing_mode().value() != this->swing_mode) { + this->cmd_frame_.set_swing_mode(call.get_swing_mode().value()); + this->ctrl_request_ = true; + } + if (this->ctrl_request_) { + this->cmd_frame_.set_beeper_feedback(this->beeper_feedback_); + this->cmd_frame_.finalize(); + } +} + +climate::ClimateTraits MideaAC::traits() { + auto traits = climate::ClimateTraits(); + traits.set_visual_min_temperature(17); + traits.set_visual_max_temperature(30); + traits.set_visual_temperature_step(0.5); + traits.set_supports_auto_mode(true); + traits.set_supports_cool_mode(true); + traits.set_supports_dry_mode(true); + traits.set_supports_heat_mode(true); + traits.set_supports_fan_only_mode(true); + traits.set_supports_fan_mode_auto(true); + traits.set_supports_fan_mode_low(true); + traits.set_supports_fan_mode_medium(true); + traits.set_supports_fan_mode_high(true); + traits.set_supports_swing_mode_off(true); + traits.set_supports_swing_mode_vertical(true); + traits.set_supports_swing_mode_horizontal(this->traits_swing_horizontal_); + traits.set_supports_swing_mode_both(this->traits_swing_both_); + traits.set_supports_current_temperature(true); + return traits; +} + +} // namespace midea_ac +} // namespace esphome diff --git a/esphome/components/midea_ac/midea_climate.h b/esphome/components/midea_ac/midea_climate.h new file mode 100644 index 0000000000..f08350b252 --- /dev/null +++ b/esphome/components/midea_ac/midea_climate.h @@ -0,0 +1,47 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/midea_dongle/midea_dongle.h" +#include "esphome/components/climate/climate.h" +#include "midea_frame.h" + +namespace esphome { +namespace midea_ac { + +class MideaAC : public midea_dongle::MideaAppliance, public climate::Climate, public Component { + public: + float get_setup_priority() const override { return setup_priority::LATE; } + void on_frame(const midea_dongle::Frame &frame) override; + void on_update() override; + void setup() override { this->parent_->set_appliance(this); } + void set_midea_dongle_parent(midea_dongle::MideaDongle *parent) { this->parent_ = parent; } + void set_outdoor_temperature_sensor(sensor::Sensor *sensor) { this->outdoor_sensor_ = sensor; } + void set_humidity_setpoint_sensor(sensor::Sensor *sensor) { this->humidity_sensor_ = sensor; } + void set_power_sensor(sensor::Sensor *sensor) { this->power_sensor_ = sensor; } + void set_beeper_feedback(bool state) { this->beeper_feedback_ = state; } + void set_swing_horizontal(bool state) { this->traits_swing_horizontal_ = state; } + void set_swing_both(bool state) { this->traits_swing_both_ = state; } + + protected: + /// Override control to change settings of the climate device. + void control(const climate::ClimateCall &call) override; + /// Return the traits of this controller. + climate::ClimateTraits traits() override; + + const QueryFrame query_frame_; + const PowerQueryFrame power_frame_; + CommandFrame cmd_frame_; + midea_dongle::MideaDongle *parent_{nullptr}; + sensor::Sensor *outdoor_sensor_{nullptr}; + sensor::Sensor *humidity_sensor_{nullptr}; + sensor::Sensor *power_sensor_{nullptr}; + uint8_t request_num_{0}; + bool ctrl_request_{false}; + bool beeper_feedback_{false}; + bool traits_swing_horizontal_{false}; + bool traits_swing_both_{false}; +}; + +} // namespace midea_ac +} // namespace esphome diff --git a/esphome/components/midea_ac/midea_frame.cpp b/esphome/components/midea_ac/midea_frame.cpp new file mode 100644 index 0000000000..2d9be1bdc5 --- /dev/null +++ b/esphome/components/midea_ac/midea_frame.cpp @@ -0,0 +1,160 @@ +#include "midea_frame.h" + +namespace esphome { +namespace midea_ac { + +const uint8_t QueryFrame::INIT[] = {0xAA, 0x22, 0xAC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x41, 0x00, + 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x68}; + +const uint8_t PowerQueryFrame::INIT[] = {0xAA, 0x22, 0xAC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x41, 0x21, + 0x01, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x17, 0x6A}; + +const uint8_t CommandFrame::INIT[] = {0xAA, 0x22, 0xAC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x02, 0x40, 0x00, + 0x00, 0x00, 0x7F, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +float PropertiesFrame::get_target_temp() const { + float temp = static_cast((this->pbuf_[12] & 0x0F) + 16); + if (this->pbuf_[12] & 0x10) + temp += 0.5; + return temp; +} + +void PropertiesFrame::set_target_temp(float temp) { + uint8_t tmp = static_cast(temp * 16.0) + 4; + tmp = ((tmp & 8) << 1) | (tmp >> 4); + this->pbuf_[12] &= ~0x1F; + this->pbuf_[12] |= tmp; +} + +static float i16tof(int16_t in) { return static_cast(in - 50) / 2.0; } +float PropertiesFrame::get_indoor_temp() const { return i16tof(this->pbuf_[21]); } +float PropertiesFrame::get_outdoor_temp() const { return i16tof(this->pbuf_[22]); } +float PropertiesFrame::get_humidity_setpoint() const { return static_cast(this->pbuf_[29] & 0x7F); } + +climate::ClimateMode PropertiesFrame::get_mode() const { + if (!this->get_power_()) + return climate::CLIMATE_MODE_OFF; + switch (this->pbuf_[12] >> 5) { + case MIDEA_MODE_AUTO: + return climate::CLIMATE_MODE_AUTO; + case MIDEA_MODE_COOL: + return climate::CLIMATE_MODE_COOL; + case MIDEA_MODE_DRY: + return climate::CLIMATE_MODE_DRY; + case MIDEA_MODE_HEAT: + return climate::CLIMATE_MODE_HEAT; + case MIDEA_MODE_FAN_ONLY: + return climate::CLIMATE_MODE_FAN_ONLY; + default: + return climate::CLIMATE_MODE_OFF; + } +} + +void PropertiesFrame::set_mode(climate::ClimateMode mode) { + uint8_t m; + switch (mode) { + case climate::CLIMATE_MODE_AUTO: + m = MIDEA_MODE_AUTO; + break; + case climate::CLIMATE_MODE_COOL: + m = MIDEA_MODE_COOL; + break; + case climate::CLIMATE_MODE_DRY: + m = MIDEA_MODE_DRY; + break; + case climate::CLIMATE_MODE_HEAT: + m = MIDEA_MODE_HEAT; + break; + case climate::CLIMATE_MODE_FAN_ONLY: + m = MIDEA_MODE_FAN_ONLY; + break; + default: + this->set_power_(false); + return; + } + this->set_power_(true); + this->pbuf_[12] &= ~0xE0; + this->pbuf_[12] |= m << 5; +} + +climate::ClimateFanMode PropertiesFrame::get_fan_mode() const { + switch (this->pbuf_[13]) { + case MIDEA_FAN_LOW: + return climate::CLIMATE_FAN_LOW; + case MIDEA_FAN_MEDIUM: + return climate::CLIMATE_FAN_MEDIUM; + case MIDEA_FAN_HIGH: + return climate::CLIMATE_FAN_HIGH; + default: + return climate::CLIMATE_FAN_AUTO; + } +} + +void PropertiesFrame::set_fan_mode(climate::ClimateFanMode mode) { + uint8_t m; + switch (mode) { + case climate::CLIMATE_FAN_LOW: + m = MIDEA_FAN_LOW; + break; + case climate::CLIMATE_FAN_MEDIUM: + m = MIDEA_FAN_MEDIUM; + break; + case climate::CLIMATE_FAN_HIGH: + m = MIDEA_FAN_HIGH; + break; + default: + m = MIDEA_FAN_AUTO; + break; + } + this->pbuf_[13] = m; +} + +climate::ClimateSwingMode PropertiesFrame::get_swing_mode() const { + switch (this->pbuf_[17] & 0x0F) { + case MIDEA_SWING_VERTICAL: + return climate::CLIMATE_SWING_VERTICAL; + case MIDEA_SWING_HORIZONTAL: + return climate::CLIMATE_SWING_HORIZONTAL; + case MIDEA_SWING_BOTH: + return climate::CLIMATE_SWING_BOTH; + default: + return climate::CLIMATE_SWING_OFF; + } +} + +void PropertiesFrame::set_swing_mode(climate::ClimateSwingMode mode) { + uint8_t m; + switch (mode) { + case climate::CLIMATE_SWING_VERTICAL: + m = MIDEA_SWING_VERTICAL; + break; + case climate::CLIMATE_SWING_HORIZONTAL: + m = MIDEA_SWING_HORIZONTAL; + break; + case climate::CLIMATE_SWING_BOTH: + m = MIDEA_SWING_BOTH; + break; + default: + m = MIDEA_SWING_OFF; + break; + } + this->pbuf_[17] = 0x30 | m; +} + +float PropertiesFrame::get_power_usage() const { + uint32_t power = 0; + const uint8_t *ptr = this->pbuf_ + 28; + for (uint32_t weight = 1;; weight *= 10, ptr--) { + power += (*ptr % 16) * weight; + weight *= 10; + power += (*ptr / 16) * weight; + if (weight == 100000) + return static_cast(power) * 0.1; + } +} + +} // namespace midea_ac +} // namespace esphome diff --git a/esphome/components/midea_ac/midea_frame.h b/esphome/components/midea_ac/midea_frame.h new file mode 100644 index 0000000000..e07a5bf946 --- /dev/null +++ b/esphome/components/midea_ac/midea_frame.h @@ -0,0 +1,137 @@ +#pragma once +#include "esphome/components/climate/climate.h" +#include "esphome/components/midea_dongle/midea_frame.h" + +namespace esphome { +namespace midea_ac { + +/// Enum for all modes a Midea device can be in. +enum MideaMode : uint8_t { + /// The Midea device is set to automatically change the heating/cooling cycle + MIDEA_MODE_AUTO = 1, + /// The Midea device is manually set to cool mode (not in auto mode!) + MIDEA_MODE_COOL = 2, + /// The Midea device is manually set to dry mode + MIDEA_MODE_DRY = 3, + /// The Midea device is manually set to heat mode (not in auto mode!) + MIDEA_MODE_HEAT = 4, + /// The Midea device is manually set to fan only mode + MIDEA_MODE_FAN_ONLY = 5, +}; + +/// Enum for all modes a Midea fan can be in +enum MideaFanMode : uint8_t { + /// The fan mode is set to Auto + MIDEA_FAN_AUTO = 102, + /// The fan mode is set to Low + MIDEA_FAN_LOW = 40, + /// The fan mode is set to Medium + MIDEA_FAN_MEDIUM = 60, + /// The fan mode is set to High + MIDEA_FAN_HIGH = 80, +}; + +/// Enum for all modes a Midea swing can be in +enum MideaSwingMode : uint8_t { + /// The sing mode is set to Off + MIDEA_SWING_OFF = 0b0000, + /// The fan mode is set to Both + MIDEA_SWING_BOTH = 0b1111, + /// The fan mode is set to Vertical + MIDEA_SWING_VERTICAL = 0b1100, + /// The fan mode is set to Horizontal + MIDEA_SWING_HORIZONTAL = 0b0011, +}; + +class PropertiesFrame : public midea_dongle::BaseFrame { + public: + PropertiesFrame() = delete; + PropertiesFrame(uint8_t *data) : BaseFrame(data) {} + PropertiesFrame(const Frame &frame) : BaseFrame(frame) {} + + bool has_properties() const { + return this->has_response_type(0xC0) && (this->has_type(0x03) || this->has_type(0x02)); + } + + bool has_power_info() const { return this->has_response_type(0xC1); } + + /* TARGET TEMPERATURE */ + + float get_target_temp() const; + void set_target_temp(float temp); + + /* MODE */ + climate::ClimateMode get_mode() const; + void set_mode(climate::ClimateMode mode); + + /* FAN SPEED */ + climate::ClimateFanMode get_fan_mode() const; + void set_fan_mode(climate::ClimateFanMode mode); + + /* SWING MODE */ + climate::ClimateSwingMode get_swing_mode() const; + void set_swing_mode(climate::ClimateSwingMode mode); + + /* INDOOR TEMPERATURE */ + float get_indoor_temp() const; + + /* OUTDOOR TEMPERATURE */ + float get_outdoor_temp() const; + + /* HUMIDITY SETPOINT */ + float get_humidity_setpoint() const; + + /* ECO MODE */ + bool get_eco_mode() const { return this->pbuf_[19]; } + void set_eco_mode(bool state) { this->set_bytemask_(19, 0xFF, state); } + + /* SLEEP MODE */ + bool get_sleep_mode() const { return this->pbuf_[20] & 0x01; } + void set_sleep_mode(bool state) { this->set_bytemask_(20, 0x01, state); } + + /* TURBO MODE */ + bool get_turbo_mode() const { return this->pbuf_[20] & 0x02; } + void set_turbo_mode(bool state) { this->set_bytemask_(20, 0x02, state); } + + /* POWER USAGE */ + float get_power_usage() const; + + /// Set properties from another frame + void set_properties(const PropertiesFrame &p) { memcpy(this->pbuf_ + 11, p.data() + 11, 10); } + + protected: + /* POWER */ + bool get_power_() const { return this->pbuf_[11] & 0x01; } + void set_power_(bool state) { this->set_bytemask_(11, 0x01, state); } +}; + +// Query state frame (read-only) +class QueryFrame : public midea_dongle::StaticFrame { + public: + QueryFrame() : StaticFrame(FPSTR(this->INIT)) {} + + private: + static const uint8_t PROGMEM INIT[]; +}; + +// Power query state frame (read-only) +class PowerQueryFrame : public midea_dongle::StaticFrame { + public: + PowerQueryFrame() : StaticFrame(FPSTR(this->INIT)) {} + + private: + static const uint8_t PROGMEM INIT[]; +}; + +// Command frame +class CommandFrame : public midea_dongle::StaticFrame { + public: + CommandFrame() : StaticFrame(FPSTR(this->INIT)) {} + void set_beeper_feedback(bool state) { this->set_bytemask_(11, 0x40, state); } + + private: + static const uint8_t PROGMEM INIT[]; +}; + +} // namespace midea_ac +} // namespace esphome diff --git a/esphome/components/midea_dongle/__init__.py b/esphome/components/midea_dongle/__init__.py new file mode 100644 index 0000000000..3efeb2661d --- /dev/null +++ b/esphome/components/midea_dongle/__init__.py @@ -0,0 +1,30 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import uart +from esphome.const import CONF_ID + +DEPENDENCIES = ["wifi", "uart"] +CODEOWNERS = ["@dudanov"] + +midea_dongle_ns = cg.esphome_ns.namespace("midea_dongle") +MideaDongle = midea_dongle_ns.class_("MideaDongle", cg.Component, uart.UARTDevice) + +CONF_MIDEA_DONGLE_ID = "midea_dongle_id" +CONF_STRENGTH_ICON = "strength_icon" +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(MideaDongle), + cv.Optional(CONF_STRENGTH_ICON, default=False): cv.boolean, + } + ) + .extend(cv.COMPONENT_SCHEMA) + .extend(uart.UART_DEVICE_SCHEMA) +) + + +def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + yield cg.register_component(var, config) + yield uart.register_uart_device(var, config) + cg.add(var.use_strength_icon(config[CONF_STRENGTH_ICON])) diff --git a/esphome/components/midea_dongle/midea_dongle.cpp b/esphome/components/midea_dongle/midea_dongle.cpp new file mode 100644 index 0000000000..8ddaba1cb6 --- /dev/null +++ b/esphome/components/midea_dongle/midea_dongle.cpp @@ -0,0 +1,98 @@ +#include "midea_dongle.h" +#include "esphome/core/log.h" +#include "esphome/core/helpers.h" + +namespace esphome { +namespace midea_dongle { + +static const char *TAG = "midea_dongle"; + +void MideaDongle::loop() { + while (this->available()) { + const uint8_t rx = this->read(); + if (this->idx_ <= OFFSET_LENGTH) { + if (this->idx_ == OFFSET_LENGTH) { + if (rx <= OFFSET_BODY || rx >= sizeof(this->buf_)) { + this->reset_(); + continue; + } + this->cnt_ = rx; + } else if (rx != SYNC_BYTE) { + continue; + } + } + this->buf_[this->idx_++] = rx; + if (--this->cnt_) + continue; + this->reset_(); + const BaseFrame frame(this->buf_); + ESP_LOGD(TAG, "RX: %s", frame.to_string().c_str()); + if (!frame.is_valid()) { + ESP_LOGW(TAG, "RX: frame check failed!"); + continue; + } + if (frame.get_type() == QUERY_NETWORK) { + this->notify_.set_type(QUERY_NETWORK); + this->need_notify_ = true; + continue; + } + if (this->appliance_ != nullptr) + this->appliance_->on_frame(frame); + } +} + +void MideaDongle::update() { + const bool is_conn = WiFi.isConnected(); + uint8_t wifi_strength = 0; + if (!this->rssi_timer_) { + if (is_conn) + wifi_strength = 4; + } else if (is_conn) { + if (--this->rssi_timer_) { + wifi_strength = this->notify_.get_signal_strength(); + } else { + this->rssi_timer_ = 60; + const long dbm = WiFi.RSSI(); + if (dbm > -63) + wifi_strength = 4; + else if (dbm > -75) + wifi_strength = 3; + else if (dbm > -88) + wifi_strength = 2; + else if (dbm > -100) + wifi_strength = 1; + } + } else { + this->rssi_timer_ = 1; + } + if (this->notify_.is_connected() != is_conn) { + this->notify_.set_connected(is_conn); + this->need_notify_ = true; + } + if (this->notify_.get_signal_strength() != wifi_strength) { + this->notify_.set_signal_strength(wifi_strength); + this->need_notify_ = true; + } + if (!--this->notify_timer_) { + this->notify_.set_type(NETWORK_NOTIFY); + this->need_notify_ = true; + } + if (this->need_notify_) { + ESP_LOGD(TAG, "TX: notify WiFi STA %s, signal strength %d", is_conn ? "connected" : "not connected", wifi_strength); + this->need_notify_ = false; + this->notify_timer_ = 600; + this->notify_.finalize(); + this->write_frame(this->notify_); + return; + } + if (this->appliance_ != nullptr) + this->appliance_->on_update(); +} + +void MideaDongle::write_frame(const Frame &frame) { + this->write_array(frame.data(), frame.size()); + ESP_LOGD(TAG, "TX: %s", frame.to_string().c_str()); +} + +} // namespace midea_dongle +} // namespace esphome diff --git a/esphome/components/midea_dongle/midea_dongle.h b/esphome/components/midea_dongle/midea_dongle.h new file mode 100644 index 0000000000..a7dfb9cf25 --- /dev/null +++ b/esphome/components/midea_dongle/midea_dongle.h @@ -0,0 +1,56 @@ +#pragma once +#include "esphome/core/component.h" +#include "esphome/components/wifi/wifi_component.h" +#include "esphome/components/uart/uart.h" +#include "midea_frame.h" + +namespace esphome { +namespace midea_dongle { + +enum MideaApplianceType : uint8_t { DEHUMIDIFIER = 0xA1, AIR_CONDITIONER = 0xAC, BROADCAST = 0xFF }; +enum MideaMessageType : uint8_t { + DEVICE_CONTROL = 0x02, + DEVICE_QUERY = 0x03, + NETWORK_NOTIFY = 0x0D, + QUERY_NETWORK = 0x63, +}; + +struct MideaAppliance { + /// Calling on update event + virtual void on_update() = 0; + /// Calling on frame receive event + virtual void on_frame(const Frame &frame) = 0; +}; + +class MideaDongle : public PollingComponent, public uart::UARTDevice { + public: + MideaDongle() : PollingComponent(1000) {} + float get_setup_priority() const override { return setup_priority::LATE; } + void update() override; + void loop() override; + void set_appliance(MideaAppliance *app) { this->appliance_ = app; } + void use_strength_icon(bool state) { this->rssi_timer_ = state; } + void write_frame(const Frame &frame); + + protected: + MideaAppliance *appliance_{nullptr}; + NotifyFrame notify_; + unsigned notify_timer_{1}; + // Buffer + uint8_t buf_[36]; + // Index + uint8_t idx_{0}; + // Reverse receive counter + uint8_t cnt_{2}; + uint8_t rssi_timer_{0}; + bool need_notify_{false}; + + // Reset receiver state + void reset_() { + this->idx_ = 0; + this->cnt_ = 2; + } +}; + +} // namespace midea_dongle +} // namespace esphome diff --git a/esphome/components/midea_dongle/midea_frame.cpp b/esphome/components/midea_dongle/midea_frame.cpp new file mode 100644 index 0000000000..acb3feee5f --- /dev/null +++ b/esphome/components/midea_dongle/midea_frame.cpp @@ -0,0 +1,95 @@ +#include "midea_frame.h" + +namespace esphome { +namespace midea_dongle { + +const uint8_t BaseFrame::CRC_TABLE[] = { + 0x00, 0x5E, 0xBC, 0xE2, 0x61, 0x3F, 0xDD, 0x83, 0xC2, 0x9C, 0x7E, 0x20, 0xA3, 0xFD, 0x1F, 0x41, 0x9D, 0xC3, 0x21, + 0x7F, 0xFC, 0xA2, 0x40, 0x1E, 0x5F, 0x01, 0xE3, 0xBD, 0x3E, 0x60, 0x82, 0xDC, 0x23, 0x7D, 0x9F, 0xC1, 0x42, 0x1C, + 0xFE, 0xA0, 0xE1, 0xBF, 0x5D, 0x03, 0x80, 0xDE, 0x3C, 0x62, 0xBE, 0xE0, 0x02, 0x5C, 0xDF, 0x81, 0x63, 0x3D, 0x7C, + 0x22, 0xC0, 0x9E, 0x1D, 0x43, 0xA1, 0xFF, 0x46, 0x18, 0xFA, 0xA4, 0x27, 0x79, 0x9B, 0xC5, 0x84, 0xDA, 0x38, 0x66, + 0xE5, 0xBB, 0x59, 0x07, 0xDB, 0x85, 0x67, 0x39, 0xBA, 0xE4, 0x06, 0x58, 0x19, 0x47, 0xA5, 0xFB, 0x78, 0x26, 0xC4, + 0x9A, 0x65, 0x3B, 0xD9, 0x87, 0x04, 0x5A, 0xB8, 0xE6, 0xA7, 0xF9, 0x1B, 0x45, 0xC6, 0x98, 0x7A, 0x24, 0xF8, 0xA6, + 0x44, 0x1A, 0x99, 0xC7, 0x25, 0x7B, 0x3A, 0x64, 0x86, 0xD8, 0x5B, 0x05, 0xE7, 0xB9, 0x8C, 0xD2, 0x30, 0x6E, 0xED, + 0xB3, 0x51, 0x0F, 0x4E, 0x10, 0xF2, 0xAC, 0x2F, 0x71, 0x93, 0xCD, 0x11, 0x4F, 0xAD, 0xF3, 0x70, 0x2E, 0xCC, 0x92, + 0xD3, 0x8D, 0x6F, 0x31, 0xB2, 0xEC, 0x0E, 0x50, 0xAF, 0xF1, 0x13, 0x4D, 0xCE, 0x90, 0x72, 0x2C, 0x6D, 0x33, 0xD1, + 0x8F, 0x0C, 0x52, 0xB0, 0xEE, 0x32, 0x6C, 0x8E, 0xD0, 0x53, 0x0D, 0xEF, 0xB1, 0xF0, 0xAE, 0x4C, 0x12, 0x91, 0xCF, + 0x2D, 0x73, 0xCA, 0x94, 0x76, 0x28, 0xAB, 0xF5, 0x17, 0x49, 0x08, 0x56, 0xB4, 0xEA, 0x69, 0x37, 0xD5, 0x8B, 0x57, + 0x09, 0xEB, 0xB5, 0x36, 0x68, 0x8A, 0xD4, 0x95, 0xCB, 0x29, 0x77, 0xF4, 0xAA, 0x48, 0x16, 0xE9, 0xB7, 0x55, 0x0B, + 0x88, 0xD6, 0x34, 0x6A, 0x2B, 0x75, 0x97, 0xC9, 0x4A, 0x14, 0xF6, 0xA8, 0x74, 0x2A, 0xC8, 0x96, 0x15, 0x4B, 0xA9, + 0xF7, 0xB6, 0xE8, 0x0A, 0x54, 0xD7, 0x89, 0x6B, 0x35}; + +const uint8_t NotifyFrame::INIT[] = {0xAA, 0x1F, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0D, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +bool BaseFrame::is_valid() const { return /*this->has_valid_crc_() &&*/ this->has_valid_cs_(); } + +void BaseFrame::finalize() { + this->update_crc_(); + this->update_cs_(); +} + +void BaseFrame::update_crc_() { + uint8_t crc = 0; + uint8_t *ptr = this->pbuf_ + OFFSET_BODY; + uint8_t len = this->length_() - OFFSET_BODY; + while (--len) + crc = pgm_read_byte(BaseFrame::CRC_TABLE + (crc ^ *ptr++)); + *ptr = crc; +} + +void BaseFrame::update_cs_() { + uint8_t cs = 0; + uint8_t *ptr = this->pbuf_ + OFFSET_LENGTH; + uint8_t len = this->length_(); + while (--len) + cs -= *ptr++; + *ptr = cs; +} + +bool BaseFrame::has_valid_crc_() const { + uint8_t crc = 0; + uint8_t len = this->length_() - OFFSET_BODY; + const uint8_t *ptr = this->pbuf_ + OFFSET_BODY; + for (; len; ptr++, len--) + crc = pgm_read_byte(BaseFrame::CRC_TABLE + (crc ^ *ptr)); + return !crc; +} + +bool BaseFrame::has_valid_cs_() const { + uint8_t cs = 0; + uint8_t len = this->length_(); + const uint8_t *ptr = this->pbuf_ + OFFSET_LENGTH; + for (; len; ptr++, len--) + cs -= *ptr; + return !cs; +} + +void BaseFrame::set_bytemask_(uint8_t idx, uint8_t mask, bool state) { + uint8_t *dst = this->pbuf_ + idx; + if (state) + *dst |= mask; + else + *dst &= ~mask; +} + +static char u4hex(uint8_t num) { return num + ((num < 10) ? '0' : ('A' - 10)); } + +String Frame::to_string() const { + String ret; + char buf[4]; + buf[2] = ' '; + buf[3] = '\0'; + ret.reserve(3 * 36); + const uint8_t *it = this->data(); + for (size_t i = 0; i < this->size(); i++, it++) { + buf[0] = u4hex(*it >> 4); + buf[1] = u4hex(*it & 15); + ret.concat(buf); + } + return ret; +} + +} // namespace midea_dongle +} // namespace esphome diff --git a/esphome/components/midea_dongle/midea_frame.h b/esphome/components/midea_dongle/midea_frame.h new file mode 100644 index 0000000000..ce89cc636e --- /dev/null +++ b/esphome/components/midea_dongle/midea_frame.h @@ -0,0 +1,104 @@ +#pragma once +#include "esphome/core/component.h" + +namespace esphome { +namespace midea_dongle { + +static const uint8_t OFFSET_START = 0; +static const uint8_t OFFSET_LENGTH = 1; +static const uint8_t OFFSET_APPTYPE = 2; +static const uint8_t OFFSET_BODY = 10; +static const uint8_t SYNC_BYTE = 0xAA; + +class Frame { + public: + Frame() = delete; + Frame(uint8_t *data) : pbuf_(data) {} + Frame(const Frame &frame) : pbuf_(frame.data()) {} + + // Frame buffer + uint8_t *data() const { return this->pbuf_; } + // Frame size + uint8_t size() const { return this->length_() + OFFSET_LENGTH; } + uint8_t app_type() const { return this->pbuf_[OFFSET_APPTYPE]; } + + template typename std::enable_if::value, T>::type as() const { + return T(*this); + } + String to_string() const; + + protected: + uint8_t *pbuf_; + uint8_t length_() const { return this->pbuf_[OFFSET_LENGTH]; } +}; + +class BaseFrame : public Frame { + public: + BaseFrame() = delete; + BaseFrame(uint8_t *data) : Frame(data) {} + BaseFrame(const Frame &frame) : Frame(frame) {} + + // Check for valid + bool is_valid() const; + // Prepare for sending to device + void finalize(); + uint8_t get_type() const { return this->pbuf_[9]; } + void set_type(uint8_t value) { this->pbuf_[9] = value; } + bool has_response_type(uint8_t type) const { return this->resp_type_() == type; } + bool has_type(uint8_t type) const { return this->get_type() == type; } + + protected: + static const uint8_t PROGMEM CRC_TABLE[256]; + void set_bytemask_(uint8_t idx, uint8_t mask, bool state); + uint8_t resp_type_() const { return this->pbuf_[OFFSET_BODY]; } + bool has_valid_crc_() const; + bool has_valid_cs_() const; + void update_crc_(); + void update_cs_(); +}; + +template class StaticFrame : public T { + public: + // Default constructor + StaticFrame() : T(this->buf_) {} + // Copy constructor + StaticFrame(const Frame &src) : T(this->buf_) { + if (src.length_() < sizeof(this->buf_)) { + memcpy(this->buf_, src.data(), src.length_() + OFFSET_LENGTH); + } + } + // Constructor for RAM data + StaticFrame(const uint8_t *src) : T(this->buf_) { + const uint8_t len = src[OFFSET_LENGTH]; + if (len < sizeof(this->buf_)) { + memcpy(this->buf_, src, len + OFFSET_LENGTH); + } + } + // Constructor for PROGMEM data + StaticFrame(const __FlashStringHelper *pgm) : T(this->buf_) { + const uint8_t *src = reinterpret_cast(pgm); + const uint8_t len = pgm_read_byte(src + OFFSET_LENGTH); + if (len < sizeof(this->buf_)) { + memcpy_P(this->buf_, src, len + OFFSET_LENGTH); + } + } + + protected: + uint8_t buf_[buf_size]; +}; + +// Device network notification frame +class NotifyFrame : public midea_dongle::StaticFrame { + public: + NotifyFrame() : StaticFrame(FPSTR(NotifyFrame::INIT)) {} + void set_signal_strength(uint8_t value) { this->pbuf_[12] = value; } + uint8_t get_signal_strength() const { return this->pbuf_[12]; } + void set_connected(bool state) { this->pbuf_[18] = state ? 0 : 1; } + bool is_connected() const { return !this->pbuf_[18]; } + + private: + static const uint8_t PROGMEM INIT[]; +}; + +} // namespace midea_dongle +} // namespace esphome diff --git a/tests/test1.yaml b/tests/test1.yaml index b50d5eeef7..a064bc0ed8 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -1475,6 +1475,23 @@ climate: name: Toshiba Climate - platform: hitachi_ac344 name: Hitachi Climate + - platform: midea_ac + visual: + min_temperature: 18 °C + max_temperature: 25 °C + temperature_step: 0.1 °C + name: "Electrolux EACS" + beeper: true + outdoor_temperature: + name: "Temp" + power_usage: + name: "Power" + humidity_setpoint: + name: "Hum" + +midea_dongle: + uart_id: uart0 + strength_icon: true switch: - platform: gpio From a96b6e70029c8ce35ed3810f0c9077cd2687d23a Mon Sep 17 00:00:00 2001 From: SenexCrenshaw <35600301+SenexCrenshaw@users.noreply.github.com> Date: Wed, 17 Mar 2021 20:54:58 -0400 Subject: [PATCH 093/111] SPI transfer fix. Use write when no miso pin is set (#1563) --- esphome/components/spi/spi.h | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/esphome/components/spi/spi.h b/esphome/components/spi/spi.h index 64364d70c9..05c64150cc 100644 --- a/esphome/components/spi/spi.h +++ b/esphome/components/spi/spi.h @@ -112,20 +112,34 @@ class SPIComponent : public Component { template uint8_t transfer_byte(uint8_t data) { - if (this->hw_spi_ != nullptr) { - return this->hw_spi_->transfer(data); + if (this->miso_ != nullptr) { + if (this->hw_spi_ != nullptr) { + return this->hw_spi_->transfer(data); + } else { + return this->transfer_(data); + } } - return this->transfer_(data); + this->write_byte(data); + return 0; } template void transfer_array(uint8_t *data, size_t length) { if (this->hw_spi_ != nullptr) { - this->hw_spi_->transfer(data, length); + if (this->miso_ != nullptr) { + this->hw_spi_->transfer(data, length); + } else { + this->hw_spi_->writeBytes(data, length); + } return; } - for (size_t i = 0; i < length; i++) { - data[i] = this->transfer_byte(data[i]); + + if (this->miso_ != nullptr) { + for (size_t i = 0; i < length; i++) { + data[i] = this->transfer_byte(data[i]); + } + } else { + this->write_array(data, length); } } From b5b203697104241e2e8d58ad80d6be4e56d7cab0 Mon Sep 17 00:00:00 2001 From: SenexCrenshaw <35600301+SenexCrenshaw@users.noreply.github.com> Date: Wed, 17 Mar 2021 20:59:47 -0400 Subject: [PATCH 094/111] 8266 hardware spi enable with just 3 pins (#1617) --- esphome/components/spi/spi.cpp | 2 +- esphome/components/spi/spi.h | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/esphome/components/spi/spi.cpp b/esphome/components/spi/spi.cpp index ea6f8d68f6..d2850b8e7d 100644 --- a/esphome/components/spi/spi.cpp +++ b/esphome/components/spi/spi.cpp @@ -38,7 +38,7 @@ void SPIComponent::setup() { #ifdef ARDUINO_ARCH_ESP8266 if (clk_pin == 6 && miso_pin == 7 && mosi_pin == 8) { // pass - } else if (clk_pin == 14 && miso_pin == 12 && mosi_pin == 13) { + } else if (clk_pin == 14 && (!has_miso || miso_pin == 12) && (!has_mosi || mosi_pin == 13)) { // pass } else { use_hw_spi = false; diff --git a/esphome/components/spi/spi.h b/esphome/components/spi/spi.h index 05c64150cc..a4a2e11def 100644 --- a/esphome/components/spi/spi.h +++ b/esphome/components/spi/spi.h @@ -98,6 +98,30 @@ class SPIComponent : public Component { this->transfer_(data); } + template + void write_byte16(const uint16_t data) { + if (this->hw_spi_ != nullptr) { + this->hw_spi_->write16(data); + return; + } + + this->write_byte(data >> 8); + this->write_byte(data); + } + + template + void write_array16(const uint16_t *data, size_t length) { + if (this->hw_spi_ != nullptr) { + for (size_t i = 0; i < length; i++) { + this->hw_spi_->write16(data[i]); + } + return; + } + for (size_t i = 0; i < length; i++) { + this->write_byte16(data[i]); + } + } + template void write_array(const uint8_t *data, size_t length) { if (this->hw_spi_ != nullptr) { @@ -222,6 +246,14 @@ class SPIDevice { return this->parent_->template write_byte(data); } + void write_byte16(uint8_t data) { + return this->parent_->template write_byte16(data); + } + + void write_array16(const uint16_t *data, size_t length) { + this->parent_->template write_array16(data, length); + } + void write_array(const uint8_t *data, size_t length) { this->parent_->template write_array(data, length); } From f63f9168ff9fc881fa5bf1b8cd9f77ac334257f1 Mon Sep 17 00:00:00 2001 From: Mike Ryan Date: Thu, 18 Mar 2021 01:08:50 -0500 Subject: [PATCH 095/111] Add addressable_light display platform (#1272) --- CODEOWNERS | 1 + .../components/addressable_light/__init__.py | 0 .../addressable_light_display.cpp | 67 +++++++++++++++++++ .../addressable_light_display.h | 59 ++++++++++++++++ .../components/addressable_light/display.py | 63 +++++++++++++++++ esphome/const.py | 3 + tests/test4.yaml | 33 +++++++++ 7 files changed, 226 insertions(+) create mode 100644 esphome/components/addressable_light/__init__.py create mode 100644 esphome/components/addressable_light/addressable_light_display.cpp create mode 100644 esphome/components/addressable_light/addressable_light_display.h create mode 100644 esphome/components/addressable_light/display.py diff --git a/CODEOWNERS b/CODEOWNERS index 47cd5f59ca..d6f90fbfe2 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -13,6 +13,7 @@ esphome/core/* @esphome/core # Integrations esphome/components/ac_dimmer/* @glmnet esphome/components/adc/* @esphome/core +esphome/components/addressable_light/* @justfalter esphome/components/animation/* @syndlex esphome/components/api/* @OttoWinter esphome/components/async_tcp/* @OttoWinter diff --git a/esphome/components/addressable_light/__init__.py b/esphome/components/addressable_light/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/addressable_light/addressable_light_display.cpp b/esphome/components/addressable_light/addressable_light_display.cpp new file mode 100644 index 0000000000..2e94e9e082 --- /dev/null +++ b/esphome/components/addressable_light/addressable_light_display.cpp @@ -0,0 +1,67 @@ +#include "addressable_light_display.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace addressable_light { + +static const char* TAG = "addressable_light.display"; + +int AddressableLightDisplay::get_width_internal() { return this->width_; } +int AddressableLightDisplay::get_height_internal() { return this->height_; } + +void AddressableLightDisplay::setup() { + this->addressable_light_buffer_.resize(this->width_ * this->height_, {0, 0, 0, 0}); +} + +void AddressableLightDisplay::update() { + if (!this->enabled_) + return; + + this->do_update_(); + this->display(); +} + +void AddressableLightDisplay::display() { + bool dirty = false; + uint8_t old_r, old_g, old_b, old_w; + Color* c; + + for (uint32_t offset = 0; offset < this->addressable_light_buffer_.size(); offset++) { + c = &(this->addressable_light_buffer_[offset]); + + light::ESPColorView pixel = (*this->light_)[offset]; + + // Track the original values for the pixel view. If it has changed updating, then + // we trigger a redraw. Avoiding redraws == avoiding flicker! + old_r = pixel.get_red(); + old_g = pixel.get_green(); + old_b = pixel.get_blue(); + old_w = pixel.get_white(); + + pixel.set_rgbw(c->r, c->g, c->b, c->w); + + // If the actual value of the pixel changed, then schedule a redraw. + if (pixel.get_red() != old_r || pixel.get_green() != old_g || pixel.get_blue() != old_b || + pixel.get_white() != old_w) { + dirty = true; + } + } + + if (dirty) { + this->light_->schedule_show(); + } +} + +void HOT AddressableLightDisplay::draw_absolute_pixel_internal(int x, int y, Color color) { + if (x >= this->get_width_internal() || x < 0 || y >= this->get_height_internal() || y < 0) + return; + + if (this->pixel_mapper_f_.has_value()) { + // Params are passed by reference, so they may be modified in call. + this->addressable_light_buffer_[(*this->pixel_mapper_f_)(x, y)] = color; + } else { + this->addressable_light_buffer_[y * this->get_width_internal() + x] = color; + } +} +} // namespace addressable_light +} // namespace esphome diff --git a/esphome/components/addressable_light/addressable_light_display.h b/esphome/components/addressable_light/addressable_light_display.h new file mode 100644 index 0000000000..163faf27b0 --- /dev/null +++ b/esphome/components/addressable_light/addressable_light_display.h @@ -0,0 +1,59 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/core/color.h" +#include "esphome/components/display/display_buffer.h" +#include "esphome/components/light/addressable_light.h" + +namespace esphome { +namespace addressable_light { + +class AddressableLightDisplay : public display::DisplayBuffer, public PollingComponent { + public: + light::AddressableLight *get_light() const { return this->light_; } + + void set_width(int32_t width) { width_ = width; } + void set_height(int32_t height) { height_ = height; } + void set_light(light::LightState *state) { + light_state_ = state; + light_ = static_cast(state->get_output()); + } + void set_enabled(bool enabled) { + if (light_state_) { + if (enabled_ && !enabled) { // enabled -> disabled + // - Tell the parent light to refresh, effectively wiping the display. Also + // restores the previous effect (if any). + light_state_->make_call().set_effect(this->last_effect_).perform(); + + } else if (!enabled_ && enabled) { // disabled -> enabled + // - Save the current effect. + this->last_effect_ = light_state_->get_effect_name(); + // - Disable any current effect. + light_state_->make_call().set_effect(0).perform(); + } + } + enabled_ = enabled; + } + bool get_enabled() { return enabled_; } + + void set_pixel_mapper(std::function &&pixel_mapper_f) { this->pixel_mapper_f_ = pixel_mapper_f; } + void setup() override; + void display(); + + protected: + int get_width_internal() override; + int get_height_internal() override; + void draw_absolute_pixel_internal(int x, int y, Color color) override; + void update() override; + + light::LightState *light_state_; + light::AddressableLight *light_; + bool enabled_{true}; + int32_t width_; + int32_t height_; + std::vector addressable_light_buffer_; + optional last_effect_; + optional> pixel_mapper_f_; +}; +} // namespace addressable_light +} // namespace esphome diff --git a/esphome/components/addressable_light/display.py b/esphome/components/addressable_light/display.py new file mode 100644 index 0000000000..e5d3ca3034 --- /dev/null +++ b/esphome/components/addressable_light/display.py @@ -0,0 +1,63 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import display, light +from esphome.const import ( + CONF_ID, + CONF_LAMBDA, + CONF_PAGES, + CONF_ADDRESSABLE_LIGHT_ID, + CONF_HEIGHT, + CONF_WIDTH, + CONF_UPDATE_INTERVAL, + CONF_PIXEL_MAPPER, +) + +CODEOWNERS = ["@justfalter"] + +addressable_light_ns = cg.esphome_ns.namespace("addressable_light") +AddressableLightDisplay = addressable_light_ns.class_( + "AddressableLightDisplay", display.DisplayBuffer, cg.PollingComponent +) + +CONFIG_SCHEMA = cv.All( + display.FULL_DISPLAY_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(AddressableLightDisplay), + cv.Required(CONF_ADDRESSABLE_LIGHT_ID): cv.use_id( + light.AddressableLightState + ), + cv.Required(CONF_WIDTH): cv.positive_int, + cv.Required(CONF_HEIGHT): cv.positive_int, + cv.Optional( + CONF_UPDATE_INTERVAL, default="16ms" + ): cv.positive_time_period_milliseconds, + cv.Optional(CONF_PIXEL_MAPPER): cv.returning_lambda, + } + ), + cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA), +) + + +def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + wrapped_light = yield cg.get_variable(config[CONF_ADDRESSABLE_LIGHT_ID]) + cg.add(var.set_width(config[CONF_WIDTH])) + cg.add(var.set_height(config[CONF_HEIGHT])) + cg.add(var.set_light(wrapped_light)) + + yield cg.register_component(var, config) + yield display.register_display(var, config) + + if CONF_PIXEL_MAPPER in config: + pixel_mapper_template_ = yield cg.process_lambda( + config[CONF_PIXEL_MAPPER], + [(int, "x"), (int, "y")], + return_type=cg.int_, + ) + cg.add(var.set_pixel_mapper(pixel_mapper_template_)) + + if CONF_LAMBDA in config: + lambda_ = yield cg.process_lambda( + config[CONF_LAMBDA], [(display.DisplayBufferRef, "it")], return_type=cg.void + ) + cg.add(var.set_writer(lambda_)) diff --git a/esphome/const.py b/esphome/const.py index 232449e647..7842f8634c 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -53,6 +53,7 @@ CONF_ACCURACY = "accuracy" CONF_ACCURACY_DECIMALS = "accuracy_decimals" CONF_ACTION_ID = "action_id" CONF_ADDRESS = "address" +CONF_ADDRESSABLE_LIGHT_ID = "addressable_light_id" CONF_ALPHA = "alpha" CONF_AND = "and" CONF_AP = "ap" @@ -223,6 +224,7 @@ CONF_HEARTBEAT = "heartbeat" CONF_HEAT_ACTION = "heat_action" CONF_HEAT_MODE = "heat_mode" CONF_HEATER = "heater" +CONF_HEIGHT = "height" CONF_HIDDEN = "hidden" CONF_HIDE_TIMESTAMP = "hide_timestamp" CONF_HIGH = "high" @@ -388,6 +390,7 @@ CONF_PIN_B = "pin_b" CONF_PIN_C = "pin_c" CONF_PIN_D = "pin_d" CONF_PINS = "pins" +CONF_PIXEL_MAPPER = "pixel_mapper" CONF_PLATFORM = "platform" CONF_PLATFORMIO_OPTIONS = "platformio_options" CONF_PM_1_0 = "pm_1_0" diff --git a/tests/test4.yaml b/tests/test4.yaml index bfeff01e93..1f6eb5442b 100644 --- a/tests/test4.yaml +++ b/tests/test4.yaml @@ -99,3 +99,36 @@ switch: - platform: tuya id: tuya_switch switch_datapoint: 1 + +light: + - platform: fastled_clockless + id: led_matrix_32x8 + name: "led_matrix_32x8" + chipset: WS2812B + pin: GPIO15 + num_leds: 256 + rgb_order: GRB + default_transition_length: 0s + color_correct: [50%, 50%, 50%] + +display: + - platform: addressable_light + id: led_matrix_32x8_display + addressable_light_id: led_matrix_32x8 + width: 32 + height: 8 + pixel_mapper: |- + if (x % 2 == 0) { + return (x * 8) + y; + } + return (x * 8) + (7 - y); + lambda: |- + Color red = Color(0xFF0000); + Color green = Color(0x00FF00); + Color blue = Color(0x0000FF); + it.rectangle(0, 0, it.get_width(), it.get_height(), red); + it.rectangle(1, 1, it.get_width()-2, it.get_height()-2, green); + it.rectangle(2, 2, it.get_width()-4, it.get_height()-4, blue); + it.rectangle(3, 3, it.get_width()-6, it.get_height()-6, red); + rotation: 0° + update_interval: 16ms From a77784a6da8ef60e065db96d769092c759faf6e4 Mon Sep 17 00:00:00 2001 From: Steve Baxter Date: Fri, 19 Mar 2021 08:16:27 +0000 Subject: [PATCH 096/111] Implement pulse_meter as an improvement on pulse_counter and pulse_width for meters (#1434) --- CODEOWNERS | 1 + esphome/components/pulse_meter/__init__.py | 0 .../pulse_meter/pulse_meter_sensor.cpp | 87 +++++++++++++++++++ .../pulse_meter/pulse_meter_sensor.h | 42 +++++++++ esphome/components/pulse_meter/sensor.py | 58 +++++++++++++ script/build_codeowners.py | 2 +- tests/test1.yaml | 7 ++ 7 files changed, 196 insertions(+), 1 deletion(-) create mode 100644 esphome/components/pulse_meter/__init__.py create mode 100644 esphome/components/pulse_meter/pulse_meter_sensor.cpp create mode 100644 esphome/components/pulse_meter/pulse_meter_sensor.h create mode 100644 esphome/components/pulse_meter/sensor.py diff --git a/CODEOWNERS b/CODEOWNERS index d6f90fbfe2..0a1f2b3ed2 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -67,6 +67,7 @@ esphome/components/pn532/* @OttoWinter @jesserockz esphome/components/pn532_i2c/* @OttoWinter @jesserockz esphome/components/pn532_spi/* @OttoWinter @jesserockz esphome/components/power_supply/* @esphome/core +esphome/components/pulse_meter/* @stevebaxter esphome/components/rc522/* @glmnet esphome/components/rc522_i2c/* @glmnet esphome/components/rc522_spi/* @glmnet diff --git a/esphome/components/pulse_meter/__init__.py b/esphome/components/pulse_meter/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/pulse_meter/pulse_meter_sensor.cpp b/esphome/components/pulse_meter/pulse_meter_sensor.cpp new file mode 100644 index 0000000000..8cf341c8a1 --- /dev/null +++ b/esphome/components/pulse_meter/pulse_meter_sensor.cpp @@ -0,0 +1,87 @@ +#include "pulse_meter_sensor.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace pulse_meter { + +static const char *TAG = "pulse_meter"; + +void PulseMeterSensor::setup() { + this->pin_->setup(); + this->isr_pin_ = pin_->to_isr(); + this->pin_->attach_interrupt(PulseMeterSensor::gpio_intr, this, CHANGE); + + this->last_detected_edge_us_ = 0; + this->last_valid_edge_us_ = 0; +} + +void PulseMeterSensor::loop() { + const uint32_t now = micros(); + + // If we've exceeded our timeout interval without receiving any pulses, assume 0 pulses/min until + // we get at least two valid pulses. + const uint32_t time_since_valid_edge_us = now - this->last_valid_edge_us_; + if ((this->last_valid_edge_us_ != 0) && (time_since_valid_edge_us > this->timeout_us_)) { + ESP_LOGD(TAG, "No pulse detected for %us, assuming 0 pulses/min", time_since_valid_edge_us / 1000000); + this->last_detected_edge_us_ = 0; + this->last_valid_edge_us_ = 0; + this->pulse_width_us_ = 0; + } + + // We quantize our pulse widths to 1 ms to avoid unnecessary jitter + const uint32_t pulse_width_ms = this->pulse_width_us_ / 1000; + if (this->pulse_width_dedupe_.next(pulse_width_ms)) { + if (pulse_width_ms == 0) { + // Treat 0 pulse width as 0 pulses/min (normally because we've not detected any pulses for a while) + this->publish_state(0); + } else { + // Calculate pulses/min from the pulse width in ms + this->publish_state((60.0 * 1000.0) / pulse_width_ms); + } + } + + if (this->total_sensor_ != nullptr) { + const uint32_t total = this->total_pulses_; + if (this->total_dedupe_.next(total)) { + this->total_sensor_->publish_state(total); + } + } +} + +void PulseMeterSensor::dump_config() { + LOG_SENSOR("", "Pulse Meter", this); + LOG_PIN(" Pin: ", this->pin_); + ESP_LOGCONFIG(TAG, " Filtering pulses shorter than %u µs", this->filter_us_); + ESP_LOGCONFIG(TAG, " Assuming 0 pulses/min after not receiving a pulse for %us", this->timeout_us_ / 1000000); +} + +void ICACHE_RAM_ATTR PulseMeterSensor::gpio_intr(PulseMeterSensor *sensor) { + // This is an interrupt handler - we can't call any virtual method from this method + + // Get the current time before we do anything else so the measurements are consistent + const uint32_t now = micros(); + + // We only look at rising edges + if (!sensor->isr_pin_->digital_read()) { + return; + } + + // Ignore the first detected pulse (we need at least two pulses to measure the width) + if (sensor->last_detected_edge_us_ != 0) { + // Check to see if we should filter this edge out + if ((now - sensor->last_detected_edge_us_) >= sensor->filter_us_) { + // Don't measure the first valid pulse (we need at least two pulses to measure the width) + if (sensor->last_valid_edge_us_ != 0) { + sensor->pulse_width_us_ = (now - sensor->last_valid_edge_us_); + } + + sensor->total_pulses_++; + sensor->last_valid_edge_us_ = now; + } + } + + sensor->last_detected_edge_us_ = now; +} + +} // namespace pulse_meter +} // namespace esphome diff --git a/esphome/components/pulse_meter/pulse_meter_sensor.h b/esphome/components/pulse_meter/pulse_meter_sensor.h new file mode 100644 index 0000000000..7d3adbbbcb --- /dev/null +++ b/esphome/components/pulse_meter/pulse_meter_sensor.h @@ -0,0 +1,42 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/core/esphal.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/core/helpers.h" + +namespace esphome { +namespace pulse_meter { + +class PulseMeterSensor : public sensor::Sensor, public Component { + public: + void set_pin(GPIOPin *pin) { this->pin_ = pin; } + void set_filter_us(uint32_t filter) { this->filter_us_ = filter; } + void set_timeout_us(uint32_t timeout) { this->timeout_us_ = timeout; } + void set_total_sensor(sensor::Sensor *sensor) { this->total_sensor_ = sensor; } + + void setup() override; + void loop() override; + float get_setup_priority() const override { return setup_priority::DATA; } + void dump_config() override; + + protected: + static void gpio_intr(PulseMeterSensor *sensor); + + GPIOPin *pin_ = nullptr; + ISRInternalGPIOPin *isr_pin_; + uint32_t filter_us_ = 0; + uint32_t timeout_us_ = 1000000UL * 60UL * 5UL; + sensor::Sensor *total_sensor_ = nullptr; + + Deduplicator pulse_width_dedupe_; + Deduplicator total_dedupe_; + + volatile uint32_t last_detected_edge_us_ = 0; + volatile uint32_t last_valid_edge_us_ = 0; + volatile uint32_t pulse_width_us_ = 0; + volatile uint32_t total_pulses_ = 0; +}; + +} // namespace pulse_meter +} // namespace esphome diff --git a/esphome/components/pulse_meter/sensor.py b/esphome/components/pulse_meter/sensor.py new file mode 100644 index 0000000000..a97f276a96 --- /dev/null +++ b/esphome/components/pulse_meter/sensor.py @@ -0,0 +1,58 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome import pins +from esphome.components import sensor +from esphome.const import CONF_ID, CONF_INTERNAL_FILTER, \ + CONF_PIN, CONF_NUMBER, CONF_TIMEOUT, CONF_TOTAL, \ + ICON_PULSE, UNIT_PULSES, UNIT_PULSES_PER_MINUTE +from esphome.core import CORE + +CODEOWNERS = ['@stevebaxter'] + +pulse_meter_ns = cg.esphome_ns.namespace('pulse_meter') + +PulseMeterSensor = pulse_meter_ns.class_('PulseMeterSensor', + sensor.Sensor, + cg.Component) + + +def validate_internal_filter(value): + return cv.positive_time_period_microseconds(value) + + +def validate_timeout(value): + value = cv.positive_time_period_microseconds(value) + if value.total_minutes > 70: + raise cv.Invalid("Maximum timeout is 70 minutes") + return value + + +def validate_pulse_meter_pin(value): + value = pins.internal_gpio_input_pin_schema(value) + if CORE.is_esp8266 and value[CONF_NUMBER] >= 16: + raise cv.Invalid("Pins GPIO16 and GPIO17 cannot be used as pulse counters on ESP8266.") + return value + + +CONFIG_SCHEMA = sensor.sensor_schema(UNIT_PULSES_PER_MINUTE, ICON_PULSE, 2).extend({ + cv.GenerateID(): cv.declare_id(PulseMeterSensor), + cv.Required(CONF_PIN): validate_pulse_meter_pin, + cv.Optional(CONF_INTERNAL_FILTER, default='13us'): validate_internal_filter, + cv.Optional(CONF_TIMEOUT, default='5min'): validate_timeout, + cv.Optional(CONF_TOTAL): sensor.sensor_schema(UNIT_PULSES, ICON_PULSE, 0) +}) + + +def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + yield cg.register_component(var, config) + yield sensor.register_sensor(var, config) + + pin = yield cg.gpio_pin_expression(config[CONF_PIN]) + cg.add(var.set_pin(pin)) + cg.add(var.set_filter_us(config[CONF_INTERNAL_FILTER])) + cg.add(var.set_timeout_us(config[CONF_TIMEOUT])) + + if CONF_TOTAL in config: + sens = yield sensor.new_sensor(config[CONF_TOTAL]) + cg.add(var.set_total_sensor(sens)) diff --git a/script/build_codeowners.py b/script/build_codeowners.py index a1e8d69046..2ee7521b91 100755 --- a/script/build_codeowners.py +++ b/script/build_codeowners.py @@ -36,7 +36,7 @@ esphome/core/* @esphome/core parts = [BASE] -# Fake some diretory so that get_component works +# Fake some directory so that get_component works CORE.config_path = str(root) codeowners = defaultdict(list) diff --git a/tests/test1.yaml b/tests/test1.yaml index a064bc0ed8..f5354231b4 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -626,6 +626,13 @@ sensor: falling_edge: DECREMENT internal_filter: 13us update_interval: 15s + - platform: pulse_meter + name: 'Pulse Meter' + pin: GPIO12 + internal_filter: 100ms + timeout: 2 min + total: + name: 'Pulse Meter Total' - platform: rotary_encoder name: 'Rotary Encoder' id: rotary_encoder1 From e5b45b6b4ba1c7809c8e4c1875f757da0fb98f60 Mon Sep 17 00:00:00 2001 From: DrZoid Date: Fri, 19 Mar 2021 09:19:34 +0100 Subject: [PATCH 097/111] e131: fix issue 1579: limitation of maximum light count (#1619) --- esphome/components/e131/e131_addressable_light_effect.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/esphome/components/e131/e131_addressable_light_effect.cpp b/esphome/components/e131/e131_addressable_light_effect.cpp index 3283f71931..5bf28f0f11 100644 --- a/esphome/components/e131/e131_addressable_light_effect.cpp +++ b/esphome/components/e131/e131_addressable_light_effect.cpp @@ -53,7 +53,8 @@ bool E131AddressableLightEffect::process_(int universe, const E131Packet &packet int output_offset = (universe - first_universe_) * get_lights_per_universe(); // limit amount of lights per universe and received - int output_end = std::min(it->size(), std::min(output_offset + get_lights_per_universe(), packet.count - 1)); + int output_end = + std::min(it->size(), std::min(output_offset + get_lights_per_universe(), output_offset + packet.count - 1)); auto input_data = packet.values + 1; ESP_LOGV(TAG, "Applying data for '%s' on %d universe, for %d-%d.", get_name().c_str(), universe, output_offset, From 251240cc90bed46527772c0537bac0b213bb5c43 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 19 Mar 2021 21:29:26 +1300 Subject: [PATCH 098/111] Bump platformio from 5.1.0 to 5.1.1 (#1618) Bumps [platformio](https://github.com/platformio/platformio) from 5.1.0 to 5.1.1. - [Release notes](https://github.com/platformio/platformio/releases) - [Changelog](https://github.com/platformio/platformio-core/blob/develop/HISTORY.rst) - [Commits](https://github.com/platformio/platformio/compare/v5.1.0...v5.1.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index be7c46f98b..ac8c4a2fd5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,6 +9,6 @@ tzlocal==2.1 pytz==2021.1 pyserial==3.5 ifaddr==0.1.7 -platformio==5.1.0 +platformio==5.1.1 esptool==2.8 click==7.1.2 From dedf343bd5b4d1b83d9d13196acd26221e42a75b Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 19 Mar 2021 21:40:11 +1300 Subject: [PATCH 099/111] Fix pulse-meter with device_class and black (#1621) --- esphome/components/pulse_meter/sensor.py | 49 ++++++++++++++++-------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/esphome/components/pulse_meter/sensor.py b/esphome/components/pulse_meter/sensor.py index a97f276a96..37827b735d 100644 --- a/esphome/components/pulse_meter/sensor.py +++ b/esphome/components/pulse_meter/sensor.py @@ -2,18 +2,27 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins from esphome.components import sensor -from esphome.const import CONF_ID, CONF_INTERNAL_FILTER, \ - CONF_PIN, CONF_NUMBER, CONF_TIMEOUT, CONF_TOTAL, \ - ICON_PULSE, UNIT_PULSES, UNIT_PULSES_PER_MINUTE +from esphome.const import ( + CONF_ID, + CONF_INTERNAL_FILTER, + CONF_PIN, + CONF_NUMBER, + CONF_TIMEOUT, + CONF_TOTAL, + ICON_PULSE, + UNIT_PULSES, + UNIT_PULSES_PER_MINUTE, + DEVICE_CLASS_EMPTY, +) from esphome.core import CORE -CODEOWNERS = ['@stevebaxter'] +CODEOWNERS = ["@stevebaxter"] -pulse_meter_ns = cg.esphome_ns.namespace('pulse_meter') +pulse_meter_ns = cg.esphome_ns.namespace("pulse_meter") -PulseMeterSensor = pulse_meter_ns.class_('PulseMeterSensor', - sensor.Sensor, - cg.Component) +PulseMeterSensor = pulse_meter_ns.class_( + "PulseMeterSensor", sensor.Sensor, cg.Component +) def validate_internal_filter(value): @@ -30,17 +39,25 @@ def validate_timeout(value): def validate_pulse_meter_pin(value): value = pins.internal_gpio_input_pin_schema(value) if CORE.is_esp8266 and value[CONF_NUMBER] >= 16: - raise cv.Invalid("Pins GPIO16 and GPIO17 cannot be used as pulse counters on ESP8266.") + raise cv.Invalid( + "Pins GPIO16 and GPIO17 cannot be used as pulse counters on ESP8266." + ) return value -CONFIG_SCHEMA = sensor.sensor_schema(UNIT_PULSES_PER_MINUTE, ICON_PULSE, 2).extend({ - cv.GenerateID(): cv.declare_id(PulseMeterSensor), - cv.Required(CONF_PIN): validate_pulse_meter_pin, - cv.Optional(CONF_INTERNAL_FILTER, default='13us'): validate_internal_filter, - cv.Optional(CONF_TIMEOUT, default='5min'): validate_timeout, - cv.Optional(CONF_TOTAL): sensor.sensor_schema(UNIT_PULSES, ICON_PULSE, 0) -}) +CONFIG_SCHEMA = sensor.sensor_schema( + UNIT_PULSES_PER_MINUTE, ICON_PULSE, 2, DEVICE_CLASS_EMPTY +).extend( + { + cv.GenerateID(): cv.declare_id(PulseMeterSensor), + cv.Required(CONF_PIN): validate_pulse_meter_pin, + cv.Optional(CONF_INTERNAL_FILTER, default="13us"): validate_internal_filter, + cv.Optional(CONF_TIMEOUT, default="5min"): validate_timeout, + cv.Optional(CONF_TOTAL): sensor.sensor_schema( + UNIT_PULSES, ICON_PULSE, 0, DEVICE_CLASS_EMPTY + ), + } +) def to_code(config): From 818a7f1c78442273ed1dd91b3fc094956f039dc7 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Fri, 19 Mar 2021 05:40:05 -0500 Subject: [PATCH 100/111] Declare Color objects in main.cpp (#1395) --- esphome/codegen.py | 1 + esphome/components/color/__init__.py | 2 +- esphome/cpp_generator.py | 23 +++++++++++++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/esphome/codegen.py b/esphome/codegen.py index 57316ab4fa..90f59f75bc 100644 --- a/esphome/codegen.py +++ b/esphome/codegen.py @@ -21,6 +21,7 @@ from esphome.cpp_generator import ( # noqa progmem_array, statement, variable, + new_variable, Pvariable, new_Pvariable, add, diff --git a/esphome/components/color/__init__.py b/esphome/components/color/__init__.py index 7c044040ee..6712d078a4 100644 --- a/esphome/components/color/__init__.py +++ b/esphome/components/color/__init__.py @@ -51,7 +51,7 @@ def to_code(config): elif CONF_WHITE_INT in config: w = config[CONF_WHITE_INT] - cg.variable( + cg.new_variable( config[CONF_ID], cg.StructInitializer(ColorStruct, ("r", r), ("g", g), ("b", b), ("w", w)), ) diff --git a/esphome/cpp_generator.py b/esphome/cpp_generator.py index 754ce229a9..999b252dde 100644 --- a/esphome/cpp_generator.py +++ b/esphome/cpp_generator.py @@ -448,6 +448,29 @@ def variable(id_: ID, rhs: SafeExpType, type_: "MockObj" = None) -> "MockObj": return obj +def new_variable(id_: ID, rhs: SafeExpType, type_: "MockObj" = None) -> "MockObj": + """Declare and define a new variable, not pointer type, in the code generation. + + :param id_: The ID used to declare the variable. + :param rhs: The expression to place on the right hand side of the assignment. + :param type_: Manually define a type for the variable, only use this when it's not possible + to do so during config validation phase (for example because of template arguments). + + :returns The new variable as a MockObj. + """ + assert isinstance(id_, ID) + rhs = safe_exp(rhs) + obj = MockObj(id_, ".") + if type_ is not None: + id_.type = type_ + decl = VariableDeclarationExpression(id_.type, "", id_) + CORE.add_global(decl) + assignment = AssignmentExpression(None, "", id_, rhs, obj) + CORE.add(assignment) + CORE.register_variable(id_, obj) + return obj + + def Pvariable(id_: ID, rhs: SafeExpType, type_: "MockObj" = None) -> "MockObj": """Declare a new pointer variable in the code generation. From 91898cb814332936d452599070e62cb380e519ee Mon Sep 17 00:00:00 2001 From: matikij Date: Sat, 20 Mar 2021 08:32:46 +0100 Subject: [PATCH 101/111] Add 2.13in-ttgo-b1 waveshare epaper module. (#1326) --- .../components/waveshare_epaper/display.py | 1 + .../waveshare_epaper/waveshare_epaper.cpp | 173 +++++++++++++----- .../waveshare_epaper/waveshare_epaper.h | 3 + tests/test1.yaml | 18 -- tests/test4.yaml | 28 +++ 5 files changed, 158 insertions(+), 65 deletions(-) diff --git a/esphome/components/waveshare_epaper/display.py b/esphome/components/waveshare_epaper/display.py index 430022e542..16f234563c 100644 --- a/esphome/components/waveshare_epaper/display.py +++ b/esphome/components/waveshare_epaper/display.py @@ -48,6 +48,7 @@ MODELS = { "1.54in": ("a", WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_1_54_IN), "2.13in": ("a", WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_2_13_IN), "2.13in-ttgo": ("a", WaveshareEPaperTypeAModel.TTGO_EPAPER_2_13_IN), + "2.13in-ttgo-b1": ("a", WaveshareEPaperTypeAModel.TTGO_EPAPER_2_13_IN_B1), "2.13in-ttgo-b73": ("a", WaveshareEPaperTypeAModel.TTGO_EPAPER_2_13_IN_B73), "2.90in": ("a", WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_2_9_IN), "2.90inv2": ("a", WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_2_9_IN_V2), diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.cpp b/esphome/components/waveshare_epaper/waveshare_epaper.cpp index adb748d308..0fb783107d 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.cpp +++ b/esphome/components/waveshare_epaper/waveshare_epaper.cpp @@ -9,6 +9,7 @@ namespace waveshare_epaper { static const char *TAG = "waveshare_epaper"; static const uint8_t LUT_SIZE_WAVESHARE = 30; + static const uint8_t FULL_UPDATE_LUT[LUT_SIZE_WAVESHARE] = {0x02, 0x02, 0x01, 0x11, 0x12, 0x12, 0x22, 0x22, 0x66, 0x69, 0x69, 0x59, 0x58, 0x99, 0x99, 0x88, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xB4, 0x13, 0x51, 0x35, 0x51, 0x51, 0x19, 0x01, 0x00}; @@ -18,7 +19,6 @@ static const uint8_t PARTIAL_UPDATE_LUT[LUT_SIZE_WAVESHARE] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x14, 0x44, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; static const uint8_t LUT_SIZE_TTGO = 70; -static const uint8_t LUT_SIZE_TTGO_B73 = 100; static const uint8_t FULL_UPDATE_LUT_TTGO[LUT_SIZE_TTGO] = { 0x80, 0x60, 0x40, 0x00, 0x00, 0x00, 0x00, // LUT0: BB: VS 0 ~7 @@ -35,6 +35,23 @@ static const uint8_t FULL_UPDATE_LUT_TTGO[LUT_SIZE_TTGO] = { 0x00, 0x00, 0x00, 0x00, 0x00, // TP6 A~D RP6 }; +static const uint8_t PARTIAL_UPDATE_LUT_TTGO[LUT_SIZE_TTGO] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // LUT0: BB: VS 0 ~7 + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // LUT1: BW: VS 0 ~7 + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // LUT2: WB: VS 0 ~7 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // LUT3: WW: VS 0 ~7 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // LUT4: VCOM: VS 0 ~7 + 0x0A, 0x00, 0x00, 0x00, 0x00, // TP0 A~D RP0 + 0x00, 0x00, 0x00, 0x00, 0x00, // TP1 A~D RP1 + 0x00, 0x00, 0x00, 0x00, 0x00, // TP2 A~D RP2 + 0x00, 0x00, 0x00, 0x00, 0x00, // TP3 A~D RP3 + 0x00, 0x00, 0x00, 0x00, 0x00, // TP4 A~D RP4 + 0x00, 0x00, 0x00, 0x00, 0x00, // TP5 A~D RP5 + 0x00, 0x00, 0x00, 0x00, 0x00, // TP6 A~D RP6 +}; + +static const uint8_t LUT_SIZE_TTGO_B73 = 100; + static const uint8_t FULL_UPDATE_LUT_TTGO_B73[LUT_SIZE_TTGO_B73] = { 0xA0, 0x90, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x90, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x90, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x90, 0xA0, 0x00, @@ -55,20 +72,15 @@ static const uint8_t PARTIAL_UPDATE_LUT_TTGO_B73[LUT_SIZE_TTGO_B73] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; -static const uint8_t PARTIAL_UPDATE_LUT_TTGO[LUT_SIZE_TTGO] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // LUT0: BB: VS 0 ~7 - 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // LUT1: BW: VS 0 ~7 - 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // LUT2: WB: VS 0 ~7 - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // LUT3: WW: VS 0 ~7 - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // LUT4: VCOM: VS 0 ~7 - 0x0A, 0x00, 0x00, 0x00, 0x00, // TP0 A~D RP0 - 0x00, 0x00, 0x00, 0x00, 0x00, // TP1 A~D RP1 - 0x00, 0x00, 0x00, 0x00, 0x00, // TP2 A~D RP2 - 0x00, 0x00, 0x00, 0x00, 0x00, // TP3 A~D RP3 - 0x00, 0x00, 0x00, 0x00, 0x00, // TP4 A~D RP4 - 0x00, 0x00, 0x00, 0x00, 0x00, // TP5 A~D RP5 - 0x00, 0x00, 0x00, 0x00, 0x00, // TP6 A~D RP6 -}; +static const uint8_t LUT_SIZE_TTGO_B1 = 29; + +static const uint8_t FULL_UPDATE_LUT_TTGO_B1[LUT_SIZE_TTGO_B1] = { + 0x22, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x01, 0x00, 0x00, 0x00, 0x00}; + +static const uint8_t PARTIAL_UPDATE_LUT_TTGO_B1[LUT_SIZE_TTGO_B1] = { + 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; void WaveshareEPaper::setup_pins_() { this->init_internal_(this->get_buffer_length_()); @@ -103,7 +115,7 @@ bool WaveshareEPaper::wait_until_idle_() { const uint32_t start = millis(); while (this->busy_pin_->digital_read()) { - if (millis() - start > 1000) { + if (millis() - start > this->idle_timeout_()) { ESP_LOGE(TAG, "Timeout while displaying image!"); return false; } @@ -177,13 +189,19 @@ void WaveshareEPaperTypeA::initialize() { // COMMAND DATA ENTRY MODE SETTING this->command(0x11); - this->data(0x03); // from top left to bottom right - - if (this->model_ == WAVESHARE_EPAPER_2_9_IN_V2) { - // RAM content option for Display Update - this->command(0x21); - this->data(0x00); - this->data(0x80); + switch (this->model_) { + case TTGO_EPAPER_2_13_IN_B1: + this->data(0x01); // x increase, y decrease : as in demo code + break; + case WAVESHARE_EPAPER_2_9_IN_V2: + this->data(0x03); // from top left to bottom right + // RAM content option for Display Update + this->command(0x21); + this->data(0x00); + this->data(0x80); + break; + default: + this->data(0x03); // from top left to bottom right } } void WaveshareEPaperTypeA::dump_config() { @@ -201,6 +219,9 @@ void WaveshareEPaperTypeA::dump_config() { case TTGO_EPAPER_2_13_IN_B73: ESP_LOGCONFIG(TAG, " Model: 2.13in (TTGO B73)"); break; + case TTGO_EPAPER_2_13_IN_B1: + ESP_LOGCONFIG(TAG, " Model: 2.13in (TTGO B1)"); + break; case WAVESHARE_EPAPER_2_9_IN: ESP_LOGCONFIG(TAG, " Model: 2.9in"); break; @@ -225,36 +246,67 @@ void HOT WaveshareEPaperTypeA::display() { if (this->full_update_every_ >= 1) { if (full_update != prev_full_update) { - if (this->model_ == TTGO_EPAPER_2_13_IN) { - this->write_lut_(full_update ? FULL_UPDATE_LUT_TTGO : PARTIAL_UPDATE_LUT_TTGO, LUT_SIZE_TTGO); - } else if (this->model_ == TTGO_EPAPER_2_13_IN_B73) { - this->write_lut_(full_update ? FULL_UPDATE_LUT_TTGO_B73 : PARTIAL_UPDATE_LUT_TTGO_B73, LUT_SIZE_TTGO_B73); - } else { - this->write_lut_(full_update ? FULL_UPDATE_LUT : PARTIAL_UPDATE_LUT, LUT_SIZE_WAVESHARE); + switch (this->model_) { + case TTGO_EPAPER_2_13_IN: + this->write_lut_(full_update ? FULL_UPDATE_LUT_TTGO : PARTIAL_UPDATE_LUT_TTGO, LUT_SIZE_TTGO); + break; + case TTGO_EPAPER_2_13_IN_B73: + this->write_lut_(full_update ? FULL_UPDATE_LUT_TTGO_B73 : PARTIAL_UPDATE_LUT_TTGO_B73, LUT_SIZE_TTGO_B73); + break; + case TTGO_EPAPER_2_13_IN_B1: + this->write_lut_(full_update ? FULL_UPDATE_LUT_TTGO_B1 : PARTIAL_UPDATE_LUT_TTGO_B1, LUT_SIZE_TTGO_B1); + break; + default: + this->write_lut_(full_update ? FULL_UPDATE_LUT : PARTIAL_UPDATE_LUT, LUT_SIZE_WAVESHARE); } } this->at_update_ = (this->at_update_ + 1) % this->full_update_every_; } // Set x & y regions we want to write to (full) - // COMMAND SET RAM X ADDRESS START END POSITION - this->command(0x44); - this->data(0x00); - this->data((this->get_width_internal() - 1) >> 3); - // COMMAND SET RAM Y ADDRESS START END POSITION - this->command(0x45); - this->data(0x00); - this->data(0x00); - this->data(this->get_height_internal() - 1); - this->data((this->get_height_internal() - 1) >> 8); + switch (this->model_) { + case TTGO_EPAPER_2_13_IN_B1: + // COMMAND SET RAM X ADDRESS START END POSITION + this->command(0x44); + this->data(0x00); + this->data((this->get_width_internal() - 1) >> 3); + // COMMAND SET RAM Y ADDRESS START END POSITION + this->command(0x45); + this->data(this->get_height_internal() - 1); + this->data((this->get_height_internal() - 1) >> 8); + this->data(0x00); + this->data(0x00); - // COMMAND SET RAM X ADDRESS COUNTER - this->command(0x4E); - this->data(0x00); - // COMMAND SET RAM Y ADDRESS COUNTER - this->command(0x4F); - this->data(0x00); - this->data(0x00); + // COMMAND SET RAM X ADDRESS COUNTER + this->command(0x4E); + this->data(0x00); + // COMMAND SET RAM Y ADDRESS COUNTER + this->command(0x4F); + this->data(this->get_height_internal() - 1); + this->data((this->get_height_internal() - 1) >> 8); + + break; + + default: + // COMMAND SET RAM X ADDRESS START END POSITION + this->command(0x44); + this->data(0x00); + this->data((this->get_width_internal() - 1) >> 3); + // COMMAND SET RAM Y ADDRESS START END POSITION + this->command(0x45); + this->data(0x00); + this->data(0x00); + this->data(this->get_height_internal() - 1); + this->data((this->get_height_internal() - 1) >> 8); + + // COMMAND SET RAM X ADDRESS COUNTER + this->command(0x4E); + this->data(0x00); + // COMMAND SET RAM Y ADDRESS COUNTER + this->command(0x4F); + this->data(0x00); + this->data(0x00); + } if (!this->wait_until_idle_()) { this->status_set_warning(); @@ -264,7 +316,20 @@ void HOT WaveshareEPaperTypeA::display() { // COMMAND WRITE RAM this->command(0x24); this->start_data_(); - this->write_array(this->buffer_, this->get_buffer_length_()); + switch (this->model_) { + case TTGO_EPAPER_2_13_IN_B1: { // block needed because of variable initializations + int16_t wb = ((this->get_width_internal()) >> 3); + for (int i = 0; i < this->get_height_internal(); i++) { + for (int j = 0; j < wb; j++) { + int idx = j + (this->get_height_internal() - 1 - i) * wb; + this->write_byte(this->buffer_[idx]); + } + } + break; + } + default: + this->write_array(this->buffer_, this->get_buffer_length_()); + } this->end_data_(); // COMMAND DISPLAY UPDATE CONTROL 2 @@ -294,6 +359,8 @@ int WaveshareEPaperTypeA::get_width_internal() { return 128; case TTGO_EPAPER_2_13_IN_B73: return 128; + case TTGO_EPAPER_2_13_IN_B1: + return 128; case WAVESHARE_EPAPER_2_9_IN: return 128; case WAVESHARE_EPAPER_2_9_IN_V2: @@ -311,6 +378,8 @@ int WaveshareEPaperTypeA::get_height_internal() { return 250; case TTGO_EPAPER_2_13_IN_B73: return 250; + case TTGO_EPAPER_2_13_IN_B1: + return 250; case WAVESHARE_EPAPER_2_9_IN: return 296; case WAVESHARE_EPAPER_2_9_IN_V2: @@ -329,6 +398,16 @@ void WaveshareEPaperTypeA::set_full_update_every(uint32_t full_update_every) { this->full_update_every_ = full_update_every; } +int WaveshareEPaperTypeA::idle_timeout_() { + switch (this->model_) { + case TTGO_EPAPER_2_13_IN_B1: + return 2500; + break; + default: + return WaveshareEPaper::idle_timeout_(); + } +} + // ======================================================== // Type B // ======================================================== diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.h b/esphome/components/waveshare_epaper/waveshare_epaper.h index 8ea73d053a..0b8958e7f0 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.h +++ b/esphome/components/waveshare_epaper/waveshare_epaper.h @@ -61,6 +61,7 @@ class WaveshareEPaper : public PollingComponent, GPIOPin *reset_pin_{nullptr}; GPIOPin *dc_pin_; GPIOPin *busy_pin_{nullptr}; + virtual int idle_timeout_() { return 1000; } // NOLINT(readability-identifier-naming) }; enum WaveshareEPaperTypeAModel { @@ -70,6 +71,7 @@ enum WaveshareEPaperTypeAModel { WAVESHARE_EPAPER_2_9_IN_V2, TTGO_EPAPER_2_13_IN, TTGO_EPAPER_2_13_IN_B73, + TTGO_EPAPER_2_13_IN_B1, }; class WaveshareEPaperTypeA : public WaveshareEPaper { @@ -106,6 +108,7 @@ class WaveshareEPaperTypeA : public WaveshareEPaper { uint32_t full_update_every_{30}; uint32_t at_update_{0}; WaveshareEPaperTypeAModel model_; + int idle_timeout_() override; }; enum WaveshareEPaperTypeBModel { diff --git a/tests/test1.yaml b/tests/test1.yaml index f5354231b4..f08cea7770 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -1887,24 +1887,6 @@ display: reset_pin: GPIO23 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); - - platform: waveshare_epaper - cs_pin: GPIO23 - dc_pin: GPIO23 - busy_pin: GPIO23 - reset_pin: GPIO23 - model: 2.90in - full_update_every: 30 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - platform: waveshare_epaper - cs_pin: GPIO23 - dc_pin: GPIO23 - busy_pin: GPIO23 - reset_pin: GPIO23 - model: 2.90inv2 - full_update_every: 30 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - platform: st7789v cs_pin: GPIO5 dc_pin: GPIO16 diff --git a/tests/test4.yaml b/tests/test4.yaml index 1f6eb5442b..ed63a1ac14 100644 --- a/tests/test4.yaml +++ b/tests/test4.yaml @@ -132,3 +132,31 @@ display: it.rectangle(3, 3, it.get_width()-6, it.get_height()-6, red); rotation: 0° update_interval: 16ms + - platform: waveshare_epaper + cs_pin: GPIO23 + dc_pin: GPIO23 + busy_pin: GPIO23 + reset_pin: GPIO23 + model: 2.13in-ttgo-b1 + full_update_every: 30 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + - platform: waveshare_epaper + cs_pin: GPIO23 + dc_pin: GPIO23 + busy_pin: GPIO23 + reset_pin: GPIO23 + model: 2.90in + full_update_every: 30 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + - platform: waveshare_epaper + cs_pin: GPIO23 + dc_pin: GPIO23 + busy_pin: GPIO23 + reset_pin: GPIO23 + model: 2.90inv2 + full_update_every: 30 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + From 60b2d57dc34b6d7d4f222e84dc8f8a6642cafbf2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 20 Mar 2021 15:21:47 +0100 Subject: [PATCH 102/111] Bump flake8 from 3.8.4 to 3.9.0 (#1612) Bumps [flake8](https://gitlab.com/pycqa/flake8) from 3.8.4 to 3.9.0. - [Release notes](https://gitlab.com/pycqa/flake8/tags) - [Commits](https://gitlab.com/pycqa/flake8/compare/3.8.4...3.9.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Otto Winter --- requirements_test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements_test.txt b/requirements_test.txt index 10c838a424..f811626cc5 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,5 +1,5 @@ pylint==2.7.2 -flake8==3.8.4 +flake8==3.9.0 black==20.8b1 pillow>4.0.0 cryptography>=2.0.0,<4 From 5eeb110d749cf7f26a7f1301438a1c7a0a34b2ea Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Sat, 20 Mar 2021 18:43:31 +0100 Subject: [PATCH 103/111] Bundle platformio lib_deps in docker images (#1625) --- .github/workflows/ci-docker.yml | 2 +- .github/workflows/ci.yml | 18 -------------- .github/workflows/release-dev.yml | 20 +-------------- .github/workflows/release.yml | 20 +-------------- docker/Dockerfile | 8 +++--- docker/Dockerfile.dev | 2 +- docker/Dockerfile.hassio | 6 +++-- docker/Dockerfile.lint | 8 +++--- docker/platformio_install_deps.py | 20 +++++++++++++++ esphome/const.py | 41 ++++++++++++++++--------------- platformio.ini | 2 +- script/ci-custom.py | 2 +- 12 files changed, 61 insertions(+), 88 deletions(-) create mode 100755 docker/platformio_install_deps.py diff --git a/.github/workflows/ci-docker.yml b/.github/workflows/ci-docker.yml index e7642c6434..446e7c3eab 100644 --- a/.github/workflows/ci-docker.yml +++ b/.github/workflows/ci-docker.yml @@ -26,7 +26,7 @@ jobs: - uses: actions/checkout@v2 - name: Set up env variables run: | - base_version="2.6.0" + base_version="2.8.0" if [[ "${{ matrix.build_type }}" == "hassio" ]]; then build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8136b0678e..ed4343202c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,15 +18,6 @@ jobs: container: esphome/esphome-lint:latest steps: - uses: actions/checkout@v2 - # Cache platformio intermediary files (like libraries etc) - # Note: platformio platform versions should be cached via the esphome-lint image - - name: Cache Platformio - uses: actions/cache@v1 - with: - path: .pio - key: lint-cpp-pio-${{ hashFiles('platformio.ini') }} - restore-keys: | - lint-cpp-pio- # Set up the pio project so that the cpp checks know how files are compiled # (build flags, libraries etc) - name: Set up platformio environment @@ -49,15 +40,6 @@ jobs: split: [1, 2, 3, 4] steps: - uses: actions/checkout@v2 - # Cache platformio intermediary files (like libraries etc) - # Note: platformio platform versions should be cached via the esphome-lint image - - name: Cache Platformio - uses: actions/cache@v1 - with: - path: .pio - key: lint-cpp-pio-${{ hashFiles('platformio.ini') }} - restore-keys: | - lint-cpp-pio- # Set up the pio project so that the cpp checks know how files are compiled # (build flags, libraries etc) - name: Set up platformio environment diff --git a/.github/workflows/release-dev.yml b/.github/workflows/release-dev.yml index 22ac278af7..1c23e7a957 100644 --- a/.github/workflows/release-dev.yml +++ b/.github/workflows/release-dev.yml @@ -15,15 +15,6 @@ jobs: container: esphome/esphome-lint:latest steps: - uses: actions/checkout@v2 - # Cache platformio intermediary files (like libraries etc) - # Note: platformio platform versions should be cached via the esphome-lint image - - name: Cache Platformio - uses: actions/cache@v1 - with: - path: .pio - key: lint-cpp-pio-${{ hashFiles('platformio.ini') }} - restore-keys: | - lint-cpp-pio- # Set up the pio project so that the cpp checks know how files are compiled # (build flags, libraries etc) - name: Set up platformio environment @@ -46,15 +37,6 @@ jobs: split: [1, 2, 3, 4] steps: - uses: actions/checkout@v2 - # Cache platformio intermediary files (like libraries etc) - # Note: platformio platform versions should be cached via the esphome-lint image - - name: Cache Platformio - uses: actions/cache@v1 - with: - path: .pio - key: lint-cpp-pio-${{ hashFiles('platformio.ini') }} - restore-keys: | - lint-cpp-pio- # Set up the pio project so that the cpp checks know how files are compiled # (build flags, libraries etc) - name: Set up platformio environment @@ -192,7 +174,7 @@ jobs: echo "TAG=${TAG}" >> $GITHUB_ENV - name: Set up env variables run: | - base_version="2.6.0" + base_version="2.8.0" if [[ "${{ matrix.build_type }}" == "hassio" ]]; then build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bbaefe4ea6..479657b065 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,15 +14,6 @@ jobs: container: esphome/esphome-lint:latest steps: - uses: actions/checkout@v2 - # Cache platformio intermediary files (like libraries etc) - # Note: platformio platform versions should be cached via the esphome-lint image - - name: Cache Platformio - uses: actions/cache@v1 - with: - path: .pio - key: lint-cpp-pio-${{ hashFiles('platformio.ini') }} - restore-keys: | - lint-cpp-pio- # Set up the pio project so that the cpp checks know how files are compiled # (build flags, libraries etc) - name: Set up platformio environment @@ -45,15 +36,6 @@ jobs: split: [1, 2, 3, 4] steps: - uses: actions/checkout@v2 - # Cache platformio intermediary files (like libraries etc) - # Note: platformio platform versions should be cached via the esphome-lint image - - name: Cache Platformio - uses: actions/cache@v1 - with: - path: .pio - key: lint-cpp-pio-${{ hashFiles('platformio.ini') }} - restore-keys: | - lint-cpp-pio- # Set up the pio project so that the cpp checks know how files are compiled # (build flags, libraries etc) - name: Set up platformio environment @@ -212,7 +194,7 @@ jobs: echo "TAG=${TAG}" >> $GITHUB_ENV - name: Set up env variables run: | - base_version="2.6.0" + base_version="2.8.0" if [[ "${{ matrix.build_type }}" == "hassio" ]]; then build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}" diff --git a/docker/Dockerfile b/docker/Dockerfile index bbee4b2434..aaa3591d9c 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,9 +1,11 @@ -ARG BUILD_FROM=esphome/esphome-base-amd64:2.6.0 +ARG BUILD_FROM=esphome/esphome-base-amd64:2.8.0 FROM ${BUILD_FROM} # First install requirements to leverage caching when requirements don't change -COPY requirements.txt / -RUN pip3 install --no-cache-dir -r /requirements.txt +COPY requirements.txt docker/platformio_install_deps.py platformio.ini / +RUN \ + pip3 install --no-cache-dir -r /requirements.txt \ + && /platformio_install_deps.py /platformio.ini # Then copy esphome and install COPY . . diff --git a/docker/Dockerfile.dev b/docker/Dockerfile.dev index 989802ab1e..081d816e4d 100644 --- a/docker/Dockerfile.dev +++ b/docker/Dockerfile.dev @@ -1,4 +1,4 @@ -FROM esphome/esphome-base-amd64:2.6.0 +FROM esphome/esphome-base-amd64:2.8.0 COPY . . diff --git a/docker/Dockerfile.hassio b/docker/Dockerfile.hassio index eb7ef23001..b6ff037c35 100644 --- a/docker/Dockerfile.hassio +++ b/docker/Dockerfile.hassio @@ -2,8 +2,10 @@ ARG BUILD_FROM FROM ${BUILD_FROM} # First install requirements to leverage caching when requirements don't change -COPY requirements.txt / -RUN pip3 install --no-cache-dir -r /requirements.txt +COPY requirements.txt docker/platformio_install_deps.py platformio.ini / +RUN \ + pip3 install --no-cache-dir -r /requirements.txt \ + && /platformio_install_deps.py /platformio.ini # Copy root filesystem COPY docker/rootfs/ / diff --git a/docker/Dockerfile.lint b/docker/Dockerfile.lint index 27f04dd33d..5eb923de82 100644 --- a/docker/Dockerfile.lint +++ b/docker/Dockerfile.lint @@ -1,7 +1,9 @@ -FROM esphome/esphome-lint-base:2.6.0 +FROM esphome/esphome-lint-base:2.8.0 -COPY requirements.txt requirements_test.txt / -RUN pip3 install --no-cache-dir -r /requirements.txt -r /requirements_test.txt +COPY requirements.txt requirements_test.txt docker/platformio_install_deps.py platformio.ini / +RUN \ + pip3 install --no-cache-dir -r /requirements.txt -r /requirements_test.txt \ + && /platformio_install_deps.py /platformio.ini VOLUME ["/esphome"] WORKDIR /esphome diff --git a/docker/platformio_install_deps.py b/docker/platformio_install_deps.py new file mode 100755 index 0000000000..6f3e9f28d5 --- /dev/null +++ b/docker/platformio_install_deps.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 +# This script is used in the docker containers to preinstall +# all platformio libraries in the global storage + +import configparser +import re +import subprocess +import sys + +config = configparser.ConfigParser() +config.read(sys.argv[1]) +libs = [] +for line in config['common']['lib_deps'].splitlines(): + # Format: '1655@1.0.2 ; TinyGPSPlus (has name conflict)' (includes comment) + m = re.search(r'([a-zA-Z0-9-_/]+@[0-9\.]+)', line) + if m is None: + continue + libs.append(m.group(1)) + +subprocess.check_call(['platformio', 'lib', '-g', 'install', *libs]) diff --git a/esphome/const.py b/esphome/const.py index 7842f8634c..94c2ce2716 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -16,30 +16,31 @@ ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789_-" # See also https://github.com/platformio/platform-espressif32/releases ARDUINO_VERSION_ESP32 = { "dev": "https://github.com/platformio/platform-espressif32.git", - "1.0.4": "espressif32@1.12.4", - "1.0.3": "espressif32@1.10.0", - "1.0.2": "espressif32@1.9.0", - "1.0.1": "espressif32@1.7.0", - "1.0.0": "espressif32@1.5.0", + "1.0.5": "platformio/espressif32@3.1.1", + "1.0.4": "platformio/espressif32@3.0.0", + "1.0.3": "platformio/espressif32@1.10.0", + "1.0.2": "platformio/espressif32@1.9.0", + "1.0.1": "platformio/espressif32@1.7.0", + "1.0.0": "platformio/espressif32@1.5.0", } # See also https://github.com/platformio/platform-espressif8266/releases ARDUINO_VERSION_ESP8266 = { "dev": "https://github.com/platformio/platform-espressif8266.git", - "2.7.4": "espressif8266@2.6.2", - "2.7.3": "espressif8266@2.6.1", - "2.7.2": "espressif8266@2.6.0", - "2.7.1": "espressif8266@2.5.3", - "2.7.0": "espressif8266@2.5.0", - "2.6.3": "espressif8266@2.4.0", - "2.6.2": "espressif8266@2.3.1", - "2.6.1": "espressif8266@2.3.0", - "2.5.2": "espressif8266@2.2.3", - "2.5.1": "espressif8266@2.1.1", - "2.5.0": "espressif8266@2.0.4", - "2.4.2": "espressif8266@1.8.0", - "2.4.1": "espressif8266@1.7.3", - "2.4.0": "espressif8266@1.6.0", - "2.3.0": "espressif8266@1.5.0", + "2.7.4": "platformio/espressif8266@2.6.2", + "2.7.3": "platformio/espressif8266@2.6.1", + "2.7.2": "platformio/espressif8266@2.6.0", + "2.7.1": "platformio/espressif8266@2.5.3", + "2.7.0": "platformio/espressif8266@2.5.0", + "2.6.3": "platformio/espressif8266@2.4.0", + "2.6.2": "platformio/espressif8266@2.3.1", + "2.6.1": "platformio/espressif8266@2.3.0", + "2.5.2": "platformio/espressif8266@2.2.3", + "2.5.1": "platformio/espressif8266@2.1.1", + "2.5.0": "platformio/espressif8266@2.0.4", + "2.4.2": "platformio/espressif8266@1.8.0", + "2.4.1": "platformio/espressif8266@1.7.3", + "2.4.0": "platformio/espressif8266@1.6.0", + "2.3.0": "platformio/espressif8266@1.5.0", } SOURCE_FILE_EXTENSIONS = {".cpp", ".hpp", ".h", ".c", ".tcc", ".ino"} HEADER_FILE_EXTENSIONS = {".h", ".hpp", ".tcc"} diff --git a/platformio.ini b/platformio.ini index 557803bb29..1dbc88cfde 100644 --- a/platformio.ini +++ b/platformio.ini @@ -14,7 +14,7 @@ lib_deps = AsyncMqttClient-esphome@0.8.4 ArduinoJson-esphomelib@5.13.3 ESPAsyncWebServer-esphome@1.2.7 - FastLED@3.3.2 + fastled/FastLED@3.3.2 NeoPixelBus-esphome@2.5.7 ESPAsyncTCP-esphome@1.2.3 1655@1.0.2 ; TinyGPSPlus (has name conflict) diff --git a/script/ci-custom.py b/script/ci-custom.py index 8ebf6eb245..3106cabeff 100755 --- a/script/ci-custom.py +++ b/script/ci-custom.py @@ -217,7 +217,7 @@ def lint_ext_check(fname): ) -@lint_file_check(exclude=["docker/rootfs/*", "script/*", "setup.py"]) +@lint_file_check(exclude=["docker/rootfs/*", "docker/*.py", "script/*", "setup.py"]) def lint_executable_bit(fname): ex = EXECUTABLE_BIT[fname] if ex != 100644: From e51b0ca15e636d022e6d157e092e5e9f8d92eb21 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 20 Mar 2021 18:58:08 +0100 Subject: [PATCH 104/111] Bump protobuf from 3.13.0 to 3.15.6 (#1607) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Otto Winter --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index ac8c4a2fd5..9e1c22916f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,7 +4,7 @@ paho-mqtt==1.5.1 colorama==0.4.4 colorlog==4.7.2 tornado==6.1 -protobuf==3.13.0 +protobuf==3.15.6 tzlocal==2.1 pytz==2021.1 pyserial==3.5 From 452ca8e4c6b6d6f4c0a3a38c0b4f44c76b58465a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 20 Mar 2021 18:58:26 +0100 Subject: [PATCH 105/111] Bump pyyaml from 5.3.1 to 5.4.1 (#1482) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Otto Winter --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 9e1c22916f..9ab99e0bb4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ voluptuous==0.12.1 -PyYAML==5.3.1 +PyYAML==5.4.1 paho-mqtt==1.5.1 colorama==0.4.4 colorlog==4.7.2 From 89d0d41c5a45225966ef615127572d6f57b925fe Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Sat, 20 Mar 2021 20:58:46 +0100 Subject: [PATCH 106/111] Switch docker images to debian (#1626) --- .github/workflows/ci-docker.yml | 4 ++-- .github/workflows/release-dev.yml | 4 ++-- .github/workflows/release.yml | 4 ++-- docker/Dockerfile | 2 +- docker/Dockerfile.dev | 2 +- docker/Dockerfile.lint | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci-docker.yml b/.github/workflows/ci-docker.yml index 446e7c3eab..7da8a3f8f1 100644 --- a/.github/workflows/ci-docker.yml +++ b/.github/workflows/ci-docker.yml @@ -26,7 +26,7 @@ jobs: - uses: actions/checkout@v2 - name: Set up env variables run: | - base_version="2.8.0" + base_version="3.0.0" if [[ "${{ matrix.build_type }}" == "hassio" ]]; then build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}" @@ -45,7 +45,7 @@ jobs: run: | docker pull "${BUILD_TO}:dev" || true - name: Register QEMU binfmt - run: docker run --rm --privileged multiarch/qemu-user-static:5.0.0-2 --reset -p yes + run: docker run --rm --privileged multiarch/qemu-user-static:5.2.0-2 --reset -p yes - run: | docker build \ --build-arg "BUILD_FROM=${BUILD_FROM}" \ diff --git a/.github/workflows/release-dev.yml b/.github/workflows/release-dev.yml index 1c23e7a957..b4c4a8f17d 100644 --- a/.github/workflows/release-dev.yml +++ b/.github/workflows/release-dev.yml @@ -174,7 +174,7 @@ jobs: echo "TAG=${TAG}" >> $GITHUB_ENV - name: Set up env variables run: | - base_version="2.8.0" + base_version="3.0.0" if [[ "${{ matrix.build_type }}" == "hassio" ]]; then build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}" @@ -193,7 +193,7 @@ jobs: run: | docker pull "${BUILD_TO}:dev" || true - name: Register QEMU binfmt - run: docker run --rm --privileged multiarch/qemu-user-static:5.0.0-2 --reset -p yes + run: docker run --rm --privileged multiarch/qemu-user-static:5.2.0-2 --reset -p yes - run: | docker build \ --build-arg "BUILD_FROM=${BUILD_FROM}" \ diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 479657b065..a1fd2dba24 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -194,7 +194,7 @@ jobs: echo "TAG=${TAG}" >> $GITHUB_ENV - name: Set up env variables run: | - base_version="2.8.0" + base_version="3.0.0" if [[ "${{ matrix.build_type }}" == "hassio" ]]; then build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}" @@ -221,7 +221,7 @@ jobs: run: | docker pull "${BUILD_TO}:${CACHE_TAG}" || true - name: Register QEMU binfmt - run: docker run --rm --privileged multiarch/qemu-user-static:5.0.0-2 --reset -p yes + run: docker run --rm --privileged multiarch/qemu-user-static:5.2.0-2 --reset -p yes - run: | docker build \ --build-arg "BUILD_FROM=${BUILD_FROM}" \ diff --git a/docker/Dockerfile b/docker/Dockerfile index aaa3591d9c..7364299ba7 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,4 +1,4 @@ -ARG BUILD_FROM=esphome/esphome-base-amd64:2.8.0 +ARG BUILD_FROM=esphome/esphome-base-amd64:3.0.0 FROM ${BUILD_FROM} # First install requirements to leverage caching when requirements don't change diff --git a/docker/Dockerfile.dev b/docker/Dockerfile.dev index 081d816e4d..52495fc8ae 100644 --- a/docker/Dockerfile.dev +++ b/docker/Dockerfile.dev @@ -1,4 +1,4 @@ -FROM esphome/esphome-base-amd64:2.8.0 +FROM esphome/esphome-base-amd64:3.0.0 COPY . . diff --git a/docker/Dockerfile.lint b/docker/Dockerfile.lint index 5eb923de82..5488e68c91 100644 --- a/docker/Dockerfile.lint +++ b/docker/Dockerfile.lint @@ -1,4 +1,4 @@ -FROM esphome/esphome-lint-base:2.8.0 +FROM esphome/esphome-lint-base:3.0.0 COPY requirements.txt requirements_test.txt docker/platformio_install_deps.py platformio.ini / RUN \ From 8f1eb77e05f963986414e0fc972f2dd5c0b66fbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Maggioni?= Date: Mon, 22 Mar 2021 00:59:41 +0100 Subject: [PATCH 107/111] Background calibration & ABC commands for SenseAir S8 (#1623) --- esphome/components/senseair/senseair.cpp | 73 ++++++++++++++++++++++-- esphome/components/senseair/senseair.h | 59 ++++++++++++++++++- esphome/components/senseair/sensor.py | 48 ++++++++++++++++ tests/test1.yaml | 5 -- tests/test2.yaml | 12 ++++ 5 files changed, 185 insertions(+), 12 deletions(-) diff --git a/esphome/components/senseair/senseair.cpp b/esphome/components/senseair/senseair.cpp index 812a4aa42d..80b67dfa17 100644 --- a/esphome/components/senseair/senseair.cpp +++ b/esphome/components/senseair/senseair.cpp @@ -6,12 +6,20 @@ namespace senseair { static const char *TAG = "senseair"; static const uint8_t SENSEAIR_REQUEST_LENGTH = 8; -static const uint8_t SENSEAIR_RESPONSE_LENGTH = 13; -static const uint8_t SENSEAIR_COMMAND_GET_PPM[] = {0xFE, 0x04, 0x00, 0x00, 0x00, 0x04, 0xE5, 0xC6}; +static const uint8_t SENSEAIR_PPM_STATUS_RESPONSE_LENGTH = 13; +static const uint8_t SENSEAIR_ABC_PERIOD_RESPONSE_LENGTH = 7; +static const uint8_t SENSEAIR_CAL_RESULT_RESPONSE_LENGTH = 7; +static const uint8_t SENSEAIR_COMMAND_GET_PPM_STATUS[] = {0xFE, 0x04, 0x00, 0x00, 0x00, 0x04, 0xE5, 0xC6}; +static const uint8_t SENSEAIR_COMMAND_CLEAR_ACK_REGISTER[] = {0xFE, 0x06, 0x00, 0x00, 0x00, 0x00, 0x9D, 0xC5}; +static const uint8_t SENSEAIR_COMMAND_BACKGROUND_CAL[] = {0xFE, 0x06, 0x00, 0x01, 0x7C, 0x06, 0x6C, 0xC7}; +static const uint8_t SENSEAIR_COMMAND_BACKGROUND_CAL_RESULT[] = {0xFE, 0x03, 0x00, 0x00, 0x00, 0x01, 0x90, 0x05}; +static const uint8_t SENSEAIR_COMMAND_ABC_ENABLE[] = {0xFE, 0x06, 0x00, 0x1F, 0x00, 0xB4, 0xAC, 0x74}; // 180 hours +static const uint8_t SENSEAIR_COMMAND_ABC_DISABLE[] = {0xFE, 0x06, 0x00, 0x1F, 0x00, 0x00, 0xAC, 0x03}; +static const uint8_t SENSEAIR_COMMAND_ABC_GET_PERIOD[] = {0xFE, 0x03, 0x00, 0x1F, 0x00, 0x01, 0xA1, 0xC3}; void SenseAirComponent::update() { - uint8_t response[SENSEAIR_RESPONSE_LENGTH]; - if (!this->senseair_write_command_(SENSEAIR_COMMAND_GET_PPM, response)) { + uint8_t response[SENSEAIR_PPM_STATUS_RESPONSE_LENGTH]; + if (!this->senseair_write_command_(SENSEAIR_COMMAND_GET_PPM_STATUS, response, SENSEAIR_PPM_STATUS_RESPONSE_LENGTH)) { ESP_LOGW(TAG, "Reading data from SenseAir failed!"); this->status_set_warning(); return; @@ -69,14 +77,67 @@ uint16_t SenseAirComponent::senseair_checksum_(uint8_t *ptr, uint8_t length) { return crc; } -bool SenseAirComponent::senseair_write_command_(const uint8_t *command, uint8_t *response) { +void SenseAirComponent::background_calibration() { + ESP_LOGD(TAG, "SenseAir Starting background calibration"); + this->senseair_write_command_(SENSEAIR_COMMAND_CLEAR_ACK_REGISTER, nullptr, 0); + this->senseair_write_command_(SENSEAIR_COMMAND_BACKGROUND_CAL, nullptr, 0); +} + +void SenseAirComponent::background_calibration_result() { + ESP_LOGD(TAG, "SenseAir Requesting background calibration result"); + uint8_t response[SENSEAIR_CAL_RESULT_RESPONSE_LENGTH]; + if (!this->senseair_write_command_(SENSEAIR_COMMAND_BACKGROUND_CAL_RESULT, response, + SENSEAIR_CAL_RESULT_RESPONSE_LENGTH)) { + ESP_LOGE(TAG, "Requesting background calibration result from SenseAir failed!"); + return; + } + + if (response[0] != 0xFE || response[1] != 0x03) { + ESP_LOGE(TAG, "Invalid reply from SenseAir! %02x%02x%02x %02x%02x %02x%02x", response[0], response[1], response[2], + response[3], response[4], response[5], response[6]); + return; + } + + ESP_LOGD(TAG, "SenseAir Result=%s (%02x%02x%02x)", response[2] == 2 ? "OK" : "NOT_OK", response[2], response[3], + response[4]); +} + +void SenseAirComponent::abc_enable() { + ESP_LOGD(TAG, "SenseAir Enabling automatic baseline calibration"); + this->senseair_write_command_(SENSEAIR_COMMAND_ABC_ENABLE, nullptr, 0); +} + +void SenseAirComponent::abc_disable() { + ESP_LOGD(TAG, "SenseAir Disabling automatic baseline calibration"); + this->senseair_write_command_(SENSEAIR_COMMAND_ABC_DISABLE, nullptr, 0); +} + +void SenseAirComponent::abc_get_period() { + ESP_LOGD(TAG, "SenseAir Requesting ABC period"); + uint8_t response[SENSEAIR_ABC_PERIOD_RESPONSE_LENGTH]; + if (!this->senseair_write_command_(SENSEAIR_COMMAND_ABC_GET_PERIOD, response, SENSEAIR_ABC_PERIOD_RESPONSE_LENGTH)) { + ESP_LOGE(TAG, "Requesting ABC period from SenseAir failed!"); + return; + } + + if (response[0] != 0xFE || response[1] != 0x03) { + ESP_LOGE(TAG, "Invalid reply from SenseAir! %02x%02x%02x %02x%02x %02x%02x", response[0], response[1], response[2], + response[3], response[4], response[5], response[6]); + return; + } + + const uint16_t hours = (uint16_t(response[3]) << 8) | response[4]; + ESP_LOGD(TAG, "SenseAir Read ABC Period: %u hours", hours); +} + +bool SenseAirComponent::senseair_write_command_(const uint8_t *command, uint8_t *response, uint8_t response_length) { this->flush(); this->write_array(command, SENSEAIR_REQUEST_LENGTH); if (response == nullptr) return true; - bool ret = this->read_array(response, SENSEAIR_RESPONSE_LENGTH); + bool ret = this->read_array(response, response_length); this->flush(); return ret; } diff --git a/esphome/components/senseair/senseair.h b/esphome/components/senseair/senseair.h index 23bcf40b5a..c03a0848e9 100644 --- a/esphome/components/senseair/senseair.h +++ b/esphome/components/senseair/senseair.h @@ -1,6 +1,7 @@ #pragma once #include "esphome/core/component.h" +#include "esphome/core/automation.h" #include "esphome/components/sensor/sensor.h" #include "esphome/components/uart/uart.h" @@ -15,12 +16,68 @@ class SenseAirComponent : public PollingComponent, public uart::UARTDevice { void update() override; void dump_config() override; + void background_calibration(); + void background_calibration_result(); + void abc_get_period(); + void abc_enable(); + void abc_disable(); + protected: uint16_t senseair_checksum_(uint8_t *ptr, uint8_t length); - bool senseair_write_command_(const uint8_t *command, uint8_t *response); + bool senseair_write_command_(const uint8_t *command, uint8_t *response, uint8_t response_length); sensor::Sensor *co2_sensor_{nullptr}; }; +template class SenseAirBackgroundCalibrationAction : public Action { + public: + SenseAirBackgroundCalibrationAction(SenseAirComponent *senseair) : senseair_(senseair) {} + + void play(Ts... x) override { this->senseair_->background_calibration(); } + + protected: + SenseAirComponent *senseair_; +}; + +template class SenseAirBackgroundCalibrationResultAction : public Action { + public: + SenseAirBackgroundCalibrationResultAction(SenseAirComponent *senseair) : senseair_(senseair) {} + + void play(Ts... x) override { this->senseair_->background_calibration_result(); } + + protected: + SenseAirComponent *senseair_; +}; + +template class SenseAirABCEnableAction : public Action { + public: + SenseAirABCEnableAction(SenseAirComponent *senseair) : senseair_(senseair) {} + + void play(Ts... x) override { this->senseair_->abc_enable(); } + + protected: + SenseAirComponent *senseair_; +}; + +template class SenseAirABCDisableAction : public Action { + public: + SenseAirABCDisableAction(SenseAirComponent *senseair) : senseair_(senseair) {} + + void play(Ts... x) override { this->senseair_->abc_disable(); } + + protected: + SenseAirComponent *senseair_; +}; + +template class SenseAirABCGetPeriodAction : public Action { + public: + SenseAirABCGetPeriodAction(SenseAirComponent *senseair) : senseair_(senseair) {} + + void play(Ts... x) override { this->senseair_->abc_get_period(); } + + protected: + SenseAirComponent *senseair_; +}; + } // namespace senseair } // namespace esphome diff --git a/esphome/components/senseair/sensor.py b/esphome/components/senseair/sensor.py index 8f5c7caa68..6f48651563 100644 --- a/esphome/components/senseair/sensor.py +++ b/esphome/components/senseair/sensor.py @@ -1,5 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv +from esphome import automation +from esphome.automation import maybe_simple_id from esphome.components import sensor, uart from esphome.const import ( CONF_CO2, @@ -15,6 +17,21 @@ senseair_ns = cg.esphome_ns.namespace("senseair") SenseAirComponent = senseair_ns.class_( "SenseAirComponent", cg.PollingComponent, uart.UARTDevice ) +SenseAirBackgroundCalibrationAction = senseair_ns.class_( + "SenseAirBackgroundCalibrationAction", automation.Action +) +SenseAirBackgroundCalibrationResultAction = senseair_ns.class_( + "SenseAirBackgroundCalibrationResultAction", automation.Action +) +SenseAirABCEnableAction = senseair_ns.class_( + "SenseAirABCEnableAction", automation.Action +) +SenseAirABCDisableAction = senseair_ns.class_( + "SenseAirABCDisableAction", automation.Action +) +SenseAirABCGetPeriodAction = senseair_ns.class_( + "SenseAirABCGetPeriodAction", automation.Action +) CONFIG_SCHEMA = ( cv.Schema( @@ -38,3 +55,34 @@ def to_code(config): if CONF_CO2 in config: sens = yield sensor.new_sensor(config[CONF_CO2]) cg.add(var.set_co2_sensor(sens)) + + +CALIBRATION_ACTION_SCHEMA = maybe_simple_id( + { + cv.Required(CONF_ID): cv.use_id(SenseAirComponent), + } +) + + +@automation.register_action( + "senseair.background_calibration", + SenseAirBackgroundCalibrationAction, + CALIBRATION_ACTION_SCHEMA, +) +@automation.register_action( + "senseair.background_calibration_result", + SenseAirBackgroundCalibrationResultAction, + CALIBRATION_ACTION_SCHEMA, +) +@automation.register_action( + "senseair.abc_enable", SenseAirABCEnableAction, CALIBRATION_ACTION_SCHEMA +) +@automation.register_action( + "senseair.abc_disable", SenseAirABCDisableAction, CALIBRATION_ACTION_SCHEMA +) +@automation.register_action( + "senseair.abc_get_period", SenseAirABCGetPeriodAction, CALIBRATION_ACTION_SCHEMA +) +def senseair_action_to_code(config, action_id, template_arg, args): + paren = yield cg.get_variable(config[CONF_ID]) + yield cg.new_Pvariable(action_id, template_arg, paren) diff --git a/tests/test1.yaml b/tests/test1.yaml index f08cea7770..461d8e5da1 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -660,11 +660,6 @@ sensor: - platform: pulse_width name: Pulse Width pin: GPIO12 - - platform: senseair - uart_id: uart0 - co2: - name: 'SenseAir CO2 Value' - update_interval: 15s - platform: sm300d2 uart_id: uart0 co2: diff --git a/tests/test2.yaml b/tests/test2.yaml index 333025358c..616fe32139 100644 --- a/tests/test2.yaml +++ b/tests/test2.yaml @@ -70,6 +70,18 @@ sensor: - platform: ble_rssi service_uuid: '11223344-5566-7788-99aa-bbccddeeff00' name: 'BLE Test Service 128' + - platform: senseair + id: senseair0 + co2: + name: 'SenseAir CO2 Value' + on_value: + then: + - senseair.background_calibration: senseair0 + - senseair.background_calibration_result: senseair0 + - senseair.abc_get_period: senseair0 + - senseair.abc_enable: senseair0 + - senseair.abc_disable: senseair0 + update_interval: 15s - platform: ruuvitag mac_address: FF:56:D3:2F:7D:E8 humidity: From af3273d93016afe2cb0a7fe46fc69b481819c64a Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 22 Mar 2021 16:26:10 +1300 Subject: [PATCH 108/111] Add trigger for http actions to receive the status code (#1599) --- esphome/components/http_request/__init__.py | 13 +++++++++++++ esphome/components/http_request/http_request.cpp | 5 ++++- esphome/components/http_request/http_request.h | 14 ++++++++++++-- tests/test1.yaml | 6 ++++++ 4 files changed, 35 insertions(+), 3 deletions(-) diff --git a/esphome/components/http_request/__init__.py b/esphome/components/http_request/__init__.py index 0cac088f3d..dee3fe8f77 100644 --- a/esphome/components/http_request/__init__.py +++ b/esphome/components/http_request/__init__.py @@ -10,6 +10,7 @@ from esphome.const import ( CONF_METHOD, CONF_ARDUINO_VERSION, ARDUINO_VERSION_ESP8266, + CONF_TRIGGER_ID, CONF_URL, ) from esphome.core import CORE, Lambda @@ -23,12 +24,16 @@ HttpRequestComponent = http_request_ns.class_("HttpRequestComponent", cg.Compone HttpRequestSendAction = http_request_ns.class_( "HttpRequestSendAction", automation.Action ) +HttpRequestResponseTrigger = http_request_ns.class_( + "HttpRequestResponseTrigger", automation.Trigger +) CONF_HEADERS = "headers" CONF_USERAGENT = "useragent" CONF_BODY = "body" CONF_JSON = "json" CONF_VERIFY_SSL = "verify_ssl" +CONF_ON_RESPONSE = "on_response" def validate_framework(config): @@ -117,6 +122,9 @@ HTTP_REQUEST_ACTION_SCHEMA = cv.Schema( cv.Schema({cv.string: cv.templatable(cv.string)}) ), cv.Optional(CONF_VERIFY_SSL, default=True): cv.boolean, + cv.Optional(CONF_ON_RESPONSE): automation.validate_automation( + {cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(HttpRequestResponseTrigger)} + ), } ).add_extra(validate_secure_url) HTTP_REQUEST_GET_ACTION_SCHEMA = automation.maybe_conf( @@ -189,4 +197,9 @@ def http_request_action_to_code(config, action_id, template_arg, args): ) cg.add(var.add_header(key, template_)) + for conf in config.get(CONF_ON_RESPONSE, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID]) + cg.add(var.register_response_trigger(trigger)) + yield automation.build_automation(trigger, [(int, "status_code")], conf) + yield var diff --git a/esphome/components/http_request/http_request.cpp b/esphome/components/http_request/http_request.cpp index 0e73a7956a..79f698441e 100644 --- a/esphome/components/http_request/http_request.cpp +++ b/esphome/components/http_request/http_request.cpp @@ -24,7 +24,7 @@ void HttpRequestComponent::set_url(std::string url) { this->client_.setReuse(true); } -void HttpRequestComponent::send() { +void HttpRequestComponent::send(const std::vector &response_triggers) { bool begin_status = false; const String url = this->url_.c_str(); #ifdef ARDUINO_ARCH_ESP32 @@ -54,6 +54,9 @@ void HttpRequestComponent::send() { } int http_code = this->client_.sendRequest(this->method_, this->body_.c_str()); + for (auto *trigger : response_triggers) + trigger->process(http_code); + if (http_code < 0) { ESP_LOGW(TAG, "HTTP Request failed; URL: %s; Error: %s", this->url_.c_str(), HTTPClient::errorToString(http_code).c_str()); diff --git a/esphome/components/http_request/http_request.h b/esphome/components/http_request/http_request.h index c69683db0e..f2c688d2f9 100644 --- a/esphome/components/http_request/http_request.h +++ b/esphome/components/http_request/http_request.h @@ -22,6 +22,8 @@ struct Header { const char *value; }; +class HttpRequestResponseTrigger; + class HttpRequestComponent : public Component { public: void dump_config() override; @@ -33,7 +35,7 @@ class HttpRequestComponent : public Component { void set_timeout(uint16_t timeout) { this->timeout_ = timeout; } void set_body(std::string body) { this->body_ = body; } void set_headers(std::list
headers) { this->headers_ = headers; } - void send(); + void send(const std::vector &response_triggers); void close(); const char *get_string(); @@ -69,6 +71,8 @@ template class HttpRequestSendAction : public Action { void set_json(std::function json_func) { this->json_func_ = json_func; } + void register_response_trigger(HttpRequestResponseTrigger *trigger) { this->response_triggers_.push_back(trigger); } + void play(Ts... x) override { this->parent_->set_url(this->url_.value(x...)); this->parent_->set_method(this->method_.value(x...)); @@ -100,7 +104,7 @@ template class HttpRequestSendAction : public Action { } this->parent_->set_headers(headers); } - this->parent_->send(); + this->parent_->send(this->response_triggers_); this->parent_->close(); } @@ -116,6 +120,12 @@ template class HttpRequestSendAction : public Action { std::map> headers_{}; std::map> json_{}; std::function json_func_{nullptr}; + std::vector response_triggers_; +}; + +class HttpRequestResponseTrigger : public Trigger { + public: + void process(int status_code) { this->trigger(status_code); } }; } // namespace http_request diff --git a/tests/test1.yaml b/tests/test1.yaml index 461d8e5da1..bcd4460d3c 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -49,6 +49,12 @@ esphome: Content-Type: application/json body: 'Some data' verify_ssl: false + on_response: + then: + - logger.log: + format: 'Response status: %d' + args: + - status_code build_path: build/test1 packages: From fb6c5ebe9a3cd2da4fcbf38d312b9e4aef72a524 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 22 Mar 2021 17:42:38 +1300 Subject: [PATCH 109/111] Bump version to v1.18.0-dev --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 94c2ce2716..f33b4ee406 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,7 +1,7 @@ """Constants used by esphome.""" MAJOR_VERSION = 1 -MINOR_VERSION = 17 +MINOR_VERSION = 18 PATCH_VERSION = "0-dev" __short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__ = f"{__short_version__}.{PATCH_VERSION}" From e2e074a3fd831472475b978bbce2a679bbb70ed8 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 22 Mar 2021 17:19:10 +1300 Subject: [PATCH 110/111] Fix bump script for double quotes --- script/bump-version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/bump-version.py b/script/bump-version.py index 54e8c95753..b7b048eb22 100755 --- a/script/bump-version.py +++ b/script/bump-version.py @@ -59,7 +59,7 @@ def write_version(version: Version): sub( "esphome/const.py", r"^PATCH_VERSION = .*$", - f"PATCH_VERSION = '{version.full_patch}'", + f'PATCH_VERSION = "{version.full_patch}"', ) From 6114d331c9006080c8d3c55a7cb18ee9fd15e824 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 22 Mar 2021 20:12:48 +1300 Subject: [PATCH 111/111] Bump version to v1.17.0b1 --- esphome/const.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/const.py b/esphome/const.py index f33b4ee406..90155f30f6 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,8 +1,8 @@ """Constants used by esphome.""" MAJOR_VERSION = 1 -MINOR_VERSION = 18 -PATCH_VERSION = "0-dev" +MINOR_VERSION = 17 +PATCH_VERSION = "0b1" __short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__ = f"{__short_version__}.{PATCH_VERSION}"