mirror of
https://github.com/esphome/esphome.git
synced 2024-11-25 00:18:11 +01: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:
parent
797aadaf26
commit
f811b1157c
41 changed files with 438 additions and 418 deletions
|
@ -51,6 +51,6 @@ matrix:
|
||||||
- clang-format-7 -version
|
- clang-format-7 -version
|
||||||
- clang-apply-replacements-7 -version
|
- clang-apply-replacements-7 -version
|
||||||
script:
|
script:
|
||||||
- script/clang-tidy.py --all-headers -j 2 --fix
|
- script/clang-tidy --all-headers -j 2 --fix
|
||||||
- script/clang-format.py -i -j 2
|
- script/clang-format -i -j 2
|
||||||
- script/ci-suggest-changes
|
- script/ci-suggest-changes
|
||||||
|
|
|
@ -1,16 +1,18 @@
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
import functools
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from esphome import const, writer, yaml_util
|
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.config import iter_components, read_config, strip_default_ids
|
||||||
from esphome.const import CONF_BAUD_RATE, CONF_BROKER, CONF_LOGGER, CONF_OTA, \
|
from esphome.const import CONF_BAUD_RATE, CONF_BROKER, CONF_LOGGER, CONF_OTA, \
|
||||||
CONF_PASSWORD, CONF_PORT
|
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.helpers import color, indent
|
||||||
from esphome.py_compat import IS_PY2, safe_input
|
from esphome.py_compat import IS_PY2, safe_input
|
||||||
from esphome.util import run_external_command, run_external_process, safe_print
|
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)
|
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):
|
def write_cpp(config):
|
||||||
_LOGGER.info("Generating C++ source...")
|
_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:
|
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()
|
CORE.flush_tasks()
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
# pylint: disable=unused-import
|
# pylint: disable=unused-import
|
||||||
from esphome.cpp_generator import ( # noqa
|
from esphome.cpp_generator import ( # noqa
|
||||||
Expression, RawExpression, RawStatement, TemplateArguments,
|
Expression, RawExpression, RawStatement, TemplateArguments,
|
||||||
StructInitializer, ArrayInitializer, safe_exp, Statement,
|
StructInitializer, ArrayInitializer, safe_exp, Statement, LineComment,
|
||||||
progmem_array, statement, variable, Pvariable, new_Pvariable,
|
progmem_array, statement, variable, Pvariable, new_Pvariable,
|
||||||
add, add_global, add_library, add_build_flag, add_define,
|
add, add_global, add_library, add_build_flag, add_define,
|
||||||
get_variable, get_variable_with_full_id, process_lambda, is_template, templatable, MockObj,
|
get_variable, get_variable_with_full_id, process_lambda, is_template, templatable, MockObj,
|
||||||
|
|
|
@ -5,7 +5,7 @@ namespace esphome {
|
||||||
|
|
||||||
namespace binary_sensor {
|
namespace binary_sensor {
|
||||||
|
|
||||||
static const char *TAG = "something.Filter";
|
static const char *TAG = "sensor.filter";
|
||||||
|
|
||||||
void Filter::output(bool value, bool is_initial) {
|
void Filter::output(bool value, bool is_initial) {
|
||||||
if (!this->dedup_.next(value))
|
if (!this->dedup_.next(value))
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace ble_presence {
|
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); }
|
void BLEPresenceDevice::dump_config() { LOG_BINARY_SENSOR("", "BLE Presence", this); }
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace esp8266_pwm {
|
namespace esp8266_pwm {
|
||||||
|
|
||||||
static const char *TAG = "something.something";
|
static const char *TAG = "esp8266_pwm";
|
||||||
|
|
||||||
void ESP8266PWM::setup() {
|
void ESP8266PWM::setup() {
|
||||||
ESP_LOGCONFIG(TAG, "Setting up ESP8266 PWM Output...");
|
ESP_LOGCONFIG(TAG, "Setting up ESP8266 PWM Output...");
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.components import light, 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.const import CONF_OUTPUT_ID, CONF_NUM_LEDS, CONF_RGB_ORDER, CONF_MAX_REFRESH_RATE
|
||||||
CONF_POWER_SUPPLY
|
|
||||||
from esphome.core import coroutine
|
from esphome.core import coroutine
|
||||||
|
|
||||||
fastled_base_ns = cg.esphome_ns.namespace('fastled_base')
|
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.Required(CONF_NUM_LEDS): cv.positive_not_null_int,
|
||||||
cv.Optional(CONF_RGB_ORDER): cv.one_of(*RGB_ORDERS, upper=True),
|
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_MAX_REFRESH_RATE): cv.positive_time_period_microseconds,
|
||||||
|
|
||||||
cv.Optional(CONF_POWER_SUPPLY): cv.use_id(power_supply.PowerSupply),
|
|
||||||
}).extend(cv.COMPONENT_SCHEMA)
|
}).extend(cv.COMPONENT_SCHEMA)
|
||||||
|
|
||||||
|
|
||||||
|
@ -37,10 +34,6 @@ def new_fastled_light(config):
|
||||||
if CONF_MAX_REFRESH_RATE in config:
|
if CONF_MAX_REFRESH_RATE in config:
|
||||||
cg.add(var.set_max_refresh_rate(config[CONF_MAX_REFRESH_RATE]))
|
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)
|
yield light.register_light(var, config)
|
||||||
cg.add_library('FastLED', '3.2.0')
|
cg.add_library('FastLED', '3.2.0')
|
||||||
yield var
|
yield var
|
||||||
|
|
|
@ -33,27 +33,6 @@ void FastLEDLightOutput::loop() {
|
||||||
this->mark_shown_();
|
this->mark_shown_();
|
||||||
|
|
||||||
ESP_LOGVV(TAG, "Writing RGB values to bus...");
|
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();
|
this->controller_->showLeds();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,10 +4,6 @@
|
||||||
#include "esphome/core/helpers.h"
|
#include "esphome/core/helpers.h"
|
||||||
#include "esphome/components/light/addressable_light.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_ESP8266_RAW_PIN_ORDER
|
||||||
#define FASTLED_ESP32_RAW_PIN_ORDER
|
#define FASTLED_ESP32_RAW_PIN_ORDER
|
||||||
#define FASTLED_RMT_BUILTIN_DRIVER true
|
#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.
|
/// 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; }
|
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.
|
/// Add some LEDS, can only be called once.
|
||||||
CLEDController &add_leds(CLEDController *controller, int num_leds) {
|
CLEDController &add_leds(CLEDController *controller, int num_leds) {
|
||||||
this->controller_ = controller;
|
this->controller_ = controller;
|
||||||
|
@ -242,10 +234,6 @@ class FastLEDLightOutput : public Component, public light::AddressableLight {
|
||||||
int num_leds_{0};
|
int num_leds_{0};
|
||||||
uint32_t last_refresh_{0};
|
uint32_t last_refresh_{0};
|
||||||
optional<uint32_t> max_refresh_rate_{};
|
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
|
} // 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.Required(CONF_TIME_UNIT): cv.enum(INTEGRATION_TIMES, lower=True),
|
||||||
cv.Optional(CONF_INTEGRATION_METHOD, default='trapezoid'):
|
cv.Optional(CONF_INTEGRATION_METHOD, default='trapezoid'):
|
||||||
cv.enum(INTEGRATION_METHODS, lower=True),
|
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)
|
}).extend(cv.COMPONENT_SCHEMA)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,20 +1,54 @@
|
||||||
|
import math
|
||||||
|
|
||||||
from esphome import pins
|
from esphome import pins
|
||||||
from esphome.components import output
|
from esphome.components import output
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.const import 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
|
CONF_ID, CONF_PIN, ESP_PLATFORM_ESP32
|
||||||
|
|
||||||
ESP_PLATFORMS = [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):
|
def validate_frequency_bit_depth(obj):
|
||||||
frequency = obj[CONF_FREQUENCY]
|
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]
|
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:
|
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
|
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_ID): cv.declare_id(LEDCOutput),
|
||||||
cv.Required(CONF_PIN): pins.internal_gpio_output_pin_schema,
|
cv.Required(CONF_PIN): pins.internal_gpio_output_pin_schema,
|
||||||
cv.Optional(CONF_FREQUENCY, default='1kHz'): cv.frequency,
|
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),
|
cv.Optional(CONF_CHANNEL): cv.int_range(min=0, max=15),
|
||||||
}).extend(cv.COMPONENT_SCHEMA), validate_frequency_bit_depth)
|
}).extend(cv.COMPONENT_SCHEMA), validate_frequency_bit_depth)
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.components import mqtt
|
from esphome.components import mqtt, power_supply
|
||||||
from esphome.const import CONF_COLOR_CORRECT, \
|
from esphome.const import CONF_COLOR_CORRECT, \
|
||||||
CONF_DEFAULT_TRANSITION_LENGTH, CONF_EFFECTS, CONF_GAMMA_CORRECT, CONF_ID, \
|
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 esphome.core import coroutine, coroutine_with_priority
|
||||||
from .automation import light_control_to_code # noqa
|
from .automation import light_control_to_code # noqa
|
||||||
from .effects import validate_effects, BINARY_EFFECTS, \
|
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.GenerateID(): cv.declare_id(AddressableLightState),
|
||||||
cv.Optional(CONF_EFFECTS): validate_effects(ADDRESSABLE_EFFECTS),
|
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_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:
|
if CONF_COLOR_CORRECT in config:
|
||||||
cg.add(output_var.set_correction(*config[CONF_COLOR_CORRECT]))
|
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:
|
if CONF_MQTT_ID in config:
|
||||||
mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], light_var)
|
mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], light_var)
|
||||||
yield mqtt.register_mqtt_component(mqtt_, config)
|
yield mqtt.register_mqtt_component(mqtt_, config)
|
||||||
|
|
|
@ -1,9 +1,14 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/core/defines.h"
|
||||||
#include "light_output.h"
|
#include "light_output.h"
|
||||||
#include "light_state.h"
|
#include "light_state.h"
|
||||||
|
|
||||||
|
#ifdef USE_POWER_SUPPLY
|
||||||
|
#include "esphome/components/power_supply/power_supply.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace light {
|
namespace light {
|
||||||
|
|
||||||
|
@ -30,6 +35,7 @@ struct ESPColor {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
uint8_t raw[4];
|
uint8_t raw[4];
|
||||||
|
uint32_t raw_32;
|
||||||
};
|
};
|
||||||
inline ESPColor() ALWAYS_INLINE : r(0), g(0), b(0), w(0) {} // NOLINT
|
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),
|
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->b = rhs.b;
|
||||||
this->w = rhs.w;
|
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 {
|
inline ESPColor &operator=(const ESPColor &rhs) ALWAYS_INLINE {
|
||||||
this->r = rhs.r;
|
this->r = rhs.r;
|
||||||
this->g = rhs.g;
|
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 setup_state(LightState *state) override { this->correction_.calculate_gamma_table(state->get_gamma_correct()); }
|
||||||
void schedule_show() { this->next_show_ = true; }
|
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:
|
protected:
|
||||||
bool should_show_() const { return this->effect_active_ || this->next_show_; }
|
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;
|
virtual ESPColorView get_view_internal(int32_t index) const = 0;
|
||||||
|
|
||||||
bool effect_active_{false};
|
bool effect_active_{false};
|
||||||
bool next_show_{true};
|
bool next_show_{true};
|
||||||
ESPColorCorrection correction_{};
|
ESPColorCorrection correction_{};
|
||||||
|
#ifdef USE_POWER_SUPPLY
|
||||||
|
power_supply::PowerSupplyRequester power_;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace light
|
} // namespace light
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome import pins
|
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, \
|
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
|
from esphome.core import CORE
|
||||||
|
|
||||||
neopixelbus_ns = cg.esphome_ns.namespace('neopixelbus')
|
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.Optional(CONF_DATA_PIN): pins.output_pin,
|
||||||
|
|
||||||
cv.Required(CONF_NUM_LEDS): cv.positive_not_null_int,
|
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)
|
}).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])))
|
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')
|
cg.add_library('NeoPixelBus', '2.4.1')
|
||||||
|
|
|
@ -9,10 +9,6 @@
|
||||||
#error The NeoPixelBus library requires at least arduino_core_version 2.4.x
|
#error The NeoPixelBus library requires at least arduino_core_version 2.4.x
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_POWER_SUPPLY
|
|
||||||
#include "esphome/components/power_supply/power_supply.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "NeoPixelBus.h"
|
#include "NeoPixelBus.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
|
@ -54,10 +50,6 @@ enum class ESPNeoPixelOrder {
|
||||||
template<typename T_METHOD, typename T_COLOR_FEATURE>
|
template<typename T_METHOD, typename T_COLOR_FEATURE>
|
||||||
class NeoPixelBusLightOutputBase : public Component, public light::AddressableLight {
|
class NeoPixelBusLightOutputBase : public Component, public light::AddressableLight {
|
||||||
public:
|
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_; }
|
NeoPixelBus<T_COLOR_FEATURE, T_METHOD> *get_controller() const { return this->controller_; }
|
||||||
|
|
||||||
void clear_effect_data() override {
|
void clear_effect_data() override {
|
||||||
|
@ -95,27 +87,6 @@ class NeoPixelBusLightOutputBase : public Component, public light::AddressableLi
|
||||||
this->mark_shown_();
|
this->mark_shown_();
|
||||||
this->controller_->Dirty();
|
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();
|
this->controller_->Show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,10 +106,6 @@ class NeoPixelBusLightOutputBase : public Component, public light::AddressableLi
|
||||||
NeoPixelBus<T_COLOR_FEATURE, T_METHOD> *controller_{nullptr};
|
NeoPixelBus<T_COLOR_FEATURE, T_METHOD> *controller_{nullptr};
|
||||||
uint8_t *effect_data_{nullptr};
|
uint8_t *effect_data_{nullptr};
|
||||||
uint8_t rgb_offsets_[4]{0, 1, 2, 3};
|
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>
|
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.
|
* @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
|
#endif
|
||||||
|
|
||||||
/// Enable this binary output.
|
/// Enable this binary output.
|
||||||
virtual void turn_on() {
|
virtual void turn_on() {
|
||||||
#ifdef USE_POWER_SUPPLY
|
#ifdef USE_POWER_SUPPLY
|
||||||
if (this->power_supply_ != nullptr && !this->has_requested_high_power_) {
|
this->power_.request();
|
||||||
this->power_supply_->request_high_power();
|
|
||||||
this->has_requested_high_power_ = true;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
this->write_state(!this->inverted_);
|
this->write_state(!this->inverted_);
|
||||||
}
|
}
|
||||||
|
@ -44,10 +41,7 @@ class BinaryOutput {
|
||||||
/// Disable this binary output.
|
/// Disable this binary output.
|
||||||
virtual void turn_off() {
|
virtual void turn_off() {
|
||||||
#ifdef USE_POWER_SUPPLY
|
#ifdef USE_POWER_SUPPLY
|
||||||
if (this->power_supply_ != nullptr && this->has_requested_high_power_) {
|
this->power_.unrequest();
|
||||||
this->power_supply_->unrequest_high_power();
|
|
||||||
this->has_requested_high_power_ = false;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
this->write_state(this->inverted_);
|
this->write_state(this->inverted_);
|
||||||
}
|
}
|
||||||
|
@ -62,8 +56,7 @@ class BinaryOutput {
|
||||||
|
|
||||||
bool inverted_{false};
|
bool inverted_{false};
|
||||||
#ifdef USE_POWER_SUPPLY
|
#ifdef USE_POWER_SUPPLY
|
||||||
power_supply::PowerSupply *power_supply_{nullptr};
|
power_supply::PowerSupplyRequester power_{};
|
||||||
bool has_requested_high_power_{false};
|
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -24,15 +24,9 @@ void FloatOutput::set_level(float state) {
|
||||||
|
|
||||||
#ifdef USE_POWER_SUPPLY
|
#ifdef USE_POWER_SUPPLY
|
||||||
if (state > 0.0f) { // ON
|
if (state > 0.0f) { // ON
|
||||||
if (this->power_supply_ != nullptr && !this->has_requested_high_power_) {
|
this->power_.request();
|
||||||
this->power_supply_->request_high_power();
|
|
||||||
this->has_requested_high_power_ = true;
|
|
||||||
}
|
|
||||||
} else { // OFF
|
} else { // OFF
|
||||||
if (this->power_supply_ != nullptr && this->has_requested_high_power_) {
|
this->power_.unrequest();
|
||||||
this->power_supply_->unrequest_high_power();
|
|
||||||
this->has_requested_high_power_ = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -132,14 +132,20 @@ void PMSX003Component::parse_data_() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PMSX003_TYPE_5003ST: {
|
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_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);
|
uint16_t formaldehyde = this->get_16_bit_uint_(28);
|
||||||
float temperature = this->get_16_bit_uint_(30) / 10.0f;
|
float temperature = this->get_16_bit_uint_(30) / 10.0f;
|
||||||
float humidity = this->get_16_bit_uint_(32) / 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",
|
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);
|
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)
|
if (this->pm_2_5_sensor_ != nullptr)
|
||||||
this->pm_2_5_sensor_->publish_state(pm_2_5_concentration);
|
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)
|
if (this->temperature_sensor_ != nullptr)
|
||||||
this->temperature_sensor_->publish_state(temperature);
|
this->temperature_sensor_->publish_state(temperature);
|
||||||
if (this->humidity_sensor_ != nullptr)
|
if (this->humidity_sensor_ != nullptr)
|
||||||
|
|
|
@ -24,9 +24,9 @@ PMSX003_TYPES = {
|
||||||
}
|
}
|
||||||
|
|
||||||
SENSORS_TO_TYPE = {
|
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_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_TEMPERATURE: [CONF_PMS5003T, CONF_PMS5003ST],
|
||||||
CONF_HUMIDITY: [CONF_PMS5003T, CONF_PMS5003ST],
|
CONF_HUMIDITY: [CONF_PMS5003T, CONF_PMS5003ST],
|
||||||
CONF_FORMALDEHYDE: [CONF_PMS5003ST],
|
CONF_FORMALDEHYDE: [CONF_PMS5003ST],
|
||||||
|
|
|
@ -42,7 +42,7 @@ void PowerSupply::request_high_power() {
|
||||||
void PowerSupply::unrequest_high_power() {
|
void PowerSupply::unrequest_high_power() {
|
||||||
this->active_requests_--;
|
this->active_requests_--;
|
||||||
if (this->active_requests_ < 0) {
|
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;
|
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.
|
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 power_supply
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -92,36 +92,27 @@ class SunTrigger : public Trigger<>, public PollingComponent, public Parented<Su
|
||||||
void set_elevation(double elevation) { elevation_ = elevation; }
|
void set_elevation(double elevation) { elevation_ = elevation; }
|
||||||
|
|
||||||
void update() override {
|
void update() override {
|
||||||
auto now = this->parent_->get_time()->utcnow();
|
double current = this->parent_->elevation();
|
||||||
if (!now.is_valid())
|
if (isnan(current))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!this->last_result_.has_value() || this->last_result_->day_of_year != now.day_of_year) {
|
bool crossed;
|
||||||
this->recalc_();
|
if (this->sunrise_) {
|
||||||
return;
|
crossed = this->last_elevation_ <= this->elevation_ && this->elevation_ < current;
|
||||||
|
} else {
|
||||||
|
crossed = this->last_elevation_ >= this->elevation_ && this->elevation_ > current;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->prev_check_ != -1) {
|
if (crossed) {
|
||||||
auto res = *this->last_result_;
|
this->trigger();
|
||||||
// now >= sunrise > prev_check
|
|
||||||
if (now.timestamp >= res.timestamp && res.timestamp > this->prev_check_) {
|
|
||||||
this->trigger();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
this->prev_check_ = now.timestamp;
|
this->last_elevation_ = current;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
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_;
|
bool sunrise_;
|
||||||
|
double last_elevation_;
|
||||||
double elevation_;
|
double elevation_;
|
||||||
time_t prev_check_{-1};
|
|
||||||
optional<time::ESPTime> last_result_{};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename... Ts> class SunCondition : public Condition<Ts...>, public Parented<Sun> {
|
template<typename... Ts> class SunCondition : public Condition<Ts...>, public Parented<Sun> {
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace time {
|
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_second(uint8_t second) { this->seconds_[second] = true; }
|
||||||
void CronTrigger::add_minute(uint8_t minute) { this->minutes_[minute] = true; }
|
void CronTrigger::add_minute(uint8_t minute) { this->minutes_[minute] = true; }
|
||||||
|
|
|
@ -69,10 +69,6 @@ class ComponentManifest(object):
|
||||||
def auto_load(self):
|
def auto_load(self):
|
||||||
return getattr(self.module, 'AUTO_LOAD', [])
|
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):
|
def _get_flags_set(self, name, config):
|
||||||
if not hasattr(self.module, name):
|
if not hasattr(self.module, name):
|
||||||
return set()
|
return set()
|
||||||
|
|
|
@ -8,6 +8,7 @@ import re
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
import uuid as uuid_
|
import uuid as uuid_
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from string import ascii_letters, digits
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
|
@ -279,8 +280,9 @@ def validate_id_name(value):
|
||||||
raise Invalid("First character in ID cannot be a digit.")
|
raise Invalid("First character in ID cannot be a digit.")
|
||||||
if '-' in value:
|
if '-' in value:
|
||||||
raise Invalid("Dashes are not supported in IDs, please use underscores instead.")
|
raise Invalid("Dashes are not supported in IDs, please use underscores instead.")
|
||||||
|
valid_chars = ascii_letters + digits + '_'
|
||||||
for char in value:
|
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"
|
raise Invalid(u"IDs must only consist of upper/lowercase characters, the underscore"
|
||||||
u"character and numbers. The character '{}' cannot be used"
|
u"character and numbers. The character '{}' cannot be used"
|
||||||
u"".format(char))
|
u"".format(char))
|
||||||
|
@ -611,7 +613,7 @@ if IS_PY2:
|
||||||
path = u' @ data[%s]' % u']['.join(map(repr, self.path)) \
|
path = u' @ data[%s]' % u']['.join(map(repr, self.path)) \
|
||||||
if self.path else ''
|
if self.path else ''
|
||||||
# pylint: disable=no-member
|
# pylint: disable=no-member
|
||||||
output = Exception.__unicode__(self)
|
output = self.message
|
||||||
if self.error_type:
|
if self.error_type:
|
||||||
output += u' for ' + self.error_type
|
output += u' for ' + self.error_type
|
||||||
return output + path
|
return output + path
|
||||||
|
|
|
@ -11,8 +11,6 @@ ESP_PLATFORM_ESP32 = 'ESP32'
|
||||||
ESP_PLATFORM_ESP8266 = 'ESP8266'
|
ESP_PLATFORM_ESP8266 = 'ESP8266'
|
||||||
ESP_PLATFORMS = [ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266]
|
ESP_PLATFORMS = [ESP_PLATFORM_ESP32, ESP_PLATFORM_ESP8266]
|
||||||
|
|
||||||
APB_CLOCK_FREQ = 80000000
|
|
||||||
|
|
||||||
ALLOWED_NAME_CHARS = u'abcdefghijklmnopqrstuvwxyz0123456789_'
|
ALLOWED_NAME_CHARS = u'abcdefghijklmnopqrstuvwxyz0123456789_'
|
||||||
ARDUINO_VERSION_ESP32_DEV = 'https://github.com/platformio/platform-espressif32.git#feature/stage'
|
ARDUINO_VERSION_ESP32_DEV = 'https://github.com/platformio/platform-espressif32.git#feature/stage'
|
||||||
ARDUINO_VERSION_ESP32_1_0_0 = 'espressif32@1.5.0'
|
ARDUINO_VERSION_ESP32_1_0_0 = 'espressif32@1.5.0'
|
||||||
|
|
|
@ -60,7 +60,7 @@ void Application::setup() {
|
||||||
this->dump_config();
|
this->dump_config();
|
||||||
}
|
}
|
||||||
void Application::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_) {
|
for (auto component : this->components_) {
|
||||||
component->dump_config();
|
component->dump_config();
|
||||||
|
|
|
@ -89,8 +89,7 @@ def default_build_path():
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.Schema({
|
CONFIG_SCHEMA = cv.Schema({
|
||||||
cv.Required(CONF_NAME): cv.valid_name,
|
cv.Required(CONF_NAME): cv.valid_name,
|
||||||
cv.Required(CONF_PLATFORM): cv.one_of('ESP8266', 'ESP32', 'ESPRESSIF32',
|
cv.Required(CONF_PLATFORM): cv.one_of('ESP8266', 'ESP32', upper=True),
|
||||||
upper=True),
|
|
||||||
cv.Required(CONF_BOARD): validate_board,
|
cv.Required(CONF_BOARD): validate_board,
|
||||||
cv.Optional(CONF_ARDUINO_VERSION, default='recommended'): validate_arduino_version,
|
cv.Optional(CONF_ARDUINO_VERSION, default='recommended'): validate_arduino_version,
|
||||||
cv.Optional(CONF_BUILD_PATH, default=default_build_path): cv.string,
|
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_INCLUDES, default=[]): cv.ensure_list(cv.file_),
|
||||||
cv.Optional(CONF_LIBRARIES, default=[]): cv.ensure_list(cv.string_strict),
|
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({
|
PRELOAD_CONFIG_SCHEMA = cv.Schema({
|
||||||
|
|
|
@ -328,6 +328,17 @@ class ExpressionStatement(Statement):
|
||||||
return u"{};".format(self.expression)
|
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):
|
class ProgmemAssignmentExpression(AssignmentExpression):
|
||||||
def __init__(self, type, name, rhs, obj):
|
def __init__(self, type, name, rhs, obj):
|
||||||
super(ProgmemAssignmentExpression, self).__init__(
|
super(ProgmemAssignmentExpression, self).__init__(
|
||||||
|
|
|
@ -729,7 +729,7 @@ def start_web_server(args):
|
||||||
|
|
||||||
webbrowser.open('localhost:{}'.format(args.port))
|
webbrowser.open('localhost:{}'.format(args.port))
|
||||||
|
|
||||||
if not settings.status_use_ping:
|
if settings.status_use_ping:
|
||||||
status_thread = PingStatusThread()
|
status_thread = PingStatusThread()
|
||||||
else:
|
else:
|
||||||
status_thread = MDNSStatusThread()
|
status_thread = MDNSStatusThread()
|
||||||
|
|
|
@ -708,3 +708,11 @@ const startWizard = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
setupWizardStart.addEventListener('click', 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>)
|
<code class="inlinecode">0-9</code> and <code class="inlinecode">_</code>)
|
||||||
</p>
|
</p>
|
||||||
<div class="input-field col s12">
|
<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>
|
<label for="node_name">Name of node</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -207,8 +207,7 @@
|
||||||
Please choose the board you're using below.
|
Please choose the board you're using below.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
If you're not sure you can also use similar ones or even the
|
<em>If unsure you can also select a similar board or choose the "Generic" option.</em>
|
||||||
"Generic" option. In most cases that will work too.
|
|
||||||
</p>
|
</p>
|
||||||
<div class="input-field col s12">
|
<div class="input-field col s12">
|
||||||
<select id="board" name="board" required>
|
<select id="board" name="board" required>
|
||||||
|
|
|
@ -1,93 +1,9 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
import codecs
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import sys
|
import sys
|
||||||
|
import os.path
|
||||||
|
|
||||||
root_path = os.path.abspath(os.path.normpath(os.path.join(__file__, '..', '..')))
|
sys.path.append(os.path.dirname(__file__))
|
||||||
basepath = os.path.join(root_path, 'esphome')
|
from helpers import build_all_include, build_compile_commands
|
||||||
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)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
|
|
@ -4,7 +4,6 @@ from __future__ import print_function
|
||||||
import codecs
|
import codecs
|
||||||
import collections
|
import collections
|
||||||
import fnmatch
|
import fnmatch
|
||||||
import functools
|
|
||||||
import os.path
|
import os.path
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
@ -155,6 +154,28 @@ def lint_pragma_once(content):
|
||||||
return None
|
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)
|
errors = collections.defaultdict(list)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,16 +2,19 @@
|
||||||
|
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|
||||||
|
import argparse
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import argparse
|
|
||||||
import click
|
|
||||||
import threading
|
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'
|
is_py2 = sys.version[0] == '2'
|
||||||
|
|
||||||
if is_py2:
|
if is_py2:
|
||||||
|
@ -50,44 +53,6 @@ def progress_bar_show(value):
|
||||||
return 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():
|
def main():
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument('-j', '--jobs', type=int,
|
parser.add_argument('-j', '--jobs', type=int,
|
|
@ -2,8 +2,6 @@
|
||||||
|
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|
||||||
import codecs
|
|
||||||
import json
|
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
@ -18,6 +16,10 @@ import argparse
|
||||||
import click
|
import click
|
||||||
import threading
|
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'
|
is_py2 = sys.version[0] == '2'
|
||||||
|
|
||||||
if is_py2:
|
if is_py2:
|
||||||
|
@ -25,11 +27,6 @@ if is_py2:
|
||||||
else:
|
else:
|
||||||
import queue as queue
|
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):
|
def run_tidy(args, tmpdir, queue, lock, failed_files):
|
||||||
while True:
|
while True:
|
||||||
|
@ -67,119 +64,6 @@ def run_tidy(args, tmpdir, queue, lock, failed_files):
|
||||||
def progress_bar_show(value):
|
def progress_bar_show(value):
|
||||||
if value is None:
|
if value is None:
|
||||||
return ''
|
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():
|
def main():
|
||||||
|
@ -212,6 +96,7 @@ def main():
|
||||||
""")
|
""")
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
build_all_include()
|
||||||
build_compile_commands()
|
build_compile_commands()
|
||||||
|
|
||||||
files = []
|
files = []
|
||||||
|
@ -231,7 +116,6 @@ def main():
|
||||||
files.sort()
|
files.sort()
|
||||||
|
|
||||||
if args.all_headers:
|
if args.all_headers:
|
||||||
build_all_include()
|
|
||||||
files.insert(0, temp_header_file)
|
files.insert(0, temp_header_file)
|
||||||
|
|
||||||
tmpdir = None
|
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
|
set -x
|
||||||
|
|
||||||
script/clang-tidy.py -c --fix --all-headers
|
script/clang-tidy -c --fix --all-headers
|
||||||
script/clang-format.py -c -i
|
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")/.."
|
import argparse
|
||||||
set -x
|
import collections
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
script/ci-custom.py
|
sys.path.append(os.path.dirname(__file__))
|
||||||
flake8 esphome
|
from helpers import basepath, get_output, walk_files, filter_changed
|
||||||
pylint esphome
|
|
||||||
|
|
||||||
|
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:
|
then:
|
||||||
- lambda: >-
|
- lambda: >-
|
||||||
ESP_LOGV("main", "ON LOOP!");
|
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
|
build_path: build/test1
|
||||||
|
|
||||||
wifi:
|
wifi:
|
||||||
|
|
Loading…
Reference in a new issue