diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000..f24d70487a --- /dev/null +++ b/.editorconfig @@ -0,0 +1,27 @@ +root = true + +# general +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 + +# python +[*.{py}] +indent_style = space +indent_size = 4 + +# C++ +[*.{cpp,h,tcc}] +indent_style = space +indent_size = 2 + +# Web +[*.{js,html,css}] +indent_style = space +indent_size = 2 + +# YAML +[*.{yaml,yml}] +indent_style = space +indent_size = 2 diff --git a/.gitpod.yml b/.gitpod.yml new file mode 100644 index 0000000000..1859ef44e4 --- /dev/null +++ b/.gitpod.yml @@ -0,0 +1,6 @@ +ports: +- port: 6052 + onOpen: open-preview +tasks: +- before: script/setup + command: python -m esphome config dashboard diff --git a/esphome/codegen.py b/esphome/codegen.py index 883d5f8636..30f9ce6d2f 100644 --- a/esphome/codegen.py +++ b/esphome/codegen.py @@ -9,7 +9,7 @@ # pylint: disable=unused-import from esphome.cpp_generator import ( # noqa - Expression, RawExpression, TemplateArguments, + Expression, RawExpression, RawStatement, TemplateArguments, StructInitializer, ArrayInitializer, safe_exp, Statement, progmem_array, statement, variable, Pvariable, new_Pvariable, add, add_global, add_library, add_build_flag, add_define, diff --git a/esphome/components/adc/adc_sensor.cpp b/esphome/components/adc/adc_sensor.cpp index 849a1f31e5..aef952f27b 100644 --- a/esphome/components/adc/adc_sensor.cpp +++ b/esphome/components/adc/adc_sensor.cpp @@ -48,6 +48,11 @@ void ADCSensor::dump_config() { } float ADCSensor::get_setup_priority() const { return setup_priority::DATA; } void ADCSensor::update() { + float value_v = this->sample(); + ESP_LOGD(TAG, "'%s': Got voltage=%.2fV", this->get_name().c_str(), value_v); + this->publish_state(value_v); +} +float ADCSensor::sample() { #ifdef ARDUINO_ARCH_ESP32 float value_v = analogRead(this->pin_) / 4095.0f; switch (this->attenuation_) { @@ -64,19 +69,16 @@ void ADCSensor::update() { value_v *= 3.9; break; } + return value_v; #endif #ifdef ARDUINO_ARCH_ESP8266 #ifdef USE_ADC_SENSOR_VCC - float value_v = ESP.getVcc() / 1024.0f; + return ESP.getVcc() / 1024.0f; #else - float value_v = analogRead(this->pin_) / 1024.0f; + return analogRead(this->pin_) / 1024.0f; #endif #endif - - ESP_LOGD(TAG, "'%s': Got voltage=%.2fV", this->get_name().c_str(), value_v); - - this->publish_state(value_v); } #ifdef ARDUINO_ARCH_ESP8266 std::string ADCSensor::unique_id() { return get_mac_address() + "-adc"; } diff --git a/esphome/components/adc/adc_sensor.h b/esphome/components/adc/adc_sensor.h index 72afe5c1e9..7d26a67ae4 100644 --- a/esphome/components/adc/adc_sensor.h +++ b/esphome/components/adc/adc_sensor.h @@ -3,19 +3,22 @@ #include "esphome/core/component.h" #include "esphome/core/esphal.h" #include "esphome/components/sensor/sensor.h" +#include "esphome/components/voltage_sampler/voltage_sampler.h" namespace esphome { namespace adc { -class ADCSensor : public sensor::Sensor, public PollingComponent { +#ifdef USE_ADC_SENSOR_VCC +ADC_MODE(ADC_VCC) +#endif + +class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage_sampler::VoltageSampler { public: #ifdef ARDUINO_ARCH_ESP32 /// Set the attenuation for this pin. Only available on the ESP32. void set_attenuation(adc_attenuation_t attenuation); #endif - // ========== INTERNAL METHODS ========== - // (In most use cases you won't need these) /// Update adc values. void update() override; /// Setup ADc @@ -24,6 +27,7 @@ class ADCSensor : public sensor::Sensor, public PollingComponent { /// `HARDWARE_LATE` setup priority. float get_setup_priority() const override; void set_pin(uint8_t pin) { this->pin_ = pin; } + float sample() override; #ifdef ARDUINO_ARCH_ESP8266 std::string unique_id() override; diff --git a/esphome/components/adc/sensor.py b/esphome/components/adc/sensor.py index c369ea5be5..6a274f04af 100644 --- a/esphome/components/adc/sensor.py +++ b/esphome/components/adc/sensor.py @@ -1,9 +1,12 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins -from esphome.components import sensor +from esphome.components import sensor, voltage_sampler from esphome.const import CONF_ATTENUATION, CONF_ID, CONF_PIN, ICON_FLASH, UNIT_VOLT + +AUTO_LOAD = ['voltage_sampler'] + ATTENUATION_MODES = { '0db': cg.global_ns.ADC_0db, '2.5db': cg.global_ns.ADC_2_5db, @@ -20,7 +23,8 @@ def validate_adc_pin(value): adc_ns = cg.esphome_ns.namespace('adc') -ADCSensor = adc_ns.class_('ADCSensor', sensor.PollingSensorComponent) +ADCSensor = adc_ns.class_('ADCSensor', sensor.Sensor, cg.PollingComponent, + voltage_sampler.VoltageSampler) CONFIG_SCHEMA = sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 2).extend({ cv.GenerateID(): cv.declare_id(ADCSensor), @@ -37,7 +41,6 @@ def to_code(config): if config[CONF_PIN] == 'VCC': cg.add_define('USE_ADC_SENSOR_VCC') - cg.add_global(cg.global_ns.ADC_MODE(cg.global_ns.ADC_VCC)) else: cg.add(var.set_pin(config[CONF_PIN])) diff --git a/esphome/components/ads1115/__init__.py b/esphome/components/ads1115/__init__.py index e34bab8582..28cfac49ec 100644 --- a/esphome/components/ads1115/__init__.py +++ b/esphome/components/ads1115/__init__.py @@ -4,7 +4,7 @@ from esphome.components import i2c from esphome.const import CONF_ID DEPENDENCIES = ['i2c'] -AUTO_LOAD = ['sensor'] +AUTO_LOAD = ['sensor', 'voltage_sampler'] MULTI_CONF = True ads1115_ns = cg.esphome_ns.namespace('ads1115') diff --git a/esphome/components/ads1115/ads1115.cpp b/esphome/components/ads1115/ads1115.cpp index 398aad3d02..94ff3a1186 100644 --- a/esphome/components/ads1115/ads1115.cpp +++ b/esphome/components/ads1115/ads1115.cpp @@ -59,7 +59,7 @@ void ADS1115Component::setup() { } for (auto *sensor : this->sensors_) { this->set_interval(sensor->get_name(), sensor->update_interval(), - [this, sensor] { this->request_measurement_(sensor); }); + [this, sensor] { this->request_measurement(sensor); }); } } void ADS1115Component::dump_config() { @@ -76,11 +76,11 @@ void ADS1115Component::dump_config() { } } float ADS1115Component::get_setup_priority() const { return setup_priority::DATA; } -void ADS1115Component::request_measurement_(ADS1115Sensor *sensor) { +float ADS1115Component::request_measurement(ADS1115Sensor *sensor) { uint16_t config; if (!this->read_byte_16(ADS1115_REGISTER_CONFIG, &config)) { this->status_set_warning(); - return; + return NAN; } // Multiplexer // 0bxBBBxxxxxxxxxxxx @@ -96,7 +96,7 @@ void ADS1115Component::request_measurement_(ADS1115Sensor *sensor) { if (!this->write_byte_16(ADS1115_REGISTER_CONFIG, config)) { this->status_set_warning(); - return; + return NAN; } // about 1.6 ms with 860 samples per second @@ -107,7 +107,7 @@ void ADS1115Component::request_measurement_(ADS1115Sensor *sensor) { if (millis() - start > 100) { ESP_LOGW(TAG, "Reading ADS1115 timed out"); this->status_set_warning(); - return; + return NAN; } yield(); } @@ -115,7 +115,7 @@ void ADS1115Component::request_measurement_(ADS1115Sensor *sensor) { uint16_t raw_conversion; if (!this->read_byte_16(ADS1115_REGISTER_CONVERSION, &raw_conversion)) { this->status_set_warning(); - return; + return NAN; } auto signed_conversion = static_cast(raw_conversion); @@ -143,16 +143,22 @@ void ADS1115Component::request_measurement_(ADS1115Sensor *sensor) { millivolts = NAN; } - float v = millivolts / 1000.0f; - ESP_LOGD(TAG, "'%s': Got Voltage=%fV", sensor->get_name().c_str(), v); - sensor->publish_state(v); this->status_clear_warning(); + return millivolts / 1e4f; } uint8_t ADS1115Sensor::get_multiplexer() const { return this->multiplexer_; } void ADS1115Sensor::set_multiplexer(ADS1115Multiplexer multiplexer) { this->multiplexer_ = multiplexer; } uint8_t ADS1115Sensor::get_gain() const { return this->gain_; } void ADS1115Sensor::set_gain(ADS1115Gain gain) { this->gain_ = gain; } +float ADS1115Sensor::sample() { return this->parent_->request_measurement(this); } +void ADS1115Sensor::update() { + float v = this->parent_->request_measurement(this); + if (!isnan(v)) { + ESP_LOGD(TAG, "'%s': Got Voltage=%fV", this->get_name().c_str(), v); + this->publish_state(v); + } +} } // namespace ads1115 } // namespace esphome diff --git a/esphome/components/ads1115/ads1115.h b/esphome/components/ads1115/ads1115.h index 863bb247aa..966541ef4f 100644 --- a/esphome/components/ads1115/ads1115.h +++ b/esphome/components/ads1115/ads1115.h @@ -3,6 +3,7 @@ #include "esphome/core/component.h" #include "esphome/components/sensor/sensor.h" #include "esphome/components/i2c/i2c.h" +#include "esphome/components/voltage_sampler/voltage_sampler.h" namespace esphome { namespace ads1115 { @@ -38,28 +39,29 @@ class ADS1115Component : public Component, public i2c::I2CDevice { /// HARDWARE_LATE setup priority float get_setup_priority() const override; - protected: /// Helper method to request a measurement from a sensor. - void request_measurement_(ADS1115Sensor *sensor); + float request_measurement(ADS1115Sensor *sensor); + protected: std::vector sensors_; }; /// Internal holder class that is in instance of Sensor so that the hub can create individual sensors. -class ADS1115Sensor : public sensor::Sensor { +class ADS1115Sensor : public sensor::Sensor, public PollingComponent, public voltage_sampler::VoltageSampler { public: + ADS1115Sensor(ADS1115Component *parent) : parent_(parent) {} + void update() override; void set_multiplexer(ADS1115Multiplexer multiplexer); void set_gain(ADS1115Gain gain); - // ========== INTERNAL METHODS ========== - // (In most use cases you won't need these) + float sample() override; uint8_t get_multiplexer() const; uint8_t get_gain() const; protected: + ADS1115Component *parent_; ADS1115Multiplexer multiplexer_; ADS1115Gain gain_; - uint32_t update_interval_; }; } // namespace ads1115 diff --git a/esphome/components/ads1115/sensor.py b/esphome/components/ads1115/sensor.py index 4a1d109c23..2fe9b6fa86 100644 --- a/esphome/components/ads1115/sensor.py +++ b/esphome/components/ads1115/sensor.py @@ -1,6 +1,6 @@ import esphome.codegen as cg import esphome.config_validation as cv -from esphome.components import sensor +from esphome.components import sensor, voltage_sampler from esphome.components.ads1115 import ADS1115Component from esphome.const import CONF_GAIN, CONF_MULTIPLEXER, ICON_FLASH, UNIT_VOLT, CONF_ID from esphome.py_compat import string_types @@ -40,7 +40,8 @@ def validate_gain(value): return cv.enum(GAIN)(value) -ADS1115Sensor = ads1115_ns.class_('ADS1115Sensor', sensor.Sensor) +ADS1115Sensor = ads1115_ns.class_('ADS1115Sensor', sensor.Sensor, cg.PollingComponent, + voltage_sampler.VoltageSampler) CONF_ADS1115_ID = 'ads1115_id' CONFIG_SCHEMA = sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 3).extend({ @@ -52,11 +53,12 @@ CONFIG_SCHEMA = sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 3).extend({ def to_code(config): - var = cg.new_Pvariable(config[CONF_ID]) + paren = yield cg.get_variable(config[CONF_ADS1115_ID]) + var = cg.new_Pvariable(config[CONF_ID], paren) yield sensor.register_sensor(var, config) + yield cg.register_component(var, config) cg.add(var.set_multiplexer(config[CONF_MULTIPLEXER])) cg.add(var.set_gain(config[CONF_GAIN])) - hub = yield cg.get_variable(config[CONF_ADS1115_ID]) - cg.add(hub.register_sensor(var)) + cg.add(paren.register_sensor(var)) diff --git a/esphome/components/api/__init__.py b/esphome/components/api/__init__.py index 97465f1f7c..b778257d66 100644 --- a/esphome/components/api/__init__.py +++ b/esphome/components/api/__init__.py @@ -67,6 +67,7 @@ def to_code(config): templ = cg.TemplateArguments(*template_args) trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], templ, conf[CONF_SERVICE], service_type_args) + cg.add(var.register_user_service(trigger)) yield automation.build_automation(trigger, func_args, conf) cg.add_define('USE_API') diff --git a/esphome/components/api/api_server.cpp b/esphome/components/api/api_server.cpp index 2e9b9f6841..a15fe59a6a 100644 --- a/esphome/components/api/api_server.cpp +++ b/esphome/components/api/api_server.cpp @@ -260,7 +260,6 @@ APIConnection::APIConnection(AsyncClient *client, APIServer *parent) } APIConnection::~APIConnection() { delete this->client_; } void APIConnection::on_error_(int8_t error) { - ESP_LOGD(TAG, "Error from client '%s': %d", this->client_info_.c_str(), error); // disconnect will also be called, nothing to do here this->remove_ = true; } diff --git a/esphome/components/bh1750/sensor.py b/esphome/components/bh1750/sensor.py index 27ee3d1b85..b3ce0eaf88 100644 --- a/esphome/components/bh1750/sensor.py +++ b/esphome/components/bh1750/sensor.py @@ -13,7 +13,7 @@ BH1750_RESOLUTIONS = { 0.5: BH1750Resolution.BH1750_RESOLUTION_0P5_LX, } -BH1750Sensor = bh1750_ns.class_('BH1750Sensor', sensor.PollingSensorComponent, i2c.I2CDevice) +BH1750Sensor = bh1750_ns.class_('BH1750Sensor', sensor.Sensor, cg.PollingComponent, i2c.I2CDevice) CONFIG_SCHEMA = sensor.sensor_schema(UNIT_LUX, ICON_BRIGHTNESS_5, 1).extend({ cv.GenerateID(): cv.declare_id(BH1750Sensor), diff --git a/esphome/components/ble_presence/binary_sensor.py b/esphome/components/ble_presence/binary_sensor.py index beab5448be..43ec9455d4 100644 --- a/esphome/components/ble_presence/binary_sensor.py +++ b/esphome/components/ble_presence/binary_sensor.py @@ -1,24 +1,24 @@ import esphome.codegen as cg import esphome.config_validation as cv -from esphome.components import binary_sensor -from esphome.components.esp32_ble_tracker import CONF_ESP32_BLE_ID, ESP_BLE_DEVICE_SCHEMA, \ - ESPBTDeviceListener -from esphome.const import CONF_MAC_ADDRESS, CONF_NAME, CONF_ID +from esphome.components import binary_sensor, esp32_ble_tracker +from esphome.const import CONF_MAC_ADDRESS, CONF_ID DEPENDENCIES = ['esp32_ble_tracker'] ble_presence_ns = cg.esphome_ns.namespace('ble_presence') BLEPresenceDevice = ble_presence_ns.class_('BLEPresenceDevice', binary_sensor.BinarySensor, - cg.Component, ESPBTDeviceListener) + cg.Component, esp32_ble_tracker.ESPBTDeviceListener) CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({ cv.GenerateID(): cv.declare_id(BLEPresenceDevice), cv.Required(CONF_MAC_ADDRESS): cv.mac_address, -}).extend(ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) +}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) def to_code(config): - hub = yield cg.get_variable(config[CONF_ESP32_BLE_ID]) - var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME], hub, config[CONF_MAC_ADDRESS].as_hex) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) + yield esp32_ble_tracker.register_ble_device(var, config) yield binary_sensor.register_binary_sensor(var, config) + + cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex)) diff --git a/esphome/components/ble_presence/ble_presence_device.h b/esphome/components/ble_presence/ble_presence_device.h index a5aba35bea..262cc3eedf 100644 --- a/esphome/components/ble_presence/ble_presence_device.h +++ b/esphome/components/ble_presence/ble_presence_device.h @@ -13,9 +13,7 @@ class BLEPresenceDevice : public binary_sensor::BinarySensor, public esp32_ble_tracker::ESPBTDeviceListener, public Component { public: - BLEPresenceDevice(const std::string &name, esp32_ble_tracker::ESP32BLETracker *parent, uint64_t address) - : binary_sensor::BinarySensor(name), esp32_ble_tracker::ESPBTDeviceListener(parent), address_(address) {} - + void set_address(uint64_t address) { address_ = address; } void on_scan_end() override { if (!this->found_) this->publish_state(false); @@ -29,7 +27,6 @@ class BLEPresenceDevice : public binary_sensor::BinarySensor, } return false; } - void setup() override { this->setup_ble(); } void dump_config() override; float get_setup_priority() const override { return setup_priority::DATA; } diff --git a/esphome/components/ble_rssi/ble_rssi_sensor.h b/esphome/components/ble_rssi/ble_rssi_sensor.h index 686e70f82f..2c296b3831 100644 --- a/esphome/components/ble_rssi/ble_rssi_sensor.h +++ b/esphome/components/ble_rssi/ble_rssi_sensor.h @@ -11,9 +11,7 @@ namespace ble_rssi { class BLERSSISensor : public sensor::Sensor, public esp32_ble_tracker::ESPBTDeviceListener, public Component { public: - BLERSSISensor(const std::string &name, esp32_ble_tracker::ESP32BLETracker *parent, uint64_t address) - : sensor::Sensor(name), esp32_ble_tracker::ESPBTDeviceListener(parent), address_(address) {} - + void set_address(uint64_t address) { address_ = address; } void on_scan_end() override { if (!this->found_) this->publish_state(NAN); @@ -27,7 +25,6 @@ class BLERSSISensor : public sensor::Sensor, public esp32_ble_tracker::ESPBTDevi } return false; } - void setup() override { this->setup_ble(); } void dump_config() override; float get_setup_priority() const override { return setup_priority::DATA; } diff --git a/esphome/components/ble_rssi/sensor.py b/esphome/components/ble_rssi/sensor.py index 22e1c82f64..ee8f71632f 100644 --- a/esphome/components/ble_rssi/sensor.py +++ b/esphome/components/ble_rssi/sensor.py @@ -1,24 +1,24 @@ import esphome.codegen as cg import esphome.config_validation as cv -from esphome.components import sensor -from esphome.components.esp32_ble_tracker import CONF_ESP32_BLE_ID, ESPBTDeviceListener, \ - ESP_BLE_DEVICE_SCHEMA -from esphome.const import CONF_MAC_ADDRESS, CONF_NAME, CONF_ID, UNIT_DECIBEL, ICON_SIGNAL +from esphome.components import sensor, esp32_ble_tracker +from esphome.const import CONF_MAC_ADDRESS, CONF_ID, UNIT_DECIBEL, ICON_SIGNAL DEPENDENCIES = ['esp32_ble_tracker'] ble_rssi_ns = cg.esphome_ns.namespace('ble_rssi') BLERSSISensor = ble_rssi_ns.class_('BLERSSISensor', sensor.Sensor, cg.Component, - ESPBTDeviceListener) + esp32_ble_tracker.ESPBTDeviceListener) CONFIG_SCHEMA = sensor.sensor_schema(UNIT_DECIBEL, ICON_SIGNAL, 0).extend({ cv.GenerateID(): cv.declare_id(BLERSSISensor), cv.Required(CONF_MAC_ADDRESS): cv.mac_address, -}).extend(ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) +}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) def to_code(config): - hub = yield cg.get_variable(config[CONF_ESP32_BLE_ID]) - var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME], hub, config[CONF_MAC_ADDRESS].as_hex) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) + yield esp32_ble_tracker.register_ble_device(var, config) yield sensor.register_sensor(var, config) + + cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex)) diff --git a/esphome/components/custom/output/__init__.py b/esphome/components/custom/output/__init__.py index 3266cbda98..cc4391d4ae 100644 --- a/esphome/components/custom/output/__init__.py +++ b/esphome/components/custom/output/__init__.py @@ -1,6 +1,6 @@ -from esphome.components import output -import esphome.config_validation as cv import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import output from esphome.const import CONF_ID, CONF_LAMBDA, CONF_OUTPUTS, CONF_TYPE from .. import custom_ns diff --git a/esphome/components/custom/sensor/__init__.py b/esphome/components/custom/sensor/__init__.py index 6503cc0c69..622d347d8a 100644 --- a/esphome/components/custom/sensor/__init__.py +++ b/esphome/components/custom/sensor/__init__.py @@ -1,7 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor -from esphome.const import CONF_ID, CONF_LAMBDA, CONF_NAME, CONF_SENSORS +from esphome.const import CONF_ID, CONF_LAMBDA, CONF_SENSORS from .. import custom_ns CustomSensorConstructor = custom_ns.class_('CustomSensorConstructor') @@ -20,6 +20,5 @@ def to_code(config): rhs = CustomSensorConstructor(template_) var = cg.variable(config[CONF_ID], rhs) for i, conf in enumerate(config[CONF_SENSORS]): - sens = cg.new_Pvariable(conf[CONF_ID], var.get_switch(i)) - cg.add(sens.set_name(conf[CONF_NAME])) + sens = cg.Pvariable(conf[CONF_ID], var.get_sensor(i)) yield sensor.register_sensor(sens, conf) diff --git a/esphome/components/custom/switch/__init__.py b/esphome/components/custom/switch/__init__.py index b0da14b4f1..dc5cb325bd 100644 --- a/esphome/components/custom/switch/__init__.py +++ b/esphome/components/custom/switch/__init__.py @@ -1,10 +1,9 @@ -from esphome.components import switch -import esphome.config_validation as cv import esphome.codegen as cg -from esphome.const import CONF_ID, CONF_LAMBDA, CONF_NAME, CONF_SWITCHES +import esphome.config_validation as cv +from esphome.components import switch +from esphome.const import CONF_ID, CONF_LAMBDA, CONF_SWITCHES from .. import custom_ns - CustomSwitchConstructor = custom_ns.class_('CustomSwitchConstructor') CONFIG_SCHEMA = cv.Schema({ @@ -24,6 +23,5 @@ def to_code(config): rhs = CustomSwitchConstructor(template_) var = cg.variable(config[CONF_ID], rhs) for i, conf in enumerate(config[CONF_SWITCHES]): - switch_ = cg.new_Pvariable(conf[CONF_ID], var.get_switch(i)) - cg.add(switch_.set_name(conf[CONF_NAME])) + switch_ = cg.Pvariable(conf[CONF_ID], var.get_switch(i)) yield switch.register_switch(switch_, conf) diff --git a/esphome/components/custom/text_sensor/__init__.py b/esphome/components/custom/text_sensor/__init__.py index 40b8be8d76..38675da142 100644 --- a/esphome/components/custom/text_sensor/__init__.py +++ b/esphome/components/custom/text_sensor/__init__.py @@ -1,7 +1,7 @@ -from esphome.components import text_sensor -import esphome.config_validation as cv import esphome.codegen as cg -from esphome.const import CONF_ID, CONF_LAMBDA, CONF_NAME, CONF_TEXT_SENSORS +import esphome.config_validation as cv +from esphome.components import text_sensor +from esphome.const import CONF_ID, CONF_LAMBDA, CONF_TEXT_SENSORS from .. import custom_ns CustomTextSensorConstructor = custom_ns.class_('CustomTextSensorConstructor') @@ -24,6 +24,5 @@ def to_code(config): var = cg.variable(config[CONF_ID], rhs) for i, conf in enumerate(config[CONF_TEXT_SENSORS]): - text = cg.new_Pvariable(conf[CONF_ID], var.get_text_sensor(i)) - cg.add(text.set_name(conf[CONF_NAME])) + text = cg.Pvariable(conf[CONF_ID], var.get_text_sensor(i)) yield text_sensor.register_text_sensor(text, conf) diff --git a/esphome/components/duty_cycle/sensor.py b/esphome/components/duty_cycle/sensor.py index d60ff0d8be..51d99aae6a 100644 --- a/esphome/components/duty_cycle/sensor.py +++ b/esphome/components/duty_cycle/sensor.py @@ -5,7 +5,7 @@ from esphome.components import sensor from esphome.const import CONF_ID, CONF_PIN, UNIT_PERCENT, ICON_PERCENT duty_cycle_ns = cg.esphome_ns.namespace('duty_cycle') -DutyCycleSensor = duty_cycle_ns.class_('DutyCycleSensor', sensor.PollingSensorComponent) +DutyCycleSensor = duty_cycle_ns.class_('DutyCycleSensor', sensor.Sensor, cg.PollingComponent) CONFIG_SCHEMA = sensor.sensor_schema(UNIT_PERCENT, ICON_PERCENT, 1).extend({ cv.GenerateID(): cv.declare_id(DutyCycleSensor), diff --git a/esphome/components/esp32_ble_tracker/__init__.py b/esphome/components/esp32_ble_tracker/__init__.py index c77e5cd440..5a4862f733 100644 --- a/esphome/components/esp32_ble_tracker/__init__.py +++ b/esphome/components/esp32_ble_tracker/__init__.py @@ -1,6 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.const import CONF_ID, CONF_SCAN_INTERVAL, ESP_PLATFORM_ESP32 +from esphome.core import coroutine ESP_PLATFORMS = [ESP_PLATFORM_ESP32] AUTO_LOAD = ['xiaomi_ble'] @@ -24,3 +25,10 @@ def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) cg.add(var.set_scan_interval(config[CONF_SCAN_INTERVAL])) + + +@coroutine +def register_ble_device(var, config): + paren = yield cg.get_variable(config[CONF_ESP32_BLE_ID]) + cg.add(paren.register_listener(var)) + yield var diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp index ff88ea79b2..5ef814c2ba 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp @@ -201,6 +201,22 @@ void ESP32BLETracker::gap_scan_result(const esp_ble_gap_cb_param_t::ble_scan_res } } +std::string hexencode(const std::string &raw_data) { + char buf[20]; + std::string res; + for (size_t i = 0; i < raw_data.size(); i++) { + if (i + 1 != raw_data.size()) { + sprintf(buf, "0x%02X.", static_cast(raw_data[i])); + } else { + sprintf(buf, "0x%02X ", static_cast(raw_data[i])); + } + res += buf; + } + sprintf(buf, "(%zu)", raw_data.size()); + res += buf; + return res; +} + ESPBTUUID::ESPBTUUID() : uuid_() {} ESPBTUUID ESPBTUUID::from_uint16(uint16_t uuid) { ESPBTUUID ret; @@ -259,12 +275,22 @@ std::string ESPBTUUID::to_string() { return sbuf; } +ESPBLEiBeacon::ESPBLEiBeacon(const uint8_t *data) { memcpy(&this->beacon_data_, data, sizeof(beacon_data_)); } +optional ESPBLEiBeacon::from_manufacturer_data(const std::string &data) { + if (data.size() != 25) + return {}; + if (data[0] != 0x4C || data[1] != 0x00) + return {}; + + return ESPBLEiBeacon(reinterpret_cast(data.data())); +} + void ESPBTDevice::parse_scan_rst(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param ¶m) { for (uint8_t i = 0; i < ESP_BD_ADDR_LEN; i++) this->address_[i] = param.bda[i]; this->address_type_ = param.ble_addr_type; this->rssi_ = param.rssi; - this->parse_adv(param); + this->parse_adv_(param); #ifdef ESPHOME_LOG_HAS_VERY_VERBOSE ESP_LOGVV(TAG, "Parse Result:"); @@ -287,7 +313,7 @@ void ESPBTDevice::parse_scan_rst(const esp_ble_gap_cb_param_t::ble_scan_result_e this->address_[2], this->address_[3], this->address_[4], this->address_[5], address_type); ESP_LOGVV(TAG, " RSSI: %d", this->rssi_); - ESP_LOGVV(TAG, " Name: %s", this->name_.c_str()); + ESP_LOGVV(TAG, " Name: '%s'", this->name_.c_str()); if (this->tx_power_.has_value()) { ESP_LOGVV(TAG, " TX Power: %d", *this->tx_power_); } @@ -300,26 +326,18 @@ void ESPBTDevice::parse_scan_rst(const esp_ble_gap_cb_param_t::ble_scan_result_e for (auto uuid : this->service_uuids_) { ESP_LOGVV(TAG, " Service UUID: %s", uuid.to_string().c_str()); } - ESP_LOGVV(TAG, " Manufacturer data: '%s'", this->manufacturer_data_.c_str()); - ESP_LOGVV(TAG, " Service data: '%s'", this->service_data_.c_str()); + ESP_LOGVV(TAG, " Manufacturer data: %s", hexencode(this->manufacturer_data_).c_str()); + ESP_LOGVV(TAG, " Service data: %s", hexencode(this->service_data_).c_str()); if (this->service_data_uuid_.has_value()) { ESP_LOGVV(TAG, " Service Data UUID: %s", this->service_data_uuid_->to_string().c_str()); } - char buffer[200]; - size_t off = 0; - for (uint8_t i = 0; i < param.adv_data_len; i++) { - int ret = snprintf(buffer + off, sizeof(buffer) - off, "%02X.", param.ble_adv[i]); - if (ret < 0) { - break; - } - off += ret; - } - ESP_LOGVV(TAG, "Adv data: %s (%u bytes)", buffer, param.adv_data_len); + ESP_LOGVV(TAG, "Adv data: %s", + hexencode(std::string(reinterpret_cast(param.ble_adv), param.adv_data_len)).c_str()); #endif } -void ESPBTDevice::parse_adv(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param ¶m) { +void ESPBTDevice::parse_adv_(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param ¶m) { size_t offset = 0; const uint8_t *payload = param.ble_adv; uint8_t len = param.adv_data_len; @@ -472,8 +490,6 @@ void ESP32BLETracker::print_bt_device_info(const ESPBTDevice &device) { } } -void ESPBTDeviceListener::setup_ble() { this->parent_->add_listener(this); } - } // namespace esp32_ble_tracker } // namespace esphome diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h index 29927ea53c..f1bcada621 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h @@ -31,12 +31,32 @@ class ESPBTUUID { esp_bt_uuid_t uuid_; }; +class ESPBLEiBeacon { + public: + ESPBLEiBeacon() { memset(&this->beacon_data_, 0, sizeof(this->beacon_data_)); } + ESPBLEiBeacon(const uint8_t *data); + static optional from_manufacturer_data(const std::string &data); + + uint16_t get_major() { return reverse_bits_16(this->beacon_data_.major); } + uint16_t get_minor() { return reverse_bits_16(this->beacon_data_.minor); } + int8_t get_signal_power() { return this->beacon_data_.signal_power; } + ESPBTUUID get_uuid() { return ESPBTUUID::from_raw(this->beacon_data_.proximity_uuid); } + + protected: + struct { + uint16_t manufacturer_id; + uint8_t sub_type; + uint8_t proximity_uuid[16]; + uint16_t major; + uint16_t minor; + int8_t signal_power; + } PACKED beacon_data_; +}; + class ESPBTDevice { public: void parse_scan_rst(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param ¶m); - void parse_adv(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param ¶m); - std::string address_str() const; uint64_t address_uint64() const; @@ -51,8 +71,13 @@ class ESPBTDevice { const std::string &get_manufacturer_data() const; const std::string &get_service_data() const; const optional &get_service_data_uuid() const; + const optional get_ibeacon() const { + return ESPBLEiBeacon::from_manufacturer_data(this->manufacturer_data_); + } protected: + void parse_adv_(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param ¶m); + esp_bd_addr_t address_{ 0, }; @@ -72,28 +97,28 @@ class ESP32BLETracker; class ESPBTDeviceListener { public: - ESPBTDeviceListener(ESP32BLETracker *parent) : parent_(parent) {} - void setup_ble(); virtual void on_scan_end() {} virtual bool parse_device(const ESPBTDevice &device) = 0; + void set_parent(ESP32BLETracker *parent) { parent_ = parent; } protected: - ESP32BLETracker *parent_; + ESP32BLETracker *parent_{nullptr}; }; class ESP32BLETracker : public Component { public: void set_scan_interval(uint32_t scan_interval); - // ========== INTERNAL METHODS ========== - // (In most use cases you won't need these) /// Setup the FreeRTOS task and the Bluetooth stack. void setup() override; void dump_config() override; void loop() override; - void add_listener(ESPBTDeviceListener *listener) { this->listeners_.push_back(listener); } + void register_listener(ESPBTDeviceListener *listener) { + listener->set_parent(this); + this->listeners_.push_back(listener); + } void print_bt_device_info(const ESPBTDevice &device); diff --git a/esphome/components/esp32_hall/esp32_hall.h b/esphome/components/esp32_hall/esp32_hall.h index 9727d45aa6..040280fff3 100644 --- a/esphome/components/esp32_hall/esp32_hall.h +++ b/esphome/components/esp32_hall/esp32_hall.h @@ -8,11 +8,8 @@ namespace esphome { namespace esp32_hall { -class ESP32HallSensor : public sensor::PollingSensorComponent { +class ESP32HallSensor : public sensor::Sensor, public PollingComponent { public: - explicit ESP32HallSensor(const std::string &name, uint32_t update_interval) - : sensor::PollingSensorComponent(name, update_interval) {} - void dump_config() override; void update() override; diff --git a/esphome/components/esp32_hall/sensor.py b/esphome/components/esp32_hall/sensor.py index 81a90c8c10..ec24f1aab6 100644 --- a/esphome/components/esp32_hall/sensor.py +++ b/esphome/components/esp32_hall/sensor.py @@ -1,21 +1,19 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor -from esphome.const import CONF_ID, CONF_NAME, CONF_UPDATE_INTERVAL, ESP_PLATFORM_ESP32, \ - UNIT_MICROTESLA, ICON_MAGNET +from esphome.const import CONF_ID, ESP_PLATFORM_ESP32, UNIT_MICROTESLA, ICON_MAGNET ESP_PLATFORMS = [ESP_PLATFORM_ESP32] esp32_hall_ns = cg.esphome_ns.namespace('esp32_hall') -ESP32HallSensor = esp32_hall_ns.class_('ESP32HallSensor', sensor.PollingSensorComponent) +ESP32HallSensor = esp32_hall_ns.class_('ESP32HallSensor', sensor.Sensor, cg.PollingComponent) CONFIG_SCHEMA = sensor.sensor_schema(UNIT_MICROTESLA, ICON_MAGNET, 1).extend({ cv.GenerateID(): cv.declare_id(ESP32HallSensor), - cv.Optional(CONF_UPDATE_INTERVAL, default='60s'): cv.update_interval, -}).extend(cv.COMPONENT_SCHEMA) +}).extend(cv.polling_component_schema('60s')) def to_code(config): - var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME], config[CONF_UPDATE_INTERVAL]) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) yield sensor.register_sensor(var, config) diff --git a/esphome/components/fastled_clockless/light.py b/esphome/components/fastled_clockless/light.py index 340dc0ab05..30fa910e59 100644 --- a/esphome/components/fastled_clockless/light.py +++ b/esphome/components/fastled_clockless/light.py @@ -4,6 +4,8 @@ from esphome import pins from esphome.components import fastled_base from esphome.const import CONF_CHIPSET, CONF_NUM_LEDS, CONF_PIN, CONF_RGB_ORDER +AUTO_LOAD = ['fastled_base'] + CHIPSETS = [ 'NEOPIXEL', 'TM1829', diff --git a/esphome/components/hx711/sensor.py b/esphome/components/hx711/sensor.py index bc22397065..2fc333a243 100644 --- a/esphome/components/hx711/sensor.py +++ b/esphome/components/hx711/sensor.py @@ -5,7 +5,7 @@ from esphome.components import sensor from esphome.const import CONF_CLK_PIN, CONF_GAIN, CONF_ID, ICON_SCALE hx711_ns = cg.esphome_ns.namespace('hx711') -HX711Sensor = hx711_ns.class_('HX711Sensor', sensor.PollingSensorComponent) +HX711Sensor = hx711_ns.class_('HX711Sensor', sensor.Sensor, cg.PollingComponent) CONF_DOUT_PIN = 'dout_pin' diff --git a/esphome/components/ledc/output.py b/esphome/components/ledc/output.py index fd75942672..63b32eedf0 100644 --- a/esphome/components/ledc/output.py +++ b/esphome/components/ledc/output.py @@ -10,8 +10,8 @@ ESP_PLATFORMS = [ESP_PLATFORM_ESP32] def validate_frequency_bit_depth(obj): - frequency = obj.get(CONF_FREQUENCY, 1000) - bit_depth = obj.get(CONF_BIT_DEPTH, 12) + frequency = obj[CONF_FREQUENCY] + bit_depth = obj[CONF_BIT_DEPTH] max_freq = APB_CLOCK_FREQ / (2**bit_depth) if frequency > max_freq: raise cv.Invalid('Maximum frequency for bit depth {} is {}Hz'.format(bit_depth, max_freq)) diff --git a/esphome/components/light/automation.h b/esphome/components/light/automation.h index f907c70963..af295df369 100644 --- a/esphome/components/light/automation.h +++ b/esphome/components/light/automation.h @@ -112,7 +112,7 @@ template class AddressableSet : public Action { void play(Ts... x) override { auto *out = (AddressableLight *) this->parent_->get_output(); int32_t range_from = this->range_from_.value_or(x..., 0); - int32_t range_to = this->range_to_.value_or(x..., out->size()); + int32_t range_to = this->range_to_.value_or(x..., out->size() - 1) + 1; auto range = out->range(range_from, range_to); if (this->red_.has_value()) range.set_red(this->red_.value(x...)); diff --git a/esphome/components/light/automation.py b/esphome/components/light/automation.py index 4718a22052..629073707e 100644 --- a/esphome/components/light/automation.py +++ b/esphome/components/light/automation.py @@ -3,8 +3,9 @@ import esphome.config_validation as cv from esphome import automation from esphome.const import CONF_ID, CONF_TRANSITION_LENGTH, CONF_STATE, CONF_FLASH_LENGTH, \ CONF_EFFECT, CONF_BRIGHTNESS, CONF_RED, CONF_GREEN, CONF_BLUE, CONF_WHITE, \ - CONF_COLOR_TEMPERATURE -from .types import DimRelativeAction, ToggleAction, LightState, LightControlAction + CONF_COLOR_TEMPERATURE, CONF_RANGE_FROM, CONF_RANGE_TO +from .types import DimRelativeAction, ToggleAction, LightState, LightControlAction, \ + AddressableLightState, AddressableSet @automation.register_action('light.toggle', ToggleAction, automation.maybe_simple_id({ @@ -87,7 +88,7 @@ def light_control_to_code(config, action_id, template_arg, args): CONF_RELATIVE_BRIGHTNESS = 'relative_brightness' LIGHT_DIM_RELATIVE_ACTION_SCHEMA = cv.Schema({ cv.Required(CONF_ID): cv.use_id(LightState), - cv.Required(CONF_RELATIVE_BRIGHTNESS): cv.templatable(cv.percentage), + cv.Required(CONF_RELATIVE_BRIGHTNESS): cv.templatable(cv.possibly_negative_percentage), cv.Optional(CONF_TRANSITION_LENGTH): cv.templatable(cv.positive_time_period_milliseconds), }) @@ -103,3 +104,44 @@ def light_dim_relative_to_code(config, action_id, template_arg, args): templ = yield cg.templatable(config[CONF_TRANSITION_LENGTH], args, cg.uint32) cg.add(var.set_transition_length(templ)) yield var + + +LIGHT_ADDRESSABLE_SET_ACTION_SCHEMA = cv.Schema({ + cv.Required(CONF_ID): cv.use_id(AddressableLightState), + cv.Optional(CONF_RANGE_FROM): cv.templatable(cv.positive_int), + cv.Optional(CONF_RANGE_TO): cv.templatable(cv.positive_int), + cv.Optional(CONF_RED): cv.templatable(cv.percentage), + cv.Optional(CONF_GREEN): cv.templatable(cv.percentage), + cv.Optional(CONF_BLUE): cv.templatable(cv.percentage), + cv.Optional(CONF_WHITE): cv.templatable(cv.percentage), +}) + + +@automation.register_action('light.addressable_set', AddressableSet, + LIGHT_ADDRESSABLE_SET_ACTION_SCHEMA) +def light_addressable_set_to_code(config, action_id, template_arg, args): + paren = yield cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, paren) + if CONF_RANGE_FROM in config: + templ = yield cg.templatable(config[CONF_RANGE_FROM], args, cg.int32) + cg.add(var.set_range_from(templ)) + if CONF_RANGE_TO in config: + templ = yield cg.templatable(config[CONF_RANGE_TO], args, cg.int32) + cg.add(var.set_range_to(templ)) + + def rgbw_to_exp(x): + return int(round(x * 255)) + + if CONF_RED in config: + templ = yield cg.templatable(config[CONF_RED], args, cg.uint8, to_exp=rgbw_to_exp) + cg.add(var.set_red(templ)) + if CONF_GREEN in config: + templ = yield cg.templatable(config[CONF_GREEN], args, cg.uint8, to_exp=rgbw_to_exp) + cg.add(var.set_green(templ)) + if CONF_BLUE in config: + templ = yield cg.templatable(config[CONF_BLUE], args, cg.uint8, to_exp=rgbw_to_exp) + cg.add(var.set_blue(templ)) + if CONF_WHITE in config: + templ = yield cg.templatable(config[CONF_WHITE], args, cg.uint8, to_exp=rgbw_to_exp) + cg.add(var.set_white(templ)) + yield var diff --git a/esphome/components/light/types.py b/esphome/components/light/types.py index c9f638a4a4..33ba759df0 100644 --- a/esphome/components/light/types.py +++ b/esphome/components/light/types.py @@ -15,6 +15,7 @@ LightColorValues = light_ns.class_('LightColorValues') ToggleAction = light_ns.class_('ToggleAction', automation.Action) LightControlAction = light_ns.class_('LightControlAction', automation.Action) DimRelativeAction = light_ns.class_('DimRelativeAction', automation.Action) +AddressableSet = light_ns.class_('AddressableSet', automation.Action) # Effects LightEffect = light_ns.class_('LightEffect') diff --git a/esphome/components/logger/__init__.py b/esphome/components/logger/__init__.py index 9e351d0b01..27141f93c2 100644 --- a/esphome/components/logger/__init__.py +++ b/esphome/components/logger/__init__.py @@ -71,6 +71,7 @@ def validate_local_no_higher_than_global(value): Logger = logger_ns.class_('Logger', cg.Component) +CONF_ESP8266_STORE_LOG_STRINGS_IN_FLASH = 'esp8266_store_log_strings_in_flash' CONFIG_SCHEMA = cv.All(cv.Schema({ cv.GenerateID(): cv.declare_id(Logger), cv.Optional(CONF_BAUD_RATE, default=115200): cv.positive_int, @@ -79,7 +80,10 @@ CONFIG_SCHEMA = cv.All(cv.Schema({ cv.Optional(CONF_LEVEL, default='DEBUG'): is_log_level, cv.Optional(CONF_LOGS, default={}): cv.Schema({ cv.string: is_log_level, - }) + }), + + cv.SplitDefault(CONF_ESP8266_STORE_LOG_STRINGS_IN_FLASH, esp8266=True): + cv.All(cv.only_on_esp8266, cv.boolean), }).extend(cv.COMPONENT_SCHEMA), validate_local_no_higher_than_global) @@ -126,7 +130,7 @@ def to_code(config): cg.add_build_flag('-DCORE_DEBUG_LEVEL=5') if CORE.is_esp32 and is_at_least_very_verbose: cg.add_build_flag('-DENABLE_I2C_DEBUG_BUFFER') - if CORE.is_esp8266: + if config.get(CONF_ESP8266_STORE_LOG_STRINGS_IN_FLASH): cg.add_build_flag('-DUSE_STORE_LOG_STR_IN_FLASH') # Register at end for safe mode diff --git a/esphome/components/max31855/sensor.py b/esphome/components/max31855/sensor.py index 7178488ebb..b4d9f82b03 100644 --- a/esphome/components/max31855/sensor.py +++ b/esphome/components/max31855/sensor.py @@ -4,7 +4,8 @@ from esphome.components import sensor, spi from esphome.const import CONF_ID, ICON_THERMOMETER, UNIT_CELSIUS max31855_ns = cg.esphome_ns.namespace('max31855') -MAX31855Sensor = max31855_ns.class_('MAX31855Sensor', sensor.PollingSensorComponent, spi.SPIDevice) +MAX31855Sensor = max31855_ns.class_('MAX31855Sensor', sensor.Sensor, cg.PollingComponent, + spi.SPIDevice) CONFIG_SCHEMA = sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1).extend({ cv.GenerateID(): cv.declare_id(MAX31855Sensor), diff --git a/esphome/components/max6675/max6675.cpp b/esphome/components/max6675/max6675.cpp index e9bbfb4a05..8ea7feb963 100644 --- a/esphome/components/max6675/max6675.cpp +++ b/esphome/components/max6675/max6675.cpp @@ -14,7 +14,7 @@ void MAX6675Sensor::update() { // Conversion time typ: 170ms, max: 220ms auto f = std::bind(&MAX6675Sensor::read_data_, this); - this->set_timeout("value", 220, f); + this->set_timeout("value", 250, f); } void MAX6675Sensor::setup() { diff --git a/esphome/components/max6675/sensor.py b/esphome/components/max6675/sensor.py index af089614f0..59d24a5283 100644 --- a/esphome/components/max6675/sensor.py +++ b/esphome/components/max6675/sensor.py @@ -4,7 +4,7 @@ from esphome.components import sensor, spi from esphome.const import CONF_ID, ICON_THERMOMETER, UNIT_CELSIUS max6675_ns = cg.esphome_ns.namespace('max6675') -MAX6675Sensor = max6675_ns.class_('MAX6675Sensor', sensor.PollingSensorComponent, +MAX6675Sensor = max6675_ns.class_('MAX6675Sensor', sensor.Sensor, cg.PollingComponent, spi.SPIDevice) CONFIG_SCHEMA = sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1).extend({ diff --git a/esphome/components/mpu6050/mpu6050.cpp b/esphome/components/mpu6050/mpu6050.cpp index 68ec35f6c0..deba316a7f 100644 --- a/esphome/components/mpu6050/mpu6050.cpp +++ b/esphome/components/mpu6050/mpu6050.cpp @@ -99,17 +99,18 @@ void MPU6050Component::dump_config() { void MPU6050Component::update() { ESP_LOGV(TAG, " Updating MPU6050..."); - uint16_t data[7]; - if (!this->read_bytes_16(MPU6050_REGISTER_ACCEL_XOUT_H, data, 7)) { + uint16_t raw_data[7]; + if (!this->read_bytes_16(MPU6050_REGISTER_ACCEL_XOUT_H, raw_data, 7)) { this->status_set_warning(); return; } + auto *data = reinterpret_cast(raw_data); float accel_x = data[0] * MPU6050_RANGE_PER_DIGIT_2G * GRAVITY_EARTH; float accel_y = data[1] * MPU6050_RANGE_PER_DIGIT_2G * GRAVITY_EARTH; float accel_z = data[2] * MPU6050_RANGE_PER_DIGIT_2G * GRAVITY_EARTH; - float temperature = data[3] / 340.0f + 36.53f; + float temperature = raw_data[3] / 340.0f + 36.53f; float gyro_x = data[4] * MPU6050_SCALE_DPS_PER_DIGIT_2000; float gyro_y = data[5] * MPU6050_SCALE_DPS_PER_DIGIT_2000; diff --git a/esphome/components/mqtt/__init__.py b/esphome/components/mqtt/__init__.py index 16a88f39e3..c363f93d06 100644 --- a/esphome/components/mqtt/__init__.py +++ b/esphome/components/mqtt/__init__.py @@ -150,12 +150,13 @@ def exp_mqtt_message(config): @coroutine_with_priority(40.0) def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + yield cg.register_component(var, config) + cg.add_library('AsyncMqttClient', '0.8.2') cg.add_define('USE_MQTT') cg.add_global(mqtt_ns.using) - var = cg.new_Pvariable(config[CONF_ID]) - cg.add(var.set_broker_address(config[CONF_BROKER])) cg.add(var.set_broker_port(config[CONF_PORT])) cg.add(var.set_username(config[CONF_USERNAME])) diff --git a/esphome/components/neopixelbus/neopixelbus_light.h b/esphome/components/neopixelbus/neopixelbus_light.h index 86ae21ddd0..68d99fb374 100644 --- a/esphome/components/neopixelbus/neopixelbus_light.h +++ b/esphome/components/neopixelbus/neopixelbus_light.h @@ -152,7 +152,7 @@ class NeoPixelRGBLightOutput : public NeoPixelBusLightOutputBasecontroller_->Pixels() + 3ULL * index; return light::ESPColorView(base + this->rgb_offsets_[0], base + this->rgb_offsets_[1], base + this->rgb_offsets_[2], nullptr, this->effect_data_ + index, &this->correction_); @@ -171,7 +171,7 @@ class NeoPixelRGBWLightOutput : public NeoPixelBusLightOutputBasecontroller_->Pixels() + 4ULL * index; return light::ESPColorView(base + this->rgb_offsets_[0], base + this->rgb_offsets_[1], base + this->rgb_offsets_[2], base + this->rgb_offsets_[3], this->effect_data_ + index, &this->correction_); diff --git a/esphome/components/partition/light_partition.h b/esphome/components/partition/light_partition.h index 398cbc6664..b68e3404f1 100644 --- a/esphome/components/partition/light_partition.h +++ b/esphome/components/partition/light_partition.h @@ -37,7 +37,23 @@ class PartitionLightOutput : public light::AddressableLight, public Component { auto &last_seg = this->segments_[this->segments_.size() - 1]; return last_seg.get_dst_offset() + last_seg.get_size(); } - light::ESPColorView operator[](int32_t index) const override { + void clear_effect_data() override { + for (auto &seg : this->segments_) { + seg.get_src()->clear_effect_data(); + } + } + light::LightTraits get_traits() override { return this->segments_[0].get_src()->get_traits(); } + void loop() override { + if (this->should_show_()) { + for (auto seg : this->segments_) { + seg.get_src()->schedule_show(); + } + this->mark_shown_(); + } + } + + protected: + light::ESPColorView get_view_internal(int32_t index) const override { uint32_t lo = 0; uint32_t hi = this->segments_.size() - 1; while (lo < hi) { @@ -61,22 +77,7 @@ class PartitionLightOutput : public light::AddressableLight, public Component { view.raw_set_color_correction(&this->correction_); return view; } - void clear_effect_data() override { - for (auto &seg : this->segments_) { - seg.get_src()->clear_effect_data(); - } - } - light::LightTraits get_traits() override { return this->segments_[0].get_src()->get_traits(); } - void loop() override { - if (this->should_show_()) { - for (auto seg : this->segments_) { - seg.get_src()->schedule_show(); - } - this->mark_shown_(); - } - } - protected: std::vector segments_; }; diff --git a/esphome/components/pulse_counter/sensor.py b/esphome/components/pulse_counter/sensor.py index 6fbdb00945..636147c138 100644 --- a/esphome/components/pulse_counter/sensor.py +++ b/esphome/components/pulse_counter/sensor.py @@ -18,7 +18,7 @@ COUNT_MODES = { COUNT_MODE_SCHEMA = cv.enum(COUNT_MODES, upper=True) PulseCounterSensor = pulse_counter_ns.class_('PulseCounterSensor', - sensor.PollingSensorComponent) + sensor.Sensor, cg.PollingComponent) def validate_internal_filter(value): diff --git a/esphome/components/pulse_width/sensor.py b/esphome/components/pulse_width/sensor.py index 42d1b0dbaa..0227adffce 100644 --- a/esphome/components/pulse_width/sensor.py +++ b/esphome/components/pulse_width/sensor.py @@ -6,7 +6,7 @@ from esphome.const import CONF_ID, CONF_PIN, UNIT_SECOND, ICON_TIMER pulse_width_ns = cg.esphome_ns.namespace('pulse_width') -PulseWidthSensor = pulse_width_ns.class_('PulseWidthSensor', sensor.PollingSensorComponent) +PulseWidthSensor = pulse_width_ns.class_('PulseWidthSensor', sensor.Sensor, cg.PollingComponent) CONFIG_SCHEMA = sensor.sensor_schema(UNIT_SECOND, ICON_TIMER, 3).extend({ cv.GenerateID(): cv.declare_id(PulseWidthSensor), diff --git a/esphome/components/sensor/__init__.py b/esphome/components/sensor/__init__.py index a233468762..0fe0fc6bca 100644 --- a/esphome/components/sensor/__init__.py +++ b/esphome/components/sensor/__init__.py @@ -51,8 +51,6 @@ sensor_ns = cg.esphome_ns.namespace('sensor') Sensor = sensor_ns.class_('Sensor', cg.Nameable) SensorPtr = Sensor.operator('ptr') -PollingSensorComponent = sensor_ns.class_('PollingSensorComponent', cg.PollingComponent, Sensor) - # Triggers SensorStateTrigger = sensor_ns.class_('SensorStateTrigger', automation.Trigger.template(cg.float_)) SensorRawStateTrigger = sensor_ns.class_('SensorRawStateTrigger', diff --git a/esphome/components/template/sensor/__init__.py b/esphome/components/template/sensor/__init__.py index 1e9fe9cdee..788520877a 100644 --- a/esphome/components/template/sensor/__init__.py +++ b/esphome/components/template/sensor/__init__.py @@ -5,7 +5,7 @@ from esphome.components import sensor from esphome.const import CONF_ID, CONF_LAMBDA, CONF_STATE, UNIT_EMPTY, ICON_EMPTY from .. import template_ns -TemplateSensor = template_ns.class_('TemplateSensor', sensor.PollingSensorComponent) +TemplateSensor = template_ns.class_('TemplateSensor', sensor.Sensor, cg.PollingComponent) CONFIG_SCHEMA = sensor.sensor_schema(UNIT_EMPTY, ICON_EMPTY, 1).extend({ cv.GenerateID(): cv.declare_id(TemplateSensor), diff --git a/esphome/components/time/real_time_clock.cpp b/esphome/components/time/real_time_clock.cpp index b2966cd8e0..f20a9fcaa2 100644 --- a/esphome/components/time/real_time_clock.cpp +++ b/esphome/components/time/real_time_clock.cpp @@ -11,16 +11,6 @@ namespace time { static const char *TAG = "time"; RealTimeClock::RealTimeClock() = default; -ESPTime RealTimeClock::now() { - time_t t = ::time(nullptr); - struct tm *c_tm = ::localtime(&t); - return ESPTime::from_tm(c_tm, t); -} -ESPTime RealTimeClock::utcnow() { - time_t t = ::time(nullptr); - struct tm *c_tm = ::gmtime(&t); - return ESPTime::from_tm(c_tm, t); -} void RealTimeClock::call_setup() { this->setup_internal_(); setenv("TZ", this->timezone_.c_str(), 1); @@ -44,7 +34,7 @@ size_t ESPTime::strftime(char *buffer, size_t buffer_len, const char *format) { struct tm c_tm = this->to_c_tm(); return ::strftime(buffer, buffer_len, format, &c_tm); } -ESPTime ESPTime::from_tm(struct tm *c_tm, time_t c_time) { +ESPTime ESPTime::from_c_tm(struct tm *c_tm, time_t c_time) { return ESPTime{.second = uint8_t(c_tm->tm_sec), .minute = uint8_t(c_tm->tm_min), .hour = uint8_t(c_tm->tm_hour), diff --git a/esphome/components/time/real_time_clock.h b/esphome/components/time/real_time_clock.h index 21d63900b6..b87636d20e 100644 --- a/esphome/components/time/real_time_clock.h +++ b/esphome/components/time/real_time_clock.h @@ -52,7 +52,26 @@ struct ESPTime { bool in_range() const; - static ESPTime from_tm(struct tm *c_tm, time_t c_time); + static ESPTime from_c_tm(struct tm *c_tm, time_t c_time); + + /** Convert an epoch timestamp to an ESPTime instance of local time. + * + * @param epoch Seconds since 1st January 1970. In UTC. + * @return The generated ESPTime + */ + static ESPTime from_epoch_local(time_t epoch) { + struct tm *c_tm = ::localtime(&epoch); + return ESPTime::from_c_tm(c_tm, epoch); + } + /** Convert an epoch timestamp to an ESPTime instance of UTC time. + * + * @param epoch Seconds since 1st January 1970. In UTC. + * @return The generated ESPTime + */ + static ESPTime from_epoch_utc(time_t epoch) { + struct tm *c_tm = ::gmtime(&epoch); + return ESPTime::from_c_tm(c_tm, epoch); + } struct tm to_c_tm(); @@ -81,10 +100,13 @@ class RealTimeClock : public Component { std::string get_timezone() { return this->timezone_; } /// Get the time in the currently defined timezone. - ESPTime now(); + ESPTime now() { return ESPTime::from_epoch_utc(this->timestamp_now()); } /// Get the time without any time zone or DST corrections. - ESPTime utcnow(); + ESPTime utcnow() { return ESPTime::from_epoch_local(this->timestamp_now()); } + + /// Get the current time as the UTC epoch since January 1st 1970. + time_t timestamp_now() { return ::time(nullptr); } void call_setup() override; diff --git a/esphome/components/tsl2561/sensor.py b/esphome/components/tsl2561/sensor.py index 5171884a14..e2e333cc81 100644 --- a/esphome/components/tsl2561/sensor.py +++ b/esphome/components/tsl2561/sensor.py @@ -27,7 +27,8 @@ def validate_integration_time(value): return cv.enum(INTEGRATION_TIMES, int=True)(value) -TSL2561Sensor = tsl2561_ns.class_('TSL2561Sensor', sensor.PollingSensorComponent, i2c.I2CDevice) +TSL2561Sensor = tsl2561_ns.class_('TSL2561Sensor', sensor.Sensor, cg.PollingComponent, + i2c.I2CDevice) CONFIG_SCHEMA = sensor.sensor_schema(UNIT_LUX, ICON_BRIGHTNESS_5, 1).extend({ cv.GenerateID(): cv.declare_id(TSL2561Sensor), diff --git a/esphome/components/ultrasonic/sensor.py b/esphome/components/ultrasonic/sensor.py index fa3934853c..e4364f271c 100644 --- a/esphome/components/ultrasonic/sensor.py +++ b/esphome/components/ultrasonic/sensor.py @@ -9,7 +9,7 @@ CONF_PULSE_TIME = 'pulse_time' ultrasonic_ns = cg.esphome_ns.namespace('ultrasonic') UltrasonicSensorComponent = ultrasonic_ns.class_('UltrasonicSensorComponent', - sensor.PollingSensorComponent) + sensor.Sensor, cg.PollingComponent) CONFIG_SCHEMA = sensor.sensor_schema(UNIT_METER, ICON_ARROW_EXPAND_VERTICAL, 2).extend({ cv.GenerateID(): cv.declare_id(UltrasonicSensorComponent), diff --git a/esphome/components/uptime/sensor.py b/esphome/components/uptime/sensor.py index e4f78c6411..1dacc99653 100644 --- a/esphome/components/uptime/sensor.py +++ b/esphome/components/uptime/sensor.py @@ -4,7 +4,7 @@ from esphome.components import sensor from esphome.const import CONF_ID, UNIT_SECOND, ICON_TIMER uptime_ns = cg.esphome_ns.namespace('uptime') -UptimeSensor = uptime_ns.class_('UptimeSensor', sensor.PollingSensorComponent) +UptimeSensor = uptime_ns.class_('UptimeSensor', sensor.Sensor, cg.PollingComponent) CONFIG_SCHEMA = sensor.sensor_schema(UNIT_SECOND, ICON_TIMER, 0).extend({ cv.GenerateID(): cv.declare_id(UptimeSensor), diff --git a/esphome/components/voltage_sampler/__init__.py b/esphome/components/voltage_sampler/__init__.py new file mode 100644 index 0000000000..64161205d8 --- /dev/null +++ b/esphome/components/voltage_sampler/__init__.py @@ -0,0 +1,4 @@ +import esphome.codegen as cg + +voltage_sampler_ns = cg.esphome_ns.namespace('voltage_sampler') +VoltageSampler = voltage_sampler_ns.class_('VoltageSampler') diff --git a/esphome/components/voltage_sampler/voltage_sampler.h b/esphome/components/voltage_sampler/voltage_sampler.h new file mode 100644 index 0000000000..d2e74d33bc --- /dev/null +++ b/esphome/components/voltage_sampler/voltage_sampler.h @@ -0,0 +1,16 @@ +#pragma once + +#include "esphome/core/component.h" + +namespace esphome { +namespace voltage_sampler { + +/// Abstract interface for components to request voltage (usually ADC readings) +class VoltageSampler { + public: + /// Get a voltage reading, in V. + virtual float sample() = 0; +}; + +} // namespace voltage_sampler +} // namespace esphome diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index 28d911606b..35735e5f08 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -468,10 +468,6 @@ std::string WiFiComponent::format_mac_addr(const uint8_t *mac) { sprintf(buf, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); return buf; } -void WiFiComponent::on_safe_shutdown() { - // Disable WiFi interface on shutdown - this->wifi_mode_(false, false); -} bool sta_field_equal(const uint8_t *field_a, const uint8_t *field_b, int len) { for (int i = 0; i < len; i++) { diff --git a/esphome/components/wifi/wifi_component.h b/esphome/components/wifi/wifi_component.h index a9e67f23c6..8e6418791c 100644 --- a/esphome/components/wifi/wifi_component.h +++ b/esphome/components/wifi/wifi_component.h @@ -164,8 +164,6 @@ class WiFiComponent : public Component { /// Reconnect WiFi if required. void loop() override; - void on_safe_shutdown() override; - bool has_sta() const; bool has_ap() const; diff --git a/esphome/components/wifi/wifi_component_esp32.cpp b/esphome/components/wifi/wifi_component_esp32.cpp index 1edfe11ce2..f50acee614 100644 --- a/esphome/components/wifi/wifi_component_esp32.cpp +++ b/esphome/components/wifi/wifi_component_esp32.cpp @@ -173,6 +173,8 @@ bool WiFiComponent::wifi_sta_connect_(WiFiAP ap) { return false; } + this->wifi_apply_hostname_(); + err = esp_wifi_connect(); if (err != ESP_OK) { ESP_LOGW(TAG, "esp_wifi_connect failed! %d", err); diff --git a/esphome/components/wifi/wifi_component_esp8266.cpp b/esphome/components/wifi/wifi_component_esp8266.cpp index 5030e87e25..a2c9ac2551 100644 --- a/esphome/components/wifi/wifi_component_esp8266.cpp +++ b/esphome/components/wifi/wifi_component_esp8266.cpp @@ -196,6 +196,8 @@ bool WiFiComponent::wifi_sta_connect_(WiFiAP ap) { return false; } + this->wifi_apply_hostname_(); + ETS_UART_INTR_DISABLE(); ret = wifi_station_connect(); ETS_UART_INTR_ENABLE(); diff --git a/esphome/components/wifi_signal/sensor.py b/esphome/components/wifi_signal/sensor.py index e53daede1f..1cc58009af 100644 --- a/esphome/components/wifi_signal/sensor.py +++ b/esphome/components/wifi_signal/sensor.py @@ -5,7 +5,7 @@ from esphome.const import CONF_ID, ICON_WIFI, UNIT_DECIBEL DEPENDENCIES = ['wifi'] wifi_signal_ns = cg.esphome_ns.namespace('wifi_signal') -WiFiSignalSensor = wifi_signal_ns.class_('WiFiSignalSensor', sensor.PollingSensorComponent) +WiFiSignalSensor = wifi_signal_ns.class_('WiFiSignalSensor', sensor.Sensor, cg.PollingComponent) CONFIG_SCHEMA = sensor.sensor_schema(UNIT_DECIBEL, ICON_WIFI, 0).extend({ cv.GenerateID(): cv.declare_id(WiFiSignalSensor), diff --git a/esphome/components/xiaomi_ble/__init__.py b/esphome/components/xiaomi_ble/__init__.py index ef5df12cd9..2b36090293 100644 --- a/esphome/components/xiaomi_ble/__init__.py +++ b/esphome/components/xiaomi_ble/__init__.py @@ -1,20 +1,18 @@ import esphome.codegen as cg import esphome.config_validation as cv -from esphome.components.esp32_ble_tracker import CONF_ESP32_BLE_ID, ESPBTDeviceListener, \ - ESP_BLE_DEVICE_SCHEMA +from esphome.components import esp32_ble_tracker from esphome.const import CONF_ID DEPENDENCIES = ['esp32_ble_tracker'] xiaomi_ble_ns = cg.esphome_ns.namespace('xiaomi_ble') -XiaomiListener = xiaomi_ble_ns.class_('XiaomiListener', cg.Component, ESPBTDeviceListener) +XiaomiListener = xiaomi_ble_ns.class_('XiaomiListener', esp32_ble_tracker.ESPBTDeviceListener) CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(XiaomiListener), -}).extend(ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) +}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA) def to_code(config): - hub = yield cg.get_variable(config[CONF_ESP32_BLE_ID]) - var = cg.new_Pvariable(config[CONF_ID], hub) - yield cg.register_component(var, config) + var = cg.new_Pvariable(config[CONF_ID]) + yield esp32_ble_tracker.register_ble_device(var, config) diff --git a/esphome/components/xiaomi_ble/xiaomi_ble.cpp b/esphome/components/xiaomi_ble/xiaomi_ble.cpp index 8addbe6401..7431b84491 100644 --- a/esphome/components/xiaomi_ble/xiaomi_ble.cpp +++ b/esphome/components/xiaomi_ble/xiaomi_ble.cpp @@ -138,8 +138,6 @@ bool XiaomiListener::parse_device(const esp32_ble_tracker::ESPBTDevice &device) return true; } -void XiaomiListener::setup() { this->setup_ble(); } -XiaomiListener::XiaomiListener(esp32_ble_tracker::ESP32BLETracker *parent) : ESPBTDeviceListener(parent) {} } // namespace xiaomi_ble } // namespace esphome diff --git a/esphome/components/xiaomi_ble/xiaomi_ble.h b/esphome/components/xiaomi_ble/xiaomi_ble.h index df70fd2fc5..058a89927b 100644 --- a/esphome/components/xiaomi_ble/xiaomi_ble.h +++ b/esphome/components/xiaomi_ble/xiaomi_ble.h @@ -22,11 +22,9 @@ bool parse_xiaomi_data_byte(uint8_t data_type, const uint8_t *data, uint8_t data optional parse_xiaomi(const esp32_ble_tracker::ESPBTDevice &device); -class XiaomiListener : public Component, public esp32_ble_tracker::ESPBTDeviceListener { +class XiaomiListener : public esp32_ble_tracker::ESPBTDeviceListener { public: - XiaomiListener(esp32_ble_tracker::ESP32BLETracker *parent); bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override; - void setup() override; }; } // namespace xiaomi_ble diff --git a/esphome/components/xiaomi_miflora/sensor.py b/esphome/components/xiaomi_miflora/sensor.py index f6b44f9239..8be06a93f3 100644 --- a/esphome/components/xiaomi_miflora/sensor.py +++ b/esphome/components/xiaomi_miflora/sensor.py @@ -1,8 +1,6 @@ import esphome.codegen as cg import esphome.config_validation as cv -from esphome.components import sensor -from esphome.components.esp32_ble_tracker import CONF_ESP32_BLE_ID, ESPBTDeviceListener, \ - ESP_BLE_DEVICE_SCHEMA +from esphome.components import sensor, esp32_ble_tracker from esphome.const import CONF_BATTERY_LEVEL, CONF_MAC_ADDRESS, CONF_TEMPERATURE, \ UNIT_CELSIUS, ICON_THERMOMETER, UNIT_PERCENT, ICON_WATER_PERCENT, ICON_BATTERY, CONF_ID, \ CONF_MOISTURE, CONF_ILLUMINANCE, ICON_BRIGHTNESS_5, UNIT_LUX, CONF_CONDUCTIVITY, \ @@ -12,7 +10,8 @@ DEPENDENCIES = ['esp32_ble_tracker'] AUTO_LOAD = ['xiaomi_ble'] xiaomi_miflora_ns = cg.esphome_ns.namespace('xiaomi_miflora') -XiaomiMiflora = xiaomi_miflora_ns.class_('XiaomiMiflora', ESPBTDeviceListener, cg.Component) +XiaomiMiflora = xiaomi_miflora_ns.class_('XiaomiMiflora', esp32_ble_tracker.ESPBTDeviceListener, + cg.Component) CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(XiaomiMiflora), @@ -23,13 +22,15 @@ CONFIG_SCHEMA = cv.Schema({ cv.Optional(CONF_CONDUCTIVITY): sensor.sensor_schema(UNIT_MICROSIEMENS_PER_CENTIMETER, ICON_FLOWER, 0), cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(UNIT_PERCENT, ICON_BATTERY, 0), -}).extend(ESP_BLE_DEVICE_SCHEMA) +}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) def to_code(config): - hub = yield cg.get_variable(config[CONF_ESP32_BLE_ID]) - var = cg.new_Pvariable(config[CONF_ID], hub, config[CONF_MAC_ADDRESS].as_hex) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) + yield esp32_ble_tracker.register_ble_device(var, config) + + cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex)) if CONF_TEMPERATURE in config: sens = yield sensor.new_sensor(config[CONF_TEMPERATURE]) diff --git a/esphome/components/xiaomi_miflora/xiaomi_miflora.h b/esphome/components/xiaomi_miflora/xiaomi_miflora.h index 987e0cc409..d1f05cdcc7 100644 --- a/esphome/components/xiaomi_miflora/xiaomi_miflora.h +++ b/esphome/components/xiaomi_miflora/xiaomi_miflora.h @@ -12,8 +12,7 @@ namespace xiaomi_miflora { class XiaomiMiflora : public Component, public esp32_ble_tracker::ESPBTDeviceListener { public: - XiaomiMiflora(esp32_ble_tracker::ESP32BLETracker *parent, uint64_t address) - : ESPBTDeviceListener(parent), address_(address) {} + void set_address(uint64_t address) { address_ = address; } bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override { if (device.address_uint64() != this->address_) diff --git a/esphome/components/xiaomi_mijia/sensor.py b/esphome/components/xiaomi_mijia/sensor.py index ef68443304..995a6cbf25 100644 --- a/esphome/components/xiaomi_mijia/sensor.py +++ b/esphome/components/xiaomi_mijia/sensor.py @@ -1,8 +1,6 @@ import esphome.codegen as cg import esphome.config_validation as cv -from esphome.components import sensor -from esphome.components.esp32_ble_tracker import CONF_ESP32_BLE_ID, ESPBTDeviceListener, \ - ESP_BLE_DEVICE_SCHEMA +from esphome.components import sensor, esp32_ble_tracker from esphome.const import CONF_BATTERY_LEVEL, CONF_HUMIDITY, CONF_MAC_ADDRESS, CONF_TEMPERATURE, \ UNIT_CELSIUS, ICON_THERMOMETER, UNIT_PERCENT, ICON_WATER_PERCENT, ICON_BATTERY, CONF_ID @@ -10,7 +8,8 @@ DEPENDENCIES = ['esp32_ble_tracker'] AUTO_LOAD = ['xiaomi_ble'] xiaomi_mijia_ns = cg.esphome_ns.namespace('xiaomi_mijia') -XiaomiMijia = xiaomi_mijia_ns.class_('XiaomiMijia', ESPBTDeviceListener, cg.Component) +XiaomiMijia = xiaomi_mijia_ns.class_('XiaomiMijia', esp32_ble_tracker.ESPBTDeviceListener, + cg.Component) CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(XiaomiMijia), @@ -18,13 +17,15 @@ CONFIG_SCHEMA = cv.Schema({ cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1), cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 1), cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(UNIT_PERCENT, ICON_BATTERY, 0), -}).extend(ESP_BLE_DEVICE_SCHEMA) +}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) def to_code(config): - hub = yield cg.get_variable(config[CONF_ESP32_BLE_ID]) - var = cg.new_Pvariable(config[CONF_ID], hub, config[CONF_MAC_ADDRESS].as_hex) + var = cg.new_Pvariable(config[CONF_ID]) yield cg.register_component(var, config) + yield esp32_ble_tracker.register_ble_device(var, config) + + cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex)) if CONF_TEMPERATURE in config: sens = yield sensor.new_sensor(config[CONF_TEMPERATURE]) diff --git a/esphome/components/xiaomi_mijia/xiaomi_mijia.h b/esphome/components/xiaomi_mijia/xiaomi_mijia.h index e5b947e6b2..814e33fa75 100644 --- a/esphome/components/xiaomi_mijia/xiaomi_mijia.h +++ b/esphome/components/xiaomi_mijia/xiaomi_mijia.h @@ -12,8 +12,7 @@ namespace xiaomi_mijia { class XiaomiMijia : public Component, public esp32_ble_tracker::ESPBTDeviceListener { public: - XiaomiMijia(esp32_ble_tracker::ESP32BLETracker *parent, uint64_t address) - : ESPBTDeviceListener(parent), address_(address) {} + void set_address(uint64_t address) { address_ = address; } bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override { if (device.address_uint64() != this->address_) diff --git a/esphome/const.py b/esphome/const.py index 10e2781b44..fa9bae4304 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -317,6 +317,8 @@ CONF_PULSE_LENGTH = 'pulse_length' CONF_QOS = 'qos' CONF_RANDOM = 'random' CONF_RANGE = 'range' +CONF_RANGE_FROM = 'range_from' +CONF_RANGE_TO = 'range_to' CONF_RATE = 'rate' CONF_RAW = 'raw' CONF_REBOOT_TIMEOUT = 'reboot_timeout' diff --git a/esphome/core/automation.h b/esphome/core/automation.h index 7f79ab3ccd..ceed28e5b8 100644 --- a/esphome/core/automation.h +++ b/esphome/core/automation.h @@ -55,7 +55,7 @@ template class Trigger { bool is_running() { if (this->automation_parent_ == nullptr) return false; - return this->automation_parent_.is_running(); + return this->automation_parent_->is_running(); } protected: diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index c9ee402a39..cc046a6962 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -18,6 +18,7 @@ #define HOT __attribute__((hot)) #define ESPDEPRECATED(msg) __attribute__((deprecated(msg))) #define ALWAYS_INLINE __attribute__((always_inline)) +#define PACKED __attribute__((packed)) namespace esphome { diff --git a/esphome/core_config.py b/esphome/core_config.py index 9a636a28da..8d2f83c4f1 100644 --- a/esphome/core_config.py +++ b/esphome/core_config.py @@ -152,7 +152,7 @@ def add_includes(includes): for include in includes: path = CORE.relative_config_path(include) res = os.path.relpath(path, CORE.relative_build_path('src')).replace(os.path.sep, '/') - cg.add_global(cg.RawExpression(u'#include "{}"'.format(res))) + cg.add_global(cg.RawStatement(u'#include "{}"'.format(res))) @coroutine_with_priority(100.0) diff --git a/esphome/dashboard/static/esphome.js b/esphome/dashboard/static/esphome.js index 9f7a23fdb8..5ec29ba0dd 100644 --- a/esphome/dashboard/static/esphome.js +++ b/esphome/dashboard/static/esphome.js @@ -430,6 +430,9 @@ const validateModal = new LogModalElem({ name: 'validate', onPrepare: (modalElem, config) => { modalElem.querySelector(".stop-logs").innerHTML = "Stop"; + modalElem.querySelector(".action-edit").setAttribute('data-node', validateModal.activeConfig); + modalElem.querySelector(".action-upload").setAttribute('data-node', validateModal.activeConfig); + modalElem.querySelector(".action-upload").classList.add('disabled'); }, onProcessExit: (modalElem, code) => { if (code === 0) { @@ -437,6 +440,7 @@ const validateModal = new LogModalElem({ html: `${validateModal.activeConfig} is valid 👍`, displayLength: 5000, }); + modalElem.querySelector(".action-upload").classList.remove('disabled'); } else { M.toast({ html: `${validateModal.activeConfig} is invalid 😕`, @@ -552,6 +556,7 @@ editor.session.setOption('useSoftTabs', true); editor.session.setOption('tabSize', 2); const saveButton = editModalElem.querySelector(".save-button"); +const saveValidateButton = editModalElem.querySelector(".save-validate-button"); const saveEditor = () => { fetch(`./edit?configuration=${activeEditorConfig}`, { credentials: "same-origin", @@ -572,12 +577,14 @@ editor.commands.addCommand({ }); saveButton.addEventListener('click', saveEditor); +saveValidateButton.addEventListener('click', saveEditor); document.querySelectorAll(".action-edit").forEach((btn) => { btn.addEventListener('click', (e) => { activeEditorConfig = e.target.getAttribute('data-node'); const modalInstance = M.Modal.getInstance(editModalElem); const filenameField = editModalElem.querySelector('.filename'); + editModalElem.querySelector(".save-validate-button").setAttribute('data-node', activeEditorConfig); filenameField.innerHTML = activeEditorConfig; fetch(`./edit?configuration=${activeEditorConfig}`, {credentials: "same-origin"}) diff --git a/esphome/dashboard/templates/index.html b/esphome/dashboard/templates/index.html index 82afee979d..e7259b719d 100644 --- a/esphome/dashboard/templates/index.html +++ b/esphome/dashboard/templates/index.html @@ -147,6 +147,8 @@

   
   
 
@@ -430,6 +432,7 @@
   
   
 
diff --git a/script/build_compile_commands.py b/script/build_compile_commands.py
new file mode 100755
index 0000000000..e6d761771f
--- /dev/null
+++ b/script/build_compile_commands.py
@@ -0,0 +1,100 @@
+#!/usr/bin/env python
+import codecs
+import json
+import os
+import re
+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 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():
+    build_all_include()
+    build_compile_commands()
+    print("Done.")
+
+
+if __name__ == '__main__':
+    main()
diff --git a/script/ci-custom.py b/script/ci-custom.py
index ce69cd9455..7637f9313c 100755
--- a/script/ci-custom.py
+++ b/script/ci-custom.py
@@ -22,8 +22,18 @@ files = []
 for root, _, fs in os.walk('esphome'):
     for f in fs:
         _, ext = os.path.splitext(f)
-        if ext in ('.h', '.c', '.cpp', '.tcc', '.py'):
+        if ext in ('.h', '.c', '.cpp', '.tcc', '.yaml', '.yml', '.ini', '.txt',
+                   '.py', '.html', '.js', '.md'):
             files.append(os.path.join(root, f))
+ignore = [
+    'esphome/dashboard/static/materialize.min.js',
+    'esphome/dashboard/static/ace.js',
+    'esphome/dashboard/static/mode-yaml.js',
+    'esphome/dashboard/static/theme-dreamweaver.js',
+    'esphome/dashboard/static/jquery.validate.min.js',
+    'esphome/dashboard/static/ext-searchbox.js',
+]
+files = [f for f in files if f not in ignore]
 files.sort()
 
 errors = collections.defaultdict(list)
diff --git a/script/clang-tidy.py b/script/clang-tidy.py
index ae850d427f..7187c16454 100755
--- a/script/clang-tidy.py
+++ b/script/clang-tidy.py
@@ -45,6 +45,8 @@ def run_tidy(args, tmpdir, queue, lock, failed_files):
         invocation.append('-p=.')
         if args.quiet:
             invocation.append('-quiet')
+        for arg in ['-Wfor-loop-analysis', '-Wshadow-field', '-Wshadow-field-in-constructor']:
+            invocation.append('-extra-arg={}'.format(arg))
         invocation.append(os.path.abspath(path))
         invocation_s = ' '.join(shlex_quote(x) for x in invocation)
 
@@ -135,9 +137,6 @@ def build_compile_commands():
     command.append('-Wall')
     command.append('-Wno-delete-non-virtual-dtor')
     command.append('-Wno-unused-variable')
-    command.append('-Wfor-loop-analysis')
-    command.append('-Wshadow-field')
-    command.append('-Wshadow-field-in-constructor')
     command.append('-Wunreachable-code')
 
     source_files = []
@@ -265,8 +264,6 @@ def main():
         print('Ctrl-C detected, goodbye.')
         if tmpdir:
             shutil.rmtree(tmpdir)
-        if os.path.exists(temp_header_file):
-            os.remove(temp_header_file)
         os.kill(0, 9)
 
     if args.fix and failed_files:
@@ -275,12 +272,8 @@ def main():
             subprocess.call(['clang-apply-replacements-7', tmpdir])
         except:
             print('Error applying fixes.\n', file=sys.stderr)
-            if os.path.exists(temp_header_file):
-                os.remove(temp_header_file)
             raise
 
-    if os.path.exists(temp_header_file):
-        os.remove(temp_header_file)
     sys.exit(return_code)
 
 
diff --git a/script/lint-cpp b/script/lint-cpp
index c38351b606..b1766d8a28 100755
--- a/script/lint-cpp
+++ b/script/lint-cpp
@@ -12,5 +12,5 @@ fi
 
 set -x
 
-script/clang-tidy.py -c --fix
+script/clang-tidy.py -c --fix --all-headers
 script/clang-format.py -c -i
diff --git a/tests/custom.h b/tests/custom.h
new file mode 100644
index 0000000000..be1e91fb94
--- /dev/null
+++ b/tests/custom.h
@@ -0,0 +1,38 @@
+
+class CustomSensor : public Component, public Sensor {
+ public:
+  void loop() override {
+    publish_state(42.0);
+  }
+};
+
+class CustomTextSensor : public Component, public TextSensor {
+ public:
+  void loop() override {
+    publish_state("Hello World");
+  }
+};
+
+class CustomBinarySensor : public Component, public BinarySensor {
+ public:
+  void loop() override {
+    publish_state(false);
+  }
+};
+
+class CustomSwitch : public Switch {
+ protected:
+  void write_state(bool state) override {
+    ESP_LOGD("custom_switch", "Setting %s", ONOFF(state));
+  }
+};
+
+class CustomComponent : public PollingComponent {
+ public:
+  void setup() override {
+    ESP_LOGD("custom_component", "Setup");
+  }
+  void update() override {
+    ESP_LOGD("custom_component", "Update");
+  }
+};
diff --git a/tests/test3.yaml b/tests/test3.yaml
index c6eab19440..2eedb25fc5 100644
--- a/tests/test3.yaml
+++ b/tests/test3.yaml
@@ -7,6 +7,8 @@ esphome:
     - wait_until:
         - api.connected
         - wifi.connected
+  includes:
+    - custom.h
 
 substitutions:
   devicename: test3
@@ -65,6 +67,7 @@ ota:
 logger:
   hardware_uart: UART1
   level: DEBUG
+  esp8266_store_log_strings_in_flash: false
 
 web_server:
 
@@ -122,6 +125,14 @@ sensor:
       name: Color Temperature
     integration_time: 700ms
     gain: 60x
+  - platform: custom
+    lambda: |-
+      auto s = new CustomSensor();
+      App.register_component(s);
+      return {s};
+    sensors:
+      - id: custom_sensor
+        name: Custom Sensor
 
 time:
 - platform: homeassistant
@@ -129,7 +140,7 @@ time:
 apds9960:
   address: 0x20
   update_interval: 60s
-  
+
 mpr121:
   id: mpr121_first
   address: 0x5A
@@ -178,6 +189,14 @@ binary_sensor:
   - platform: ttp229_lsf
     channel: 1
     name: TTP229 LSF Test
+  - platform: custom
+    lambda: |-
+      auto s = new CustomBinarySensor();
+      App.register_component(s);
+      return {s};
+    binary_sensors:
+      - id: custom_binary_sensor
+        name: Custom Binary Sensor
 
 remote_receiver:
   pin: GPIO12
@@ -211,6 +230,14 @@ text_sensor:
   - platform: homeassistant
     entity_id: sensor.hello_world2
     id: ha_hello_world2
+  - platform: custom
+    lambda: |-
+      auto s = new CustomTextSensor();
+      App.register_component(s);
+      return {s};
+    text_sensors:
+      - id: custom_text_sensor
+        name: Custom Text Sensor
 
 script:
   - id: my_script
@@ -233,6 +260,19 @@ switch:
     id: gpio_switch2
     pin: GPIO1
     interlock: *interlock
+  - platform: custom
+    lambda: |-
+      auto s = new CustomSwitch();
+      return {s};
+    switches:
+      - id: custom_switch
+        name: Custom Switch
+
+custom_component:
+   lambda: |-
+     auto s = new CustomComponent();
+     s->set_update_interval(15000);
+     return {s};
 
 stepper:
   - platform: uln2003