mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 15:12:06 +00:00 
			
		
		
		
	Updates for 1.13 (#546)
* Update CI matcher * Check Executable bit * Quicklint * Updates * Allow pm1.0 and pm10.0 for PMS5003ST Fixes https://github.com/esphome/feature-requests/issues/225 * PowerSupplyRequester * Lint * Include debug data in generated main.cpp * Updates * Auto-select bit_depth * Updates
This commit is contained in:
		| @@ -51,6 +51,6 @@ matrix: | ||||
|         - clang-format-7 -version | ||||
|         - clang-apply-replacements-7 -version | ||||
|       script: | ||||
|         - script/clang-tidy.py --all-headers -j 2 --fix | ||||
|         - script/clang-format.py -i -j 2 | ||||
|         - script/clang-tidy --all-headers -j 2 --fix | ||||
|         - script/clang-format -i -j 2 | ||||
|         - script/ci-suggest-changes | ||||
|   | ||||
| @@ -1,16 +1,18 @@ | ||||
| from __future__ import print_function | ||||
|  | ||||
| import argparse | ||||
| import functools | ||||
| import logging | ||||
| import os | ||||
| import sys | ||||
| 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 | ||||
| from esphome.core import CORE, EsphomeError | ||||
| from esphome.core import CORE, EsphomeError, coroutine, coroutine_with_priority | ||||
| from esphome.helpers import color, indent | ||||
| from esphome.py_compat import IS_PY2, safe_input | ||||
| from esphome.util import run_external_command, run_external_process, safe_print | ||||
| @@ -117,12 +119,27 @@ def run_miniterm(config, port): | ||||
|                 config, line, backtrace_state=backtrace_state) | ||||
|  | ||||
|  | ||||
| def wrap_to_code(name, comp): | ||||
|     coro = coroutine(comp.to_code) | ||||
|  | ||||
|     @functools.wraps(comp.to_code) | ||||
|     @coroutine_with_priority(coro.priority) | ||||
|     def wrapped(conf): | ||||
|         cg.add(cg.LineComment(u"{}:".format(name))) | ||||
|         if comp.config_schema is not None: | ||||
|             cg.add(cg.LineComment(indent(yaml_util.dump(conf).decode('utf-8')))) | ||||
|         yield coro(conf) | ||||
|  | ||||
|     return wrapped | ||||
|  | ||||
|  | ||||
| def write_cpp(config): | ||||
|     _LOGGER.info("Generating C++ source...") | ||||
|  | ||||
|     for _, component, conf in iter_components(CORE.config): | ||||
|     for name, component, conf in iter_components(CORE.config): | ||||
|         if component.to_code is not None: | ||||
|             CORE.add_job(component.to_code, conf) | ||||
|             coro = wrap_to_code(name, component) | ||||
|             CORE.add_job(coro, conf) | ||||
|  | ||||
|     CORE.flush_tasks() | ||||
|  | ||||
|   | ||||
| @@ -10,7 +10,7 @@ | ||||
| # pylint: disable=unused-import | ||||
| from esphome.cpp_generator import (  # noqa | ||||
|     Expression, RawExpression, RawStatement, TemplateArguments, | ||||
|     StructInitializer, ArrayInitializer, safe_exp, Statement, | ||||
|     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, | ||||
|   | ||||
| @@ -5,7 +5,7 @@ namespace esphome { | ||||
|  | ||||
| namespace binary_sensor { | ||||
|  | ||||
| static const char *TAG = "something.Filter"; | ||||
| static const char *TAG = "sensor.filter"; | ||||
|  | ||||
| void Filter::output(bool value, bool is_initial) { | ||||
|   if (!this->dedup_.next(value)) | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
| namespace esphome { | ||||
| namespace ble_presence { | ||||
|  | ||||
| static const char *TAG = "something.something"; | ||||
| static const char *TAG = "ble_presence"; | ||||
|  | ||||
| void BLEPresenceDevice::dump_config() { LOG_BINARY_SENSOR("", "BLE Presence", this); } | ||||
|  | ||||
|   | ||||
| @@ -11,7 +11,7 @@ | ||||
| namespace esphome { | ||||
| namespace esp8266_pwm { | ||||
|  | ||||
| static const char *TAG = "something.something"; | ||||
| static const char *TAG = "esp8266_pwm"; | ||||
|  | ||||
| void ESP8266PWM::setup() { | ||||
|   ESP_LOGCONFIG(TAG, "Setting up ESP8266 PWM Output..."); | ||||
|   | ||||
| @@ -1,8 +1,7 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome.components import light, power_supply | ||||
| from esphome.const import CONF_OUTPUT_ID, CONF_NUM_LEDS, CONF_RGB_ORDER, CONF_MAX_REFRESH_RATE, \ | ||||
|     CONF_POWER_SUPPLY | ||||
| from esphome.components import light | ||||
| from esphome.const import CONF_OUTPUT_ID, CONF_NUM_LEDS, CONF_RGB_ORDER, CONF_MAX_REFRESH_RATE | ||||
| from esphome.core import coroutine | ||||
|  | ||||
| fastled_base_ns = cg.esphome_ns.namespace('fastled_base') | ||||
| @@ -24,8 +23,6 @@ BASE_SCHEMA = light.ADDRESSABLE_LIGHT_SCHEMA.extend({ | ||||
|     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, | ||||
|  | ||||
|     cv.Optional(CONF_POWER_SUPPLY): cv.use_id(power_supply.PowerSupply), | ||||
| }).extend(cv.COMPONENT_SCHEMA) | ||||
|  | ||||
|  | ||||
| @@ -37,10 +34,6 @@ def new_fastled_light(config): | ||||
|     if CONF_MAX_REFRESH_RATE in config: | ||||
|         cg.add(var.set_max_refresh_rate(config[CONF_MAX_REFRESH_RATE])) | ||||
|  | ||||
|     if CONF_POWER_SUPPLY in config: | ||||
|         var_ = yield cg.get_variable(config[CONF_POWER_SUPPLY]) | ||||
|         cg.add(var.set_power_supply(var_)) | ||||
|  | ||||
|     yield light.register_light(var, config) | ||||
|     cg.add_library('FastLED', '3.2.0') | ||||
|     yield var | ||||
|   | ||||
| @@ -33,27 +33,6 @@ void FastLEDLightOutput::loop() { | ||||
|   this->mark_shown_(); | ||||
|  | ||||
|   ESP_LOGVV(TAG, "Writing RGB values to bus..."); | ||||
|  | ||||
| #ifdef USE_POWER_SUPPLY | ||||
|   if (this->power_supply_ != nullptr) { | ||||
|     bool is_on = false; | ||||
|     for (int i = 0; i < this->num_leds_; i++) { | ||||
|       if (bool(this->leds_[i])) { | ||||
|         is_on = true; | ||||
|         break; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     if (is_on && !this->has_requested_high_power_) { | ||||
|       this->power_supply_->request_high_power(); | ||||
|       this->has_requested_high_power_ = true; | ||||
|     } | ||||
|     if (!is_on && this->has_requested_high_power_) { | ||||
|       this->power_supply_->unrequest_high_power(); | ||||
|       this->has_requested_high_power_ = false; | ||||
|     } | ||||
|   } | ||||
| #endif | ||||
|   this->controller_->showLeds(); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -4,10 +4,6 @@ | ||||
| #include "esphome/core/helpers.h" | ||||
| #include "esphome/components/light/addressable_light.h" | ||||
|  | ||||
| #ifdef USE_POWER_SUPPLY | ||||
| #include "esphome/components/power_supply/power_supply.h" | ||||
| #endif | ||||
|  | ||||
| #define FASTLED_ESP8266_RAW_PIN_ORDER | ||||
| #define FASTLED_ESP32_RAW_PIN_ORDER | ||||
| #define FASTLED_RMT_BUILTIN_DRIVER true | ||||
| @@ -30,10 +26,6 @@ class FastLEDLightOutput : public Component, public light::AddressableLight { | ||||
|   /// Set a maximum refresh rate in µs as some lights do not like being updated too often. | ||||
|   void set_max_refresh_rate(uint32_t interval_us) { this->max_refresh_rate_ = interval_us; } | ||||
|  | ||||
| #ifdef USE_POWER_SUPPLY | ||||
|   void set_power_supply(power_supply::PowerSupply *power_supply) { this->power_supply_ = power_supply; } | ||||
| #endif | ||||
|  | ||||
|   /// Add some LEDS, can only be called once. | ||||
|   CLEDController &add_leds(CLEDController *controller, int num_leds) { | ||||
|     this->controller_ = controller; | ||||
| @@ -242,10 +234,6 @@ class FastLEDLightOutput : public Component, public light::AddressableLight { | ||||
|   int num_leds_{0}; | ||||
|   uint32_t last_refresh_{0}; | ||||
|   optional<uint32_t> max_refresh_rate_{}; | ||||
| #ifdef USE_POWER_SUPPLY | ||||
|   power_supply::PowerSupply *power_supply_{nullptr}; | ||||
|   bool has_requested_high_power_{false}; | ||||
| #endif | ||||
| }; | ||||
|  | ||||
| }  // namespace fastled_base | ||||
|   | ||||
| @@ -33,7 +33,7 @@ CONFIG_SCHEMA = sensor.SENSOR_SCHEMA.extend({ | ||||
|     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=True): cv.boolean, | ||||
|     cv.Optional(CONF_RESTORE, default=False): cv.boolean, | ||||
| }).extend(cv.COMPONENT_SCHEMA) | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -1,20 +1,54 @@ | ||||
| import math | ||||
|  | ||||
| from esphome import pins | ||||
| from esphome.components import output | ||||
| import esphome.config_validation as cv | ||||
| import esphome.codegen as cg | ||||
| from esphome.const import APB_CLOCK_FREQ, CONF_BIT_DEPTH, CONF_CHANNEL, CONF_FREQUENCY, \ | ||||
| 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) | ||||
|  | ||||
|  | ||||
| def calc_min_frequency(bit_depth): | ||||
|     # LEDC_DIV_NUM_HSTIMER is 15-bit unsigned integer | ||||
|     # lower 8 bits represent fractional part | ||||
|     max_div_num = ((1 << 16) - 1) / 256.0 | ||||
|     return 80e6 / (max_div_num * (2**bit_depth)) | ||||
|  | ||||
|  | ||||
| def validate_frequency_bit_depth(obj): | ||||
|     frequency = obj[CONF_FREQUENCY] | ||||
|     if CONF_BIT_DEPTH not in obj: | ||||
|         obj = obj.copy() | ||||
|         for bit_depth in range(15, 0, -1): | ||||
|             if calc_min_frequency(bit_depth) <= frequency <= calc_max_frequency(bit_depth): | ||||
|                 obj[CONF_BIT_DEPTH] = bit_depth | ||||
|                 break | ||||
|         else: | ||||
|             min_freq = min(calc_min_frequency(x) for x in range(1, 16)) | ||||
|             max_freq = max(calc_max_frequency(x) for x in range(1, 16)) | ||||
|             if frequency < min_freq: | ||||
|                 raise cv.Invalid("This frequency setting is not possible, please choose a higher " | ||||
|                                  "frequency (at least {}Hz)".format(int(min_freq))) | ||||
|             if frequency > 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("Invalid frequency!") | ||||
|  | ||||
|     bit_depth = obj[CONF_BIT_DEPTH] | ||||
|     max_freq = APB_CLOCK_FREQ / (2**bit_depth) | ||||
|     min_freq = calc_min_frequency(bit_depth) | ||||
|     max_freq = calc_max_frequency(bit_depth) | ||||
|     if frequency > max_freq: | ||||
|         raise cv.Invalid('Maximum frequency for bit depth {} is {}Hz'.format(bit_depth, max_freq)) | ||||
|         raise cv.Invalid('Maximum frequency for bit depth {} is {}Hz. Please decrease the ' | ||||
|                          'bit_depth.'.format(bit_depth, int(math.floor(max_freq)))) | ||||
|     if frequency < calc_min_frequency(bit_depth): | ||||
|         raise cv.Invalid('Minimum frequency for bit depth {} is {}Hz. Please increase the ' | ||||
|                          'bit_depth.'.format(bit_depth, int(math.ceil(min_freq)))) | ||||
|     return obj | ||||
|  | ||||
|  | ||||
| @@ -25,7 +59,7 @@ CONFIG_SCHEMA = cv.All(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_BIT_DEPTH, default=12): cv.int_range(min=1, max=15), | ||||
|     cv.Optional(CONF_BIT_DEPTH): cv.int_range(min=1, max=15), | ||||
|     cv.Optional(CONF_CHANNEL): cv.int_range(min=0, max=15), | ||||
| }).extend(cv.COMPONENT_SCHEMA), validate_frequency_bit_depth) | ||||
|  | ||||
|   | ||||
| @@ -1,9 +1,9 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome.components import mqtt | ||||
| 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_INTERNAL, CONF_NAME, CONF_MQTT_ID, CONF_POWER_SUPPLY | ||||
| from esphome.core import coroutine, coroutine_with_priority | ||||
| from .automation import light_control_to_code  # noqa | ||||
| from .effects import validate_effects, BINARY_EFFECTS, \ | ||||
| @@ -35,6 +35,7 @@ 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), | ||||
| }) | ||||
|  | ||||
|  | ||||
| @@ -52,6 +53,10 @@ def setup_light_core_(light_var, output_var, config): | ||||
|     if CONF_COLOR_CORRECT in config: | ||||
|         cg.add(output_var.set_correction(*config[CONF_COLOR_CORRECT])) | ||||
|  | ||||
|     if CONF_POWER_SUPPLY in config: | ||||
|         var_ = yield cg.get_variable(config[CONF_POWER_SUPPLY]) | ||||
|         cg.add(output_var.set_power_supply(var_)) | ||||
|  | ||||
|     if CONF_MQTT_ID in config: | ||||
|         mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], light_var) | ||||
|         yield mqtt.register_mqtt_component(mqtt_, config) | ||||
|   | ||||
| @@ -1,9 +1,14 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/core/defines.h" | ||||
| #include "light_output.h" | ||||
| #include "light_state.h" | ||||
|  | ||||
| #ifdef USE_POWER_SUPPLY | ||||
| #include "esphome/components/power_supply/power_supply.h" | ||||
| #endif | ||||
|  | ||||
| namespace esphome { | ||||
| namespace light { | ||||
|  | ||||
| @@ -30,6 +35,7 @@ struct ESPColor { | ||||
|       }; | ||||
|     }; | ||||
|     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), | ||||
| @@ -47,7 +53,7 @@ struct ESPColor { | ||||
|     this->b = rhs.b; | ||||
|     this->w = rhs.w; | ||||
|   } | ||||
|   inline bool is_on() ALWAYS_INLINE { return this->r != 0 || this->g != 0 || this->b != 0 || this->w != 0; } | ||||
|   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; | ||||
| @@ -527,14 +533,32 @@ class AddressableLight : public LightOutput { | ||||
|   void setup_state(LightState *state) override { this->correction_.calculate_gamma_table(state->get_gamma_correct()); } | ||||
|   void schedule_show() { this->next_show_ = true; } | ||||
|  | ||||
| #ifdef USE_POWER_SUPPLY | ||||
|   void set_power_supply(power_supply::PowerSupply *power_supply) { this->power_.set_parent(power_supply); } | ||||
| #endif | ||||
|  | ||||
|  protected: | ||||
|   bool should_show_() const { return this->effect_active_ || this->next_show_; } | ||||
|   void mark_shown_() { this->next_show_ = false; } | ||||
|   void mark_shown_() { | ||||
|     this->next_show_ = false; | ||||
| #ifdef USE_POWER_SUPPLY | ||||
|     for (auto c : *this) { | ||||
|       if (c.get().is_on()) { | ||||
|         this->power_.request(); | ||||
|         return; | ||||
|       } | ||||
|     } | ||||
|     this->power_.unrequest(); | ||||
| #endif | ||||
|   } | ||||
|   virtual ESPColorView get_view_internal(int32_t index) const = 0; | ||||
|  | ||||
|   bool effect_active_{false}; | ||||
|   bool next_show_{true}; | ||||
|   ESPColorCorrection correction_{}; | ||||
| #ifdef USE_POWER_SUPPLY | ||||
|   power_supply::PowerSupplyRequester power_; | ||||
| #endif | ||||
| }; | ||||
|  | ||||
| }  // namespace light | ||||
|   | ||||
| @@ -1,9 +1,9 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| from esphome import pins | ||||
| from esphome.components import light, power_supply | ||||
| from esphome.components import light | ||||
| from esphome.const import CONF_CLOCK_PIN, CONF_DATA_PIN, CONF_METHOD, CONF_NUM_LEDS, CONF_PIN, \ | ||||
|     CONF_POWER_SUPPLY, CONF_TYPE, CONF_VARIANT, CONF_OUTPUT_ID | ||||
|     CONF_TYPE, CONF_VARIANT, CONF_OUTPUT_ID | ||||
| from esphome.core import CORE | ||||
|  | ||||
| neopixelbus_ns = cg.esphome_ns.namespace('neopixelbus') | ||||
| @@ -138,8 +138,6 @@ CONFIG_SCHEMA = cv.All(light.ADDRESSABLE_LIGHT_SCHEMA.extend({ | ||||
|     cv.Optional(CONF_DATA_PIN): pins.output_pin, | ||||
|  | ||||
|     cv.Required(CONF_NUM_LEDS): cv.positive_not_null_int, | ||||
|  | ||||
|     cv.Optional(CONF_POWER_SUPPLY): cv.use_id(power_supply.PowerSupply), | ||||
| }).extend(cv.COMPONENT_SCHEMA), validate, validate_method_pin) | ||||
|  | ||||
|  | ||||
| @@ -162,8 +160,4 @@ def to_code(config): | ||||
|  | ||||
|     cg.add(var.set_pixel_order(getattr(ESPNeoPixelOrder, config[CONF_TYPE]))) | ||||
|  | ||||
|     if CONF_POWER_SUPPLY in config: | ||||
|         var_ = yield cg.get_variable(config[CONF_POWER_SUPPLY]) | ||||
|         cg.add(var.set_power_supply(var_)) | ||||
|  | ||||
|     cg.add_library('NeoPixelBus', '2.4.1') | ||||
|   | ||||
| @@ -9,10 +9,6 @@ | ||||
| #error The NeoPixelBus library requires at least arduino_core_version 2.4.x | ||||
| #endif | ||||
|  | ||||
| #ifdef USE_POWER_SUPPLY | ||||
| #include "esphome/components/power_supply/power_supply.h" | ||||
| #endif | ||||
|  | ||||
| #include "NeoPixelBus.h" | ||||
|  | ||||
| namespace esphome { | ||||
| @@ -54,10 +50,6 @@ enum class ESPNeoPixelOrder { | ||||
| template<typename T_METHOD, typename T_COLOR_FEATURE> | ||||
| class NeoPixelBusLightOutputBase : public Component, public light::AddressableLight { | ||||
|  public: | ||||
| #ifdef USE_POWER_SUPPLY | ||||
|   void set_power_supply(power_supply::PowerSupply *power_supply) { this->power_supply_ = power_supply; } | ||||
| #endif | ||||
|  | ||||
|   NeoPixelBus<T_COLOR_FEATURE, T_METHOD> *get_controller() const { return this->controller_; } | ||||
|  | ||||
|   void clear_effect_data() override { | ||||
| @@ -95,27 +87,6 @@ class NeoPixelBusLightOutputBase : public Component, public light::AddressableLi | ||||
|     this->mark_shown_(); | ||||
|     this->controller_->Dirty(); | ||||
|  | ||||
| #ifdef USE_POWER_SUPPLY | ||||
|     if (this->power_supply_ != nullptr) { | ||||
|       bool is_light_on = false; | ||||
|       for (int i = 0; i < this->size(); i++) { | ||||
|         if ((*this)[i].get().is_on()) { | ||||
|           is_light_on = true; | ||||
|           break; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       if (is_light_on && !this->has_requested_high_power_) { | ||||
|         this->power_supply_->request_high_power(); | ||||
|         this->has_requested_high_power_ = true; | ||||
|       } | ||||
|       if (!is_light_on && this->has_requested_high_power_) { | ||||
|         this->power_supply_->unrequest_high_power(); | ||||
|         this->has_requested_high_power_ = false; | ||||
|       } | ||||
|     } | ||||
| #endif | ||||
|  | ||||
|     this->controller_->Show(); | ||||
|   } | ||||
|  | ||||
| @@ -135,10 +106,6 @@ class NeoPixelBusLightOutputBase : public Component, public light::AddressableLi | ||||
|   NeoPixelBus<T_COLOR_FEATURE, T_METHOD> *controller_{nullptr}; | ||||
|   uint8_t *effect_data_{nullptr}; | ||||
|   uint8_t rgb_offsets_[4]{0, 1, 2, 3}; | ||||
| #ifdef USE_POWER_SUPPLY | ||||
|   power_supply::PowerSupply *power_supply_{nullptr}; | ||||
|   bool has_requested_high_power_{false}; | ||||
| #endif | ||||
| }; | ||||
|  | ||||
| template<typename T_METHOD, typename T_COLOR_FEATURE = NeoRgbFeature> | ||||
|   | ||||
| @@ -27,16 +27,13 @@ class BinaryOutput { | ||||
|    * | ||||
|    * @param power_supply The PowerSupplyComponent, set this to nullptr to disable the power supply. | ||||
|    */ | ||||
|   void set_power_supply(power_supply::PowerSupply *power_supply) { this->power_supply_ = power_supply; } | ||||
|   void set_power_supply(power_supply::PowerSupply *power_supply) { this->power_.set_parent(power_supply); } | ||||
| #endif | ||||
|  | ||||
|   /// Enable this binary output. | ||||
|   virtual void turn_on() { | ||||
| #ifdef USE_POWER_SUPPLY | ||||
|     if (this->power_supply_ != nullptr && !this->has_requested_high_power_) { | ||||
|       this->power_supply_->request_high_power(); | ||||
|       this->has_requested_high_power_ = true; | ||||
|     } | ||||
|     this->power_.request(); | ||||
| #endif | ||||
|     this->write_state(!this->inverted_); | ||||
|   } | ||||
| @@ -44,10 +41,7 @@ class BinaryOutput { | ||||
|   /// Disable this binary output. | ||||
|   virtual void turn_off() { | ||||
| #ifdef USE_POWER_SUPPLY | ||||
|     if (this->power_supply_ != nullptr && this->has_requested_high_power_) { | ||||
|       this->power_supply_->unrequest_high_power(); | ||||
|       this->has_requested_high_power_ = false; | ||||
|     } | ||||
|     this->power_.unrequest(); | ||||
| #endif | ||||
|     this->write_state(this->inverted_); | ||||
|   } | ||||
| @@ -62,8 +56,7 @@ class BinaryOutput { | ||||
|  | ||||
|   bool inverted_{false}; | ||||
| #ifdef USE_POWER_SUPPLY | ||||
|   power_supply::PowerSupply *power_supply_{nullptr}; | ||||
|   bool has_requested_high_power_{false}; | ||||
|   power_supply::PowerSupplyRequester power_{}; | ||||
| #endif | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -24,15 +24,9 @@ void FloatOutput::set_level(float state) { | ||||
|  | ||||
| #ifdef USE_POWER_SUPPLY | ||||
|   if (state > 0.0f) {  // ON | ||||
|     if (this->power_supply_ != nullptr && !this->has_requested_high_power_) { | ||||
|       this->power_supply_->request_high_power(); | ||||
|       this->has_requested_high_power_ = true; | ||||
|     } | ||||
|     this->power_.request(); | ||||
|   } else {  // OFF | ||||
|     if (this->power_supply_ != nullptr && this->has_requested_high_power_) { | ||||
|       this->power_supply_->unrequest_high_power(); | ||||
|       this->has_requested_high_power_ = false; | ||||
|     } | ||||
|     this->power_.unrequest(); | ||||
|   } | ||||
| #endif | ||||
|  | ||||
|   | ||||
| @@ -132,14 +132,20 @@ void PMSX003Component::parse_data_() { | ||||
|       break; | ||||
|     } | ||||
|     case PMSX003_TYPE_5003ST: { | ||||
|       uint16_t pm_1_0_concentration = this->get_16_bit_uint_(10); | ||||
|       uint16_t pm_2_5_concentration = this->get_16_bit_uint_(12); | ||||
|       uint16_t pm_10_0_concentration = this->get_16_bit_uint_(14); | ||||
|       uint16_t formaldehyde = this->get_16_bit_uint_(28); | ||||
|       float temperature = this->get_16_bit_uint_(30) / 10.0f; | ||||
|       float humidity = this->get_16_bit_uint_(32) / 10.0f; | ||||
|       ESP_LOGD(TAG, "Got PM2.5 Concentration: %u µg/m^3, Temperature: %.1f°C, Humidity: %.1f%% Formaldehyde: %u µg/m^3", | ||||
|                pm_2_5_concentration, temperature, humidity, formaldehyde); | ||||
|       if (this->pm_1_0_sensor_ != nullptr) | ||||
|         this->pm_1_0_sensor_->publish_state(pm_1_0_concentration); | ||||
|       if (this->pm_2_5_sensor_ != nullptr) | ||||
|         this->pm_2_5_sensor_->publish_state(pm_2_5_concentration); | ||||
|       if (this->pm_10_0_sensor_ != nullptr) | ||||
|         this->pm_10_0_sensor_->publish_state(pm_10_0_concentration); | ||||
|       if (this->temperature_sensor_ != nullptr) | ||||
|         this->temperature_sensor_->publish_state(temperature); | ||||
|       if (this->humidity_sensor_ != nullptr) | ||||
|   | ||||
| @@ -24,9 +24,9 @@ PMSX003_TYPES = { | ||||
| } | ||||
|  | ||||
| SENSORS_TO_TYPE = { | ||||
|     CONF_PM_1_0: [CONF_PMSX003], | ||||
|     CONF_PM_1_0: [CONF_PMSX003, CONF_PMS5003ST], | ||||
|     CONF_PM_2_5: [CONF_PMSX003, CONF_PMS5003T, CONF_PMS5003ST], | ||||
|     CONF_PM_10_0: [CONF_PMSX003], | ||||
|     CONF_PM_10_0: [CONF_PMSX003, CONF_PMS5003ST], | ||||
|     CONF_TEMPERATURE: [CONF_PMS5003T, CONF_PMS5003ST], | ||||
|     CONF_HUMIDITY: [CONF_PMS5003T, CONF_PMS5003ST], | ||||
|     CONF_FORMALDEHYDE: [CONF_PMS5003ST], | ||||
|   | ||||
| @@ -42,7 +42,7 @@ void PowerSupply::request_high_power() { | ||||
| void PowerSupply::unrequest_high_power() { | ||||
|   this->active_requests_--; | ||||
|   if (this->active_requests_ < 0) { | ||||
|     // if asserts are disabled we're just going to use 0 as our now counter. | ||||
|     // we're just going to use 0 as our now counter. | ||||
|     this->active_requests_ = 0; | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -39,5 +39,26 @@ class PowerSupply : public Component { | ||||
|   int16_t active_requests_{0};  // use signed integer to make catching negative requests easier. | ||||
| }; | ||||
|  | ||||
| class PowerSupplyRequester { | ||||
|  public: | ||||
|   void set_parent(PowerSupply *parent) { parent_ = parent; } | ||||
|   void request() { | ||||
|     if (!this->requested_ && this->parent_ != nullptr) { | ||||
|       this->parent_->request_high_power(); | ||||
|       this->requested_ = true; | ||||
|     } | ||||
|   } | ||||
|   void unrequest() { | ||||
|     if (this->requested_ && this->parent_ != nullptr) { | ||||
|       this->parent_->unrequest_high_power(); | ||||
|       this->requested_ = false; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|  protected: | ||||
|   PowerSupply *parent_{nullptr}; | ||||
|   bool requested_{false}; | ||||
| }; | ||||
|  | ||||
| }  // namespace power_supply | ||||
| }  // namespace esphome | ||||
|   | ||||
| @@ -92,36 +92,27 @@ class SunTrigger : public Trigger<>, public PollingComponent, public Parented<Su | ||||
|   void set_elevation(double elevation) { elevation_ = elevation; } | ||||
|  | ||||
|   void update() override { | ||||
|     auto now = this->parent_->get_time()->utcnow(); | ||||
|     if (!now.is_valid()) | ||||
|     double current = this->parent_->elevation(); | ||||
|     if (isnan(current)) | ||||
|       return; | ||||
|  | ||||
|     if (!this->last_result_.has_value() || this->last_result_->day_of_year != now.day_of_year) { | ||||
|       this->recalc_(); | ||||
|       return; | ||||
|     bool crossed; | ||||
|     if (this->sunrise_) { | ||||
|       crossed = this->last_elevation_ <= this->elevation_ && this->elevation_ < current; | ||||
|     } else { | ||||
|       crossed = this->last_elevation_ >= this->elevation_ && this->elevation_ > current; | ||||
|     } | ||||
|  | ||||
|     if (this->prev_check_ != -1) { | ||||
|       auto res = *this->last_result_; | ||||
|       // now >= sunrise > prev_check | ||||
|       if (now.timestamp >= res.timestamp && res.timestamp > this->prev_check_) { | ||||
|         this->trigger(); | ||||
|       } | ||||
|     if (crossed) { | ||||
|       this->trigger(); | ||||
|     } | ||||
|     this->prev_check_ = now.timestamp; | ||||
|     this->last_elevation_ = current; | ||||
|   } | ||||
|  | ||||
|  protected: | ||||
|   void recalc_() { | ||||
|     if (this->sunrise_) | ||||
|       this->last_result_ = this->parent_->sunrise(this->elevation_); | ||||
|     else | ||||
|       this->last_result_ = this->parent_->sunset(this->elevation_); | ||||
|   } | ||||
|   bool sunrise_; | ||||
|   double last_elevation_; | ||||
|   double elevation_; | ||||
|   time_t prev_check_{-1}; | ||||
|   optional<time::ESPTime> last_result_{}; | ||||
| }; | ||||
|  | ||||
| template<typename... Ts> class SunCondition : public Condition<Ts...>, public Parented<Sun> { | ||||
|   | ||||
| @@ -4,7 +4,7 @@ | ||||
| namespace esphome { | ||||
| namespace time { | ||||
|  | ||||
| static const char *TAG = "something.something"; | ||||
| static const char *TAG = "automation"; | ||||
|  | ||||
| void CronTrigger::add_second(uint8_t second) { this->seconds_[second] = true; } | ||||
| void CronTrigger::add_minute(uint8_t minute) { this->minutes_[minute] = true; } | ||||
|   | ||||
| @@ -69,10 +69,6 @@ class ComponentManifest(object): | ||||
|     def auto_load(self): | ||||
|         return getattr(self.module, 'AUTO_LOAD', []) | ||||
|  | ||||
|     @property | ||||
|     def to_code_priority(self): | ||||
|         return getattr(self.module, 'TO_CODE_PRIORITY', []) | ||||
|  | ||||
|     def _get_flags_set(self, name, config): | ||||
|         if not hasattr(self.module, name): | ||||
|             return set() | ||||
|   | ||||
| @@ -8,6 +8,7 @@ import re | ||||
| from contextlib import contextmanager | ||||
| import uuid as uuid_ | ||||
| from datetime import datetime | ||||
| from string import ascii_letters, digits | ||||
|  | ||||
| import voluptuous as vol | ||||
|  | ||||
| @@ -279,8 +280,9 @@ def validate_id_name(value): | ||||
|         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 + '_' | ||||
|     for char in value: | ||||
|         if char != '_' and not char.isalnum(): | ||||
|         if char not in valid_chars: | ||||
|             raise Invalid(u"IDs must only consist of upper/lowercase characters, the underscore" | ||||
|                           u"character and numbers. The character '{}' cannot be used" | ||||
|                           u"".format(char)) | ||||
| @@ -611,7 +613,7 @@ if IS_PY2: | ||||
|         path = u' @ data[%s]' % u']['.join(map(repr, self.path)) \ | ||||
|             if self.path else '' | ||||
|         # pylint: disable=no-member | ||||
|         output = Exception.__unicode__(self) | ||||
|         output = self.message | ||||
|         if self.error_type: | ||||
|             output += u' for ' + self.error_type | ||||
|         return output + path | ||||
|   | ||||
| @@ -11,8 +11,6 @@ ESP_PLATFORM_ESP32 = 'ESP32' | ||||
| ESP_PLATFORM_ESP8266 = 'ESP8266' | ||||
| ESP_PLATFORMS = [ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266] | ||||
|  | ||||
| APB_CLOCK_FREQ = 80000000 | ||||
|  | ||||
| ALLOWED_NAME_CHARS = u'abcdefghijklmnopqrstuvwxyz0123456789_' | ||||
| ARDUINO_VERSION_ESP32_DEV = 'https://github.com/platformio/platform-espressif32.git#feature/stage' | ||||
| ARDUINO_VERSION_ESP32_1_0_0 = 'espressif32@1.5.0' | ||||
|   | ||||
| @@ -60,7 +60,7 @@ void Application::setup() { | ||||
|   this->dump_config(); | ||||
| } | ||||
| void Application::dump_config() { | ||||
|   ESP_LOGI(TAG, "esphome-core version " ESPHOME_VERSION " compiled on %s", this->compilation_time_.c_str()); | ||||
|   ESP_LOGI(TAG, "esphome version " ESPHOME_VERSION " compiled on %s", this->compilation_time_.c_str()); | ||||
|  | ||||
|   for (auto component : this->components_) { | ||||
|     component->dump_config(); | ||||
|   | ||||
| @@ -89,8 +89,7 @@ def default_build_path(): | ||||
|  | ||||
| CONFIG_SCHEMA = cv.Schema({ | ||||
|     cv.Required(CONF_NAME): cv.valid_name, | ||||
|     cv.Required(CONF_PLATFORM): cv.one_of('ESP8266', 'ESP32', 'ESPRESSIF32', | ||||
|                                           upper=True), | ||||
|     cv.Required(CONF_PLATFORM): cv.one_of('ESP8266', 'ESP32', upper=True), | ||||
|     cv.Required(CONF_BOARD): validate_board, | ||||
|     cv.Optional(CONF_ARDUINO_VERSION, default='recommended'): validate_arduino_version, | ||||
|     cv.Optional(CONF_BUILD_PATH, default=default_build_path): cv.string, | ||||
| @@ -114,6 +113,10 @@ CONFIG_SCHEMA = cv.Schema({ | ||||
|     }), | ||||
|     cv.Optional(CONF_INCLUDES, default=[]): cv.ensure_list(cv.file_), | ||||
|     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.") | ||||
| }) | ||||
|  | ||||
| PRELOAD_CONFIG_SCHEMA = cv.Schema({ | ||||
|   | ||||
| @@ -328,6 +328,17 @@ class ExpressionStatement(Statement): | ||||
|         return u"{};".format(self.expression) | ||||
|  | ||||
|  | ||||
| class LineComment(Statement): | ||||
|     def __init__(self, value):  # type: (unicode) -> None | ||||
|         super(LineComment, self).__init__() | ||||
|         self._value = value | ||||
|  | ||||
|     def __str__(self): | ||||
|         parts = self._value.split(u'\n') | ||||
|         parts = [u'// {}'.format(x) for x in parts] | ||||
|         return u'\n'.join(parts) | ||||
|  | ||||
|  | ||||
| class ProgmemAssignmentExpression(AssignmentExpression): | ||||
|     def __init__(self, type, name, rhs, obj): | ||||
|         super(ProgmemAssignmentExpression, self).__init__( | ||||
|   | ||||
| @@ -729,7 +729,7 @@ def start_web_server(args): | ||||
|  | ||||
|             webbrowser.open('localhost:{}'.format(args.port)) | ||||
|  | ||||
|     if not settings.status_use_ping: | ||||
|     if settings.status_use_ping: | ||||
|         status_thread = PingStatusThread() | ||||
|     else: | ||||
|         status_thread = MDNSStatusThread() | ||||
|   | ||||
| @@ -708,3 +708,11 @@ const startWizard = () => { | ||||
| }; | ||||
|  | ||||
| setupWizardStart.addEventListener('click', startWizard); | ||||
|  | ||||
| jQuery.validator.addMethod("nospaces", (value, element) => { | ||||
|   return value.indexOf(' ') < 0; | ||||
| }, "Name must not contain spaces."); | ||||
|  | ||||
| jQuery.validator.addMethod("lowercase", (value, element) => { | ||||
|   return value === value.toLowerCase(); | ||||
| }, "Name must be lowercase."); | ||||
|   | ||||
| @@ -189,7 +189,7 @@ | ||||
|               <code class="inlinecode">0-9</code> and <code class="inlinecode">_</code>) | ||||
|             </p> | ||||
|             <div class="input-field col s12"> | ||||
|               <input id="node_name" class="validate" type="text" name="name" required> | ||||
|               <input id="node_name" class="validate" type="text" name="name" data-rule-nospaces="true" data-rule-lowercase="true" required> | ||||
|               <label for="node_name">Name of node</label> | ||||
|             </div> | ||||
|           </div> | ||||
| @@ -207,8 +207,7 @@ | ||||
|               Please choose the board you're using below. | ||||
|             </p> | ||||
|             <p> | ||||
|               If you're not sure you can also use similar ones or even the | ||||
|               "Generic" option. In most cases that will work too. | ||||
|               <em>If unsure you can also select a similar board or choose the "Generic" option.</em> | ||||
|             </p> | ||||
|             <div class="input-field col s12"> | ||||
|               <select id="board" name="board" required> | ||||
|   | ||||
| @@ -1,93 +1,9 @@ | ||||
| #!/usr/bin/env python | ||||
| import codecs | ||||
| import json | ||||
| import os | ||||
| import re | ||||
| import sys | ||||
| import os.path | ||||
|  | ||||
| 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 walk_files(path): | ||||
|     for root, _, files in os.walk(path): | ||||
|         for name in files: | ||||
|             yield os.path.join(root, name) | ||||
|  | ||||
|  | ||||
| def shlex_quote(s): | ||||
|     if not s: | ||||
|         return u"''" | ||||
|     if re.search(r'[^\w@%+=:,./-]', s) is None: | ||||
|         return s | ||||
|  | ||||
|     return u"'" + s.replace(u"'", u"'\"'\"'") + u"'" | ||||
|  | ||||
|  | ||||
| def build_all_include(): | ||||
|     # Build a cpp file that includes all header files in this repo. | ||||
|     # Otherwise header-only integrations would not be tested by clang-tidy | ||||
|     headers = [] | ||||
|     for path in walk_files(basepath): | ||||
|         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, '/') | ||||
|             headers.append('#include "{}"'.format(include_p)) | ||||
|     headers.sort() | ||||
|     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') | ||||
|     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.') | ||||
|         sys.exit(1) | ||||
|     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 = ['-I{}'.format(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') | ||||
|  | ||||
|     source_files = [] | ||||
|     for path in walk_files(basepath): | ||||
|         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') | ||||
|     if os.path.isfile(compile_commands_json): | ||||
|         with codecs.open(compile_commands_json, 'r', encoding='utf-8') as f: | ||||
|             try: | ||||
|                 if json.load(f) == compile_commands: | ||||
|                     return | ||||
|             except: | ||||
|                 pass | ||||
|     with codecs.open(compile_commands_json, 'w', encoding='utf-8') as f: | ||||
|         json.dump(compile_commands, f, indent=2) | ||||
| sys.path.append(os.path.dirname(__file__)) | ||||
| from helpers import build_all_include, build_compile_commands | ||||
|  | ||||
|  | ||||
| def main(): | ||||
|   | ||||
| @@ -4,7 +4,6 @@ from __future__ import print_function | ||||
| import codecs | ||||
| import collections | ||||
| import fnmatch | ||||
| import functools | ||||
| import os.path | ||||
| import subprocess | ||||
| import sys | ||||
| @@ -155,6 +154,28 @@ def lint_pragma_once(content): | ||||
|     return None | ||||
|  | ||||
|  | ||||
| @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/core/component.h', | ||||
|     'esphome/core/esphal.h', | ||||
|     'esphome/core/log.h', | ||||
|     'tests/custom.h', | ||||
| ]) | ||||
| def lint_log_in_header(): | ||||
|     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) | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -2,16 +2,19 @@ | ||||
| 
 | ||||
| from __future__ import print_function | ||||
| 
 | ||||
| import argparse | ||||
| import multiprocessing | ||||
| import os | ||||
| import re | ||||
| import subprocess | ||||
| import sys | ||||
| 
 | ||||
| import argparse | ||||
| import click | ||||
| import threading | ||||
| 
 | ||||
| import click | ||||
| 
 | ||||
| sys.path.append(os.path.dirname(__file__)) | ||||
| from helpers import basepath, get_output, walk_files, filter_changed | ||||
| 
 | ||||
| is_py2 = sys.version[0] == '2' | ||||
| 
 | ||||
| if is_py2: | ||||
| @@ -50,44 +53,6 @@ def progress_bar_show(value): | ||||
|     return value | ||||
| 
 | ||||
| 
 | ||||
| def walk_files(path): | ||||
|     for root, _, files in os.walk(path): | ||||
|         for name in files: | ||||
|             yield os.path.join(root, name) | ||||
| 
 | ||||
| 
 | ||||
| def get_output(*args): | ||||
|     proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) | ||||
|     output, err = proc.communicate() | ||||
|     return output.decode('utf-8') | ||||
| 
 | ||||
| 
 | ||||
| def splitlines_no_ends(string): | ||||
|     return [s.strip() for s in string.splitlines()] | ||||
| 
 | ||||
| 
 | ||||
| def filter_changed(files): | ||||
|     for remote in ('upstream', 'origin'): | ||||
|         command = ['git', 'merge-base', '{}/dev'.format(remote), 'HEAD'] | ||||
|         try: | ||||
|             merge_base = splitlines_no_ends(get_output(*command))[0] | ||||
|             break | ||||
|         except: | ||||
|             pass | ||||
|     else: | ||||
|         return files | ||||
|     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} | ||||
|     print("Changed Files:") | ||||
|     files = [p for p in files if p in changed] | ||||
|     for p in files: | ||||
|         print("  {}".format(p)) | ||||
|     if not files: | ||||
|         print("  No changed files") | ||||
|     return files | ||||
| 
 | ||||
| 
 | ||||
| def main(): | ||||
|     parser = argparse.ArgumentParser() | ||||
|     parser.add_argument('-j', '--jobs', type=int, | ||||
| @@ -2,8 +2,6 @@ | ||||
| 
 | ||||
| from __future__ import print_function | ||||
| 
 | ||||
| import codecs | ||||
| import json | ||||
| import multiprocessing | ||||
| import os | ||||
| import re | ||||
| @@ -18,6 +16,10 @@ import argparse | ||||
| import click | ||||
| import threading | ||||
| 
 | ||||
| sys.path.append(os.path.dirname(__file__)) | ||||
| from helpers import basepath, shlex_quote, get_output, build_compile_commands, \ | ||||
|     build_all_include, temp_header_file, walk_files, filter_changed | ||||
| 
 | ||||
| is_py2 = sys.version[0] == '2' | ||||
| 
 | ||||
| if is_py2: | ||||
| @@ -25,11 +27,6 @@ if is_py2: | ||||
| else: | ||||
|     import queue as queue | ||||
| 
 | ||||
| root_path = os.path.abspath(os.path.normpath(os.path.join(__file__, '..', '..'))) | ||||
| basepath = os.path.join(root_path, 'esphome') | ||||
| rel_basepath = os.path.relpath(basepath, os.getcwd()) | ||||
| temp_header_file = os.path.join(root_path, '.temp-clang-tidy.cpp') | ||||
| 
 | ||||
| 
 | ||||
| def run_tidy(args, tmpdir, queue, lock, failed_files): | ||||
|     while True: | ||||
| @@ -67,119 +64,6 @@ def run_tidy(args, tmpdir, queue, lock, failed_files): | ||||
| def progress_bar_show(value): | ||||
|     if value is None: | ||||
|         return '' | ||||
|     return value | ||||
| 
 | ||||
| 
 | ||||
| def walk_files(path): | ||||
|     for root, _, files in os.walk(path): | ||||
|         for name in files: | ||||
|             yield os.path.join(root, name) | ||||
| 
 | ||||
| 
 | ||||
| def get_output(*args): | ||||
|     proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) | ||||
|     output, err = proc.communicate() | ||||
|     return output.decode('utf-8') | ||||
| 
 | ||||
| 
 | ||||
| def splitlines_no_ends(string): | ||||
|     return [s.strip() for s in string.splitlines()] | ||||
| 
 | ||||
| 
 | ||||
| def filter_changed(files): | ||||
|     for remote in ('upstream', 'origin'): | ||||
|         command = ['git', 'merge-base', '{}/dev'.format(remote), 'HEAD'] | ||||
|         try: | ||||
|             merge_base = splitlines_no_ends(get_output(*command))[0] | ||||
|             break | ||||
|         except: | ||||
|             pass | ||||
|     else: | ||||
|         return files | ||||
|     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} | ||||
|     print("Changed Files:") | ||||
|     files = [p for p in files if p in changed] | ||||
|     for p in files: | ||||
|         print("  {}".format(p)) | ||||
|     if not files: | ||||
|         print("  No changed files") | ||||
|     return files | ||||
| 
 | ||||
| 
 | ||||
| def shlex_quote(s): | ||||
|     if not s: | ||||
|         return u"''" | ||||
|     if re.search(r'[^\w@%+=:,./-]', s) is None: | ||||
|         return s | ||||
| 
 | ||||
|     return u"'" + s.replace(u"'", u"'\"'\"'") + u"'" | ||||
| 
 | ||||
| 
 | ||||
| def build_compile_commands(): | ||||
|     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.') | ||||
|         sys.exit(1) | ||||
|     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 = ['-I{}'.format(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') | ||||
| 
 | ||||
|     source_files = [] | ||||
|     for path in walk_files(basepath): | ||||
|         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') | ||||
|     if os.path.isfile(compile_commands_json): | ||||
|         with codecs.open(compile_commands_json, 'r', encoding='utf-8') as f: | ||||
|             try: | ||||
|                 if json.load(f) == compile_commands: | ||||
|                     return | ||||
|             except: | ||||
|                 pass | ||||
|     with codecs.open(compile_commands_json, 'w', encoding='utf-8') as f: | ||||
|         json.dump(compile_commands, f, indent=2) | ||||
| 
 | ||||
| 
 | ||||
| def build_all_include(): | ||||
|     # Build a cpp file that includes all header files in this repo. | ||||
|     # Otherwise header-only integrations would not be tested by clang-tidy | ||||
|     headers = [] | ||||
|     for path in walk_files(basepath): | ||||
|         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, '/') | ||||
|             headers.append('#include "{}"'.format(include_p)) | ||||
|     headers.sort() | ||||
|     headers.append('') | ||||
|     content = '\n'.join(headers) | ||||
|     with codecs.open(temp_header_file, 'w', encoding='utf-8') as f: | ||||
|         f.write(content) | ||||
| 
 | ||||
| 
 | ||||
| def main(): | ||||
| @@ -212,6 +96,7 @@ def main(): | ||||
|         """) | ||||
|         return 1 | ||||
| 
 | ||||
|     build_all_include() | ||||
|     build_compile_commands() | ||||
| 
 | ||||
|     files = [] | ||||
| @@ -231,7 +116,6 @@ def main(): | ||||
|     files.sort() | ||||
| 
 | ||||
|     if args.all_headers: | ||||
|         build_all_include() | ||||
|         files.insert(0, temp_header_file) | ||||
| 
 | ||||
|     tmpdir = None | ||||
							
								
								
									
										128
									
								
								script/helpers.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								script/helpers.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,128 @@ | ||||
| import codecs | ||||
| import json | ||||
| import os.path | ||||
| 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') | ||||
|  | ||||
|  | ||||
| def shlex_quote(s): | ||||
|     if not s: | ||||
|         return u"''" | ||||
|     if re.search(r'[^\w@%+=:,./-]', s) is None: | ||||
|         return s | ||||
|  | ||||
|     return u"'" + s.replace(u"'", u"'\"'\"'") + u"'" | ||||
|  | ||||
|  | ||||
| def build_all_include(): | ||||
|     # Build a cpp file that includes all header files in this repo. | ||||
|     # Otherwise header-only integrations would not be tested by clang-tidy | ||||
|     headers = [] | ||||
|     for path in walk_files(basepath): | ||||
|         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, '/') | ||||
|             headers.append('#include "{}"'.format(include_p)) | ||||
|     headers.sort() | ||||
|     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') | ||||
|     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.') | ||||
|         sys.exit(1) | ||||
|     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 = ['-I{}'.format(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') | ||||
|  | ||||
|     source_files = [] | ||||
|     for path in walk_files(basepath): | ||||
|         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') | ||||
|     if os.path.isfile(compile_commands_json): | ||||
|         with codecs.open(compile_commands_json, 'r', encoding='utf-8') as f: | ||||
|             try: | ||||
|                 if json.load(f) == compile_commands: | ||||
|                     return | ||||
|             except: | ||||
|                 pass | ||||
|     with codecs.open(compile_commands_json, 'w', encoding='utf-8') as f: | ||||
|         json.dump(compile_commands, f, indent=2) | ||||
|  | ||||
|  | ||||
| def walk_files(path): | ||||
|     for root, _, files in os.walk(path): | ||||
|         for name in files: | ||||
|             yield os.path.join(root, name) | ||||
|  | ||||
|  | ||||
| def get_output(*args): | ||||
|     proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) | ||||
|     output, err = proc.communicate() | ||||
|     return output.decode('utf-8') | ||||
|  | ||||
|  | ||||
| def splitlines_no_ends(string): | ||||
|     return [s.strip() for s in string.splitlines()] | ||||
|  | ||||
|  | ||||
| def changed_files(): | ||||
|     for remote in ('upstream', 'origin'): | ||||
|         command = ['git', 'merge-base', '{}/dev'.format(remote), 'HEAD'] | ||||
|         try: | ||||
|             merge_base = splitlines_no_ends(get_output(*command))[0] | ||||
|             break | ||||
|         except: | ||||
|             pass | ||||
|     else: | ||||
|         raise ValueError("Git not configured") | ||||
|     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() | ||||
|     return changed | ||||
|  | ||||
|  | ||||
| def filter_changed(files): | ||||
|     changed = changed_files() | ||||
|     files = [f for f in files if f in changed] | ||||
|     print("Changed files:") | ||||
|     if not files: | ||||
|         print("    No changed files!") | ||||
|     for c in files: | ||||
|         print("    {}".format(c)) | ||||
|     return files | ||||
| @@ -12,5 +12,5 @@ fi | ||||
|  | ||||
| set -x | ||||
|  | ||||
| script/clang-tidy.py -c --fix --all-headers | ||||
| script/clang-format.py -c -i | ||||
| script/clang-tidy -c --fix --all-headers | ||||
| script/clang-format -c -i | ||||
|   | ||||
| @@ -1,10 +1,74 @@ | ||||
| #!/usr/bin/env bash | ||||
| #!/usr/bin/env python | ||||
|  | ||||
| set -e | ||||
| from __future__ import print_function | ||||
|  | ||||
| cd "$(dirname "$0")/.." | ||||
| set -x | ||||
| import argparse | ||||
| import collections | ||||
| import os | ||||
| import re | ||||
| import sys | ||||
|  | ||||
| script/ci-custom.py | ||||
| flake8 esphome | ||||
| pylint esphome | ||||
| sys.path.append(os.path.dirname(__file__)) | ||||
| from helpers import basepath, get_output, walk_files, filter_changed | ||||
|  | ||||
|  | ||||
| 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') | ||||
|     args = parser.parse_args() | ||||
|  | ||||
|     files = [] | ||||
|     for path in walk_files(basepath): | ||||
|         filetypes = ('.py',) | ||||
|         ext = os.path.splitext(path)[1] | ||||
|         if ext in filetypes: | ||||
|             path = os.path.relpath(path, os.getcwd()) | ||||
|             files.append(path) | ||||
|     # Match against re | ||||
|     file_name_re = re.compile('|'.join(args.files)) | ||||
|     files = [p for p in files if file_name_re.search(p)] | ||||
|  | ||||
|     if args.changed: | ||||
|         files = filter_changed(files) | ||||
|  | ||||
|     files.sort() | ||||
|  | ||||
|     errors = collections.defaultdict(list) | ||||
|     cmd = ['flake8'] + files | ||||
|     print("Running flake8...") | ||||
|     log = get_output(*cmd) | ||||
|     for line in log.splitlines(): | ||||
|         line = line.split(':') | ||||
|         if len(line) < 4: | ||||
|             continue | ||||
|         file_ = line[0] | ||||
|         linno = line[1] | ||||
|         msg = (u':'.join(line[3:])).strip() | ||||
|         errors[file_].append(u'{}:{} - {}'.format(file_, linno, msg)) | ||||
|  | ||||
|     cmd = ['pylint', '-f', 'parseable', '--persistent=n'] + files | ||||
|     print("Running pylint...") | ||||
|     log = get_output(*cmd) | ||||
|     for line in log.splitlines(): | ||||
|         line = line.split(':') | ||||
|         if len(line) < 3: | ||||
|             continue | ||||
|         file_ = line[0] | ||||
|         linno = line[1] | ||||
|         msg = (u':'.join(line[3:])).strip() | ||||
|         errors[file_].append(u'{}:{} - {}'.format(file_, linno, msg)) | ||||
|  | ||||
|     for f, errs in sorted(errors.items()): | ||||
|         print("\033[0;32m************* File \033[1;32m{}\033[0m".format(f)) | ||||
|         for err in errs: | ||||
|             print(err) | ||||
|     print() | ||||
|  | ||||
|     sys.exit(len(errors)) | ||||
|  | ||||
|  | ||||
| if __name__ == '__main__': | ||||
|     main() | ||||
|   | ||||
							
								
								
									
										11
									
								
								script/quicklint
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										11
									
								
								script/quicklint
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| #!/usr/bin/env bash | ||||
|  | ||||
| set -e | ||||
|  | ||||
| cd "$(dirname "$0")/.." | ||||
|  | ||||
| set -x | ||||
|  | ||||
| script/ci-custom.py | ||||
| script/lint-python -c | ||||
| script/lint-cpp | ||||
| @@ -15,6 +15,14 @@ esphome: | ||||
|     then: | ||||
|       - lambda: >- | ||||
|           ESP_LOGV("main", "ON LOOP!"); | ||||
|       - light.addressable_set: | ||||
|           id: addr1 | ||||
|           range_from: 1 | ||||
|           range_to: 100 | ||||
|           red: 100% | ||||
|           green: !lambda 'return 255;' | ||||
|           blue: 0% | ||||
|           white: 100% | ||||
|   build_path: build/test1 | ||||
|  | ||||
| wifi: | ||||
|   | ||||
		Reference in New Issue
	
	Block a user