mirror of
https://github.com/esphome/esphome.git
synced 2024-12-02 03:34:18 +01:00
commit
b4c9b6511c
150 changed files with 2548 additions and 687 deletions
13
.gitignore
vendored
13
.gitignore
vendored
|
@ -6,6 +6,19 @@ __pycache__/
|
||||||
# C extensions
|
# C extensions
|
||||||
*.so
|
*.so
|
||||||
|
|
||||||
|
# Hide sublime text stuff
|
||||||
|
*.sublime-project
|
||||||
|
*.sublime-workspace
|
||||||
|
|
||||||
|
# Hide some OS X stuff
|
||||||
|
.DS_Store
|
||||||
|
.AppleDouble
|
||||||
|
.LSOverride
|
||||||
|
Icon
|
||||||
|
|
||||||
|
# Thumbnails
|
||||||
|
._*
|
||||||
|
|
||||||
# Distribution / packaging
|
# Distribution / packaging
|
||||||
.Python
|
.Python
|
||||||
build/
|
build/
|
||||||
|
|
|
@ -51,6 +51,6 @@ matrix:
|
||||||
- clang-format-7 -version
|
- clang-format-7 -version
|
||||||
- clang-apply-replacements-7 -version
|
- clang-apply-replacements-7 -version
|
||||||
script:
|
script:
|
||||||
- script/clang-tidy.py --all-headers -j 2 --fix
|
- script/clang-tidy --all-headers -j 2 --fix
|
||||||
- script/clang-format.py -i -j 2
|
- script/clang-format -i -j 2
|
||||||
- script/ci-suggest-changes
|
- script/ci-suggest-changes
|
||||||
|
|
|
@ -1,16 +1,18 @@
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
import functools
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from esphome import const, writer, yaml_util
|
from esphome import const, writer, yaml_util
|
||||||
|
import esphome.codegen as cg
|
||||||
from esphome.config import iter_components, read_config, strip_default_ids
|
from esphome.config import iter_components, read_config, strip_default_ids
|
||||||
from esphome.const import CONF_BAUD_RATE, CONF_BROKER, CONF_LOGGER, CONF_OTA, \
|
from esphome.const import CONF_BAUD_RATE, CONF_BROKER, CONF_LOGGER, CONF_OTA, \
|
||||||
CONF_PASSWORD, CONF_PORT
|
CONF_PASSWORD, CONF_PORT
|
||||||
from esphome.core import CORE, EsphomeError
|
from esphome.core import CORE, EsphomeError, coroutine, coroutine_with_priority
|
||||||
from esphome.helpers import color, indent
|
from esphome.helpers import color, indent
|
||||||
from esphome.py_compat import IS_PY2, safe_input
|
from esphome.py_compat import IS_PY2, safe_input
|
||||||
from esphome.util import run_external_command, run_external_process, safe_print
|
from esphome.util import run_external_command, run_external_process, safe_print
|
||||||
|
@ -117,12 +119,27 @@ def run_miniterm(config, port):
|
||||||
config, line, backtrace_state=backtrace_state)
|
config, line, backtrace_state=backtrace_state)
|
||||||
|
|
||||||
|
|
||||||
|
def wrap_to_code(name, comp):
|
||||||
|
coro = coroutine(comp.to_code)
|
||||||
|
|
||||||
|
@functools.wraps(comp.to_code)
|
||||||
|
@coroutine_with_priority(coro.priority)
|
||||||
|
def wrapped(conf):
|
||||||
|
cg.add(cg.LineComment(u"{}:".format(name)))
|
||||||
|
if comp.config_schema is not None:
|
||||||
|
cg.add(cg.LineComment(indent(yaml_util.dump(conf).decode('utf-8'))))
|
||||||
|
yield coro(conf)
|
||||||
|
|
||||||
|
return wrapped
|
||||||
|
|
||||||
|
|
||||||
def write_cpp(config):
|
def write_cpp(config):
|
||||||
_LOGGER.info("Generating C++ source...")
|
_LOGGER.info("Generating C++ source...")
|
||||||
|
|
||||||
for _, component, conf in iter_components(CORE.config):
|
for name, component, conf in iter_components(CORE.config):
|
||||||
if component.to_code is not None:
|
if component.to_code is not None:
|
||||||
CORE.add_job(component.to_code, conf)
|
coro = wrap_to_code(name, component)
|
||||||
|
CORE.add_job(coro, conf)
|
||||||
|
|
||||||
CORE.flush_tasks()
|
CORE.flush_tasks()
|
||||||
|
|
||||||
|
@ -245,7 +262,7 @@ def command_vscode(args):
|
||||||
from esphome import vscode
|
from esphome import vscode
|
||||||
|
|
||||||
CORE.config_path = args.configuration
|
CORE.config_path = args.configuration
|
||||||
vscode.read_config()
|
vscode.read_config(args)
|
||||||
|
|
||||||
|
|
||||||
def command_compile(args, config):
|
def command_compile(args, config):
|
||||||
|
@ -423,7 +440,8 @@ def parse_args(argv):
|
||||||
dashboard.add_argument("--socket",
|
dashboard.add_argument("--socket",
|
||||||
help="Make the dashboard serve under a unix socket", type=str)
|
help="Make the dashboard serve under a unix socket", type=str)
|
||||||
|
|
||||||
subparsers.add_parser('vscode', help=argparse.SUPPRESS)
|
vscode = subparsers.add_parser('vscode', help=argparse.SUPPRESS)
|
||||||
|
vscode.add_argument('--ace', action='store_true')
|
||||||
|
|
||||||
return parser.parse_args(argv[1:])
|
return parser.parse_args(argv[1:])
|
||||||
|
|
||||||
|
|
|
@ -128,7 +128,7 @@ def or_condition_to_code(config, condition_id, template_arg, args):
|
||||||
yield cg.new_Pvariable(condition_id, template_arg, conditions)
|
yield cg.new_Pvariable(condition_id, template_arg, conditions)
|
||||||
|
|
||||||
|
|
||||||
@register_condition('not', NotCondition, validate_condition)
|
@register_condition('not', NotCondition, validate_potentially_and_condition)
|
||||||
def not_condition_to_code(config, condition_id, template_arg, args):
|
def not_condition_to_code(config, condition_id, template_arg, args):
|
||||||
condition = yield build_condition(config, template_arg, args)
|
condition = yield build_condition(config, template_arg, args)
|
||||||
yield cg.new_Pvariable(condition_id, template_arg, condition)
|
yield cg.new_Pvariable(condition_id, template_arg, condition)
|
||||||
|
|
|
@ -10,16 +10,16 @@
|
||||||
# pylint: disable=unused-import
|
# pylint: disable=unused-import
|
||||||
from esphome.cpp_generator import ( # noqa
|
from esphome.cpp_generator import ( # noqa
|
||||||
Expression, RawExpression, RawStatement, TemplateArguments,
|
Expression, RawExpression, RawStatement, TemplateArguments,
|
||||||
StructInitializer, ArrayInitializer, safe_exp, Statement,
|
StructInitializer, ArrayInitializer, safe_exp, Statement, LineComment,
|
||||||
progmem_array, statement, variable, Pvariable, new_Pvariable,
|
progmem_array, statement, variable, Pvariable, new_Pvariable,
|
||||||
add, add_global, add_library, add_build_flag, add_define,
|
add, add_global, add_library, add_build_flag, add_define,
|
||||||
get_variable, get_variable_with_full_id, process_lambda, is_template, templatable, MockObj,
|
get_variable, get_variable_with_full_id, process_lambda, is_template, templatable, MockObj,
|
||||||
MockObjClass)
|
MockObjClass)
|
||||||
from esphome.cpp_helpers import ( # noqa
|
from esphome.cpp_helpers import ( # noqa
|
||||||
gpio_pin_expression, register_component, build_registry_entry,
|
gpio_pin_expression, register_component, build_registry_entry,
|
||||||
build_registry_list, extract_registry_entry_config)
|
build_registry_list, extract_registry_entry_config, register_parented)
|
||||||
from esphome.cpp_types import ( # noqa
|
from esphome.cpp_types import ( # noqa
|
||||||
global_ns, void, nullptr, float_, bool_, std_ns, std_string,
|
global_ns, void, nullptr, float_, double, bool_, std_ns, std_string,
|
||||||
std_vector, uint8, uint16, uint32, int32, const_char_ptr, NAN,
|
std_vector, uint8, uint16, uint32, int32, const_char_ptr, NAN,
|
||||||
esphome_ns, App, Nameable, Component, ComponentPtr,
|
esphome_ns, App, Nameable, Component, ComponentPtr,
|
||||||
PollingComponent, Application, optional, arduino_json_ns, JsonObject,
|
PollingComponent, Application, optional, arduino_json_ns, JsonObject,
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
#include "esphome/components/adc/adc_sensor.h"
|
#include "esphome/components/adc/adc_sensor.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
#ifdef USE_ADC_SENSOR_VCC
|
||||||
|
ADC_MODE(ADC_VCC)
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace adc {
|
namespace adc {
|
||||||
|
|
||||||
|
|
|
@ -2,16 +2,13 @@
|
||||||
|
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
#include "esphome/core/esphal.h"
|
#include "esphome/core/esphal.h"
|
||||||
|
#include "esphome/core/defines.h"
|
||||||
#include "esphome/components/sensor/sensor.h"
|
#include "esphome/components/sensor/sensor.h"
|
||||||
#include "esphome/components/voltage_sampler/voltage_sampler.h"
|
#include "esphome/components/voltage_sampler/voltage_sampler.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace adc {
|
namespace adc {
|
||||||
|
|
||||||
#ifdef USE_ADC_SENSOR_VCC
|
|
||||||
ADC_MODE(ADC_VCC)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage_sampler::VoltageSampler {
|
class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage_sampler::VoltageSampler {
|
||||||
public:
|
public:
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
|
|
|
@ -87,7 +87,7 @@ HOMEASSISTANT_SERVICE_ACTION_SCHEMA = cv.Schema({
|
||||||
cv.string: cv.string,
|
cv.string: cv.string,
|
||||||
}),
|
}),
|
||||||
cv.Optional(CONF_VARIABLES): cv.Schema({
|
cv.Optional(CONF_VARIABLES): cv.Schema({
|
||||||
cv.string: cv.lambda_,
|
cv.string: cv.returning_lambda,
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ from esphome.const import CONF_AWAY_CONFIG, CONF_COOL_ACTION, \
|
||||||
CONF_ID, CONF_IDLE_ACTION, CONF_SENSOR
|
CONF_ID, CONF_IDLE_ACTION, CONF_SENSOR
|
||||||
|
|
||||||
bang_bang_ns = cg.esphome_ns.namespace('bang_bang')
|
bang_bang_ns = cg.esphome_ns.namespace('bang_bang')
|
||||||
BangBangClimate = bang_bang_ns.class_('BangBangClimate', climate.ClimateDevice)
|
BangBangClimate = bang_bang_ns.class_('BangBangClimate', climate.Climate)
|
||||||
BangBangClimateTargetTempConfig = bang_bang_ns.struct('BangBangClimateTargetTempConfig')
|
BangBangClimateTargetTempConfig = bang_bang_ns.struct('BangBangClimateTargetTempConfig')
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.All(climate.CLIMATE_SCHEMA.extend({
|
CONFIG_SCHEMA = cv.All(climate.CLIMATE_SCHEMA.extend({
|
||||||
|
|
|
@ -70,7 +70,7 @@ def delayed_off_filter_to_code(config, filter_id):
|
||||||
yield var
|
yield var
|
||||||
|
|
||||||
|
|
||||||
@FILTER_REGISTRY.register('lambda', LambdaFilter, cv.lambda_)
|
@FILTER_REGISTRY.register('lambda', LambdaFilter, cv.returning_lambda)
|
||||||
def lambda_filter_to_code(config, filter_id):
|
def lambda_filter_to_code(config, filter_id):
|
||||||
lambda_ = yield cg.process_lambda(config, [(bool, 'x')], return_type=cg.optional.template(bool))
|
lambda_ = yield cg.process_lambda(config, [(bool, 'x')], return_type=cg.optional.template(bool))
|
||||||
yield cg.new_Pvariable(filter_id, lambda_)
|
yield cg.new_Pvariable(filter_id, lambda_)
|
||||||
|
|
|
@ -5,7 +5,7 @@ namespace esphome {
|
||||||
|
|
||||||
namespace binary_sensor {
|
namespace binary_sensor {
|
||||||
|
|
||||||
static const char *TAG = "something.Filter";
|
static const char *TAG = "sensor.filter";
|
||||||
|
|
||||||
void Filter::output(bool value, bool is_initial) {
|
void Filter::output(bool value, bool is_initial) {
|
||||||
if (!this->dedup_.next(value))
|
if (!this->dedup_.next(value))
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace ble_presence {
|
namespace ble_presence {
|
||||||
|
|
||||||
static const char *TAG = "something.something";
|
static const char *TAG = "ble_presence";
|
||||||
|
|
||||||
void BLEPresenceDevice::dump_config() { LOG_BINARY_SENSOR("", "BLE Presence", this); }
|
void BLEPresenceDevice::dump_config() { LOG_BINARY_SENSOR("", "BLE Presence", this); }
|
||||||
|
|
||||||
|
|
|
@ -232,7 +232,7 @@ float BME680Component::get_setup_priority() const { return setup_priority::DATA;
|
||||||
void BME680Component::update() {
|
void BME680Component::update() {
|
||||||
uint8_t meas_control = 0; // No need to fetch, we're setting all fields
|
uint8_t meas_control = 0; // No need to fetch, we're setting all fields
|
||||||
meas_control |= (this->temperature_oversampling_ & 0b111) << 5;
|
meas_control |= (this->temperature_oversampling_ & 0b111) << 5;
|
||||||
meas_control |= (this->pressure_oversampling_ & 0b111) << 5;
|
meas_control |= (this->pressure_oversampling_ & 0b111) << 2;
|
||||||
meas_control |= 0b01; // forced mode
|
meas_control |= 0b01; // forced mode
|
||||||
if (!this->write_byte(BME680_REGISTER_CONTROL_MEAS, meas_control)) {
|
if (!this->write_byte(BME680_REGISTER_CONTROL_MEAS, meas_control)) {
|
||||||
this->status_set_warning();
|
this->status_set_warning();
|
||||||
|
|
0
esphome/components/ccs811/__init__.py
Normal file
0
esphome/components/ccs811/__init__.py
Normal file
147
esphome/components/ccs811/ccs811.cpp
Normal file
147
esphome/components/ccs811/ccs811.cpp
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
#include "ccs811.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace ccs811 {
|
||||||
|
|
||||||
|
static const char *TAG = "ccs811";
|
||||||
|
|
||||||
|
// based on
|
||||||
|
// - https://cdn.sparkfun.com/datasheets/BreakoutBoards/CCS811_Programming_Guide.pdf
|
||||||
|
|
||||||
|
#define CHECK_TRUE(f, error_code) \
|
||||||
|
if (!(f)) { \
|
||||||
|
this->mark_failed(); \
|
||||||
|
this->error_code_ = (error_code); \
|
||||||
|
return; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CHECKED_IO(f) CHECK_TRUE(f, COMMUNICAITON_FAILED)
|
||||||
|
|
||||||
|
void CCS811Component::setup() {
|
||||||
|
// page 9 programming guide - hwid is always 0x81
|
||||||
|
uint8_t hw_id;
|
||||||
|
CHECKED_IO(this->read_byte(0x20, &hw_id))
|
||||||
|
CHECK_TRUE(hw_id == 0x81, INVALID_ID)
|
||||||
|
|
||||||
|
// software reset, page 3 - allowed to fail
|
||||||
|
this->write_bytes(0xFF, {0x11, 0xE5, 0x72, 0x8A});
|
||||||
|
delay(5);
|
||||||
|
|
||||||
|
// page 10, APP_START
|
||||||
|
CHECK_TRUE(!this->status_has_error_(), SENSOR_REPORTED_ERROR)
|
||||||
|
CHECK_TRUE(this->status_app_is_valid_(), APP_INVALID)
|
||||||
|
CHECK_TRUE(this->write_bytes(0xF4, {}), APP_START_FAILED)
|
||||||
|
// App setup, wait for it to load
|
||||||
|
delay(1);
|
||||||
|
|
||||||
|
// set MEAS_MODE (page 5)
|
||||||
|
uint8_t meas_mode = 0;
|
||||||
|
uint32_t interval = this->get_update_interval();
|
||||||
|
if (interval <= 1000)
|
||||||
|
meas_mode = 1 << 4;
|
||||||
|
else if (interval <= 10000)
|
||||||
|
meas_mode = 2 << 4;
|
||||||
|
else
|
||||||
|
meas_mode = 3 << 4;
|
||||||
|
|
||||||
|
CHECKED_IO(this->write_byte(0x01, meas_mode))
|
||||||
|
|
||||||
|
if (this->baseline_.has_value()) {
|
||||||
|
// baseline available, write to sensor
|
||||||
|
this->write_bytes(0x11, decode_uint16(*this->baseline_));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void CCS811Component::update() {
|
||||||
|
if (!this->status_has_data_())
|
||||||
|
this->status_set_warning();
|
||||||
|
|
||||||
|
// page 12 - alg result data
|
||||||
|
auto alg_data = this->read_bytes<4>(0x02);
|
||||||
|
if (!alg_data.has_value()) {
|
||||||
|
ESP_LOGW(TAG, "Reading CCS811 data failed!");
|
||||||
|
this->status_set_warning();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto res = *alg_data;
|
||||||
|
uint16_t co2 = encode_uint16(res[0], res[1]);
|
||||||
|
uint16_t tvoc = encode_uint16(res[2], res[3]);
|
||||||
|
|
||||||
|
// also print baseline
|
||||||
|
auto baseline_data = this->read_bytes<2>(0x11);
|
||||||
|
uint16_t baseline = 0;
|
||||||
|
if (baseline_data.has_value()) {
|
||||||
|
baseline = encode_uint16((*baseline_data)[0], (*baseline_data)[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "Got co2=%u ppm, tvoc=%u ppb, baseline=0x%04X", co2, tvoc, baseline);
|
||||||
|
|
||||||
|
if (this->co2_ != nullptr)
|
||||||
|
this->co2_->publish_state(co2);
|
||||||
|
if (this->tvoc_ != nullptr)
|
||||||
|
this->tvoc_->publish_state(tvoc);
|
||||||
|
|
||||||
|
this->status_clear_warning();
|
||||||
|
|
||||||
|
this->send_env_data_();
|
||||||
|
}
|
||||||
|
void CCS811Component::send_env_data_() {
|
||||||
|
if (this->humidity_ == nullptr && this->temperature_ == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
float humidity = NAN;
|
||||||
|
if (this->humidity_ != nullptr)
|
||||||
|
humidity = this->humidity_->state;
|
||||||
|
if (isnan(humidity) || humidity < 0 || humidity > 100)
|
||||||
|
humidity = 50;
|
||||||
|
float temperature = NAN;
|
||||||
|
if (this->temperature_ != nullptr)
|
||||||
|
temperature = this->temperature_->state;
|
||||||
|
if (isnan(temperature) || temperature < -25 || temperature > 50)
|
||||||
|
temperature = 25;
|
||||||
|
// temperature has a 25° offset to allow negative temperatures
|
||||||
|
temperature += 25;
|
||||||
|
|
||||||
|
// only 0.5 fractions are supported (application note)
|
||||||
|
auto hum_value = static_cast<uint8_t>(roundf(humidity * 2));
|
||||||
|
auto temp_value = static_cast<uint8_t>(roundf(temperature * 2));
|
||||||
|
this->write_bytes(0x05, {hum_value, 0x00, temp_value, 0x00});
|
||||||
|
}
|
||||||
|
void CCS811Component::dump_config() {
|
||||||
|
ESP_LOGCONFIG(TAG, "CCS811");
|
||||||
|
LOG_I2C_DEVICE(this)
|
||||||
|
LOG_UPDATE_INTERVAL(this)
|
||||||
|
LOG_SENSOR(" ", "CO2 Sensor", this->co2_)
|
||||||
|
LOG_SENSOR(" ", "TVOC Sensor", this->tvoc_)
|
||||||
|
if (this->baseline_) {
|
||||||
|
ESP_LOGCONFIG(TAG, " Baseline: %04X", *this->baseline_);
|
||||||
|
} else {
|
||||||
|
ESP_LOGCONFIG(TAG, " Baseline: NOT SET");
|
||||||
|
}
|
||||||
|
if (this->is_failed()) {
|
||||||
|
switch (this->error_code_) {
|
||||||
|
case COMMUNICAITON_FAILED:
|
||||||
|
ESP_LOGW(TAG, "Communication failed! Is the sensor connected?");
|
||||||
|
break;
|
||||||
|
case INVALID_ID:
|
||||||
|
ESP_LOGW(TAG, "Sensor reported an invalid ID. Is this a CCS811?");
|
||||||
|
break;
|
||||||
|
case SENSOR_REPORTED_ERROR:
|
||||||
|
ESP_LOGW(TAG, "Sensor reported internal error");
|
||||||
|
break;
|
||||||
|
case APP_INVALID:
|
||||||
|
ESP_LOGW(TAG, "Sensor reported invalid APP installed.");
|
||||||
|
break;
|
||||||
|
case APP_START_FAILED:
|
||||||
|
ESP_LOGW(TAG, "Sensor reported APP start failed.");
|
||||||
|
break;
|
||||||
|
case UNKNOWN:
|
||||||
|
default:
|
||||||
|
ESP_LOGW(TAG, "Unknown setup error!");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ccs811
|
||||||
|
} // namespace esphome
|
54
esphome/components/ccs811/ccs811.h
Normal file
54
esphome/components/ccs811/ccs811.h
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/core/preferences.h"
|
||||||
|
#include "esphome/components/sensor/sensor.h"
|
||||||
|
#include "esphome/components/i2c/i2c.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace ccs811 {
|
||||||
|
|
||||||
|
class CCS811Component : public PollingComponent, public i2c::I2CDevice {
|
||||||
|
public:
|
||||||
|
void set_co2(sensor::Sensor *co2) { co2_ = co2; }
|
||||||
|
void set_tvoc(sensor::Sensor *tvoc) { tvoc_ = tvoc; }
|
||||||
|
void set_baseline(uint16_t baseline) { baseline_ = baseline; }
|
||||||
|
void set_humidity(sensor::Sensor *humidity) { humidity_ = humidity; }
|
||||||
|
void set_temperature(sensor::Sensor *temperature) { temperature_ = temperature; }
|
||||||
|
|
||||||
|
/// Setup the sensor and test for a connection.
|
||||||
|
void setup() override;
|
||||||
|
/// Schedule temperature+pressure readings.
|
||||||
|
void update() override;
|
||||||
|
|
||||||
|
void dump_config() override;
|
||||||
|
|
||||||
|
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
optional<uint8_t> read_status_() { return this->read_byte(0x00); }
|
||||||
|
bool status_has_error_() { return this->read_status_().value_or(1) & 1; }
|
||||||
|
bool status_app_is_valid_() { return this->read_status_().value_or(0) & (1 << 4); }
|
||||||
|
bool status_has_data_() { return this->read_status_().value_or(0) & (1 << 3); }
|
||||||
|
void send_env_data_();
|
||||||
|
|
||||||
|
enum ErrorCode {
|
||||||
|
UNKNOWN,
|
||||||
|
COMMUNICAITON_FAILED,
|
||||||
|
INVALID_ID,
|
||||||
|
SENSOR_REPORTED_ERROR,
|
||||||
|
APP_INVALID,
|
||||||
|
APP_START_FAILED,
|
||||||
|
} error_code_{UNKNOWN};
|
||||||
|
|
||||||
|
sensor::Sensor *co2_{nullptr};
|
||||||
|
sensor::Sensor *tvoc_{nullptr};
|
||||||
|
optional<uint16_t> baseline_{};
|
||||||
|
/// Input sensor for humidity reading.
|
||||||
|
sensor::Sensor *humidity_{nullptr};
|
||||||
|
/// Input sensor for temperature reading.
|
||||||
|
sensor::Sensor *temperature_{nullptr};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ccs811
|
||||||
|
} // namespace esphome
|
46
esphome/components/ccs811/sensor.py
Normal file
46
esphome/components/ccs811/sensor.py
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.components import i2c, sensor
|
||||||
|
from esphome.const import CONF_ID, ICON_RADIATOR, UNIT_PARTS_PER_MILLION, \
|
||||||
|
UNIT_PARTS_PER_BILLION, CONF_TEMPERATURE, CONF_HUMIDITY, ICON_PERIODIC_TABLE_CO2
|
||||||
|
|
||||||
|
DEPENDENCIES = ['i2c']
|
||||||
|
|
||||||
|
ccs811_ns = cg.esphome_ns.namespace('ccs811')
|
||||||
|
CCS811Component = ccs811_ns.class_('CCS811Component', cg.PollingComponent, i2c.I2CDevice)
|
||||||
|
|
||||||
|
CONF_ECO2 = 'eco2'
|
||||||
|
CONF_TVOC = 'tvoc'
|
||||||
|
CONF_BASELINE = 'baseline'
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = cv.Schema({
|
||||||
|
cv.GenerateID(): cv.declare_id(CCS811Component),
|
||||||
|
cv.Required(CONF_ECO2): sensor.sensor_schema(UNIT_PARTS_PER_MILLION, ICON_PERIODIC_TABLE_CO2,
|
||||||
|
0),
|
||||||
|
cv.Required(CONF_TVOC): sensor.sensor_schema(UNIT_PARTS_PER_BILLION, ICON_RADIATOR, 0),
|
||||||
|
|
||||||
|
cv.Optional(CONF_BASELINE): cv.hex_uint16_t,
|
||||||
|
cv.Optional(CONF_TEMPERATURE): cv.use_id(sensor.Sensor),
|
||||||
|
cv.Optional(CONF_HUMIDITY): cv.use_id(sensor.Sensor),
|
||||||
|
}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x5A))
|
||||||
|
|
||||||
|
|
||||||
|
def to_code(config):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
yield cg.register_component(var, config)
|
||||||
|
yield i2c.register_i2c_device(var, config)
|
||||||
|
|
||||||
|
sens = yield sensor.new_sensor(config[CONF_ECO2])
|
||||||
|
cg.add(var.set_co2(sens))
|
||||||
|
sens = yield sensor.new_sensor(config[CONF_TVOC])
|
||||||
|
cg.add(var.set_tvoc(sens))
|
||||||
|
|
||||||
|
if CONF_BASELINE in config:
|
||||||
|
cg.add(var.set_baseline(config[CONF_BASELINE]))
|
||||||
|
|
||||||
|
if CONF_TEMPERATURE in config:
|
||||||
|
sens = yield cg.get_variable(config[CONF_TEMPERATURE])
|
||||||
|
cg.add(var.set_temperature(sens))
|
||||||
|
if CONF_HUMIDITY in config:
|
||||||
|
sens = yield cg.get_variable(config[CONF_HUMIDITY])
|
||||||
|
cg.add(var.set_humidity(sens))
|
|
@ -12,7 +12,7 @@ IS_PLATFORM_COMPONENT = True
|
||||||
|
|
||||||
climate_ns = cg.esphome_ns.namespace('climate')
|
climate_ns = cg.esphome_ns.namespace('climate')
|
||||||
|
|
||||||
ClimateDevice = climate_ns.class_('Climate', cg.Nameable)
|
Climate = climate_ns.class_('Climate', cg.Nameable)
|
||||||
ClimateCall = climate_ns.class_('ClimateCall')
|
ClimateCall = climate_ns.class_('ClimateCall')
|
||||||
ClimateTraits = climate_ns.class_('ClimateTraits')
|
ClimateTraits = climate_ns.class_('ClimateTraits')
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ validate_climate_mode = cv.enum(CLIMATE_MODES, upper=True)
|
||||||
ControlAction = climate_ns.class_('ControlAction', automation.Action)
|
ControlAction = climate_ns.class_('ControlAction', automation.Action)
|
||||||
|
|
||||||
CLIMATE_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend({
|
CLIMATE_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend({
|
||||||
cv.GenerateID(): cv.declare_id(ClimateDevice),
|
cv.GenerateID(): cv.declare_id(Climate),
|
||||||
cv.OnlyWith(CONF_MQTT_ID, 'mqtt'): cv.declare_id(mqtt.MQTTClimateComponent),
|
cv.OnlyWith(CONF_MQTT_ID, 'mqtt'): cv.declare_id(mqtt.MQTTClimateComponent),
|
||||||
cv.Optional(CONF_VISUAL, default={}): cv.Schema({
|
cv.Optional(CONF_VISUAL, default={}): cv.Schema({
|
||||||
cv.Optional(CONF_MIN_TEMPERATURE): cv.temperature,
|
cv.Optional(CONF_MIN_TEMPERATURE): cv.temperature,
|
||||||
|
@ -68,7 +68,7 @@ def register_climate(var, config):
|
||||||
|
|
||||||
|
|
||||||
CLIMATE_CONTROL_ACTION_SCHEMA = cv.Schema({
|
CLIMATE_CONTROL_ACTION_SCHEMA = cv.Schema({
|
||||||
cv.Required(CONF_ID): cv.use_id(ClimateDevice),
|
cv.Required(CONF_ID): cv.use_id(Climate),
|
||||||
cv.Optional(CONF_MODE): cv.templatable(validate_climate_mode),
|
cv.Optional(CONF_MODE): cv.templatable(validate_climate_mode),
|
||||||
cv.Optional(CONF_TARGET_TEMPERATURE): cv.templatable(cv.temperature),
|
cv.Optional(CONF_TARGET_TEMPERATURE): cv.templatable(cv.temperature),
|
||||||
cv.Optional(CONF_TARGET_TEMPERATURE_LOW): cv.templatable(cv.temperature),
|
cv.Optional(CONF_TARGET_TEMPERATURE_LOW): cv.templatable(cv.temperature),
|
||||||
|
|
|
@ -207,11 +207,6 @@ ClimateTraits Climate::get_traits() {
|
||||||
return traits;
|
return traits;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_MQTT_CLIMATE
|
|
||||||
MQTTClimateComponent *Climate::get_mqtt() const { return this->mqtt_; }
|
|
||||||
void Climate::set_mqtt(MQTTClimateComponent *mqtt) { this->mqtt_ = mqtt; }
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void Climate::set_visual_min_temperature_override(float visual_min_temperature_override) {
|
void Climate::set_visual_min_temperature_override(float visual_min_temperature_override) {
|
||||||
this->visual_min_temperature_override_ = visual_min_temperature_override;
|
this->visual_min_temperature_override_ = visual_min_temperature_override;
|
||||||
}
|
}
|
||||||
|
|
|
@ -169,11 +169,6 @@ class Climate : public Nameable {
|
||||||
*/
|
*/
|
||||||
ClimateTraits get_traits();
|
ClimateTraits get_traits();
|
||||||
|
|
||||||
#ifdef USE_MQTT_COVER
|
|
||||||
MQTTClimateComponent *get_mqtt() const;
|
|
||||||
void set_mqtt(MQTTClimateComponent *mqtt);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void set_visual_min_temperature_override(float visual_min_temperature_override);
|
void set_visual_min_temperature_override(float visual_min_temperature_override);
|
||||||
void set_visual_max_temperature_override(float visual_max_temperature_override);
|
void set_visual_max_temperature_override(float visual_max_temperature_override);
|
||||||
void set_visual_temperature_step_override(float visual_temperature_step_override);
|
void set_visual_temperature_step_override(float visual_temperature_step_override);
|
||||||
|
|
|
@ -6,13 +6,13 @@ namespace climate {
|
||||||
const char *climate_mode_to_string(ClimateMode mode) {
|
const char *climate_mode_to_string(ClimateMode mode) {
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case CLIMATE_MODE_OFF:
|
case CLIMATE_MODE_OFF:
|
||||||
return "OFF";
|
return "off";
|
||||||
case CLIMATE_MODE_AUTO:
|
case CLIMATE_MODE_AUTO:
|
||||||
return "AUTO";
|
return "auto";
|
||||||
case CLIMATE_MODE_COOL:
|
case CLIMATE_MODE_COOL:
|
||||||
return "COOL";
|
return "cool";
|
||||||
case CLIMATE_MODE_HEAT:
|
case CLIMATE_MODE_HEAT:
|
||||||
return "HEAT";
|
return "heat";
|
||||||
default:
|
default:
|
||||||
return "UNKNOWN";
|
return "UNKNOWN";
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ CustomBinarySensorConstructor = custom_ns.class_('CustomBinarySensorConstructor'
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.Schema({
|
CONFIG_SCHEMA = cv.Schema({
|
||||||
cv.GenerateID(): cv.declare_id(CustomBinarySensorConstructor),
|
cv.GenerateID(): cv.declare_id(CustomBinarySensorConstructor),
|
||||||
cv.Required(CONF_LAMBDA): cv.lambda_,
|
cv.Required(CONF_LAMBDA): cv.returning_lambda,
|
||||||
cv.Required(CONF_BINARY_SENSORS): cv.ensure_list(binary_sensor.BINARY_SENSOR_SCHEMA),
|
cv.Required(CONF_BINARY_SENSORS): cv.ensure_list(binary_sensor.BINARY_SENSOR_SCHEMA),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
26
esphome/components/custom/climate/__init__.py
Normal file
26
esphome/components/custom/climate/__init__.py
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.components import climate
|
||||||
|
from esphome.const import CONF_ID, CONF_LAMBDA
|
||||||
|
from .. import custom_ns
|
||||||
|
|
||||||
|
CustomClimateConstructor = custom_ns.class_('CustomClimateConstructor')
|
||||||
|
CONF_CLIMATES = 'climates'
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = cv.Schema({
|
||||||
|
cv.GenerateID(): cv.declare_id(CustomClimateConstructor),
|
||||||
|
cv.Required(CONF_LAMBDA): cv.returning_lambda,
|
||||||
|
cv.Required(CONF_CLIMATES): cv.ensure_list(climate.CLIMATE_SCHEMA),
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
def to_code(config):
|
||||||
|
template_ = yield cg.process_lambda(
|
||||||
|
config[CONF_LAMBDA], [],
|
||||||
|
return_type=cg.std_vector.template(climate.Climate.operator('ptr')))
|
||||||
|
|
||||||
|
rhs = CustomClimateConstructor(template_)
|
||||||
|
custom = cg.variable(config[CONF_ID], rhs)
|
||||||
|
for i, conf in enumerate(config[CONF_CLIMATES]):
|
||||||
|
rhs = custom.Pget_climate(i)
|
||||||
|
yield climate.register_climate(rhs, conf)
|
20
esphome/components/custom/climate/custom_climate.h
Normal file
20
esphome/components/custom/climate/custom_climate.h
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/components/climate/climate.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace custom {
|
||||||
|
|
||||||
|
class CustomClimateConstructor {
|
||||||
|
public:
|
||||||
|
CustomClimateConstructor(const std::function<std::vector<climate::Climate *>()> &init) { this->climates_ = init(); }
|
||||||
|
|
||||||
|
climate::Climate *get_climate(int i) { return this->climates_[i]; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::vector<climate::Climate *> climates_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace custom
|
||||||
|
} // namespace esphome
|
26
esphome/components/custom/cover/__init__.py
Normal file
26
esphome/components/custom/cover/__init__.py
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.components import cover
|
||||||
|
from esphome.const import CONF_ID, CONF_LAMBDA
|
||||||
|
from .. import custom_ns
|
||||||
|
|
||||||
|
CustomCoverConstructor = custom_ns.class_('CustomCoverConstructor')
|
||||||
|
CONF_COVERS = 'covers'
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = cv.Schema({
|
||||||
|
cv.GenerateID(): cv.declare_id(CustomCoverConstructor),
|
||||||
|
cv.Required(CONF_LAMBDA): cv.returning_lambda,
|
||||||
|
cv.Required(CONF_COVERS): cv.ensure_list(cover.COVER_SCHEMA),
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
def to_code(config):
|
||||||
|
template_ = yield cg.process_lambda(
|
||||||
|
config[CONF_LAMBDA], [],
|
||||||
|
return_type=cg.std_vector.template(cover.Cover.operator('ptr')))
|
||||||
|
|
||||||
|
rhs = CustomCoverConstructor(template_)
|
||||||
|
custom = cg.variable(config[CONF_ID], rhs)
|
||||||
|
for i, conf in enumerate(config[CONF_COVERS]):
|
||||||
|
rhs = custom.Pget_cover(i)
|
||||||
|
yield cover.register_cover(rhs, conf)
|
19
esphome/components/custom/cover/custom_cover.h
Normal file
19
esphome/components/custom/cover/custom_cover.h
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/components/cover/cover.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace custom {
|
||||||
|
|
||||||
|
class CustomCoverConstructor {
|
||||||
|
public:
|
||||||
|
CustomCoverConstructor(const std::function<std::vector<cover::Cover *>()> &init) { this->covers_ = init(); }
|
||||||
|
|
||||||
|
cover::Cover *get_cover(int i) { return this->covers_[i]; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::vector<cover::Cover *> covers_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace custom
|
||||||
|
} // namespace esphome
|
26
esphome/components/custom/light/__init__.py
Normal file
26
esphome/components/custom/light/__init__.py
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.components import light
|
||||||
|
from esphome.const import CONF_ID, CONF_LAMBDA
|
||||||
|
from .. import custom_ns
|
||||||
|
|
||||||
|
CustomLightOutputConstructor = custom_ns.class_('CustomLightOutputConstructor')
|
||||||
|
CONF_LIGHTS = 'lights'
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = cv.Schema({
|
||||||
|
cv.GenerateID(): cv.declare_id(CustomLightOutputConstructor),
|
||||||
|
cv.Required(CONF_LAMBDA): cv.returning_lambda,
|
||||||
|
cv.Required(CONF_LIGHTS): cv.ensure_list(light.RGB_LIGHT_SCHEMA),
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
def to_code(config):
|
||||||
|
template_ = yield cg.process_lambda(
|
||||||
|
config[CONF_LAMBDA], [],
|
||||||
|
return_type=cg.std_vector.template(light.LightOutput.operator('ptr')))
|
||||||
|
|
||||||
|
rhs = CustomLightOutputConstructor(template_)
|
||||||
|
custom = cg.variable(config[CONF_ID], rhs)
|
||||||
|
for i, conf in enumerate(config[CONF_LIGHTS]):
|
||||||
|
rhs = custom.Pget_light(i)
|
||||||
|
yield light.register_light(rhs, conf)
|
22
esphome/components/custom/light/custom_light_output.h
Normal file
22
esphome/components/custom/light/custom_light_output.h
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/components/light/light_output.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace custom {
|
||||||
|
|
||||||
|
class CustomLightOutputConstructor {
|
||||||
|
public:
|
||||||
|
CustomLightOutputConstructor(const std::function<std::vector<light::LightOutput *>()> &init) {
|
||||||
|
this->outputs_ = init();
|
||||||
|
}
|
||||||
|
|
||||||
|
light::LightOutput *get_light(int i) { return this->outputs_[i]; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::vector<light::LightOutput *> outputs_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace custom
|
||||||
|
} // namespace esphome
|
|
@ -7,42 +7,27 @@ from .. import custom_ns
|
||||||
CustomBinaryOutputConstructor = custom_ns.class_('CustomBinaryOutputConstructor')
|
CustomBinaryOutputConstructor = custom_ns.class_('CustomBinaryOutputConstructor')
|
||||||
CustomFloatOutputConstructor = custom_ns.class_('CustomFloatOutputConstructor')
|
CustomFloatOutputConstructor = custom_ns.class_('CustomFloatOutputConstructor')
|
||||||
|
|
||||||
BINARY_SCHEMA = cv.Schema({
|
CONF_BINARY = 'binary'
|
||||||
|
CONF_FLOAT = 'float'
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = cv.typed_schema({
|
||||||
|
CONF_BINARY: cv.Schema({
|
||||||
cv.GenerateID(): cv.declare_id(CustomBinaryOutputConstructor),
|
cv.GenerateID(): cv.declare_id(CustomBinaryOutputConstructor),
|
||||||
cv.Required(CONF_LAMBDA): cv.lambda_,
|
cv.Required(CONF_LAMBDA): cv.returning_lambda,
|
||||||
cv.Required(CONF_TYPE): 'binary',
|
|
||||||
cv.Required(CONF_OUTPUTS):
|
cv.Required(CONF_OUTPUTS):
|
||||||
cv.ensure_list(output.BINARY_OUTPUT_SCHEMA.extend({
|
cv.ensure_list(output.BINARY_OUTPUT_SCHEMA.extend({
|
||||||
cv.GenerateID(): cv.declare_id(output.BinaryOutput),
|
cv.GenerateID(): cv.declare_id(output.BinaryOutput),
|
||||||
})),
|
})),
|
||||||
})
|
}),
|
||||||
|
CONF_FLOAT: cv.Schema({
|
||||||
FLOAT_SCHEMA = cv.Schema({
|
|
||||||
cv.GenerateID(): cv.declare_id(CustomFloatOutputConstructor),
|
cv.GenerateID(): cv.declare_id(CustomFloatOutputConstructor),
|
||||||
cv.Required(CONF_LAMBDA): cv.lambda_,
|
cv.Required(CONF_LAMBDA): cv.returning_lambda,
|
||||||
cv.Required(CONF_TYPE): 'float',
|
|
||||||
cv.Required(CONF_OUTPUTS):
|
cv.Required(CONF_OUTPUTS):
|
||||||
cv.ensure_list(output.FLOAT_OUTPUT_SCHEMA.extend({
|
cv.ensure_list(output.FLOAT_OUTPUT_SCHEMA.extend({
|
||||||
cv.GenerateID(): cv.declare_id(output.FloatOutput),
|
cv.GenerateID(): cv.declare_id(output.FloatOutput),
|
||||||
})),
|
})),
|
||||||
})
|
})
|
||||||
|
}, lower=True)
|
||||||
|
|
||||||
def validate_custom_output(value):
|
|
||||||
if not isinstance(value, dict):
|
|
||||||
raise cv.Invalid("Value must be dict")
|
|
||||||
if CONF_TYPE not in value:
|
|
||||||
raise cv.Invalid("type not specified!")
|
|
||||||
type = cv.string_strict(value[CONF_TYPE]).lower()
|
|
||||||
value[CONF_TYPE] = type
|
|
||||||
if type == 'binary':
|
|
||||||
return BINARY_SCHEMA(value)
|
|
||||||
if type == 'float':
|
|
||||||
return FLOAT_SCHEMA(value)
|
|
||||||
raise cv.Invalid("type must either be binary or float, not {}!".format(type))
|
|
||||||
|
|
||||||
|
|
||||||
CONFIG_SCHEMA = validate_custom_output
|
|
||||||
|
|
||||||
|
|
||||||
def to_code(config):
|
def to_code(config):
|
||||||
|
|
|
@ -8,7 +8,7 @@ CustomSensorConstructor = custom_ns.class_('CustomSensorConstructor')
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.Schema({
|
CONFIG_SCHEMA = cv.Schema({
|
||||||
cv.GenerateID(): cv.declare_id(CustomSensorConstructor),
|
cv.GenerateID(): cv.declare_id(CustomSensorConstructor),
|
||||||
cv.Required(CONF_LAMBDA): cv.lambda_,
|
cv.Required(CONF_LAMBDA): cv.returning_lambda,
|
||||||
cv.Required(CONF_SENSORS): cv.ensure_list(sensor.SENSOR_SCHEMA),
|
cv.Required(CONF_SENSORS): cv.ensure_list(sensor.SENSOR_SCHEMA),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ CustomSwitchConstructor = custom_ns.class_('CustomSwitchConstructor')
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.Schema({
|
CONFIG_SCHEMA = cv.Schema({
|
||||||
cv.GenerateID(): cv.declare_id(CustomSwitchConstructor),
|
cv.GenerateID(): cv.declare_id(CustomSwitchConstructor),
|
||||||
cv.Required(CONF_LAMBDA): cv.lambda_,
|
cv.Required(CONF_LAMBDA): cv.returning_lambda,
|
||||||
cv.Required(CONF_SWITCHES):
|
cv.Required(CONF_SWITCHES):
|
||||||
cv.ensure_list(switch.SWITCH_SCHEMA.extend({
|
cv.ensure_list(switch.SWITCH_SCHEMA.extend({
|
||||||
cv.GenerateID(): cv.declare_id(switch.Switch),
|
cv.GenerateID(): cv.declare_id(switch.Switch),
|
||||||
|
|
|
@ -8,7 +8,7 @@ CustomTextSensorConstructor = custom_ns.class_('CustomTextSensorConstructor')
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.Schema({
|
CONFIG_SCHEMA = cv.Schema({
|
||||||
cv.GenerateID(): cv.declare_id(CustomTextSensorConstructor),
|
cv.GenerateID(): cv.declare_id(CustomTextSensorConstructor),
|
||||||
cv.Required(CONF_LAMBDA): cv.lambda_,
|
cv.Required(CONF_LAMBDA): cv.returning_lambda,
|
||||||
cv.Required(CONF_TEXT_SENSORS):
|
cv.Required(CONF_TEXT_SENSORS):
|
||||||
cv.ensure_list(text_sensor.TEXT_SENSOR_SCHEMA.extend({
|
cv.ensure_list(text_sensor.TEXT_SENSOR_SCHEMA.extend({
|
||||||
cv.GenerateID(): cv.declare_id(text_sensor.TextSensor),
|
cv.GenerateID(): cv.declare_id(text_sensor.TextSensor),
|
||||||
|
|
|
@ -8,7 +8,7 @@ CustomComponentConstructor = custom_component_ns.class_('CustomComponentConstruc
|
||||||
MULTI_CONF = True
|
MULTI_CONF = True
|
||||||
CONFIG_SCHEMA = cv.Schema({
|
CONFIG_SCHEMA = cv.Schema({
|
||||||
cv.GenerateID(): cv.declare_id(CustomComponentConstructor),
|
cv.GenerateID(): cv.declare_id(CustomComponentConstructor),
|
||||||
cv.Required(CONF_LAMBDA): cv.lambda_,
|
cv.Required(CONF_LAMBDA): cv.returning_lambda,
|
||||||
cv.Optional(CONF_COMPONENTS): cv.ensure_list(cv.Schema({
|
cv.Optional(CONF_COMPONENTS): cv.ensure_list(cv.Schema({
|
||||||
cv.GenerateID(): cv.declare_id(cg.Component)
|
cv.GenerateID(): cv.declare_id(cg.Component)
|
||||||
}).extend(cv.COMPONENT_SCHEMA)),
|
}).extend(cv.COMPONENT_SCHEMA)),
|
||||||
|
|
|
@ -13,7 +13,7 @@ CONFIG_SCHEMA = cv.All(sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1).e
|
||||||
|
|
||||||
cv.Optional(CONF_ADDRESS): cv.hex_int,
|
cv.Optional(CONF_ADDRESS): cv.hex_int,
|
||||||
cv.Optional(CONF_INDEX): cv.positive_int,
|
cv.Optional(CONF_INDEX): cv.positive_int,
|
||||||
cv.Optional(CONF_RESOLUTION, default=12): cv.All(cv.int_, cv.Range(min=9, max=12)),
|
cv.Optional(CONF_RESOLUTION, default=12): cv.int_range(min=9, max=12),
|
||||||
}), cv.has_exactly_one_key(CONF_ADDRESS, CONF_INDEX))
|
}), cv.has_exactly_one_key(CONF_ADDRESS, CONF_INDEX))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,7 @@ CONF_BRIGHTNESS = 'brightness'
|
||||||
CONF_SATURATION = 'saturation'
|
CONF_SATURATION = 'saturation'
|
||||||
CONF_TEST_PATTERN = 'test_pattern'
|
CONF_TEST_PATTERN = 'test_pattern'
|
||||||
|
|
||||||
camera_range_param = cv.All(cv.int_, cv.Range(min=-2, max=2))
|
camera_range_param = cv.int_range(min=-2, max=2)
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.Schema({
|
CONFIG_SCHEMA = cv.Schema({
|
||||||
cv.GenerateID(): cv.declare_id(ESP32Camera),
|
cv.GenerateID(): cv.declare_id(ESP32Camera),
|
||||||
|
@ -81,7 +81,7 @@ CONFIG_SCHEMA = cv.Schema({
|
||||||
cv.Optional(CONF_IDLE_FRAMERATE, default='0.1 fps'): cv.All(cv.framerate,
|
cv.Optional(CONF_IDLE_FRAMERATE, default='0.1 fps'): cv.All(cv.framerate,
|
||||||
cv.Range(min=0, max=1)),
|
cv.Range(min=0, max=1)),
|
||||||
cv.Optional(CONF_RESOLUTION, default='640X480'): cv.enum(FRAME_SIZES, upper=True),
|
cv.Optional(CONF_RESOLUTION, default='640X480'): cv.enum(FRAME_SIZES, upper=True),
|
||||||
cv.Optional(CONF_JPEG_QUALITY, default=10): cv.All(cv.int_, cv.Range(min=10, max=63)),
|
cv.Optional(CONF_JPEG_QUALITY, default=10): cv.int_range(min=10, max=63),
|
||||||
cv.Optional(CONF_CONTRAST, default=0): camera_range_param,
|
cv.Optional(CONF_CONTRAST, default=0): camera_range_param,
|
||||||
cv.Optional(CONF_BRIGHTNESS, default=0): camera_range_param,
|
cv.Optional(CONF_BRIGHTNESS, default=0): camera_range_param,
|
||||||
cv.Optional(CONF_SATURATION, default=0): camera_range_param,
|
cv.Optional(CONF_SATURATION, default=0): camera_range_param,
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace esp8266_pwm {
|
namespace esp8266_pwm {
|
||||||
|
|
||||||
static const char *TAG = "something.something";
|
static const char *TAG = "esp8266_pwm";
|
||||||
|
|
||||||
void ESP8266PWM::setup() {
|
void ESP8266PWM::setup() {
|
||||||
ESP_LOGCONFIG(TAG, "Setting up ESP8266 PWM Output...");
|
ESP_LOGCONFIG(TAG, "Setting up ESP8266 PWM Output...");
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.components import light, power_supply
|
from esphome.components import light
|
||||||
from esphome.const import CONF_OUTPUT_ID, CONF_NUM_LEDS, CONF_RGB_ORDER, CONF_MAX_REFRESH_RATE, \
|
from esphome.const import CONF_OUTPUT_ID, CONF_NUM_LEDS, CONF_RGB_ORDER, CONF_MAX_REFRESH_RATE
|
||||||
CONF_POWER_SUPPLY
|
|
||||||
from esphome.core import coroutine
|
from esphome.core import coroutine
|
||||||
|
|
||||||
fastled_base_ns = cg.esphome_ns.namespace('fastled_base')
|
fastled_base_ns = cg.esphome_ns.namespace('fastled_base')
|
||||||
|
@ -24,8 +23,6 @@ BASE_SCHEMA = light.ADDRESSABLE_LIGHT_SCHEMA.extend({
|
||||||
cv.Required(CONF_NUM_LEDS): cv.positive_not_null_int,
|
cv.Required(CONF_NUM_LEDS): cv.positive_not_null_int,
|
||||||
cv.Optional(CONF_RGB_ORDER): cv.one_of(*RGB_ORDERS, upper=True),
|
cv.Optional(CONF_RGB_ORDER): cv.one_of(*RGB_ORDERS, upper=True),
|
||||||
cv.Optional(CONF_MAX_REFRESH_RATE): cv.positive_time_period_microseconds,
|
cv.Optional(CONF_MAX_REFRESH_RATE): cv.positive_time_period_microseconds,
|
||||||
|
|
||||||
cv.Optional(CONF_POWER_SUPPLY): cv.use_id(power_supply.PowerSupply),
|
|
||||||
}).extend(cv.COMPONENT_SCHEMA)
|
}).extend(cv.COMPONENT_SCHEMA)
|
||||||
|
|
||||||
|
|
||||||
|
@ -37,10 +34,6 @@ def new_fastled_light(config):
|
||||||
if CONF_MAX_REFRESH_RATE in config:
|
if CONF_MAX_REFRESH_RATE in config:
|
||||||
cg.add(var.set_max_refresh_rate(config[CONF_MAX_REFRESH_RATE]))
|
cg.add(var.set_max_refresh_rate(config[CONF_MAX_REFRESH_RATE]))
|
||||||
|
|
||||||
if CONF_POWER_SUPPLY in config:
|
|
||||||
var_ = yield cg.get_variable(config[CONF_POWER_SUPPLY])
|
|
||||||
cg.add(var.set_power_supply(var_))
|
|
||||||
|
|
||||||
yield light.register_light(var, config)
|
yield light.register_light(var, config)
|
||||||
cg.add_library('FastLED', '3.2.0')
|
cg.add_library('FastLED', '3.2.0')
|
||||||
yield var
|
yield var
|
||||||
|
|
|
@ -33,27 +33,6 @@ void FastLEDLightOutput::loop() {
|
||||||
this->mark_shown_();
|
this->mark_shown_();
|
||||||
|
|
||||||
ESP_LOGVV(TAG, "Writing RGB values to bus...");
|
ESP_LOGVV(TAG, "Writing RGB values to bus...");
|
||||||
|
|
||||||
#ifdef USE_POWER_SUPPLY
|
|
||||||
if (this->power_supply_ != nullptr) {
|
|
||||||
bool is_on = false;
|
|
||||||
for (int i = 0; i < this->num_leds_; i++) {
|
|
||||||
if (bool(this->leds_[i])) {
|
|
||||||
is_on = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_on && !this->has_requested_high_power_) {
|
|
||||||
this->power_supply_->request_high_power();
|
|
||||||
this->has_requested_high_power_ = true;
|
|
||||||
}
|
|
||||||
if (!is_on && this->has_requested_high_power_) {
|
|
||||||
this->power_supply_->unrequest_high_power();
|
|
||||||
this->has_requested_high_power_ = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
this->controller_->showLeds();
|
this->controller_->showLeds();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,10 +4,6 @@
|
||||||
#include "esphome/core/helpers.h"
|
#include "esphome/core/helpers.h"
|
||||||
#include "esphome/components/light/addressable_light.h"
|
#include "esphome/components/light/addressable_light.h"
|
||||||
|
|
||||||
#ifdef USE_POWER_SUPPLY
|
|
||||||
#include "esphome/components/power_supply/power_supply.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define FASTLED_ESP8266_RAW_PIN_ORDER
|
#define FASTLED_ESP8266_RAW_PIN_ORDER
|
||||||
#define FASTLED_ESP32_RAW_PIN_ORDER
|
#define FASTLED_ESP32_RAW_PIN_ORDER
|
||||||
#define FASTLED_RMT_BUILTIN_DRIVER true
|
#define FASTLED_RMT_BUILTIN_DRIVER true
|
||||||
|
@ -30,10 +26,6 @@ class FastLEDLightOutput : public Component, public light::AddressableLight {
|
||||||
/// Set a maximum refresh rate in µs as some lights do not like being updated too often.
|
/// Set a maximum refresh rate in µs as some lights do not like being updated too often.
|
||||||
void set_max_refresh_rate(uint32_t interval_us) { this->max_refresh_rate_ = interval_us; }
|
void set_max_refresh_rate(uint32_t interval_us) { this->max_refresh_rate_ = interval_us; }
|
||||||
|
|
||||||
#ifdef USE_POWER_SUPPLY
|
|
||||||
void set_power_supply(power_supply::PowerSupply *power_supply) { this->power_supply_ = power_supply; }
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// Add some LEDS, can only be called once.
|
/// Add some LEDS, can only be called once.
|
||||||
CLEDController &add_leds(CLEDController *controller, int num_leds) {
|
CLEDController &add_leds(CLEDController *controller, int num_leds) {
|
||||||
this->controller_ = controller;
|
this->controller_ = controller;
|
||||||
|
@ -242,10 +234,6 @@ class FastLEDLightOutput : public Component, public light::AddressableLight {
|
||||||
int num_leds_{0};
|
int num_leds_{0};
|
||||||
uint32_t last_refresh_{0};
|
uint32_t last_refresh_{0};
|
||||||
optional<uint32_t> max_refresh_rate_{};
|
optional<uint32_t> max_refresh_rate_{};
|
||||||
#ifdef USE_POWER_SUPPLY
|
|
||||||
power_supply::PowerSupply *power_supply_{nullptr};
|
|
||||||
bool has_requested_high_power_{false};
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace fastled_base
|
} // namespace fastled_base
|
||||||
|
|
|
@ -70,7 +70,7 @@ FONT_SCHEMA = cv.Schema({
|
||||||
cv.Required(CONF_ID): cv.declare_id(Font),
|
cv.Required(CONF_ID): cv.declare_id(Font),
|
||||||
cv.Required(CONF_FILE): validate_truetype_file,
|
cv.Required(CONF_FILE): validate_truetype_file,
|
||||||
cv.Optional(CONF_GLYPHS, default=DEFAULT_GLYPHS): validate_glyphs,
|
cv.Optional(CONF_GLYPHS, default=DEFAULT_GLYPHS): validate_glyphs,
|
||||||
cv.Optional(CONF_SIZE, default=20): cv.All(cv.int_, cv.Range(min=1)),
|
cv.Optional(CONF_SIZE, default=20): cv.int_range(min=1),
|
||||||
cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_id(cg.uint8),
|
cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_id(cg.uint8),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
23
esphome/components/gps/__init__.py
Normal file
23
esphome/components/gps/__init__.py
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.components import uart
|
||||||
|
from esphome.const import CONF_ID
|
||||||
|
|
||||||
|
DEPENDENCIES = ['uart']
|
||||||
|
|
||||||
|
gps_ns = cg.esphome_ns.namespace('gps')
|
||||||
|
GPS = gps_ns.class_('GPS', cg.Component, uart.UARTDevice)
|
||||||
|
GPSListener = gps_ns.class_('GPSListener')
|
||||||
|
|
||||||
|
CONF_GPS_ID = 'gps_id'
|
||||||
|
MULTI_CONF = True
|
||||||
|
CONFIG_SCHEMA = cv.Schema({
|
||||||
|
cv.GenerateID(): cv.declare_id(GPS),
|
||||||
|
}).extend(cv.COMPONENT_SCHEMA).extend(uart.UART_DEVICE_SCHEMA)
|
||||||
|
|
||||||
|
|
||||||
|
def to_code(config):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
yield cg.register_component(var, config)
|
||||||
|
yield uart.register_uart_device(var, config)
|
||||||
|
cg.add_library('TinyGPSPlus', '1.0.2')
|
12
esphome/components/gps/gps.cpp
Normal file
12
esphome/components/gps/gps.cpp
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
#include "gps.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace gps {
|
||||||
|
|
||||||
|
static const char *TAG = "gps";
|
||||||
|
|
||||||
|
TinyGPSPlus &GPSListener::get_tiny_gps() { return this->parent_->get_tiny_gps(); }
|
||||||
|
|
||||||
|
} // namespace gps
|
||||||
|
} // namespace esphome
|
47
esphome/components/gps/gps.h
Normal file
47
esphome/components/gps/gps.h
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/components/uart/uart.h"
|
||||||
|
#include <TinyGPS++.h>
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace gps {
|
||||||
|
|
||||||
|
class GPS;
|
||||||
|
|
||||||
|
class GPSListener {
|
||||||
|
public:
|
||||||
|
virtual void on_update(TinyGPSPlus &tiny_gps) = 0;
|
||||||
|
TinyGPSPlus &get_tiny_gps();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
friend GPS;
|
||||||
|
|
||||||
|
GPS *parent_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class GPS : public Component, public uart::UARTDevice {
|
||||||
|
public:
|
||||||
|
void register_listener(GPSListener *listener) {
|
||||||
|
listener->parent_ = this;
|
||||||
|
this->listeners_.push_back(listener);
|
||||||
|
}
|
||||||
|
float get_setup_priority() const override { return setup_priority::HARDWARE; }
|
||||||
|
void loop() override {
|
||||||
|
while (this->available() && !this->has_time_) {
|
||||||
|
if (this->tiny_gps_.encode(this->read())) {
|
||||||
|
for (auto *listener : this->listeners_)
|
||||||
|
listener->on_update(this->tiny_gps_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TinyGPSPlus &get_tiny_gps() { return this->tiny_gps_; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool has_time_{false};
|
||||||
|
TinyGPSPlus tiny_gps_;
|
||||||
|
std::vector<GPSListener *> listeners_{};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace gps
|
||||||
|
} // namespace esphome
|
23
esphome/components/gps/time/__init__.py
Normal file
23
esphome/components/gps/time/__init__.py
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
from esphome.components import time as time_
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
import esphome.codegen as cg
|
||||||
|
from esphome.const import CONF_ID
|
||||||
|
from .. import gps_ns, GPSListener, CONF_GPS_ID, GPS
|
||||||
|
|
||||||
|
DEPENDENCIES = ['gps']
|
||||||
|
|
||||||
|
GPSTime = gps_ns.class_('GPSTime', time_.RealTimeClock, GPSListener)
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = time_.TIME_SCHEMA.extend({
|
||||||
|
cv.GenerateID(): cv.declare_id(GPSTime),
|
||||||
|
cv.GenerateID(CONF_GPS_ID): cv.use_id(GPS),
|
||||||
|
}).extend(cv.COMPONENT_SCHEMA)
|
||||||
|
|
||||||
|
|
||||||
|
def to_code(config):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
yield time_.register_time(var, config)
|
||||||
|
yield cg.register_component(var, config)
|
||||||
|
|
||||||
|
paren = yield cg.get_variable(config[CONF_GPS_ID])
|
||||||
|
cg.add(paren.register_listener(var))
|
10
esphome/components/gps/time/gps_time.cpp
Normal file
10
esphome/components/gps/time/gps_time.cpp
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
#include "gps_time.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace gps {
|
||||||
|
|
||||||
|
static const char *TAG = "gps.time";
|
||||||
|
|
||||||
|
} // namespace gps
|
||||||
|
} // namespace esphome
|
39
esphome/components/gps/time/gps_time.h
Normal file
39
esphome/components/gps/time/gps_time.h
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/components/time/real_time_clock.h"
|
||||||
|
#include "esphome/components/gps/gps.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace gps {
|
||||||
|
|
||||||
|
class GPSTime : public time::RealTimeClock, public GPSListener {
|
||||||
|
public:
|
||||||
|
void on_update(TinyGPSPlus &tiny_gps) override {
|
||||||
|
if (!this->has_time_)
|
||||||
|
this->from_tiny_gps_(tiny_gps);
|
||||||
|
}
|
||||||
|
void setup() override {
|
||||||
|
this->set_interval(5 * 60 * 1000, [this]() { this->from_tiny_gps_(this->get_tiny_gps()); });
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void from_tiny_gps_(TinyGPSPlus &tiny_gps) {
|
||||||
|
if (!tiny_gps.time.isValid() || !tiny_gps.date.isValid())
|
||||||
|
return;
|
||||||
|
time::ESPTime val{};
|
||||||
|
val.year = tiny_gps.date.year();
|
||||||
|
val.month = tiny_gps.date.month();
|
||||||
|
val.day_of_month = tiny_gps.date.day();
|
||||||
|
val.hour = tiny_gps.time.hour();
|
||||||
|
val.minute = tiny_gps.time.minute();
|
||||||
|
val.second = tiny_gps.time.second();
|
||||||
|
val.recalc_timestamp_utc(false);
|
||||||
|
this->synchronize_epoch_(val.timestamp);
|
||||||
|
this->has_time_ = true;
|
||||||
|
}
|
||||||
|
bool has_time_{false};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace gps
|
||||||
|
} // namespace esphome
|
|
@ -13,8 +13,8 @@ void HLW8012Component::setup() {
|
||||||
ESP_LOGCONFIG(TAG, "Setting up HLW8012...");
|
ESP_LOGCONFIG(TAG, "Setting up HLW8012...");
|
||||||
this->sel_pin_->setup();
|
this->sel_pin_->setup();
|
||||||
this->sel_pin_->digital_write(this->current_mode_);
|
this->sel_pin_->digital_write(this->current_mode_);
|
||||||
this->cf_store_.setup(this->cf_pin_);
|
this->cf_store_.pulse_counter_setup(this->cf_pin_);
|
||||||
this->cf1_store_.setup(this->cf1_pin_);
|
this->cf1_store_.pulse_counter_setup(this->cf1_pin_);
|
||||||
}
|
}
|
||||||
void HLW8012Component::dump_config() {
|
void HLW8012Component::dump_config() {
|
||||||
ESP_LOGCONFIG(TAG, "HLW8012:");
|
ESP_LOGCONFIG(TAG, "HLW8012:");
|
||||||
|
@ -32,18 +32,18 @@ void HLW8012Component::dump_config() {
|
||||||
float HLW8012Component::get_setup_priority() const { return setup_priority::DATA; }
|
float HLW8012Component::get_setup_priority() const { return setup_priority::DATA; }
|
||||||
void HLW8012Component::update() {
|
void HLW8012Component::update() {
|
||||||
// HLW8012 has 50% duty cycle
|
// HLW8012 has 50% duty cycle
|
||||||
const uint32_t last_rise_cf = this->cf_store_.get_last_rise();
|
pulse_counter::pulse_counter_t raw_cf = this->cf_store_.read_raw_value();
|
||||||
const uint32_t last_rise_cf1 = this->cf1_store_.get_last_rise();
|
pulse_counter::pulse_counter_t raw_cf1 = this->cf1_store_.read_raw_value();
|
||||||
const uint32_t now = micros();
|
float cf_hz = raw_cf / (this->get_update_interval() / 1000.0f);
|
||||||
float full_cycle_cf = this->cf_store_.get_pulse_width_s() * 2;
|
if (raw_cf <= 1) {
|
||||||
float full_cycle_cf1 = this->cf1_store_.get_pulse_width_s() * 2;
|
// don't count single pulse as power
|
||||||
float cf_hz = 0.0f, cf1_hz = 0.0f;
|
cf_hz = 0.0f;
|
||||||
auto update_interval_micros = static_cast<uint32_t>(this->update_interval_ * 1e3f);
|
}
|
||||||
|
float cf1_hz = raw_cf1 / (this->get_update_interval() / 1000.0f);
|
||||||
if (full_cycle_cf != 0.0f && now - last_rise_cf < update_interval_micros * 3)
|
if (raw_cf1 <= 1) {
|
||||||
cf_hz = 1.0f / full_cycle_cf;
|
// don't count single pulse as anything
|
||||||
if (full_cycle_cf1 != 0.0f && now - last_rise_cf1 < update_interval_micros * 3)
|
cf1_hz = 0.0f;
|
||||||
cf1_hz = 1.0f / full_cycle_cf1;
|
}
|
||||||
|
|
||||||
if (this->nth_value_++ < 2) {
|
if (this->nth_value_++ < 2) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
#include "esphome/core/esphal.h"
|
#include "esphome/core/esphal.h"
|
||||||
#include "esphome/components/sensor/sensor.h"
|
#include "esphome/components/sensor/sensor.h"
|
||||||
#include "esphome/components/pulse_width/pulse_width.h"
|
#include "esphome/components/pulse_counter/pulse_counter_sensor.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace hlw8012 {
|
namespace hlw8012 {
|
||||||
|
@ -34,9 +34,9 @@ class HLW8012Component : public PollingComponent {
|
||||||
float voltage_divider_{2351};
|
float voltage_divider_{2351};
|
||||||
GPIOPin *sel_pin_;
|
GPIOPin *sel_pin_;
|
||||||
GPIOPin *cf_pin_;
|
GPIOPin *cf_pin_;
|
||||||
pulse_width::PulseWidthSensorStore cf_store_;
|
pulse_counter::PulseCounterStorage cf_store_;
|
||||||
GPIOPin *cf1_pin_;
|
GPIOPin *cf1_pin_;
|
||||||
pulse_width::PulseWidthSensorStore cf1_store_;
|
pulse_counter::PulseCounterStorage cf1_store_;
|
||||||
sensor::Sensor *voltage_sensor_{nullptr};
|
sensor::Sensor *voltage_sensor_{nullptr};
|
||||||
sensor::Sensor *current_sensor_{nullptr};
|
sensor::Sensor *current_sensor_{nullptr};
|
||||||
sensor::Sensor *power_sensor_{nullptr};
|
sensor::Sensor *power_sensor_{nullptr};
|
||||||
|
|
|
@ -6,7 +6,7 @@ from esphome.const import CONF_CHANGE_MODE_EVERY, CONF_CURRENT, \
|
||||||
CONF_CURRENT_RESISTOR, CONF_ID, CONF_POWER, CONF_SEL_PIN, CONF_VOLTAGE, CONF_VOLTAGE_DIVIDER, \
|
CONF_CURRENT_RESISTOR, CONF_ID, CONF_POWER, CONF_SEL_PIN, CONF_VOLTAGE, CONF_VOLTAGE_DIVIDER, \
|
||||||
ICON_FLASH, UNIT_VOLT, UNIT_AMPERE, UNIT_WATT
|
ICON_FLASH, UNIT_VOLT, UNIT_AMPERE, UNIT_WATT
|
||||||
|
|
||||||
AUTO_LOAD = ['pulse_width']
|
AUTO_LOAD = ['pulse_counter']
|
||||||
|
|
||||||
hlw8012_ns = cg.esphome_ns.namespace('hlw8012')
|
hlw8012_ns = cg.esphome_ns.namespace('hlw8012')
|
||||||
HLW8012Component = hlw8012_ns.class_('HLW8012Component', cg.PollingComponent)
|
HLW8012Component = hlw8012_ns.class_('HLW8012Component', cg.PollingComponent)
|
||||||
|
|
|
@ -163,6 +163,14 @@ class I2CDevice {
|
||||||
*/
|
*/
|
||||||
bool read_bytes(uint8_t a_register, uint8_t *data, uint8_t len, uint32_t conversion = 0); // NOLINT
|
bool read_bytes(uint8_t a_register, uint8_t *data, uint8_t len, uint32_t conversion = 0); // NOLINT
|
||||||
|
|
||||||
|
template<size_t N> optional<std::array<uint8_t, N>> read_bytes(uint8_t a_register) { // NOLINT
|
||||||
|
std::array<uint8_t, N> res;
|
||||||
|
if (!this->read_bytes(a_register, res.data(), N)) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
/** Read len amount of 16-bit words (MSB first) from a register into data.
|
/** Read len amount of 16-bit words (MSB first) from a register into data.
|
||||||
*
|
*
|
||||||
* @param a_register The register number to write to the bus before reading.
|
* @param a_register The register number to write to the bus before reading.
|
||||||
|
@ -176,6 +184,13 @@ class I2CDevice {
|
||||||
/// Read a single byte from a register into the data variable. Return true if successful.
|
/// Read a single byte from a register into the data variable. Return true if successful.
|
||||||
bool read_byte(uint8_t a_register, uint8_t *data, uint32_t conversion = 0); // NOLINT
|
bool read_byte(uint8_t a_register, uint8_t *data, uint32_t conversion = 0); // NOLINT
|
||||||
|
|
||||||
|
optional<uint8_t> read_byte(uint8_t a_register) { // NOLINT
|
||||||
|
uint8_t data;
|
||||||
|
if (!this->read_byte(a_register, &data))
|
||||||
|
return {};
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
/// Read a single 16-bit words (MSB first) from a register into the data variable. Return true if successful.
|
/// Read a single 16-bit words (MSB first) from a register into the data variable. Return true if successful.
|
||||||
bool read_byte_16(uint8_t a_register, uint16_t *data, uint32_t conversion = 0); // NOLINT
|
bool read_byte_16(uint8_t a_register, uint16_t *data, uint32_t conversion = 0); // NOLINT
|
||||||
|
|
||||||
|
@ -188,6 +203,20 @@ class I2CDevice {
|
||||||
*/
|
*/
|
||||||
bool write_bytes(uint8_t a_register, const uint8_t *data, uint8_t len); // NOLINT
|
bool write_bytes(uint8_t a_register, const uint8_t *data, uint8_t len); // NOLINT
|
||||||
|
|
||||||
|
/** Write a vector of data to a register.
|
||||||
|
*
|
||||||
|
* @param a_register The register to write to.
|
||||||
|
* @param data The data to write.
|
||||||
|
* @return If the operation was successful.
|
||||||
|
*/
|
||||||
|
bool write_bytes(uint8_t a_register, const std::vector<uint8_t> &data) { // NOLINT
|
||||||
|
return this->write_bytes(a_register, data.data(), data.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<size_t N> bool write_bytes(uint8_t a_register, const std::array<uint8_t, N> &data) { // NOLINT
|
||||||
|
return this->write_bytes(a_register, data.data(), data.size());
|
||||||
|
}
|
||||||
|
|
||||||
/** Write len amount of 16-bit words (MSB first) to the specified register.
|
/** Write len amount of 16-bit words (MSB first) to the specified register.
|
||||||
*
|
*
|
||||||
* @param a_register The register to write the values to.
|
* @param a_register The register to write the values to.
|
||||||
|
|
|
@ -33,7 +33,7 @@ CONFIG_SCHEMA = sensor.SENSOR_SCHEMA.extend({
|
||||||
cv.Required(CONF_TIME_UNIT): cv.enum(INTEGRATION_TIMES, lower=True),
|
cv.Required(CONF_TIME_UNIT): cv.enum(INTEGRATION_TIMES, lower=True),
|
||||||
cv.Optional(CONF_INTEGRATION_METHOD, default='trapezoid'):
|
cv.Optional(CONF_INTEGRATION_METHOD, default='trapezoid'):
|
||||||
cv.enum(INTEGRATION_METHODS, lower=True),
|
cv.enum(INTEGRATION_METHODS, lower=True),
|
||||||
cv.Optional(CONF_RESTORE, default=True): cv.boolean,
|
cv.Optional(CONF_RESTORE, default=False): cv.boolean,
|
||||||
}).extend(cv.COMPONENT_SCHEMA)
|
}).extend(cv.COMPONENT_SCHEMA)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,20 +1,54 @@
|
||||||
|
import math
|
||||||
|
|
||||||
from esphome import pins
|
from esphome import pins
|
||||||
from esphome.components import output
|
from esphome.components import output
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.const import APB_CLOCK_FREQ, CONF_BIT_DEPTH, CONF_CHANNEL, CONF_FREQUENCY, \
|
from esphome.const import CONF_BIT_DEPTH, CONF_CHANNEL, CONF_FREQUENCY, \
|
||||||
CONF_ID, CONF_PIN, ESP_PLATFORM_ESP32
|
CONF_ID, CONF_PIN, ESP_PLATFORM_ESP32
|
||||||
|
|
||||||
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
|
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
|
||||||
|
|
||||||
|
|
||||||
|
def calc_max_frequency(bit_depth):
|
||||||
|
return 80e6 / (2**bit_depth)
|
||||||
|
|
||||||
|
|
||||||
|
def calc_min_frequency(bit_depth):
|
||||||
|
# LEDC_DIV_NUM_HSTIMER is 15-bit unsigned integer
|
||||||
|
# lower 8 bits represent fractional part
|
||||||
|
max_div_num = ((1 << 16) - 1) / 256.0
|
||||||
|
return 80e6 / (max_div_num * (2**bit_depth))
|
||||||
|
|
||||||
|
|
||||||
def validate_frequency_bit_depth(obj):
|
def validate_frequency_bit_depth(obj):
|
||||||
frequency = obj[CONF_FREQUENCY]
|
frequency = obj[CONF_FREQUENCY]
|
||||||
bit_depth = obj[CONF_BIT_DEPTH]
|
if CONF_BIT_DEPTH not in obj:
|
||||||
max_freq = APB_CLOCK_FREQ / (2**bit_depth)
|
obj = obj.copy()
|
||||||
|
for bit_depth in range(15, 0, -1):
|
||||||
|
if calc_min_frequency(bit_depth) <= frequency <= calc_max_frequency(bit_depth):
|
||||||
|
obj[CONF_BIT_DEPTH] = bit_depth
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
min_freq = min(calc_min_frequency(x) for x in range(1, 16))
|
||||||
|
max_freq = max(calc_max_frequency(x) for x in range(1, 16))
|
||||||
|
if frequency < min_freq:
|
||||||
|
raise cv.Invalid("This frequency setting is not possible, please choose a higher "
|
||||||
|
"frequency (at least {}Hz)".format(int(min_freq)))
|
||||||
if frequency > max_freq:
|
if frequency > max_freq:
|
||||||
raise cv.Invalid('Maximum frequency for bit depth {} is {}Hz'.format(bit_depth, max_freq))
|
raise cv.Invalid("This frequency setting is not possible, please choose a lower "
|
||||||
|
"frequency (at most {}Hz)".format(int(max_freq)))
|
||||||
|
raise cv.Invalid("Invalid frequency!")
|
||||||
|
|
||||||
|
bit_depth = obj[CONF_BIT_DEPTH]
|
||||||
|
min_freq = calc_min_frequency(bit_depth)
|
||||||
|
max_freq = calc_max_frequency(bit_depth)
|
||||||
|
if frequency > max_freq:
|
||||||
|
raise cv.Invalid('Maximum frequency for bit depth {} is {}Hz. Please decrease the '
|
||||||
|
'bit_depth.'.format(bit_depth, int(math.floor(max_freq))))
|
||||||
|
if frequency < calc_min_frequency(bit_depth):
|
||||||
|
raise cv.Invalid('Minimum frequency for bit depth {} is {}Hz. Please increase the '
|
||||||
|
'bit_depth.'.format(bit_depth, int(math.ceil(min_freq))))
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
|
|
||||||
|
@ -25,8 +59,8 @@ CONFIG_SCHEMA = cv.All(output.FLOAT_OUTPUT_SCHEMA.extend({
|
||||||
cv.Required(CONF_ID): cv.declare_id(LEDCOutput),
|
cv.Required(CONF_ID): cv.declare_id(LEDCOutput),
|
||||||
cv.Required(CONF_PIN): pins.internal_gpio_output_pin_schema,
|
cv.Required(CONF_PIN): pins.internal_gpio_output_pin_schema,
|
||||||
cv.Optional(CONF_FREQUENCY, default='1kHz'): cv.frequency,
|
cv.Optional(CONF_FREQUENCY, default='1kHz'): cv.frequency,
|
||||||
cv.Optional(CONF_BIT_DEPTH, default=12): cv.All(cv.int_, cv.Range(min=1, max=15)),
|
cv.Optional(CONF_BIT_DEPTH): cv.int_range(min=1, max=15),
|
||||||
cv.Optional(CONF_CHANNEL): cv.All(cv.int_, cv.Range(min=0, max=15))
|
cv.Optional(CONF_CHANNEL): cv.int_range(min=0, max=15),
|
||||||
}).extend(cv.COMPONENT_SCHEMA), validate_frequency_bit_depth)
|
}).extend(cv.COMPONENT_SCHEMA), validate_frequency_bit_depth)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.components import mqtt
|
from esphome.components import mqtt, power_supply
|
||||||
from esphome.const import CONF_COLOR_CORRECT, \
|
from esphome.const import CONF_COLOR_CORRECT, \
|
||||||
CONF_DEFAULT_TRANSITION_LENGTH, CONF_EFFECTS, CONF_GAMMA_CORRECT, CONF_ID, \
|
CONF_DEFAULT_TRANSITION_LENGTH, CONF_EFFECTS, CONF_GAMMA_CORRECT, CONF_ID, \
|
||||||
CONF_INTERNAL, CONF_NAME, CONF_MQTT_ID
|
CONF_INTERNAL, CONF_NAME, CONF_MQTT_ID, CONF_POWER_SUPPLY
|
||||||
from esphome.core import coroutine, coroutine_with_priority
|
from esphome.core import coroutine, coroutine_with_priority
|
||||||
from .automation import light_control_to_code # noqa
|
from .automation import light_control_to_code # noqa
|
||||||
from .effects import validate_effects, BINARY_EFFECTS, \
|
from .effects import validate_effects, BINARY_EFFECTS, \
|
||||||
|
@ -35,6 +35,7 @@ ADDRESSABLE_LIGHT_SCHEMA = RGB_LIGHT_SCHEMA.extend({
|
||||||
cv.GenerateID(): cv.declare_id(AddressableLightState),
|
cv.GenerateID(): cv.declare_id(AddressableLightState),
|
||||||
cv.Optional(CONF_EFFECTS): validate_effects(ADDRESSABLE_EFFECTS),
|
cv.Optional(CONF_EFFECTS): validate_effects(ADDRESSABLE_EFFECTS),
|
||||||
cv.Optional(CONF_COLOR_CORRECT): cv.All([cv.percentage], cv.Length(min=3, max=4)),
|
cv.Optional(CONF_COLOR_CORRECT): cv.All([cv.percentage], cv.Length(min=3, max=4)),
|
||||||
|
cv.Optional(CONF_POWER_SUPPLY): cv.use_id(power_supply.PowerSupply),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@ -52,6 +53,10 @@ def setup_light_core_(light_var, output_var, config):
|
||||||
if CONF_COLOR_CORRECT in config:
|
if CONF_COLOR_CORRECT in config:
|
||||||
cg.add(output_var.set_correction(*config[CONF_COLOR_CORRECT]))
|
cg.add(output_var.set_correction(*config[CONF_COLOR_CORRECT]))
|
||||||
|
|
||||||
|
if CONF_POWER_SUPPLY in config:
|
||||||
|
var_ = yield cg.get_variable(config[CONF_POWER_SUPPLY])
|
||||||
|
cg.add(output_var.set_power_supply(var_))
|
||||||
|
|
||||||
if CONF_MQTT_ID in config:
|
if CONF_MQTT_ID in config:
|
||||||
mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], light_var)
|
mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], light_var)
|
||||||
yield mqtt.register_mqtt_component(mqtt_, config)
|
yield mqtt.register_mqtt_component(mqtt_, config)
|
||||||
|
|
|
@ -1,9 +1,14 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/core/defines.h"
|
||||||
#include "light_output.h"
|
#include "light_output.h"
|
||||||
#include "light_state.h"
|
#include "light_state.h"
|
||||||
|
|
||||||
|
#ifdef USE_POWER_SUPPLY
|
||||||
|
#include "esphome/components/power_supply/power_supply.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace light {
|
namespace light {
|
||||||
|
|
||||||
|
@ -30,6 +35,7 @@ struct ESPColor {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
uint8_t raw[4];
|
uint8_t raw[4];
|
||||||
|
uint32_t raw_32;
|
||||||
};
|
};
|
||||||
inline ESPColor() ALWAYS_INLINE : r(0), g(0), b(0), w(0) {} // NOLINT
|
inline ESPColor() ALWAYS_INLINE : r(0), g(0), b(0), w(0) {} // NOLINT
|
||||||
inline ESPColor(uint8_t red, uint8_t green, uint8_t blue, uint8_t white) ALWAYS_INLINE : r(red),
|
inline ESPColor(uint8_t red, uint8_t green, uint8_t blue, uint8_t white) ALWAYS_INLINE : r(red),
|
||||||
|
@ -47,7 +53,7 @@ struct ESPColor {
|
||||||
this->b = rhs.b;
|
this->b = rhs.b;
|
||||||
this->w = rhs.w;
|
this->w = rhs.w;
|
||||||
}
|
}
|
||||||
inline bool is_on() ALWAYS_INLINE { return this->r != 0 || this->g != 0 || this->b != 0 || this->w != 0; }
|
inline bool is_on() ALWAYS_INLINE { return this->raw_32 != 0; }
|
||||||
inline ESPColor &operator=(const ESPColor &rhs) ALWAYS_INLINE {
|
inline ESPColor &operator=(const ESPColor &rhs) ALWAYS_INLINE {
|
||||||
this->r = rhs.r;
|
this->r = rhs.r;
|
||||||
this->g = rhs.g;
|
this->g = rhs.g;
|
||||||
|
@ -527,14 +533,32 @@ class AddressableLight : public LightOutput {
|
||||||
void setup_state(LightState *state) override { this->correction_.calculate_gamma_table(state->get_gamma_correct()); }
|
void setup_state(LightState *state) override { this->correction_.calculate_gamma_table(state->get_gamma_correct()); }
|
||||||
void schedule_show() { this->next_show_ = true; }
|
void schedule_show() { this->next_show_ = true; }
|
||||||
|
|
||||||
|
#ifdef USE_POWER_SUPPLY
|
||||||
|
void set_power_supply(power_supply::PowerSupply *power_supply) { this->power_.set_parent(power_supply); }
|
||||||
|
#endif
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool should_show_() const { return this->effect_active_ || this->next_show_; }
|
bool should_show_() const { return this->effect_active_ || this->next_show_; }
|
||||||
void mark_shown_() { this->next_show_ = false; }
|
void mark_shown_() {
|
||||||
|
this->next_show_ = false;
|
||||||
|
#ifdef USE_POWER_SUPPLY
|
||||||
|
for (auto c : *this) {
|
||||||
|
if (c.get().is_on()) {
|
||||||
|
this->power_.request();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this->power_.unrequest();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
virtual ESPColorView get_view_internal(int32_t index) const = 0;
|
virtual ESPColorView get_view_internal(int32_t index) const = 0;
|
||||||
|
|
||||||
bool effect_active_{false};
|
bool effect_active_{false};
|
||||||
bool next_show_{true};
|
bool next_show_{true};
|
||||||
ESPColorCorrection correction_{};
|
ESPColorCorrection correction_{};
|
||||||
|
#ifdef USE_POWER_SUPPLY
|
||||||
|
power_supply::PowerSupplyRequester power_;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace light
|
} // namespace light
|
||||||
|
|
|
@ -112,7 +112,7 @@ def to_code(config):
|
||||||
|
|
||||||
if CORE.is_esp8266 and has_serial_logging and is_at_least_verbose:
|
if CORE.is_esp8266 and has_serial_logging and is_at_least_verbose:
|
||||||
debug_serial_port = HARDWARE_UART_TO_SERIAL[config.get(CONF_HARDWARE_UART)]
|
debug_serial_port = HARDWARE_UART_TO_SERIAL[config.get(CONF_HARDWARE_UART)]
|
||||||
cg.add_build_flag("-DDEBUG_ESP_PORT{}".format(debug_serial_port))
|
cg.add_build_flag("-DDEBUG_ESP_PORT={}".format(debug_serial_port))
|
||||||
cg.add_build_flag("-DLWIP_DEBUG")
|
cg.add_build_flag("-DLWIP_DEBUG")
|
||||||
DEBUG_COMPONENTS = {
|
DEBUG_COMPONENTS = {
|
||||||
'HTTP_CLIENT',
|
'HTTP_CLIENT',
|
||||||
|
|
|
@ -12,8 +12,8 @@ MAX7219ComponentRef = MAX7219Component.operator('ref')
|
||||||
CONFIG_SCHEMA = display.BASIC_DISPLAY_SCHEMA.extend({
|
CONFIG_SCHEMA = display.BASIC_DISPLAY_SCHEMA.extend({
|
||||||
cv.GenerateID(): cv.declare_id(MAX7219Component),
|
cv.GenerateID(): cv.declare_id(MAX7219Component),
|
||||||
|
|
||||||
cv.Optional(CONF_NUM_CHIPS, default=1): cv.All(cv.uint8_t, cv.Range(min=1)),
|
cv.Optional(CONF_NUM_CHIPS, default=1): cv.int_range(min=1, max=255),
|
||||||
cv.Optional(CONF_INTENSITY, default=15): cv.All(cv.uint8_t, cv.Range(min=0, max=15)),
|
cv.Optional(CONF_INTENSITY, default=15): cv.int_range(min=0, max=15),
|
||||||
}).extend(cv.polling_component_schema('1s')).extend(spi.SPI_DEVICE_SCHEMA)
|
}).extend(cv.polling_component_schema('1s')).extend(spi.SPI_DEVICE_SCHEMA)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -110,7 +110,7 @@ void MPU6050Component::update() {
|
||||||
float accel_y = data[1] * 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 accel_z = data[2] * MPU6050_RANGE_PER_DIGIT_2G * GRAVITY_EARTH;
|
||||||
|
|
||||||
float temperature = raw_data[3] / 340.0f + 36.53f;
|
float temperature = data[3] / 340.0f + 36.53f;
|
||||||
|
|
||||||
float gyro_x = data[4] * MPU6050_SCALE_DPS_PER_DIGIT_2000;
|
float gyro_x = data[4] * MPU6050_SCALE_DPS_PER_DIGIT_2000;
|
||||||
float gyro_y = data[5] * MPU6050_SCALE_DPS_PER_DIGIT_2000;
|
float gyro_y = data[5] * MPU6050_SCALE_DPS_PER_DIGIT_2000;
|
||||||
|
|
|
@ -24,12 +24,12 @@ void MQTTClimateComponent::send_discovery(JsonObject &root, mqtt::SendDiscoveryC
|
||||||
JsonArray &modes = root.createNestedArray("modes");
|
JsonArray &modes = root.createNestedArray("modes");
|
||||||
// sort array for nice UI in HA
|
// sort array for nice UI in HA
|
||||||
if (traits.supports_mode(CLIMATE_MODE_AUTO))
|
if (traits.supports_mode(CLIMATE_MODE_AUTO))
|
||||||
modes.add("auto");
|
modes.add(climate_mode_to_string(CLIMATE_MODE_AUTO));
|
||||||
modes.add("off");
|
modes.add(climate_mode_to_string(CLIMATE_MODE_OFF));
|
||||||
if (traits.supports_mode(CLIMATE_MODE_COOL))
|
if (traits.supports_mode(CLIMATE_MODE_COOL))
|
||||||
modes.add("cool");
|
modes.add(climate_mode_to_string(CLIMATE_MODE_COOL));
|
||||||
if (traits.supports_mode(CLIMATE_MODE_HEAT))
|
if (traits.supports_mode(CLIMATE_MODE_HEAT))
|
||||||
modes.add("heat");
|
modes.add(climate_mode_to_string(CLIMATE_MODE_HEAT));
|
||||||
|
|
||||||
if (traits.get_supports_two_point_target_temperature()) {
|
if (traits.get_supports_two_point_target_temperature()) {
|
||||||
// temperature_low_command_topic
|
// temperature_low_command_topic
|
||||||
|
@ -60,6 +60,8 @@ void MQTTClimateComponent::send_discovery(JsonObject &root, mqtt::SendDiscoveryC
|
||||||
// away_mode_state_topic
|
// away_mode_state_topic
|
||||||
root["away_mode_state_topic"] = this->get_away_state_topic();
|
root["away_mode_state_topic"] = this->get_away_state_topic();
|
||||||
}
|
}
|
||||||
|
config.state_topic = false;
|
||||||
|
config.command_topic = false;
|
||||||
}
|
}
|
||||||
void MQTTClimateComponent::setup() {
|
void MQTTClimateComponent::setup() {
|
||||||
auto traits = this->device_->get_traits();
|
auto traits = this->device_->get_traits();
|
||||||
|
@ -144,7 +146,7 @@ bool MQTTClimateComponent::publish_state_() {
|
||||||
if (!this->publish(this->get_mode_state_topic(), mode_s))
|
if (!this->publish(this->get_mode_state_topic(), mode_s))
|
||||||
success = false;
|
success = false;
|
||||||
int8_t accuracy = traits.get_temperature_accuracy_decimals();
|
int8_t accuracy = traits.get_temperature_accuracy_decimals();
|
||||||
if (traits.get_supports_current_temperature()) {
|
if (traits.get_supports_current_temperature() && !isnan(this->device_->current_temperature)) {
|
||||||
std::string payload = value_accuracy_to_string(this->device_->current_temperature, accuracy);
|
std::string payload = value_accuracy_to_string(this->device_->current_temperature, accuracy);
|
||||||
if (!this->publish(this->get_current_temperature_state_topic(), payload))
|
if (!this->publish(this->get_current_temperature_state_topic(), payload))
|
||||||
success = false;
|
success = false;
|
||||||
|
|
|
@ -13,8 +13,8 @@ CONFIG_SCHEMA = cv.Schema({
|
||||||
cv.GenerateID(): cv.declare_id(MY9231OutputComponent),
|
cv.GenerateID(): cv.declare_id(MY9231OutputComponent),
|
||||||
cv.Required(CONF_DATA_PIN): pins.gpio_output_pin_schema,
|
cv.Required(CONF_DATA_PIN): pins.gpio_output_pin_schema,
|
||||||
cv.Required(CONF_CLOCK_PIN): pins.gpio_output_pin_schema,
|
cv.Required(CONF_CLOCK_PIN): pins.gpio_output_pin_schema,
|
||||||
cv.Optional(CONF_NUM_CHANNELS, default=6): cv.All(cv.int_, cv.Range(min=3, max=1020)),
|
cv.Optional(CONF_NUM_CHANNELS, default=6): cv.int_range(min=3, max=1020),
|
||||||
cv.Optional(CONF_NUM_CHIPS, default=2): cv.All(cv.int_, cv.Range(min=1, max=255)),
|
cv.Optional(CONF_NUM_CHIPS, default=2): cv.int_range(min=1, max=255),
|
||||||
cv.Optional(CONF_BIT_DEPTH, default=16): cv.one_of(8, 12, 14, 16, int=True),
|
cv.Optional(CONF_BIT_DEPTH, default=16): cv.one_of(8, 12, 14, 16, int=True),
|
||||||
}).extend(cv.COMPONENT_SCHEMA)
|
}).extend(cv.COMPONENT_SCHEMA)
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/core/esphal.h"
|
||||||
#include "esphome/components/output/float_output.h"
|
#include "esphome/components/output/float_output.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
|
|
|
@ -13,7 +13,7 @@ CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend({
|
||||||
cv.GenerateID(CONF_MY9231_ID): cv.use_id(MY9231OutputComponent),
|
cv.GenerateID(CONF_MY9231_ID): cv.use_id(MY9231OutputComponent),
|
||||||
|
|
||||||
cv.Required(CONF_ID): cv.declare_id(Channel),
|
cv.Required(CONF_ID): cv.declare_id(Channel),
|
||||||
cv.Required(CONF_CHANNEL): cv.All(cv.int_, cv.Range(min=0, max=65535)),
|
cv.Required(CONF_CHANNEL): cv.uint16_t,
|
||||||
}).extend(cv.COMPONENT_SCHEMA)
|
}).extend(cv.COMPONENT_SCHEMA)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome import pins
|
from esphome import pins
|
||||||
from esphome.components import light, power_supply
|
from esphome.components import light
|
||||||
from esphome.const import CONF_CLOCK_PIN, CONF_DATA_PIN, CONF_METHOD, CONF_NUM_LEDS, CONF_PIN, \
|
from esphome.const import CONF_CLOCK_PIN, CONF_DATA_PIN, CONF_METHOD, CONF_NUM_LEDS, CONF_PIN, \
|
||||||
CONF_POWER_SUPPLY, CONF_TYPE, CONF_VARIANT, CONF_OUTPUT_ID
|
CONF_TYPE, CONF_VARIANT, CONF_OUTPUT_ID
|
||||||
from esphome.core import CORE
|
from esphome.core import CORE
|
||||||
|
|
||||||
neopixelbus_ns = cg.esphome_ns.namespace('neopixelbus')
|
neopixelbus_ns = cg.esphome_ns.namespace('neopixelbus')
|
||||||
|
@ -138,8 +138,6 @@ CONFIG_SCHEMA = cv.All(light.ADDRESSABLE_LIGHT_SCHEMA.extend({
|
||||||
cv.Optional(CONF_DATA_PIN): pins.output_pin,
|
cv.Optional(CONF_DATA_PIN): pins.output_pin,
|
||||||
|
|
||||||
cv.Required(CONF_NUM_LEDS): cv.positive_not_null_int,
|
cv.Required(CONF_NUM_LEDS): cv.positive_not_null_int,
|
||||||
|
|
||||||
cv.Optional(CONF_POWER_SUPPLY): cv.use_id(power_supply.PowerSupply),
|
|
||||||
}).extend(cv.COMPONENT_SCHEMA), validate, validate_method_pin)
|
}).extend(cv.COMPONENT_SCHEMA), validate, validate_method_pin)
|
||||||
|
|
||||||
|
|
||||||
|
@ -162,8 +160,4 @@ def to_code(config):
|
||||||
|
|
||||||
cg.add(var.set_pixel_order(getattr(ESPNeoPixelOrder, config[CONF_TYPE])))
|
cg.add(var.set_pixel_order(getattr(ESPNeoPixelOrder, config[CONF_TYPE])))
|
||||||
|
|
||||||
if CONF_POWER_SUPPLY in config:
|
|
||||||
var_ = yield cg.get_variable(config[CONF_POWER_SUPPLY])
|
|
||||||
cg.add(var.set_power_supply(var_))
|
|
||||||
|
|
||||||
cg.add_library('NeoPixelBus', '2.4.1')
|
cg.add_library('NeoPixelBus', '2.4.1')
|
||||||
|
|
|
@ -9,10 +9,6 @@
|
||||||
#error The NeoPixelBus library requires at least arduino_core_version 2.4.x
|
#error The NeoPixelBus library requires at least arduino_core_version 2.4.x
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_POWER_SUPPLY
|
|
||||||
#include "esphome/components/power_supply/power_supply.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "NeoPixelBus.h"
|
#include "NeoPixelBus.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
|
@ -54,10 +50,6 @@ enum class ESPNeoPixelOrder {
|
||||||
template<typename T_METHOD, typename T_COLOR_FEATURE>
|
template<typename T_METHOD, typename T_COLOR_FEATURE>
|
||||||
class NeoPixelBusLightOutputBase : public Component, public light::AddressableLight {
|
class NeoPixelBusLightOutputBase : public Component, public light::AddressableLight {
|
||||||
public:
|
public:
|
||||||
#ifdef USE_POWER_SUPPLY
|
|
||||||
void set_power_supply(power_supply::PowerSupply *power_supply) { this->power_supply_ = power_supply; }
|
|
||||||
#endif
|
|
||||||
|
|
||||||
NeoPixelBus<T_COLOR_FEATURE, T_METHOD> *get_controller() const { return this->controller_; }
|
NeoPixelBus<T_COLOR_FEATURE, T_METHOD> *get_controller() const { return this->controller_; }
|
||||||
|
|
||||||
void clear_effect_data() override {
|
void clear_effect_data() override {
|
||||||
|
@ -95,27 +87,6 @@ class NeoPixelBusLightOutputBase : public Component, public light::AddressableLi
|
||||||
this->mark_shown_();
|
this->mark_shown_();
|
||||||
this->controller_->Dirty();
|
this->controller_->Dirty();
|
||||||
|
|
||||||
#ifdef USE_POWER_SUPPLY
|
|
||||||
if (this->power_supply_ != nullptr) {
|
|
||||||
bool is_light_on = false;
|
|
||||||
for (int i = 0; i < this->size(); i++) {
|
|
||||||
if ((*this)[i].get().is_on()) {
|
|
||||||
is_light_on = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_light_on && !this->has_requested_high_power_) {
|
|
||||||
this->power_supply_->request_high_power();
|
|
||||||
this->has_requested_high_power_ = true;
|
|
||||||
}
|
|
||||||
if (!is_light_on && this->has_requested_high_power_) {
|
|
||||||
this->power_supply_->unrequest_high_power();
|
|
||||||
this->has_requested_high_power_ = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
this->controller_->Show();
|
this->controller_->Show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,10 +106,6 @@ class NeoPixelBusLightOutputBase : public Component, public light::AddressableLi
|
||||||
NeoPixelBus<T_COLOR_FEATURE, T_METHOD> *controller_{nullptr};
|
NeoPixelBus<T_COLOR_FEATURE, T_METHOD> *controller_{nullptr};
|
||||||
uint8_t *effect_data_{nullptr};
|
uint8_t *effect_data_{nullptr};
|
||||||
uint8_t rgb_offsets_[4]{0, 1, 2, 3};
|
uint8_t rgb_offsets_[4]{0, 1, 2, 3};
|
||||||
#ifdef USE_POWER_SUPPLY
|
|
||||||
power_supply::PowerSupply *power_supply_{nullptr};
|
|
||||||
bool has_requested_high_power_{false};
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T_METHOD, typename T_COLOR_FEATURE = NeoRgbFeature>
|
template<typename T_METHOD, typename T_COLOR_FEATURE = NeoRgbFeature>
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
#include "esphome/core/helpers.h"
|
#include "esphome/core/helpers.h"
|
||||||
#include "esphome/core/application.h"
|
#include "esphome/core/application.h"
|
||||||
#include "esphome/core/util.h"
|
#include "esphome/core/util.h"
|
||||||
//#include "esphome/components/status_led.h"
|
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <MD5Builder.h>
|
#include <MD5Builder.h>
|
||||||
|
|
|
@ -27,16 +27,13 @@ class BinaryOutput {
|
||||||
*
|
*
|
||||||
* @param power_supply The PowerSupplyComponent, set this to nullptr to disable the power supply.
|
* @param power_supply The PowerSupplyComponent, set this to nullptr to disable the power supply.
|
||||||
*/
|
*/
|
||||||
void set_power_supply(power_supply::PowerSupply *power_supply) { this->power_supply_ = power_supply; }
|
void set_power_supply(power_supply::PowerSupply *power_supply) { this->power_.set_parent(power_supply); }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/// Enable this binary output.
|
/// Enable this binary output.
|
||||||
virtual void turn_on() {
|
virtual void turn_on() {
|
||||||
#ifdef USE_POWER_SUPPLY
|
#ifdef USE_POWER_SUPPLY
|
||||||
if (this->power_supply_ != nullptr && !this->has_requested_high_power_) {
|
this->power_.request();
|
||||||
this->power_supply_->request_high_power();
|
|
||||||
this->has_requested_high_power_ = true;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
this->write_state(!this->inverted_);
|
this->write_state(!this->inverted_);
|
||||||
}
|
}
|
||||||
|
@ -44,10 +41,7 @@ class BinaryOutput {
|
||||||
/// Disable this binary output.
|
/// Disable this binary output.
|
||||||
virtual void turn_off() {
|
virtual void turn_off() {
|
||||||
#ifdef USE_POWER_SUPPLY
|
#ifdef USE_POWER_SUPPLY
|
||||||
if (this->power_supply_ != nullptr && this->has_requested_high_power_) {
|
this->power_.unrequest();
|
||||||
this->power_supply_->unrequest_high_power();
|
|
||||||
this->has_requested_high_power_ = false;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
this->write_state(this->inverted_);
|
this->write_state(this->inverted_);
|
||||||
}
|
}
|
||||||
|
@ -62,8 +56,7 @@ class BinaryOutput {
|
||||||
|
|
||||||
bool inverted_{false};
|
bool inverted_{false};
|
||||||
#ifdef USE_POWER_SUPPLY
|
#ifdef USE_POWER_SUPPLY
|
||||||
power_supply::PowerSupply *power_supply_{nullptr};
|
power_supply::PowerSupplyRequester power_{};
|
||||||
bool has_requested_high_power_{false};
|
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -24,15 +24,9 @@ void FloatOutput::set_level(float state) {
|
||||||
|
|
||||||
#ifdef USE_POWER_SUPPLY
|
#ifdef USE_POWER_SUPPLY
|
||||||
if (state > 0.0f) { // ON
|
if (state > 0.0f) { // ON
|
||||||
if (this->power_supply_ != nullptr && !this->has_requested_high_power_) {
|
this->power_.request();
|
||||||
this->power_supply_->request_high_power();
|
|
||||||
this->has_requested_high_power_ = true;
|
|
||||||
}
|
|
||||||
} else { // OFF
|
} else { // OFF
|
||||||
if (this->power_supply_ != nullptr && this->has_requested_high_power_) {
|
this->power_.unrequest();
|
||||||
this->power_supply_->unrequest_high_power();
|
|
||||||
this->has_requested_high_power_ = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend({
|
||||||
cv.Required(CONF_ID): cv.declare_id(PCA9685Channel),
|
cv.Required(CONF_ID): cv.declare_id(PCA9685Channel),
|
||||||
cv.GenerateID(CONF_PCA9685_ID): cv.use_id(PCA9685Output),
|
cv.GenerateID(CONF_PCA9685_ID): cv.use_id(PCA9685Output),
|
||||||
|
|
||||||
cv.Required(CONF_CHANNEL): cv.All(cv.Coerce(int), cv.Range(min=0, max=15)),
|
cv.Required(CONF_CHANNEL): cv.int_range(min=0, max=15),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -132,14 +132,20 @@ void PMSX003Component::parse_data_() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PMSX003_TYPE_5003ST: {
|
case PMSX003_TYPE_5003ST: {
|
||||||
|
uint16_t pm_1_0_concentration = this->get_16_bit_uint_(10);
|
||||||
uint16_t pm_2_5_concentration = this->get_16_bit_uint_(12);
|
uint16_t pm_2_5_concentration = this->get_16_bit_uint_(12);
|
||||||
|
uint16_t pm_10_0_concentration = this->get_16_bit_uint_(14);
|
||||||
uint16_t formaldehyde = this->get_16_bit_uint_(28);
|
uint16_t formaldehyde = this->get_16_bit_uint_(28);
|
||||||
float temperature = this->get_16_bit_uint_(30) / 10.0f;
|
float temperature = this->get_16_bit_uint_(30) / 10.0f;
|
||||||
float humidity = this->get_16_bit_uint_(32) / 10.0f;
|
float humidity = this->get_16_bit_uint_(32) / 10.0f;
|
||||||
ESP_LOGD(TAG, "Got PM2.5 Concentration: %u µg/m^3, Temperature: %.1f°C, Humidity: %.1f%% Formaldehyde: %u µg/m^3",
|
ESP_LOGD(TAG, "Got PM2.5 Concentration: %u µg/m^3, Temperature: %.1f°C, Humidity: %.1f%% Formaldehyde: %u µg/m^3",
|
||||||
pm_2_5_concentration, temperature, humidity, formaldehyde);
|
pm_2_5_concentration, temperature, humidity, formaldehyde);
|
||||||
|
if (this->pm_1_0_sensor_ != nullptr)
|
||||||
|
this->pm_1_0_sensor_->publish_state(pm_1_0_concentration);
|
||||||
if (this->pm_2_5_sensor_ != nullptr)
|
if (this->pm_2_5_sensor_ != nullptr)
|
||||||
this->pm_2_5_sensor_->publish_state(pm_2_5_concentration);
|
this->pm_2_5_sensor_->publish_state(pm_2_5_concentration);
|
||||||
|
if (this->pm_10_0_sensor_ != nullptr)
|
||||||
|
this->pm_10_0_sensor_->publish_state(pm_10_0_concentration);
|
||||||
if (this->temperature_sensor_ != nullptr)
|
if (this->temperature_sensor_ != nullptr)
|
||||||
this->temperature_sensor_->publish_state(temperature);
|
this->temperature_sensor_->publish_state(temperature);
|
||||||
if (this->humidity_sensor_ != nullptr)
|
if (this->humidity_sensor_ != nullptr)
|
||||||
|
|
|
@ -24,9 +24,9 @@ PMSX003_TYPES = {
|
||||||
}
|
}
|
||||||
|
|
||||||
SENSORS_TO_TYPE = {
|
SENSORS_TO_TYPE = {
|
||||||
CONF_PM_1_0: [CONF_PMSX003],
|
CONF_PM_1_0: [CONF_PMSX003, CONF_PMS5003ST],
|
||||||
CONF_PM_2_5: [CONF_PMSX003, CONF_PMS5003T, CONF_PMS5003ST],
|
CONF_PM_2_5: [CONF_PMSX003, CONF_PMS5003T, CONF_PMS5003ST],
|
||||||
CONF_PM_10_0: [CONF_PMSX003],
|
CONF_PM_10_0: [CONF_PMSX003, CONF_PMS5003ST],
|
||||||
CONF_TEMPERATURE: [CONF_PMS5003T, CONF_PMS5003ST],
|
CONF_TEMPERATURE: [CONF_PMS5003T, CONF_PMS5003ST],
|
||||||
CONF_HUMIDITY: [CONF_PMS5003T, CONF_PMS5003ST],
|
CONF_HUMIDITY: [CONF_PMS5003T, CONF_PMS5003ST],
|
||||||
CONF_FORMALDEHYDE: [CONF_PMS5003ST],
|
CONF_FORMALDEHYDE: [CONF_PMS5003ST],
|
||||||
|
|
|
@ -42,7 +42,7 @@ void PowerSupply::request_high_power() {
|
||||||
void PowerSupply::unrequest_high_power() {
|
void PowerSupply::unrequest_high_power() {
|
||||||
this->active_requests_--;
|
this->active_requests_--;
|
||||||
if (this->active_requests_ < 0) {
|
if (this->active_requests_ < 0) {
|
||||||
// if asserts are disabled we're just going to use 0 as our now counter.
|
// we're just going to use 0 as our now counter.
|
||||||
this->active_requests_ = 0;
|
this->active_requests_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,5 +39,26 @@ class PowerSupply : public Component {
|
||||||
int16_t active_requests_{0}; // use signed integer to make catching negative requests easier.
|
int16_t active_requests_{0}; // use signed integer to make catching negative requests easier.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class PowerSupplyRequester {
|
||||||
|
public:
|
||||||
|
void set_parent(PowerSupply *parent) { parent_ = parent; }
|
||||||
|
void request() {
|
||||||
|
if (!this->requested_ && this->parent_ != nullptr) {
|
||||||
|
this->parent_->request_high_power();
|
||||||
|
this->requested_ = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void unrequest() {
|
||||||
|
if (this->requested_ && this->parent_ != nullptr) {
|
||||||
|
this->parent_->unrequest_high_power();
|
||||||
|
this->requested_ = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
PowerSupply *parent_{nullptr};
|
||||||
|
bool requested_{false};
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace power_supply
|
} // namespace power_supply
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -28,7 +28,9 @@ void ICACHE_RAM_ATTR PulseCounterStorage::gpio_intr(PulseCounterStorage *arg) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bool PulseCounterStorage::pulse_counter_setup() {
|
bool PulseCounterStorage::pulse_counter_setup(GPIOPin *pin) {
|
||||||
|
this->pin = pin;
|
||||||
|
this->pin->setup();
|
||||||
this->isr_pin = this->pin->to_isr();
|
this->isr_pin = this->pin->to_isr();
|
||||||
this->pin->attach_interrupt(PulseCounterStorage::gpio_intr, this, CHANGE);
|
this->pin->attach_interrupt(PulseCounterStorage::gpio_intr, this, CHANGE);
|
||||||
return true;
|
return true;
|
||||||
|
@ -42,7 +44,9 @@ pulse_counter_t PulseCounterStorage::read_raw_value() {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
bool PulseCounterStorage::pulse_counter_setup() {
|
bool PulseCounterStorage::pulse_counter_setup(GPIOPin *pin) {
|
||||||
|
this->pin = pin;
|
||||||
|
this->pin->setup();
|
||||||
this->pcnt_unit = next_pcnt_unit;
|
this->pcnt_unit = next_pcnt_unit;
|
||||||
next_pcnt_unit = pcnt_unit_t(int(next_pcnt_unit) + 1); // NOLINT
|
next_pcnt_unit = pcnt_unit_t(int(next_pcnt_unit) + 1); // NOLINT
|
||||||
|
|
||||||
|
@ -133,9 +137,7 @@ pulse_counter_t PulseCounterStorage::read_raw_value() {
|
||||||
|
|
||||||
void PulseCounterSensor::setup() {
|
void PulseCounterSensor::setup() {
|
||||||
ESP_LOGCONFIG(TAG, "Setting up pulse counter '%s'...", this->name_.c_str());
|
ESP_LOGCONFIG(TAG, "Setting up pulse counter '%s'...", this->name_.c_str());
|
||||||
this->pin_->setup();
|
if (!this->storage_.pulse_counter_setup(this->pin_)) {
|
||||||
this->storage_.pin = this->pin_;
|
|
||||||
if (!this->storage_.pulse_counter_setup()) {
|
|
||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ using pulse_counter_t = int32_t;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct PulseCounterStorage {
|
struct PulseCounterStorage {
|
||||||
bool pulse_counter_setup();
|
bool pulse_counter_setup(GPIOPin *pin);
|
||||||
pulse_counter_t read_raw_value();
|
pulse_counter_t read_raw_value();
|
||||||
|
|
||||||
static void gpio_intr(PulseCounterStorage *arg);
|
static void gpio_intr(PulseCounterStorage *arg);
|
||||||
|
@ -42,9 +42,9 @@ struct PulseCounterStorage {
|
||||||
#ifdef ARDUINO_ARCH_ESP8266
|
#ifdef ARDUINO_ARCH_ESP8266
|
||||||
ISRInternalGPIOPin *isr_pin;
|
ISRInternalGPIOPin *isr_pin;
|
||||||
#endif
|
#endif
|
||||||
PulseCounterCountMode rising_edge_mode{};
|
PulseCounterCountMode rising_edge_mode{PULSE_COUNTER_INCREMENT};
|
||||||
PulseCounterCountMode falling_edge_mode{};
|
PulseCounterCountMode falling_edge_mode{PULSE_COUNTER_DISABLE};
|
||||||
uint32_t filter_us{};
|
uint32_t filter_us{0};
|
||||||
pulse_counter_t last_value{0};
|
pulse_counter_t last_value{0};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ from esphome.components import binary_sensor
|
||||||
from esphome.const import CONF_DATA, CONF_ID, CONF_TRIGGER_ID, CONF_NBITS, CONF_ADDRESS, \
|
from esphome.const import CONF_DATA, CONF_ID, CONF_TRIGGER_ID, CONF_NBITS, CONF_ADDRESS, \
|
||||||
CONF_COMMAND, CONF_CODE, CONF_PULSE_LENGTH, CONF_SYNC, CONF_ZERO, CONF_ONE, CONF_INVERTED, \
|
CONF_COMMAND, CONF_CODE, CONF_PULSE_LENGTH, CONF_SYNC, CONF_ZERO, CONF_ONE, CONF_INVERTED, \
|
||||||
CONF_PROTOCOL, CONF_GROUP, CONF_DEVICE, CONF_STATE, CONF_CHANNEL, CONF_FAMILY, CONF_REPEAT, \
|
CONF_PROTOCOL, CONF_GROUP, CONF_DEVICE, CONF_STATE, CONF_CHANNEL, CONF_FAMILY, CONF_REPEAT, \
|
||||||
CONF_WAIT_TIME, CONF_TIMES, CONF_TYPE_ID
|
CONF_WAIT_TIME, CONF_TIMES, CONF_TYPE_ID, CONF_CARRIER_FREQUENCY
|
||||||
from esphome.core import coroutine
|
from esphome.core import coroutine
|
||||||
from esphome.py_compat import string_types, text_type
|
from esphome.py_compat import string_types, text_type
|
||||||
from esphome.util import Registry, SimpleRegistry
|
from esphome.util import Registry, SimpleRegistry
|
||||||
|
@ -359,7 +359,9 @@ def raw_dumper(var, config):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@register_action('raw', RawAction, RAW_SCHEMA)
|
@register_action('raw', RawAction, RAW_SCHEMA.extend({
|
||||||
|
cv.Optional(CONF_CARRIER_FREQUENCY, default='0Hz'): cv.All(cv.frequency, cv.int_),
|
||||||
|
}))
|
||||||
def raw_action(var, config, args):
|
def raw_action(var, config, args):
|
||||||
code_ = config[CONF_CODE]
|
code_ = config[CONF_CODE]
|
||||||
if cg.is_template(code_):
|
if cg.is_template(code_):
|
||||||
|
@ -369,6 +371,8 @@ def raw_action(var, config, args):
|
||||||
code_ = config[CONF_CODE]
|
code_ = config[CONF_CODE]
|
||||||
arr = cg.progmem_array(config[CONF_CODE_STORAGE_ID], code_)
|
arr = cg.progmem_array(config[CONF_CODE_STORAGE_ID], code_)
|
||||||
cg.add(var.set_code_static(arr, len(code_)))
|
cg.add(var.set_code_static(arr, len(code_)))
|
||||||
|
templ = yield cg.templatable(config[CONF_CARRIER_FREQUENCY], args, cg.uint32)
|
||||||
|
cg.add(var.set_carrier_frequency(templ))
|
||||||
|
|
||||||
|
|
||||||
# RC5
|
# RC5
|
||||||
|
@ -410,7 +414,7 @@ def rc5_action(var, config, args):
|
||||||
RC_SWITCH_TIMING_SCHEMA = cv.All([cv.uint8_t], cv.Length(min=2, max=2))
|
RC_SWITCH_TIMING_SCHEMA = cv.All([cv.uint8_t], cv.Length(min=2, max=2))
|
||||||
|
|
||||||
RC_SWITCH_PROTOCOL_SCHEMA = cv.Any(
|
RC_SWITCH_PROTOCOL_SCHEMA = cv.Any(
|
||||||
cv.All(cv.Coerce(int), cv.Range(min=1, max=7)),
|
cv.int_range(min=1, max=7),
|
||||||
cv.Schema({
|
cv.Schema({
|
||||||
cv.Required(CONF_PULSE_LENGTH): cv.uint32_t,
|
cv.Required(CONF_PULSE_LENGTH): cv.uint32_t,
|
||||||
cv.Optional(CONF_SYNC, default=[1, 31]): RC_SWITCH_TIMING_SCHEMA,
|
cv.Optional(CONF_SYNC, default=[1, 31]): RC_SWITCH_TIMING_SCHEMA,
|
||||||
|
@ -457,25 +461,32 @@ RC_SWITCH_TYPE_A_SCHEMA = cv.Schema({
|
||||||
cv.Optional(CONF_PROTOCOL, default=1): RC_SWITCH_PROTOCOL_SCHEMA,
|
cv.Optional(CONF_PROTOCOL, default=1): RC_SWITCH_PROTOCOL_SCHEMA,
|
||||||
})
|
})
|
||||||
RC_SWITCH_TYPE_B_SCHEMA = cv.Schema({
|
RC_SWITCH_TYPE_B_SCHEMA = cv.Schema({
|
||||||
cv.Required(CONF_ADDRESS): cv.All(cv.uint8_t, cv.Range(min=1, max=4)),
|
cv.Required(CONF_ADDRESS): cv.int_range(min=1, max=4),
|
||||||
cv.Required(CONF_CHANNEL): cv.All(cv.uint8_t, cv.Range(min=1, max=4)),
|
cv.Required(CONF_CHANNEL): cv.int_range(min=1, max=4),
|
||||||
cv.Required(CONF_STATE): cv.boolean,
|
cv.Required(CONF_STATE): cv.boolean,
|
||||||
cv.Optional(CONF_PROTOCOL, default=1): RC_SWITCH_PROTOCOL_SCHEMA,
|
cv.Optional(CONF_PROTOCOL, default=1): RC_SWITCH_PROTOCOL_SCHEMA,
|
||||||
})
|
})
|
||||||
RC_SWITCH_TYPE_C_SCHEMA = cv.Schema({
|
RC_SWITCH_TYPE_C_SCHEMA = cv.Schema({
|
||||||
cv.Required(CONF_FAMILY): cv.one_of('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k',
|
cv.Required(CONF_FAMILY): cv.one_of('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k',
|
||||||
'l', 'm', 'n', 'o', 'p', lower=True),
|
'l', 'm', 'n', 'o', 'p', lower=True),
|
||||||
cv.Required(CONF_GROUP): cv.All(cv.uint8_t, cv.Range(min=1, max=4)),
|
cv.Required(CONF_GROUP): cv.int_range(min=1, max=4),
|
||||||
cv.Required(CONF_DEVICE): cv.All(cv.uint8_t, cv.Range(min=1, max=4)),
|
cv.Required(CONF_DEVICE): cv.int_range(min=1, max=4),
|
||||||
cv.Required(CONF_STATE): cv.boolean,
|
cv.Required(CONF_STATE): cv.boolean,
|
||||||
cv.Optional(CONF_PROTOCOL, default=1): RC_SWITCH_PROTOCOL_SCHEMA,
|
cv.Optional(CONF_PROTOCOL, default=1): RC_SWITCH_PROTOCOL_SCHEMA,
|
||||||
})
|
})
|
||||||
RC_SWITCH_TYPE_D_SCHEMA = cv.Schema({
|
RC_SWITCH_TYPE_D_SCHEMA = cv.Schema({
|
||||||
cv.Required(CONF_GROUP): cv.one_of('a', 'b', 'c', 'd', lower=True),
|
cv.Required(CONF_GROUP): cv.one_of('a', 'b', 'c', 'd', lower=True),
|
||||||
cv.Required(CONF_DEVICE): cv.All(cv.uint8_t, cv.Range(min=1, max=3)),
|
cv.Required(CONF_DEVICE): cv.int_range(min=1, max=3),
|
||||||
cv.Required(CONF_STATE): cv.boolean,
|
cv.Required(CONF_STATE): cv.boolean,
|
||||||
cv.Optional(CONF_PROTOCOL, default=1): RC_SWITCH_PROTOCOL_SCHEMA,
|
cv.Optional(CONF_PROTOCOL, default=1): RC_SWITCH_PROTOCOL_SCHEMA,
|
||||||
})
|
})
|
||||||
|
RC_SWITCH_TRANSMITTER = cv.Schema({
|
||||||
|
cv.Optional(CONF_REPEAT, default={CONF_TIMES: 5}): cv.Schema({
|
||||||
|
cv.Required(CONF_TIMES): cv.templatable(cv.positive_int),
|
||||||
|
cv.Optional(CONF_WAIT_TIME, default='10ms'):
|
||||||
|
cv.templatable(cv.positive_time_period_milliseconds),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
rc_switch_protocols = ns.rc_switch_protocols
|
rc_switch_protocols = ns.rc_switch_protocols
|
||||||
RCSwitchBase = ns.class_('RCSwitchBase')
|
RCSwitchBase = ns.class_('RCSwitchBase')
|
||||||
|
@ -494,7 +505,8 @@ def rc_switch_raw_binary_sensor(var, config):
|
||||||
cg.add(var.set_code(config[CONF_CODE]))
|
cg.add(var.set_code(config[CONF_CODE]))
|
||||||
|
|
||||||
|
|
||||||
@register_action('rc_switch_raw', RCSwitchRawAction, RC_SWITCH_RAW_SCHEMA)
|
@register_action('rc_switch_raw', RCSwitchRawAction,
|
||||||
|
RC_SWITCH_RAW_SCHEMA.extend(RC_SWITCH_TRANSMITTER))
|
||||||
def rc_switch_raw_action(var, config, args):
|
def rc_switch_raw_action(var, config, args):
|
||||||
proto = yield cg.templatable(config[CONF_PROTOCOL], args, RCSwitchBase,
|
proto = yield cg.templatable(config[CONF_PROTOCOL], args, RCSwitchBase,
|
||||||
to_exp=build_rc_switch_protocol)
|
to_exp=build_rc_switch_protocol)
|
||||||
|
@ -508,7 +520,8 @@ def rc_switch_type_a_binary_sensor(var, config):
|
||||||
cg.add(var.set_type_a(config[CONF_GROUP], config[CONF_DEVICE], config[CONF_STATE]))
|
cg.add(var.set_type_a(config[CONF_GROUP], config[CONF_DEVICE], config[CONF_STATE]))
|
||||||
|
|
||||||
|
|
||||||
@register_action('rc_switch_type_a', RCSwitchTypeAAction, RC_SWITCH_TYPE_A_SCHEMA)
|
@register_action('rc_switch_type_a', RCSwitchTypeAAction,
|
||||||
|
RC_SWITCH_TYPE_A_SCHEMA.extend(RC_SWITCH_TRANSMITTER))
|
||||||
def rc_switch_type_a_action(var, config, args):
|
def rc_switch_type_a_action(var, config, args):
|
||||||
proto = yield cg.templatable(config[CONF_PROTOCOL], args, RCSwitchBase,
|
proto = yield cg.templatable(config[CONF_PROTOCOL], args, RCSwitchBase,
|
||||||
to_exp=build_rc_switch_protocol)
|
to_exp=build_rc_switch_protocol)
|
||||||
|
@ -524,7 +537,8 @@ def rc_switch_type_b_binary_sensor(var, config):
|
||||||
cg.add(var.set_type_b(config[CONF_ADDRESS], config[CONF_CHANNEL], config[CONF_STATE]))
|
cg.add(var.set_type_b(config[CONF_ADDRESS], config[CONF_CHANNEL], config[CONF_STATE]))
|
||||||
|
|
||||||
|
|
||||||
@register_action('rc_switch_type_b', RCSwitchTypeBAction, RC_SWITCH_TYPE_B_SCHEMA)
|
@register_action('rc_switch_type_b', RCSwitchTypeBAction,
|
||||||
|
RC_SWITCH_TYPE_B_SCHEMA.extend(RC_SWITCH_TRANSMITTER))
|
||||||
def rc_switch_type_b_action(var, config, args):
|
def rc_switch_type_b_action(var, config, args):
|
||||||
proto = yield cg.templatable(config[CONF_PROTOCOL], args, RCSwitchBase,
|
proto = yield cg.templatable(config[CONF_PROTOCOL], args, RCSwitchBase,
|
||||||
to_exp=build_rc_switch_protocol)
|
to_exp=build_rc_switch_protocol)
|
||||||
|
@ -541,7 +555,8 @@ def rc_switch_type_c_binary_sensor(var, config):
|
||||||
config[CONF_STATE]))
|
config[CONF_STATE]))
|
||||||
|
|
||||||
|
|
||||||
@register_action('rc_switch_type_c', RCSwitchTypeCAction, RC_SWITCH_TYPE_C_SCHEMA)
|
@register_action('rc_switch_type_c', RCSwitchTypeCAction,
|
||||||
|
RC_SWITCH_TYPE_C_SCHEMA.extend(RC_SWITCH_TRANSMITTER))
|
||||||
def rc_switch_type_c_action(var, config, args):
|
def rc_switch_type_c_action(var, config, args):
|
||||||
proto = yield cg.templatable(config[CONF_PROTOCOL], args, RCSwitchBase,
|
proto = yield cg.templatable(config[CONF_PROTOCOL], args, RCSwitchBase,
|
||||||
to_exp=build_rc_switch_protocol)
|
to_exp=build_rc_switch_protocol)
|
||||||
|
@ -552,13 +567,15 @@ def rc_switch_type_c_action(var, config, args):
|
||||||
cg.add(var.set_state((yield cg.templatable(config[CONF_STATE], args, bool))))
|
cg.add(var.set_state((yield cg.templatable(config[CONF_STATE], args, bool))))
|
||||||
|
|
||||||
|
|
||||||
@register_binary_sensor('rc_switch_type_d', RCSwitchRawReceiver, RC_SWITCH_TYPE_D_SCHEMA)
|
@register_binary_sensor('rc_switch_type_d', RCSwitchRawReceiver,
|
||||||
|
RC_SWITCH_TYPE_D_SCHEMA.extend(RC_SWITCH_TRANSMITTER))
|
||||||
def rc_switch_type_d_binary_sensor(var, config):
|
def rc_switch_type_d_binary_sensor(var, config):
|
||||||
cg.add(var.set_protocol(build_rc_switch_protocol(config[CONF_PROTOCOL])))
|
cg.add(var.set_protocol(build_rc_switch_protocol(config[CONF_PROTOCOL])))
|
||||||
cg.add(var.set_type_d(config[CONF_GROUP], config[CONF_DEVICE], config[CONF_STATE]))
|
cg.add(var.set_type_d(config[CONF_GROUP], config[CONF_DEVICE], config[CONF_STATE]))
|
||||||
|
|
||||||
|
|
||||||
@register_action('rc_switch_type_d', RCSwitchTypeDAction, RC_SWITCH_TYPE_D_SCHEMA)
|
@register_action('rc_switch_type_d', RCSwitchTypeDAction,
|
||||||
|
RC_SWITCH_TYPE_D_SCHEMA.extend(RC_SWITCH_TRANSMITTER))
|
||||||
def rc_switch_type_d_action(var, config, args):
|
def rc_switch_type_d_action(var, config, args):
|
||||||
proto = yield cg.templatable(config[CONF_PROTOCOL], args, RCSwitchBase,
|
proto = yield cg.templatable(config[CONF_PROTOCOL], args, RCSwitchBase,
|
||||||
to_exp=build_rc_switch_protocol)
|
to_exp=build_rc_switch_protocol)
|
||||||
|
|
|
@ -6,7 +6,7 @@ namespace remote_base {
|
||||||
|
|
||||||
static const char *TAG = "remote.raw";
|
static const char *TAG = "remote.raw";
|
||||||
|
|
||||||
void RawDumper::dump(RemoteReceiveData src) {
|
bool RawDumper::dump(RemoteReceiveData src) {
|
||||||
char buffer[256];
|
char buffer[256];
|
||||||
uint32_t buffer_offset = 0;
|
uint32_t buffer_offset = 0;
|
||||||
buffer_offset += sprintf(buffer, "Received Raw: ");
|
buffer_offset += sprintf(buffer, "Received Raw: ");
|
||||||
|
@ -16,7 +16,7 @@ void RawDumper::dump(RemoteReceiveData src) {
|
||||||
const uint32_t remaining_length = sizeof(buffer) - buffer_offset;
|
const uint32_t remaining_length = sizeof(buffer) - buffer_offset;
|
||||||
int written;
|
int written;
|
||||||
|
|
||||||
if (i + 1 < src.size()) {
|
if (i + 1 < src.size() - 1) {
|
||||||
written = snprintf(buffer + buffer_offset, remaining_length, "%d, ", value);
|
written = snprintf(buffer + buffer_offset, remaining_length, "%d, ", value);
|
||||||
} else {
|
} else {
|
||||||
written = snprintf(buffer + buffer_offset, remaining_length, "%d", value);
|
written = snprintf(buffer + buffer_offset, remaining_length, "%d", value);
|
||||||
|
@ -40,6 +40,7 @@ void RawDumper::dump(RemoteReceiveData src) {
|
||||||
if (buffer_offset != 0) {
|
if (buffer_offset != 0) {
|
||||||
ESP_LOGD(TAG, "%s", buffer);
|
ESP_LOGD(TAG, "%s", buffer);
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace remote_base
|
} // namespace remote_base
|
||||||
|
|
|
@ -44,9 +44,9 @@ template<typename... Ts> class RawAction : public RemoteTransmitterActionBase<Ts
|
||||||
this->code_static_ = code;
|
this->code_static_ = code;
|
||||||
this->code_static_len_ = len;
|
this->code_static_len_ = len;
|
||||||
}
|
}
|
||||||
|
TEMPLATABLE_VALUE(uint32_t, carrier_frequency);
|
||||||
|
|
||||||
void encode(RemoteTransmitData *dst, Ts... x) override {
|
void encode(RemoteTransmitData *dst, Ts... x) override {
|
||||||
// dst->set_data(data);
|
|
||||||
if (this->code_static_ != nullptr) {
|
if (this->code_static_ != nullptr) {
|
||||||
for (size_t i = 0; i < this->code_static_len_; i++) {
|
for (size_t i = 0; i < this->code_static_len_; i++) {
|
||||||
auto val = this->code_static_[i];
|
auto val = this->code_static_[i];
|
||||||
|
@ -58,6 +58,7 @@ template<typename... Ts> class RawAction : public RemoteTransmitterActionBase<Ts
|
||||||
} else {
|
} else {
|
||||||
dst->set_data(this->code_func_(x...));
|
dst->set_data(this->code_func_(x...));
|
||||||
}
|
}
|
||||||
|
dst->set_carrier_frequency(this->carrier_frequency_.value(x...));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -68,7 +69,8 @@ template<typename... Ts> class RawAction : public RemoteTransmitterActionBase<Ts
|
||||||
|
|
||||||
class RawDumper : public RemoteReceiverDumperBase {
|
class RawDumper : public RemoteReceiverDumperBase {
|
||||||
public:
|
public:
|
||||||
void dump(RemoteReceiveData src) override;
|
bool dump(RemoteReceiveData src) override;
|
||||||
|
bool is_secondary() override { return true; }
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace remote_base
|
} // namespace remote_base
|
||||||
|
|
|
@ -53,6 +53,7 @@ void RCSwitchBase::sync(RemoteTransmitData *dst) const {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void RCSwitchBase::transmit(RemoteTransmitData *dst, uint32_t code, uint8_t len) const {
|
void RCSwitchBase::transmit(RemoteTransmitData *dst, uint32_t code, uint8_t len) const {
|
||||||
|
dst->set_carrier_frequency(0);
|
||||||
for (int16_t i = len - 1; i >= 0; i--) {
|
for (int16_t i = len - 1; i >= 0; i--) {
|
||||||
if (code & (1 << i))
|
if (code & (1 << i))
|
||||||
this->one(dst);
|
this->one(dst);
|
||||||
|
@ -224,21 +225,25 @@ bool RCSwitchRawReceiver::matches(RemoteReceiveData src) {
|
||||||
|
|
||||||
return decoded_nbits == this->nbits_ && decoded_code == this->code_;
|
return decoded_nbits == this->nbits_ && decoded_code == this->code_;
|
||||||
}
|
}
|
||||||
void RCSwitchDumper::dump(RemoteReceiveData src) {
|
bool RCSwitchDumper::dump(RemoteReceiveData src) {
|
||||||
for (uint8_t i = 1; i <= 7; i++) {
|
for (uint8_t i = 1; i <= 7; i++) {
|
||||||
src.reset();
|
src.reset();
|
||||||
uint32_t out_data;
|
uint32_t out_data;
|
||||||
uint8_t out_nbits;
|
uint8_t out_nbits;
|
||||||
RCSwitchBase *protocol = &rc_switch_protocols[i];
|
RCSwitchBase *protocol = &rc_switch_protocols[i];
|
||||||
if (protocol->decode(src, &out_data, &out_nbits)) {
|
if (protocol->decode(src, &out_data, &out_nbits) && out_nbits >= 3) {
|
||||||
char buffer[32];
|
char buffer[32];
|
||||||
for (uint8_t j = 0; j < out_nbits; j++)
|
for (uint8_t j = 0; j < out_nbits; j++)
|
||||||
buffer[j] = (out_data & (1 << (out_nbits - j - 1))) ? '1' : '0';
|
buffer[j] = (out_data & (1 << (out_nbits - j - 1))) ? '1' : '0';
|
||||||
|
|
||||||
buffer[out_nbits] = '\0';
|
buffer[out_nbits] = '\0';
|
||||||
ESP_LOGD(TAG, "Received RCSwitch Raw: protocol=%u data='%s'", i, buffer);
|
ESP_LOGD(TAG, "Received RCSwitch Raw: protocol=%u data='%s'", i, buffer);
|
||||||
|
|
||||||
|
// only send first decoded protocol
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace remote_base
|
} // namespace remote_base
|
||||||
|
|
|
@ -197,7 +197,7 @@ class RCSwitchRawReceiver : public RemoteReceiverBinarySensorBase {
|
||||||
|
|
||||||
class RCSwitchDumper : public RemoteReceiverDumperBase {
|
class RCSwitchDumper : public RemoteReceiverDumperBase {
|
||||||
public:
|
public:
|
||||||
void dump(RemoteReceiveData src) override;
|
bool dump(RemoteReceiveData src) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace remote_base
|
} // namespace remote_base
|
||||||
|
|
|
@ -212,14 +212,21 @@ class RemoteReceiverListener {
|
||||||
|
|
||||||
class RemoteReceiverDumperBase {
|
class RemoteReceiverDumperBase {
|
||||||
public:
|
public:
|
||||||
virtual void dump(RemoteReceiveData src) = 0;
|
virtual bool dump(RemoteReceiveData src) = 0;
|
||||||
|
virtual bool is_secondary() { return false; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class RemoteReceiverBase : public RemoteComponentBase {
|
class RemoteReceiverBase : public RemoteComponentBase {
|
||||||
public:
|
public:
|
||||||
RemoteReceiverBase(GPIOPin *pin) : RemoteComponentBase(pin) {}
|
RemoteReceiverBase(GPIOPin *pin) : RemoteComponentBase(pin) {}
|
||||||
void register_listener(RemoteReceiverListener *listener) { this->listeners_.push_back(listener); }
|
void register_listener(RemoteReceiverListener *listener) { this->listeners_.push_back(listener); }
|
||||||
void register_dumper(RemoteReceiverDumperBase *dumper) { this->dumpers_.push_back(dumper); }
|
void register_dumper(RemoteReceiverDumperBase *dumper) {
|
||||||
|
if (dumper->is_secondary()) {
|
||||||
|
this->secondary_dumpers_.push_back(dumper);
|
||||||
|
} else {
|
||||||
|
this->dumpers_.push_back(dumper);
|
||||||
|
}
|
||||||
|
}
|
||||||
void set_tolerance(uint8_t tolerance) { tolerance_ = tolerance; }
|
void set_tolerance(uint8_t tolerance) { tolerance_ = tolerance; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -233,11 +240,19 @@ class RemoteReceiverBase : public RemoteComponentBase {
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
void call_dumpers_() {
|
void call_dumpers_() {
|
||||||
|
bool success = false;
|
||||||
for (auto *dumper : this->dumpers_) {
|
for (auto *dumper : this->dumpers_) {
|
||||||
|
auto data = RemoteReceiveData(&this->temp_, this->tolerance_);
|
||||||
|
if (dumper->dump(data))
|
||||||
|
success = true;
|
||||||
|
}
|
||||||
|
if (!success) {
|
||||||
|
for (auto *dumper : this->secondary_dumpers_) {
|
||||||
auto data = RemoteReceiveData(&this->temp_, this->tolerance_);
|
auto data = RemoteReceiveData(&this->temp_, this->tolerance_);
|
||||||
dumper->dump(data);
|
dumper->dump(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
void call_listeners_dumpers_() {
|
void call_listeners_dumpers_() {
|
||||||
if (this->call_listeners_())
|
if (this->call_listeners_())
|
||||||
return;
|
return;
|
||||||
|
@ -247,6 +262,7 @@ class RemoteReceiverBase : public RemoteComponentBase {
|
||||||
|
|
||||||
std::vector<RemoteReceiverListener *> listeners_;
|
std::vector<RemoteReceiverListener *> listeners_;
|
||||||
std::vector<RemoteReceiverDumperBase *> dumpers_;
|
std::vector<RemoteReceiverDumperBase *> dumpers_;
|
||||||
|
std::vector<RemoteReceiverDumperBase *> secondary_dumpers_;
|
||||||
std::vector<int32_t> temp_;
|
std::vector<int32_t> temp_;
|
||||||
uint8_t tolerance_{25};
|
uint8_t tolerance_{25};
|
||||||
};
|
};
|
||||||
|
@ -323,12 +339,13 @@ template<typename... Ts> class RemoteTransmitterActionBase : public Action<Ts...
|
||||||
|
|
||||||
template<typename T, typename D> class RemoteReceiverDumper : public RemoteReceiverDumperBase {
|
template<typename T, typename D> class RemoteReceiverDumper : public RemoteReceiverDumperBase {
|
||||||
public:
|
public:
|
||||||
void dump(RemoteReceiveData src) override {
|
bool dump(RemoteReceiveData src) override {
|
||||||
auto proto = T();
|
auto proto = T();
|
||||||
auto decoded = proto.decode(src);
|
auto decoded = proto.decode(src);
|
||||||
if (!decoded.has_value())
|
if (!decoded.has_value())
|
||||||
return;
|
return false;
|
||||||
proto.dump(*decoded);
|
proto.dump(*decoded);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -53,6 +53,10 @@ void RemoteReceiverComponent::setup() {
|
||||||
void RemoteReceiverComponent::dump_config() {
|
void RemoteReceiverComponent::dump_config() {
|
||||||
ESP_LOGCONFIG(TAG, "Remote Receiver:");
|
ESP_LOGCONFIG(TAG, "Remote Receiver:");
|
||||||
LOG_PIN(" Pin: ", this->pin_);
|
LOG_PIN(" Pin: ", this->pin_);
|
||||||
|
if (this->pin_->digital_read()) {
|
||||||
|
ESP_LOGW(TAG, "Remote Receiver Signal starts with a HIGH value. Usually this means you have to "
|
||||||
|
"invert the signal using 'inverted: True' in the pin schema!");
|
||||||
|
}
|
||||||
ESP_LOGCONFIG(TAG, " Channel: %d", this->channel_);
|
ESP_LOGCONFIG(TAG, " Channel: %d", this->channel_);
|
||||||
ESP_LOGCONFIG(TAG, " Clock divider: %u", this->clock_divider_);
|
ESP_LOGCONFIG(TAG, " Clock divider: %u", this->clock_divider_);
|
||||||
ESP_LOGCONFIG(TAG, " Tolerance: %u%%", this->tolerance_);
|
ESP_LOGCONFIG(TAG, " Tolerance: %u%%", this->tolerance_);
|
||||||
|
|
|
@ -33,7 +33,7 @@ CONFIG_SCHEMA = cv.All(cv.Schema({
|
||||||
sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_CHEMICAL_WEAPON, 1),
|
sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_CHEMICAL_WEAPON, 1),
|
||||||
|
|
||||||
cv.Optional(CONF_RX_ONLY, default=False): cv.boolean,
|
cv.Optional(CONF_RX_ONLY, default=False): cv.boolean,
|
||||||
cv.Optional(CONF_UPDATE_INTERVAL, default='0min'): cv.positive_time_period_minutes,
|
cv.Optional(CONF_UPDATE_INTERVAL): cv.positive_time_period_minutes,
|
||||||
}).extend(cv.COMPONENT_SCHEMA).extend(uart.UART_DEVICE_SCHEMA), validate_sds011_rx_mode)
|
}).extend(cv.COMPONENT_SCHEMA).extend(uart.UART_DEVICE_SCHEMA), validate_sds011_rx_mode)
|
||||||
|
|
||||||
|
|
||||||
|
@ -42,6 +42,7 @@ def to_code(config):
|
||||||
yield cg.register_component(var, config)
|
yield cg.register_component(var, config)
|
||||||
yield uart.register_uart_device(var, config)
|
yield uart.register_uart_device(var, config)
|
||||||
|
|
||||||
|
if CONF_UPDATE_INTERVAL in config:
|
||||||
cg.add(var.set_update_interval_min(config[CONF_UPDATE_INTERVAL]))
|
cg.add(var.set_update_interval_min(config[CONF_UPDATE_INTERVAL]))
|
||||||
cg.add(var.set_rx_mode_only(config[CONF_RX_ONLY]))
|
cg.add(var.set_rx_mode_only(config[CONF_RX_ONLY]))
|
||||||
|
|
||||||
|
|
|
@ -148,7 +148,7 @@ def exponential_moving_average_filter_to_code(config, filter_id):
|
||||||
yield cg.new_Pvariable(filter_id, config[CONF_ALPHA], config[CONF_SEND_EVERY])
|
yield cg.new_Pvariable(filter_id, config[CONF_ALPHA], config[CONF_SEND_EVERY])
|
||||||
|
|
||||||
|
|
||||||
@FILTER_REGISTRY.register('lambda', LambdaFilter, cv.lambda_)
|
@FILTER_REGISTRY.register('lambda', LambdaFilter, cv.returning_lambda)
|
||||||
def lambda_filter_to_code(config, filter_id):
|
def lambda_filter_to_code(config, filter_id):
|
||||||
lambda_ = yield cg.process_lambda(config, [(float, 'x')],
|
lambda_ = yield cg.process_lambda(config, [(float, 'x')],
|
||||||
return_type=cg.optional.template(float))
|
return_type=cg.optional.template(float))
|
||||||
|
|
|
@ -88,10 +88,8 @@ std::string Sensor::unique_id() { return ""; }
|
||||||
void Sensor::internal_send_state_to_frontend(float state) {
|
void Sensor::internal_send_state_to_frontend(float state) {
|
||||||
this->has_state_ = true;
|
this->has_state_ = true;
|
||||||
this->state = state;
|
this->state = state;
|
||||||
if (this->filter_list_ != nullptr) {
|
|
||||||
ESP_LOGD(TAG, "'%s': Sending state %.5f %s with %d decimals of accuracy", this->get_name().c_str(), state,
|
ESP_LOGD(TAG, "'%s': Sending state %.5f %s with %d decimals of accuracy", this->get_name().c_str(), state,
|
||||||
this->get_unit_of_measurement().c_str(), this->get_accuracy_decimals());
|
this->get_unit_of_measurement().c_str(), this->get_accuracy_decimals());
|
||||||
}
|
|
||||||
this->callback_.call(state);
|
this->callback_.call(state);
|
||||||
}
|
}
|
||||||
bool Sensor::has_state() const { return this->has_state_; }
|
bool Sensor::has_state() const { return this->has_state_; }
|
||||||
|
|
103
esphome/components/sun/__init__.py
Normal file
103
esphome/components/sun/__init__.py
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome import automation
|
||||||
|
from esphome.components import time
|
||||||
|
from esphome.const import CONF_TIME_ID, CONF_ID, CONF_TRIGGER_ID
|
||||||
|
|
||||||
|
sun_ns = cg.esphome_ns.namespace('sun')
|
||||||
|
|
||||||
|
Sun = sun_ns.class_('Sun')
|
||||||
|
SunTrigger = sun_ns.class_('SunTrigger', cg.PollingComponent, automation.Trigger.template())
|
||||||
|
SunCondition = sun_ns.class_('SunCondition', automation.Condition)
|
||||||
|
|
||||||
|
CONF_SUN_ID = 'sun_id'
|
||||||
|
CONF_LATITUDE = 'latitude'
|
||||||
|
CONF_LONGITUDE = 'longitude'
|
||||||
|
CONF_ELEVATION = 'elevation'
|
||||||
|
CONF_ON_SUNRISE = 'on_sunrise'
|
||||||
|
CONF_ON_SUNSET = 'on_sunset'
|
||||||
|
|
||||||
|
ELEVATION_MAP = {
|
||||||
|
'sunrise': 0.0,
|
||||||
|
'sunset': 0.0,
|
||||||
|
'civil': -6.0,
|
||||||
|
'nautical': -12.0,
|
||||||
|
'astronomical': -18.0,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def elevation(value):
|
||||||
|
if isinstance(value, str):
|
||||||
|
try:
|
||||||
|
value = ELEVATION_MAP[cv.one_of(*ELEVATION_MAP, lower=True, space='_')]
|
||||||
|
except cv.Invalid:
|
||||||
|
pass
|
||||||
|
value = cv.angle(value)
|
||||||
|
return cv.float_range(min=-180, max=180)(value)
|
||||||
|
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = cv.Schema({
|
||||||
|
cv.GenerateID(): cv.declare_id(Sun),
|
||||||
|
cv.GenerateID(CONF_TIME_ID): cv.use_id(time.RealTimeClock),
|
||||||
|
cv.Required(CONF_LATITUDE): cv.float_range(min=-90, max=90),
|
||||||
|
cv.Required(CONF_LONGITUDE): cv.float_range(min=-180, max=180),
|
||||||
|
|
||||||
|
cv.Optional(CONF_ON_SUNRISE): automation.validate_automation({
|
||||||
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SunTrigger),
|
||||||
|
cv.Optional(CONF_ELEVATION, default=0.0): elevation,
|
||||||
|
}),
|
||||||
|
cv.Optional(CONF_ON_SUNSET): automation.validate_automation({
|
||||||
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SunTrigger),
|
||||||
|
cv.Optional(CONF_ELEVATION, default=0.0): elevation,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
def to_code(config):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
time_ = yield cg.get_variable(config[CONF_TIME_ID])
|
||||||
|
cg.add(var.set_time(time_))
|
||||||
|
cg.add(var.set_latitude(config[CONF_LATITUDE]))
|
||||||
|
cg.add(var.set_longitude(config[CONF_LONGITUDE]))
|
||||||
|
|
||||||
|
for conf in config.get(CONF_ON_SUNRISE, []):
|
||||||
|
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID])
|
||||||
|
yield cg.register_component(trigger, conf)
|
||||||
|
yield cg.register_parented(trigger, var)
|
||||||
|
cg.add(trigger.set_sunrise(True))
|
||||||
|
cg.add(trigger.set_elevation(conf[CONF_ELEVATION]))
|
||||||
|
yield automation.build_automation(trigger, [], conf)
|
||||||
|
|
||||||
|
for conf in config.get(CONF_ON_SUNSET, []):
|
||||||
|
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID])
|
||||||
|
yield cg.register_component(trigger, conf)
|
||||||
|
yield cg.register_parented(trigger, var)
|
||||||
|
cg.add(trigger.set_sunrise(False))
|
||||||
|
cg.add(trigger.set_elevation(conf[CONF_ELEVATION]))
|
||||||
|
yield automation.build_automation(trigger, [], conf)
|
||||||
|
|
||||||
|
|
||||||
|
@automation.register_condition('sun.is_above_horizon', SunCondition, cv.Schema({
|
||||||
|
cv.GenerateID(): cv.use_id(Sun),
|
||||||
|
cv.Optional(CONF_ELEVATION, default=0): cv.templatable(elevation),
|
||||||
|
}))
|
||||||
|
def sun_above_horizon_to_code(config, condition_id, template_arg, args):
|
||||||
|
var = cg.new_Pvariable(condition_id, template_arg)
|
||||||
|
yield cg.register_parented(var, config[CONF_ID])
|
||||||
|
templ = yield cg.templatable(config[CONF_ELEVATION], args, cg.double)
|
||||||
|
cg.add(var.set_elevation(templ))
|
||||||
|
cg.add(var.set_above(True))
|
||||||
|
yield var
|
||||||
|
|
||||||
|
|
||||||
|
@automation.register_condition('sun.is_below_horizon', SunCondition, cv.Schema({
|
||||||
|
cv.GenerateID(): cv.use_id(Sun),
|
||||||
|
cv.Optional(CONF_ELEVATION, default=0): cv.templatable(elevation),
|
||||||
|
}))
|
||||||
|
def sun_below_horizon_to_code(config, condition_id, template_arg, args):
|
||||||
|
var = cg.new_Pvariable(condition_id, template_arg)
|
||||||
|
yield cg.register_parented(var, config[CONF_ID])
|
||||||
|
templ = yield cg.templatable(config[CONF_ELEVATION], args, cg.double)
|
||||||
|
cg.add(var.set_elevation(templ))
|
||||||
|
cg.add(var.set_above(False))
|
||||||
|
yield var
|
30
esphome/components/sun/sensor/__init__.py
Normal file
30
esphome/components/sun/sensor/__init__.py
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.components import sensor
|
||||||
|
from esphome.const import UNIT_DEGREES, ICON_WEATHER_SUNSET, CONF_ID, CONF_TYPE
|
||||||
|
from .. import sun_ns, CONF_SUN_ID, Sun
|
||||||
|
|
||||||
|
DEPENDENCIES = ['sun']
|
||||||
|
|
||||||
|
SunSensor = sun_ns.class_('SunSensor', sensor.Sensor, cg.PollingComponent)
|
||||||
|
SensorType = sun_ns.enum('SensorType')
|
||||||
|
TYPES = {
|
||||||
|
'elevation': SensorType.SUN_SENSOR_ELEVATION,
|
||||||
|
'azimuth': SensorType.SUN_SENSOR_AZIMUTH,
|
||||||
|
}
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = sensor.sensor_schema(UNIT_DEGREES, ICON_WEATHER_SUNSET, 1).extend({
|
||||||
|
cv.GenerateID(): cv.declare_id(SunSensor),
|
||||||
|
cv.GenerateID(CONF_SUN_ID): cv.use_id(Sun),
|
||||||
|
cv.Required(CONF_TYPE): cv.enum(TYPES, lower=True),
|
||||||
|
}).extend(cv.polling_component_schema('60s'))
|
||||||
|
|
||||||
|
|
||||||
|
def to_code(config):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
yield cg.register_component(var, config)
|
||||||
|
yield sensor.register_sensor(var, config)
|
||||||
|
|
||||||
|
cg.add(var.set_type(config[CONF_TYPE]))
|
||||||
|
paren = yield cg.get_variable(config[CONF_SUN_ID])
|
||||||
|
cg.add(var.set_parent(paren))
|
12
esphome/components/sun/sensor/sun_sensor.cpp
Normal file
12
esphome/components/sun/sensor/sun_sensor.cpp
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
#include "sun_sensor.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace sun {
|
||||||
|
|
||||||
|
static const char *TAG = "sun.sensor";
|
||||||
|
|
||||||
|
void SunSensor::dump_config() { LOG_SENSOR("", "Sun Sensor", this); }
|
||||||
|
|
||||||
|
} // namespace sun
|
||||||
|
} // namespace esphome
|
41
esphome/components/sun/sensor/sun_sensor.h
Normal file
41
esphome/components/sun/sensor/sun_sensor.h
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/components/sun/sun.h"
|
||||||
|
#include "esphome/components/sensor/sensor.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace sun {
|
||||||
|
|
||||||
|
enum SensorType {
|
||||||
|
SUN_SENSOR_ELEVATION,
|
||||||
|
SUN_SENSOR_AZIMUTH,
|
||||||
|
};
|
||||||
|
|
||||||
|
class SunSensor : public sensor::Sensor, public PollingComponent {
|
||||||
|
public:
|
||||||
|
void set_parent(Sun *parent) { parent_ = parent; }
|
||||||
|
void set_type(SensorType type) { type_ = type; }
|
||||||
|
void dump_config() override;
|
||||||
|
void update() override {
|
||||||
|
double val;
|
||||||
|
switch (this->type_) {
|
||||||
|
case SUN_SENSOR_ELEVATION:
|
||||||
|
val = this->parent_->elevation();
|
||||||
|
break;
|
||||||
|
case SUN_SENSOR_AZIMUTH:
|
||||||
|
val = this->parent_->azimuth();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this->publish_state(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
sun::Sun *parent_;
|
||||||
|
SensorType type_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace sun
|
||||||
|
} // namespace esphome
|
168
esphome/components/sun/sun.cpp
Normal file
168
esphome/components/sun/sun.cpp
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
#include "sun.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace sun {
|
||||||
|
|
||||||
|
static const char *TAG = "sun";
|
||||||
|
|
||||||
|
#undef PI
|
||||||
|
|
||||||
|
/* Usually, ESPHome uses single-precision floating point values
|
||||||
|
* because those tend to be accurate enough and are more efficient.
|
||||||
|
*
|
||||||
|
* However, some of the data in this class has to be quite accurate, so double is
|
||||||
|
* used everywhere.
|
||||||
|
*/
|
||||||
|
static const double PI = 3.141592653589793;
|
||||||
|
static const double TAU = 6.283185307179586;
|
||||||
|
static const double TO_RADIANS = PI / 180.0;
|
||||||
|
static const double TO_DEGREES = 180.0 / PI;
|
||||||
|
static const double EARTH_TILT = 23.44 * TO_RADIANS;
|
||||||
|
|
||||||
|
optional<time::ESPTime> Sun::sunrise(double elevation) {
|
||||||
|
auto time = this->time_->now();
|
||||||
|
if (!time.is_valid())
|
||||||
|
return {};
|
||||||
|
double sun_time = this->sun_time_for_elevation_(time.day_of_year, elevation, true);
|
||||||
|
if (isnan(sun_time))
|
||||||
|
return {};
|
||||||
|
uint32_t epoch = this->calc_epoch_(time, sun_time);
|
||||||
|
return time::ESPTime::from_epoch_local(epoch);
|
||||||
|
}
|
||||||
|
optional<time::ESPTime> Sun::sunset(double elevation) {
|
||||||
|
auto time = this->time_->now();
|
||||||
|
if (!time.is_valid())
|
||||||
|
return {};
|
||||||
|
double sun_time = this->sun_time_for_elevation_(time.day_of_year, elevation, false);
|
||||||
|
if (isnan(sun_time))
|
||||||
|
return {};
|
||||||
|
uint32_t epoch = this->calc_epoch_(time, sun_time);
|
||||||
|
return time::ESPTime::from_epoch_local(epoch);
|
||||||
|
}
|
||||||
|
double Sun::elevation() {
|
||||||
|
auto time = this->current_sun_time_();
|
||||||
|
if (isnan(time))
|
||||||
|
return NAN;
|
||||||
|
return this->elevation_(time);
|
||||||
|
}
|
||||||
|
double Sun::azimuth() {
|
||||||
|
auto time = this->current_sun_time_();
|
||||||
|
if (isnan(time))
|
||||||
|
return NAN;
|
||||||
|
return this->azimuth_(time);
|
||||||
|
}
|
||||||
|
double Sun::sun_declination_(double sun_time) {
|
||||||
|
double n = sun_time - 1.0;
|
||||||
|
// maximum declination
|
||||||
|
const double tot = -sin(EARTH_TILT);
|
||||||
|
|
||||||
|
// eccentricity of the earth's orbit (ellipse)
|
||||||
|
double eccentricity = 0.0167;
|
||||||
|
|
||||||
|
// days since perihelion (January 3rd)
|
||||||
|
double days_since_perihelion = n - 2;
|
||||||
|
// days since december solstice (december 22)
|
||||||
|
double days_since_december_solstice = n + 10;
|
||||||
|
const double c = TAU / 365.24;
|
||||||
|
double v = cos(c * days_since_december_solstice + 2 * eccentricity * sin(c * days_since_perihelion));
|
||||||
|
// Make sure value is in range (double error may lead to results slightly larger than 1)
|
||||||
|
double x = clamp(tot * v, 0, 1);
|
||||||
|
return asin(x);
|
||||||
|
}
|
||||||
|
double Sun::elevation_ratio_(double sun_time) {
|
||||||
|
double decl = this->sun_declination_(sun_time);
|
||||||
|
double hangle = this->hour_angle_(sun_time);
|
||||||
|
double a = sin(this->latitude_rad_()) * sin(decl);
|
||||||
|
double b = cos(this->latitude_rad_()) * cos(decl) * cos(hangle);
|
||||||
|
double val = clamp(a + b, -1.0, 1.0);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
double Sun::latitude_rad_() { return this->latitude_ * TO_RADIANS; }
|
||||||
|
double Sun::hour_angle_(double sun_time) {
|
||||||
|
double time_of_day = fmod(sun_time, 1.0) * 24.0;
|
||||||
|
return -PI * (time_of_day - 12) / 12;
|
||||||
|
}
|
||||||
|
double Sun::elevation_(double sun_time) { return this->elevation_rad_(sun_time) * TO_DEGREES; }
|
||||||
|
double Sun::elevation_rad_(double sun_time) { return asin(this->elevation_ratio_(sun_time)); }
|
||||||
|
double Sun::zenith_rad_(double sun_time) { return acos(this->elevation_ratio_(sun_time)); }
|
||||||
|
double Sun::azimuth_rad_(double sun_time) {
|
||||||
|
double hangle = -this->hour_angle_(sun_time);
|
||||||
|
double decl = this->sun_declination_(sun_time);
|
||||||
|
double zen = this->zenith_rad_(sun_time);
|
||||||
|
double nom = cos(zen) * sin(this->latitude_rad_()) - sin(decl);
|
||||||
|
double denom = sin(zen) * cos(this->latitude_rad_());
|
||||||
|
double v = clamp(nom / denom, -1.0, 1.0);
|
||||||
|
double az = PI - acos(v);
|
||||||
|
if (hangle > 0)
|
||||||
|
az = -az;
|
||||||
|
if (az < 0)
|
||||||
|
az += TAU;
|
||||||
|
return az;
|
||||||
|
}
|
||||||
|
double Sun::azimuth_(double sun_time) { return this->azimuth_rad_(sun_time) * TO_DEGREES; }
|
||||||
|
double Sun::calc_sun_time_(const time::ESPTime &time) {
|
||||||
|
// Time as seen at 0° longitude
|
||||||
|
if (!time.is_valid())
|
||||||
|
return NAN;
|
||||||
|
|
||||||
|
double base = (time.day_of_year + time.hour / 24.0 + time.minute / 24.0 / 60.0 + time.second / 24.0 / 60.0 / 60.0);
|
||||||
|
// Add longitude correction
|
||||||
|
double add = this->longitude_ / 360.0;
|
||||||
|
return base + add;
|
||||||
|
}
|
||||||
|
uint32_t Sun::calc_epoch_(time::ESPTime base, double sun_time) {
|
||||||
|
sun_time -= this->longitude_ / 360.0;
|
||||||
|
base.day_of_year = uint32_t(floor(sun_time));
|
||||||
|
|
||||||
|
sun_time = (sun_time - base.day_of_year) * 24.0;
|
||||||
|
base.hour = uint32_t(floor(sun_time));
|
||||||
|
|
||||||
|
sun_time = (sun_time - base.hour) * 60.0;
|
||||||
|
base.minute = uint32_t(floor(sun_time));
|
||||||
|
|
||||||
|
sun_time = (sun_time - base.minute) * 60.0;
|
||||||
|
base.second = uint32_t(floor(sun_time));
|
||||||
|
|
||||||
|
base.recalc_timestamp_utc(true);
|
||||||
|
return base.timestamp;
|
||||||
|
}
|
||||||
|
double Sun::sun_time_for_elevation_(int32_t day_of_year, double elevation, bool rising) {
|
||||||
|
// Use binary search, newton's method would be better but binary search already
|
||||||
|
// converges quite well (19 cycles) and much simpler. Function is guaranteed to be
|
||||||
|
// monotonous.
|
||||||
|
double lo, hi;
|
||||||
|
if (rising) {
|
||||||
|
lo = day_of_year + 0.0;
|
||||||
|
hi = day_of_year + 0.5;
|
||||||
|
} else {
|
||||||
|
lo = day_of_year + 1.0;
|
||||||
|
hi = day_of_year + 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
double min_elevation = this->elevation_(lo);
|
||||||
|
double max_elevation = this->elevation_(hi);
|
||||||
|
if (elevation < min_elevation || elevation > max_elevation)
|
||||||
|
return NAN;
|
||||||
|
|
||||||
|
// Accuracy: 0.1s
|
||||||
|
const double accuracy = 1.0 / (24.0 * 60.0 * 60.0 * 10.0);
|
||||||
|
|
||||||
|
while (fabs(hi - lo) > accuracy) {
|
||||||
|
double mid = (lo + hi) / 2.0;
|
||||||
|
double value = this->elevation_(mid) - elevation;
|
||||||
|
if (value < 0) {
|
||||||
|
lo = mid;
|
||||||
|
} else if (value > 0) {
|
||||||
|
hi = mid;
|
||||||
|
} else {
|
||||||
|
lo = hi = mid;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (lo + hi) / 2.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace sun
|
||||||
|
} // namespace esphome
|
137
esphome/components/sun/sun.h
Normal file
137
esphome/components/sun/sun.h
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/core/helpers.h"
|
||||||
|
#include "esphome/core/automation.h"
|
||||||
|
#include "esphome/components/time/real_time_clock.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace sun {
|
||||||
|
|
||||||
|
class Sun {
|
||||||
|
public:
|
||||||
|
void set_time(time::RealTimeClock *time) { time_ = time; }
|
||||||
|
time::RealTimeClock *get_time() const { return time_; }
|
||||||
|
void set_latitude(double latitude) { latitude_ = latitude; }
|
||||||
|
void set_longitude(double longitude) { longitude_ = longitude; }
|
||||||
|
|
||||||
|
optional<time::ESPTime> sunrise(double elevation = 0.0);
|
||||||
|
optional<time::ESPTime> sunset(double elevation = 0.0);
|
||||||
|
|
||||||
|
double elevation();
|
||||||
|
double azimuth();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
double current_sun_time_() { return this->calc_sun_time_(this->time_->utcnow()); }
|
||||||
|
|
||||||
|
/** Calculate the declination of the sun in rad.
|
||||||
|
*
|
||||||
|
* See https://en.wikipedia.org/wiki/Position_of_the_Sun#Declination_of_the_Sun_as_seen_from_Earth
|
||||||
|
*
|
||||||
|
* Accuracy: ±0.2°
|
||||||
|
*
|
||||||
|
* @param sun_time The day of the year, 1 means January 1st. See calc_sun_time_.
|
||||||
|
* @return Sun declination in degrees
|
||||||
|
*/
|
||||||
|
double sun_declination_(double sun_time);
|
||||||
|
|
||||||
|
double elevation_ratio_(double sun_time);
|
||||||
|
|
||||||
|
/** Calculate the hour angle based on the sun time of day in hours.
|
||||||
|
*
|
||||||
|
* Positive in morning, 0 at noon, negative in afternoon.
|
||||||
|
*
|
||||||
|
* @param sun_time Sun time, see calc_sun_time_.
|
||||||
|
* @return Hour angle in rad.
|
||||||
|
*/
|
||||||
|
double hour_angle_(double sun_time);
|
||||||
|
|
||||||
|
double elevation_(double sun_time);
|
||||||
|
|
||||||
|
double elevation_rad_(double sun_time);
|
||||||
|
|
||||||
|
double zenith_rad_(double sun_time);
|
||||||
|
|
||||||
|
double azimuth_rad_(double sun_time);
|
||||||
|
|
||||||
|
double azimuth_(double sun_time);
|
||||||
|
|
||||||
|
/** Return the sun time given by the time_ object.
|
||||||
|
*
|
||||||
|
* Sun time is defined as doubleing point day of year.
|
||||||
|
* Integer part encodes the day of the year (1=January 1st)
|
||||||
|
* Decimal part encodes time of day (1/24 = 1 hour)
|
||||||
|
*/
|
||||||
|
double calc_sun_time_(const time::ESPTime &time);
|
||||||
|
|
||||||
|
uint32_t calc_epoch_(time::ESPTime base, double sun_time);
|
||||||
|
|
||||||
|
/** Calculate the sun time of day
|
||||||
|
*
|
||||||
|
* @param day_of_year
|
||||||
|
* @param elevation
|
||||||
|
* @param rising
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
double sun_time_for_elevation_(int32_t day_of_year, double elevation, bool rising);
|
||||||
|
|
||||||
|
double latitude_rad_();
|
||||||
|
|
||||||
|
time::RealTimeClock *time_;
|
||||||
|
/// Latitude in degrees, range: -90 to 90.
|
||||||
|
double latitude_;
|
||||||
|
/// Longitude in degrees, range: -180 to 180.
|
||||||
|
double longitude_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SunTrigger : public Trigger<>, public PollingComponent, public Parented<Sun> {
|
||||||
|
public:
|
||||||
|
SunTrigger() : PollingComponent(1000) {}
|
||||||
|
|
||||||
|
void set_sunrise(bool sunrise) { sunrise_ = sunrise; }
|
||||||
|
void set_elevation(double elevation) { elevation_ = elevation; }
|
||||||
|
|
||||||
|
void update() override {
|
||||||
|
double current = this->parent_->elevation();
|
||||||
|
if (isnan(current))
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool crossed;
|
||||||
|
if (this->sunrise_) {
|
||||||
|
crossed = this->last_elevation_ <= this->elevation_ && this->elevation_ < current;
|
||||||
|
} else {
|
||||||
|
crossed = this->last_elevation_ >= this->elevation_ && this->elevation_ > current;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (crossed) {
|
||||||
|
this->trigger();
|
||||||
|
}
|
||||||
|
this->last_elevation_ = current;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool sunrise_;
|
||||||
|
double last_elevation_;
|
||||||
|
double elevation_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename... Ts> class SunCondition : public Condition<Ts...>, public Parented<Sun> {
|
||||||
|
public:
|
||||||
|
TEMPLATABLE_VALUE(double, elevation);
|
||||||
|
void set_above(bool above) { above_ = above; }
|
||||||
|
|
||||||
|
bool check(Ts... x) override {
|
||||||
|
double elevation = this->elevation_.value(x...);
|
||||||
|
double current = this->parent_->elevation();
|
||||||
|
if (this->above_)
|
||||||
|
return current > elevation;
|
||||||
|
else
|
||||||
|
return current < elevation;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool above_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace sun
|
||||||
|
} // namespace esphome
|
45
esphome/components/sun/text_sensor/__init__.py
Normal file
45
esphome/components/sun/text_sensor/__init__.py
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
from esphome.components import text_sensor
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
import esphome.codegen as cg
|
||||||
|
from esphome.const import CONF_ICON, ICON_WEATHER_SUNSET_DOWN, ICON_WEATHER_SUNSET_UP, CONF_TYPE, \
|
||||||
|
CONF_ID, CONF_FORMAT
|
||||||
|
from .. import sun_ns, CONF_SUN_ID, Sun, CONF_ELEVATION, elevation
|
||||||
|
|
||||||
|
DEPENDENCIES = ['sun']
|
||||||
|
|
||||||
|
SunTextSensor = sun_ns.class_('SunTextSensor', text_sensor.TextSensor, cg.PollingComponent)
|
||||||
|
SUN_TYPES = {
|
||||||
|
'sunset': False,
|
||||||
|
'sunrise': True,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def validate_optional_icon(config):
|
||||||
|
if CONF_ICON not in config:
|
||||||
|
config = config.copy()
|
||||||
|
config[CONF_ICON] = {
|
||||||
|
'sunset': ICON_WEATHER_SUNSET_DOWN,
|
||||||
|
'sunrise': ICON_WEATHER_SUNSET_UP,
|
||||||
|
}[config[CONF_TYPE]]
|
||||||
|
return config
|
||||||
|
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = cv.All(text_sensor.TEXT_SENSOR_SCHEMA.extend({
|
||||||
|
cv.GenerateID(): cv.declare_id(SunTextSensor),
|
||||||
|
cv.GenerateID(CONF_SUN_ID): cv.use_id(Sun),
|
||||||
|
cv.Required(CONF_TYPE): cv.one_of(*SUN_TYPES, lower=True),
|
||||||
|
cv.Optional(CONF_ELEVATION, default=0): elevation,
|
||||||
|
cv.Optional(CONF_FORMAT, default='%X'): cv.string_strict,
|
||||||
|
}).extend(cv.polling_component_schema('60s')), validate_optional_icon)
|
||||||
|
|
||||||
|
|
||||||
|
def to_code(config):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
yield cg.register_component(var, config)
|
||||||
|
yield text_sensor.register_text_sensor(var, config)
|
||||||
|
|
||||||
|
paren = yield cg.get_variable(config[CONF_SUN_ID])
|
||||||
|
cg.add(var.set_parent(paren))
|
||||||
|
cg.add(var.set_sunrise(SUN_TYPES[config[CONF_TYPE]]))
|
||||||
|
cg.add(var.set_elevation(config[CONF_ELEVATION]))
|
||||||
|
cg.add(var.set_format(config[CONF_FORMAT]))
|
12
esphome/components/sun/text_sensor/sun_text_sensor.cpp
Normal file
12
esphome/components/sun/text_sensor/sun_text_sensor.cpp
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
#include "sun_text_sensor.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace sun {
|
||||||
|
|
||||||
|
static const char *TAG = "sun.text_sensor";
|
||||||
|
|
||||||
|
void SunTextSensor::dump_config() { LOG_TEXT_SENSOR("", "Sun Text Sensor", this); }
|
||||||
|
|
||||||
|
} // namespace sun
|
||||||
|
} // namespace esphome
|
41
esphome/components/sun/text_sensor/sun_text_sensor.h
Normal file
41
esphome/components/sun/text_sensor/sun_text_sensor.h
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/components/sun/sun.h"
|
||||||
|
#include "esphome/components/text_sensor/text_sensor.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace sun {
|
||||||
|
|
||||||
|
class SunTextSensor : public text_sensor::TextSensor, public PollingComponent {
|
||||||
|
public:
|
||||||
|
void set_parent(Sun *parent) { parent_ = parent; }
|
||||||
|
void set_elevation(double elevation) { elevation_ = elevation; }
|
||||||
|
void set_sunrise(bool sunrise) { sunrise_ = sunrise; }
|
||||||
|
void set_format(const std::string &format) { format_ = format; }
|
||||||
|
|
||||||
|
void update() override {
|
||||||
|
optional<time::ESPTime> res;
|
||||||
|
if (this->sunrise_)
|
||||||
|
res = this->parent_->sunrise(this->elevation_);
|
||||||
|
else
|
||||||
|
res = this->parent_->sunset(this->elevation_);
|
||||||
|
if (!res) {
|
||||||
|
this->publish_state("");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->publish_state(res->strftime(this->format_));
|
||||||
|
}
|
||||||
|
|
||||||
|
void dump_config() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::string format_{};
|
||||||
|
Sun *parent_;
|
||||||
|
double elevation_;
|
||||||
|
bool sunrise_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace sun
|
||||||
|
} // namespace esphome
|
|
@ -10,7 +10,7 @@ TemplateBinarySensor = template_ns.class_('TemplateBinarySensor', binary_sensor.
|
||||||
|
|
||||||
CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({
|
CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({
|
||||||
cv.GenerateID(): cv.declare_id(TemplateBinarySensor),
|
cv.GenerateID(): cv.declare_id(TemplateBinarySensor),
|
||||||
cv.Optional(CONF_LAMBDA): cv.lambda_,
|
cv.Optional(CONF_LAMBDA): cv.returning_lambda,
|
||||||
}).extend(cv.COMPONENT_SCHEMA)
|
}).extend(cv.COMPONENT_SCHEMA)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ RESTORE_MODES = {
|
||||||
|
|
||||||
CONFIG_SCHEMA = cover.COVER_SCHEMA.extend({
|
CONFIG_SCHEMA = cover.COVER_SCHEMA.extend({
|
||||||
cv.GenerateID(): cv.declare_id(TemplateCover),
|
cv.GenerateID(): cv.declare_id(TemplateCover),
|
||||||
cv.Optional(CONF_LAMBDA): cv.lambda_,
|
cv.Optional(CONF_LAMBDA): cv.returning_lambda,
|
||||||
cv.Optional(CONF_OPTIMISTIC, default=False): cv.boolean,
|
cv.Optional(CONF_OPTIMISTIC, default=False): cv.boolean,
|
||||||
cv.Optional(CONF_ASSUMED_STATE, default=False): cv.boolean,
|
cv.Optional(CONF_ASSUMED_STATE, default=False): cv.boolean,
|
||||||
cv.Optional(CONF_OPEN_ACTION): automation.validate_automation(single=True),
|
cv.Optional(CONF_OPEN_ACTION): automation.validate_automation(single=True),
|
||||||
|
|
35
esphome/components/template/output/__init__.py
Normal file
35
esphome/components/template/output/__init__.py
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome import automation
|
||||||
|
from esphome.components import output
|
||||||
|
from esphome.const import CONF_ID, CONF_TYPE
|
||||||
|
from .. import template_ns
|
||||||
|
|
||||||
|
TemplateBinaryOutput = template_ns.class_('TemplateBinaryOutput', output.BinaryOutput)
|
||||||
|
TemplateFloatOutput = template_ns.class_('TemplateFloatOutput', output.FloatOutput)
|
||||||
|
|
||||||
|
CONF_BINARY = 'binary'
|
||||||
|
CONF_FLOAT = 'float'
|
||||||
|
CONF_WRITE_ACTION = 'write_action'
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = cv.typed_schema({
|
||||||
|
CONF_BINARY: output.BINARY_OUTPUT_SCHEMA.extend({
|
||||||
|
cv.GenerateID(): cv.declare_id(TemplateBinaryOutput),
|
||||||
|
cv.Required(CONF_WRITE_ACTION): automation.validate_automation(single=True),
|
||||||
|
}),
|
||||||
|
CONF_FLOAT: output.FLOAT_OUTPUT_SCHEMA.extend({
|
||||||
|
cv.GenerateID(): cv.declare_id(TemplateFloatOutput),
|
||||||
|
cv.Required(CONF_WRITE_ACTION): automation.validate_automation(single=True),
|
||||||
|
}),
|
||||||
|
}, lower=True)
|
||||||
|
|
||||||
|
|
||||||
|
def to_code(config):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
if config[CONF_TYPE] == CONF_BINARY:
|
||||||
|
yield automation.build_automation(var.get_trigger(), [(bool, 'state')],
|
||||||
|
config[CONF_WRITE_ACTION])
|
||||||
|
else:
|
||||||
|
yield automation.build_automation(var.get_trigger(), [(float, 'state')],
|
||||||
|
config[CONF_WRITE_ACTION])
|
||||||
|
yield output.register_output(var, config)
|
31
esphome/components/template/output/template_output.h
Normal file
31
esphome/components/template/output/template_output.h
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/automation.h"
|
||||||
|
#include "esphome/components/output/binary_output.h"
|
||||||
|
#include "esphome/components/output/float_output.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace template_ {
|
||||||
|
|
||||||
|
class TemplateBinaryOutput : public output::BinaryOutput {
|
||||||
|
public:
|
||||||
|
Trigger<bool> *get_trigger() const { return trigger_; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void write_state(bool state) override { this->trigger_->trigger(state); }
|
||||||
|
|
||||||
|
Trigger<bool> *trigger_ = new Trigger<bool>();
|
||||||
|
};
|
||||||
|
|
||||||
|
class TemplateFloatOutput : public output::FloatOutput {
|
||||||
|
public:
|
||||||
|
Trigger<float> *get_trigger() const { return trigger_; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void write_state(float state) override { this->trigger_->trigger(state); }
|
||||||
|
|
||||||
|
Trigger<float> *trigger_ = new Trigger<float>();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace template_
|
||||||
|
} // namespace esphome
|
|
@ -9,7 +9,7 @@ TemplateSensor = template_ns.class_('TemplateSensor', sensor.Sensor, cg.PollingC
|
||||||
|
|
||||||
CONFIG_SCHEMA = sensor.sensor_schema(UNIT_EMPTY, ICON_EMPTY, 1).extend({
|
CONFIG_SCHEMA = sensor.sensor_schema(UNIT_EMPTY, ICON_EMPTY, 1).extend({
|
||||||
cv.GenerateID(): cv.declare_id(TemplateSensor),
|
cv.GenerateID(): cv.declare_id(TemplateSensor),
|
||||||
cv.Optional(CONF_LAMBDA): cv.lambda_,
|
cv.Optional(CONF_LAMBDA): cv.returning_lambda,
|
||||||
}).extend(cv.polling_component_schema('60s'))
|
}).extend(cv.polling_component_schema('60s'))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ TemplateSwitch = template_ns.class_('TemplateSwitch', switch.Switch, cg.Componen
|
||||||
|
|
||||||
CONFIG_SCHEMA = switch.SWITCH_SCHEMA.extend({
|
CONFIG_SCHEMA = switch.SWITCH_SCHEMA.extend({
|
||||||
cv.GenerateID(): cv.declare_id(TemplateSwitch),
|
cv.GenerateID(): cv.declare_id(TemplateSwitch),
|
||||||
cv.Optional(CONF_LAMBDA): cv.lambda_,
|
cv.Optional(CONF_LAMBDA): cv.returning_lambda,
|
||||||
cv.Optional(CONF_OPTIMISTIC, default=False): cv.boolean,
|
cv.Optional(CONF_OPTIMISTIC, default=False): cv.boolean,
|
||||||
cv.Optional(CONF_ASSUMED_STATE, default=False): cv.boolean,
|
cv.Optional(CONF_ASSUMED_STATE, default=False): cv.boolean,
|
||||||
cv.Optional(CONF_TURN_OFF_ACTION): automation.validate_automation(single=True),
|
cv.Optional(CONF_TURN_OFF_ACTION): automation.validate_automation(single=True),
|
||||||
|
|
|
@ -11,7 +11,7 @@ TemplateTextSensor = template_ns.class_('TemplateTextSensor', text_sensor.TextSe
|
||||||
|
|
||||||
CONFIG_SCHEMA = text_sensor.TEXT_SENSOR_SCHEMA.extend({
|
CONFIG_SCHEMA = text_sensor.TEXT_SENSOR_SCHEMA.extend({
|
||||||
cv.GenerateID(): cv.declare_id(TemplateTextSensor),
|
cv.GenerateID(): cv.declare_id(TemplateTextSensor),
|
||||||
cv.Optional(CONF_LAMBDA): cv.lambda_,
|
cv.Optional(CONF_LAMBDA): cv.returning_lambda,
|
||||||
}).extend(cv.polling_component_schema('60s'))
|
}).extend(cv.polling_component_schema('60s'))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace time {
|
namespace time {
|
||||||
|
|
||||||
static const char *TAG = "something.something";
|
static const char *TAG = "automation";
|
||||||
|
|
||||||
void CronTrigger::add_second(uint8_t second) { this->seconds_[second] = true; }
|
void CronTrigger::add_second(uint8_t second) { this->seconds_[second] = true; }
|
||||||
void CronTrigger::add_minute(uint8_t minute) { this->minutes_[minute] = true; }
|
void CronTrigger::add_minute(uint8_t minute) { this->minutes_[minute] = true; }
|
||||||
|
@ -38,11 +38,11 @@ void CronTrigger::loop() {
|
||||||
}
|
}
|
||||||
|
|
||||||
this->last_check_ = time;
|
this->last_check_ = time;
|
||||||
if (!time.in_range()) {
|
if (!time.fields_in_range()) {
|
||||||
ESP_LOGW(TAG, "Time is out of range!");
|
ESP_LOGW(TAG, "Time is out of range!");
|
||||||
ESP_LOGD(TAG, "Second=%02u Minute=%02u Hour=%02u DayOfWeek=%u DayOfMonth=%u DayOfYear=%u Month=%u time=%ld",
|
ESP_LOGD(TAG, "Second=%02u Minute=%02u Hour=%02u DayOfWeek=%u DayOfMonth=%u DayOfYear=%u Month=%u time=%ld",
|
||||||
time.second, time.minute, time.hour, time.day_of_week, time.day_of_month, time.day_of_year, time.month,
|
time.second, time.minute, time.hour, time.day_of_week, time.day_of_month, time.day_of_year, time.month,
|
||||||
time.time);
|
time.timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->matches(time))
|
if (this->matches(time))
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue