mirror of
https://github.com/esphome/esphome.git
synced 2024-12-02 03:34:18 +01:00
commit
5f66aac1b8
76 changed files with 576 additions and 220 deletions
27
.editorconfig
Normal file
27
.editorconfig
Normal file
|
@ -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
|
6
.gitpod.yml
Normal file
6
.gitpod.yml
Normal file
|
@ -0,0 +1,6 @@
|
|||
ports:
|
||||
- port: 6052
|
||||
onOpen: open-preview
|
||||
tasks:
|
||||
- before: script/setup
|
||||
command: python -m esphome config dashboard
|
|
@ -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,
|
||||
|
|
|
@ -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"; }
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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]))
|
||||
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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<int16_t>(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
|
||||
|
|
|
@ -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<ADS1115Sensor *> 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
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<uint8_t>(raw_data[i]));
|
||||
} else {
|
||||
sprintf(buf, "0x%02X ", static_cast<uint8_t>(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> 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<const uint8_t *>(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<const char *>(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
|
||||
|
||||
|
|
|
@ -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<ESPBLEiBeacon> 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<ESPBTUUID> &get_service_data_uuid() const;
|
||||
const optional<ESPBLEiBeacon> 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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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'
|
||||
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -112,7 +112,7 @@ template<typename... Ts> class AddressableSet : public Action<Ts...> {
|
|||
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...));
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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<int16_t *>(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;
|
||||
|
|
|
@ -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]))
|
||||
|
|
|
@ -152,7 +152,7 @@ class NeoPixelRGBLightOutput : public NeoPixelBusLightOutputBase<T_METHOD, T_COL
|
|||
}
|
||||
|
||||
protected:
|
||||
light::ESPColorView get_view_internal(int32_t index) const override {
|
||||
light::ESPColorView get_view_internal(int32_t index) const override { // NOLINT
|
||||
uint8_t *base = this->controller_->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 NeoPixelBusLightOutputBase<T_METHOD, T_CO
|
|||
}
|
||||
|
||||
protected:
|
||||
light::ESPColorView get_view_internal(int32_t index) const override {
|
||||
light::ESPColorView get_view_internal(int32_t index) const override { // NOLINT
|
||||
uint8_t *base = this->controller_->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_);
|
||||
|
|
|
@ -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<AddressableSegment> segments_;
|
||||
};
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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),
|
||||
|
|
4
esphome/components/voltage_sampler/__init__.py
Normal file
4
esphome/components/voltage_sampler/__init__.py
Normal file
|
@ -0,0 +1,4 @@
|
|||
import esphome.codegen as cg
|
||||
|
||||
voltage_sampler_ns = cg.esphome_ns.namespace('voltage_sampler')
|
||||
VoltageSampler = voltage_sampler_ns.class_('VoltageSampler')
|
16
esphome/components/voltage_sampler/voltage_sampler.h
Normal file
16
esphome/components/voltage_sampler/voltage_sampler.h
Normal file
|
@ -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
|
|
@ -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++) {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -22,11 +22,9 @@ bool parse_xiaomi_data_byte(uint8_t data_type, const uint8_t *data, uint8_t data
|
|||
|
||||
optional<XiaomiParseResult> 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
|
||||
|
|
|
@ -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])
|
||||
|
|
|
@ -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_)
|
||||
|
|
|
@ -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])
|
||||
|
|
|
@ -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_)
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -55,7 +55,7 @@ template<typename... Ts> 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:
|
||||
|
|
|
@ -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 {
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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: `<code class="inlinecode">${validateModal.activeConfig}</code> is valid 👍`,
|
||||
displayLength: 5000,
|
||||
});
|
||||
modalElem.querySelector(".action-upload").classList.remove('disabled');
|
||||
} else {
|
||||
M.toast({
|
||||
html: `<code class="inlinecode">${validateModal.activeConfig}</code> 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"})
|
||||
|
|
|
@ -147,6 +147,8 @@
|
|||
<pre class="log"></pre>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a class="modal-close waves-effect waves-green btn-flat action-edit">Edit</a>
|
||||
<a class="modal-close waves-effect waves-green btn-flat action-upload">Upload</a>
|
||||
<a class="modal-close waves-effect waves-green btn-flat stop-logs">Stop</a>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -430,6 +432,7 @@
|
|||
</div>
|
||||
<div class="modal-footer">
|
||||
<a class="waves-effect waves-green btn-flat save-button">Save</a>
|
||||
<a class="modal-close waves-effect waves-green btn-flat action-validate save-validate-button">Save & Validate</a>
|
||||
<a class="modal-close waves-effect waves-green btn-flat">Close</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
100
script/build_compile_commands.py
Executable file
100
script/build_compile_commands.py
Executable file
|
@ -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()
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
38
tests/custom.h
Normal file
38
tests/custom.h
Normal file
|
@ -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");
|
||||
}
|
||||
};
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue