mirror of
https://github.com/esphome/esphome.git
synced 2026-05-22 01:42:49 +08:00
+13
@@ -6,6 +6,19 @@ __pycache__/
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Hide sublime text stuff
|
||||
*.sublime-project
|
||||
*.sublime-workspace
|
||||
|
||||
# Hide some OS X stuff
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
Icon
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
|
||||
+2
-2
@@ -51,6 +51,6 @@ matrix:
|
||||
- clang-format-7 -version
|
||||
- clang-apply-replacements-7 -version
|
||||
script:
|
||||
- script/clang-tidy.py --all-headers -j 2 --fix
|
||||
- script/clang-format.py -i -j 2
|
||||
- script/clang-tidy --all-headers -j 2 --fix
|
||||
- script/clang-format -i -j 2
|
||||
- script/ci-suggest-changes
|
||||
|
||||
@@ -2,12 +2,12 @@ server {
|
||||
listen %%port%% default_server ssl http2;
|
||||
|
||||
include /etc/nginx/includes/server_params.conf;
|
||||
include /etc/nginx/includes/proxy_params.conf;
|
||||
include /etc/nginx/includes/ssl_params.conf;
|
||||
# Clear Hass.io Ingress header
|
||||
proxy_set_header X-Hassio-Ingress "";
|
||||
include /etc/nginx/includes/proxy_params.conf;
|
||||
include /etc/nginx/includes/ssl_params.conf;
|
||||
# Clear Hass.io Ingress header
|
||||
proxy_set_header X-Hassio-Ingress "";
|
||||
|
||||
# Redirect http requests to https on the same port.
|
||||
# Redirect http requests to https on the same port.
|
||||
# https://rageagainstshell.com/2016/11/redirect-http-to-https-on-the-same-port-in-nginx/
|
||||
error_page 497 https://$http_host$request_uri;
|
||||
|
||||
|
||||
@@ -7,10 +7,10 @@ server {
|
||||
proxy_set_header X-Hassio-Ingress "YES";
|
||||
|
||||
location / {
|
||||
# Only allow from Hass.io supervisor
|
||||
# Only allow from Hass.io supervisor
|
||||
allow 172.30.32.2;
|
||||
deny all;
|
||||
|
||||
proxy_pass http://esphome;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+23
-5
@@ -1,16 +1,18 @@
|
||||
from __future__ import print_function
|
||||
|
||||
import argparse
|
||||
import functools
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
from datetime import datetime
|
||||
|
||||
from esphome import const, writer, yaml_util
|
||||
import esphome.codegen as cg
|
||||
from esphome.config import iter_components, read_config, strip_default_ids
|
||||
from esphome.const import CONF_BAUD_RATE, CONF_BROKER, CONF_LOGGER, CONF_OTA, \
|
||||
CONF_PASSWORD, CONF_PORT
|
||||
from esphome.core import CORE, EsphomeError
|
||||
from esphome.core import CORE, EsphomeError, coroutine, coroutine_with_priority
|
||||
from esphome.helpers import color, indent
|
||||
from esphome.py_compat import IS_PY2, safe_input
|
||||
from esphome.util import run_external_command, run_external_process, safe_print
|
||||
@@ -117,12 +119,27 @@ def run_miniterm(config, port):
|
||||
config, line, backtrace_state=backtrace_state)
|
||||
|
||||
|
||||
def wrap_to_code(name, comp):
|
||||
coro = coroutine(comp.to_code)
|
||||
|
||||
@functools.wraps(comp.to_code)
|
||||
@coroutine_with_priority(coro.priority)
|
||||
def wrapped(conf):
|
||||
cg.add(cg.LineComment(u"{}:".format(name)))
|
||||
if comp.config_schema is not None:
|
||||
cg.add(cg.LineComment(indent(yaml_util.dump(conf).decode('utf-8'))))
|
||||
yield coro(conf)
|
||||
|
||||
return wrapped
|
||||
|
||||
|
||||
def write_cpp(config):
|
||||
_LOGGER.info("Generating C++ source...")
|
||||
|
||||
for _, component, conf in iter_components(CORE.config):
|
||||
for name, component, conf in iter_components(CORE.config):
|
||||
if component.to_code is not None:
|
||||
CORE.add_job(component.to_code, conf)
|
||||
coro = wrap_to_code(name, component)
|
||||
CORE.add_job(coro, conf)
|
||||
|
||||
CORE.flush_tasks()
|
||||
|
||||
@@ -245,7 +262,7 @@ def command_vscode(args):
|
||||
from esphome import vscode
|
||||
|
||||
CORE.config_path = args.configuration
|
||||
vscode.read_config()
|
||||
vscode.read_config(args)
|
||||
|
||||
|
||||
def command_compile(args, config):
|
||||
@@ -423,7 +440,8 @@ def parse_args(argv):
|
||||
dashboard.add_argument("--socket",
|
||||
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:])
|
||||
|
||||
|
||||
@@ -128,7 +128,7 @@ def or_condition_to_code(config, condition_id, template_arg, args):
|
||||
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):
|
||||
condition = yield build_condition(config, template_arg, args)
|
||||
yield cg.new_Pvariable(condition_id, template_arg, condition)
|
||||
|
||||
+3
-3
@@ -10,16 +10,16 @@
|
||||
# pylint: disable=unused-import
|
||||
from esphome.cpp_generator import ( # noqa
|
||||
Expression, RawExpression, RawStatement, TemplateArguments,
|
||||
StructInitializer, ArrayInitializer, safe_exp, Statement,
|
||||
StructInitializer, ArrayInitializer, safe_exp, Statement, LineComment,
|
||||
progmem_array, statement, variable, Pvariable, new_Pvariable,
|
||||
add, add_global, add_library, add_build_flag, add_define,
|
||||
get_variable, get_variable_with_full_id, process_lambda, is_template, templatable, MockObj,
|
||||
MockObjClass)
|
||||
from esphome.cpp_helpers import ( # noqa
|
||||
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
|
||||
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,
|
||||
esphome_ns, App, Nameable, Component, ComponentPtr,
|
||||
PollingComponent, Application, optional, arduino_json_ns, JsonObject,
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
#include "esphome/components/adc/adc_sensor.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#ifdef USE_ADC_SENSOR_VCC
|
||||
ADC_MODE(ADC_VCC)
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace adc {
|
||||
|
||||
|
||||
@@ -2,16 +2,13 @@
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/esphal.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/voltage_sampler/voltage_sampler.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace adc {
|
||||
|
||||
#ifdef USE_ADC_SENSOR_VCC
|
||||
ADC_MODE(ADC_VCC)
|
||||
#endif
|
||||
|
||||
class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage_sampler::VoltageSampler {
|
||||
public:
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
|
||||
@@ -87,7 +87,7 @@ HOMEASSISTANT_SERVICE_ACTION_SCHEMA = cv.Schema({
|
||||
cv.string: cv.string,
|
||||
}),
|
||||
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
|
||||
|
||||
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')
|
||||
|
||||
CONFIG_SCHEMA = cv.All(climate.CLIMATE_SCHEMA.extend({
|
||||
|
||||
@@ -70,7 +70,7 @@ def delayed_off_filter_to_code(config, filter_id):
|
||||
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):
|
||||
lambda_ = yield cg.process_lambda(config, [(bool, 'x')], return_type=cg.optional.template(bool))
|
||||
yield cg.new_Pvariable(filter_id, lambda_)
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace esphome {
|
||||
|
||||
namespace binary_sensor {
|
||||
|
||||
static const char *TAG = "something.Filter";
|
||||
static const char *TAG = "sensor.filter";
|
||||
|
||||
void Filter::output(bool value, bool is_initial) {
|
||||
if (!this->dedup_.next(value))
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
namespace esphome {
|
||||
namespace ble_presence {
|
||||
|
||||
static const char *TAG = "something.something";
|
||||
static const char *TAG = "ble_presence";
|
||||
|
||||
void BLEPresenceDevice::dump_config() { LOG_BINARY_SENSOR("", "BLE Presence", this); }
|
||||
|
||||
|
||||
@@ -232,7 +232,7 @@ float BME680Component::get_setup_priority() const { return setup_priority::DATA;
|
||||
void BME680Component::update() {
|
||||
uint8_t meas_control = 0; // No need to fetch, we're setting all fields
|
||||
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
|
||||
if (!this->write_byte(BME680_REGISTER_CONTROL_MEAS, meas_control)) {
|
||||
this->status_set_warning();
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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')
|
||||
|
||||
ClimateDevice = climate_ns.class_('Climate', cg.Nameable)
|
||||
Climate = climate_ns.class_('Climate', cg.Nameable)
|
||||
ClimateCall = climate_ns.class_('ClimateCall')
|
||||
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)
|
||||
|
||||
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.Optional(CONF_VISUAL, default={}): cv.Schema({
|
||||
cv.Optional(CONF_MIN_TEMPERATURE): cv.temperature,
|
||||
@@ -68,7 +68,7 @@ def register_climate(var, config):
|
||||
|
||||
|
||||
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_TARGET_TEMPERATURE): cv.templatable(cv.temperature),
|
||||
cv.Optional(CONF_TARGET_TEMPERATURE_LOW): cv.templatable(cv.temperature),
|
||||
|
||||
@@ -207,11 +207,6 @@ ClimateTraits Climate::get_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) {
|
||||
this->visual_min_temperature_override_ = visual_min_temperature_override;
|
||||
}
|
||||
|
||||
@@ -169,11 +169,6 @@ class Climate : public Nameable {
|
||||
*/
|
||||
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_max_temperature_override(float visual_max_temperature_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) {
|
||||
switch (mode) {
|
||||
case CLIMATE_MODE_OFF:
|
||||
return "OFF";
|
||||
return "off";
|
||||
case CLIMATE_MODE_AUTO:
|
||||
return "AUTO";
|
||||
return "auto";
|
||||
case CLIMATE_MODE_COOL:
|
||||
return "COOL";
|
||||
return "cool";
|
||||
case CLIMATE_MODE_HEAT:
|
||||
return "HEAT";
|
||||
return "heat";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ CustomBinarySensorConstructor = custom_ns.class_('CustomBinarySensorConstructor'
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema({
|
||||
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),
|
||||
})
|
||||
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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')
|
||||
CustomFloatOutputConstructor = custom_ns.class_('CustomFloatOutputConstructor')
|
||||
|
||||
BINARY_SCHEMA = cv.Schema({
|
||||
cv.GenerateID(): cv.declare_id(CustomBinaryOutputConstructor),
|
||||
cv.Required(CONF_LAMBDA): cv.lambda_,
|
||||
cv.Required(CONF_TYPE): 'binary',
|
||||
cv.Required(CONF_OUTPUTS):
|
||||
cv.ensure_list(output.BINARY_OUTPUT_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_id(output.BinaryOutput),
|
||||
})),
|
||||
})
|
||||
CONF_BINARY = 'binary'
|
||||
CONF_FLOAT = 'float'
|
||||
|
||||
FLOAT_SCHEMA = cv.Schema({
|
||||
cv.GenerateID(): cv.declare_id(CustomFloatOutputConstructor),
|
||||
cv.Required(CONF_LAMBDA): cv.lambda_,
|
||||
cv.Required(CONF_TYPE): 'float',
|
||||
cv.Required(CONF_OUTPUTS):
|
||||
cv.ensure_list(output.FLOAT_OUTPUT_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_id(output.FloatOutput),
|
||||
})),
|
||||
})
|
||||
|
||||
|
||||
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
|
||||
CONFIG_SCHEMA = cv.typed_schema({
|
||||
CONF_BINARY: cv.Schema({
|
||||
cv.GenerateID(): cv.declare_id(CustomBinaryOutputConstructor),
|
||||
cv.Required(CONF_LAMBDA): cv.returning_lambda,
|
||||
cv.Required(CONF_OUTPUTS):
|
||||
cv.ensure_list(output.BINARY_OUTPUT_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_id(output.BinaryOutput),
|
||||
})),
|
||||
}),
|
||||
CONF_FLOAT: cv.Schema({
|
||||
cv.GenerateID(): cv.declare_id(CustomFloatOutputConstructor),
|
||||
cv.Required(CONF_LAMBDA): cv.returning_lambda,
|
||||
cv.Required(CONF_OUTPUTS):
|
||||
cv.ensure_list(output.FLOAT_OUTPUT_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_id(output.FloatOutput),
|
||||
})),
|
||||
})
|
||||
}, lower=True)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
|
||||
@@ -8,7 +8,7 @@ CustomSensorConstructor = custom_ns.class_('CustomSensorConstructor')
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema({
|
||||
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),
|
||||
})
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ CustomSwitchConstructor = custom_ns.class_('CustomSwitchConstructor')
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema({
|
||||
cv.GenerateID(): cv.declare_id(CustomSwitchConstructor),
|
||||
cv.Required(CONF_LAMBDA): cv.lambda_,
|
||||
cv.Required(CONF_LAMBDA): cv.returning_lambda,
|
||||
cv.Required(CONF_SWITCHES):
|
||||
cv.ensure_list(switch.SWITCH_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_id(switch.Switch),
|
||||
|
||||
@@ -8,7 +8,7 @@ CustomTextSensorConstructor = custom_ns.class_('CustomTextSensorConstructor')
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema({
|
||||
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.ensure_list(text_sensor.TEXT_SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_id(text_sensor.TextSensor),
|
||||
|
||||
@@ -8,7 +8,7 @@ CustomComponentConstructor = custom_component_ns.class_('CustomComponentConstruc
|
||||
MULTI_CONF = True
|
||||
CONFIG_SCHEMA = cv.Schema({
|
||||
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.GenerateID(): cv.declare_id(cg.Component)
|
||||
}).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_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))
|
||||
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ CONF_BRIGHTNESS = 'brightness'
|
||||
CONF_SATURATION = 'saturation'
|
||||
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({
|
||||
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.Range(min=0, max=1)),
|
||||
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_BRIGHTNESS, default=0): camera_range_param,
|
||||
cv.Optional(CONF_SATURATION, default=0): camera_range_param,
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
namespace esphome {
|
||||
namespace esp8266_pwm {
|
||||
|
||||
static const char *TAG = "something.something";
|
||||
static const char *TAG = "esp8266_pwm";
|
||||
|
||||
void ESP8266PWM::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up ESP8266 PWM Output...");
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import light, power_supply
|
||||
from esphome.const import CONF_OUTPUT_ID, CONF_NUM_LEDS, CONF_RGB_ORDER, CONF_MAX_REFRESH_RATE, \
|
||||
CONF_POWER_SUPPLY
|
||||
from esphome.components import light
|
||||
from esphome.const import CONF_OUTPUT_ID, CONF_NUM_LEDS, CONF_RGB_ORDER, CONF_MAX_REFRESH_RATE
|
||||
from esphome.core import coroutine
|
||||
|
||||
fastled_base_ns = cg.esphome_ns.namespace('fastled_base')
|
||||
@@ -24,8 +23,6 @@ BASE_SCHEMA = light.ADDRESSABLE_LIGHT_SCHEMA.extend({
|
||||
cv.Required(CONF_NUM_LEDS): cv.positive_not_null_int,
|
||||
cv.Optional(CONF_RGB_ORDER): cv.one_of(*RGB_ORDERS, upper=True),
|
||||
cv.Optional(CONF_MAX_REFRESH_RATE): cv.positive_time_period_microseconds,
|
||||
|
||||
cv.Optional(CONF_POWER_SUPPLY): cv.use_id(power_supply.PowerSupply),
|
||||
}).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
|
||||
@@ -37,10 +34,6 @@ def new_fastled_light(config):
|
||||
if CONF_MAX_REFRESH_RATE in config:
|
||||
cg.add(var.set_max_refresh_rate(config[CONF_MAX_REFRESH_RATE]))
|
||||
|
||||
if CONF_POWER_SUPPLY in config:
|
||||
var_ = yield cg.get_variable(config[CONF_POWER_SUPPLY])
|
||||
cg.add(var.set_power_supply(var_))
|
||||
|
||||
yield light.register_light(var, config)
|
||||
cg.add_library('FastLED', '3.2.0')
|
||||
yield var
|
||||
|
||||
@@ -33,27 +33,6 @@ void FastLEDLightOutput::loop() {
|
||||
this->mark_shown_();
|
||||
|
||||
ESP_LOGVV(TAG, "Writing RGB values to bus...");
|
||||
|
||||
#ifdef USE_POWER_SUPPLY
|
||||
if (this->power_supply_ != nullptr) {
|
||||
bool is_on = false;
|
||||
for (int i = 0; i < this->num_leds_; i++) {
|
||||
if (bool(this->leds_[i])) {
|
||||
is_on = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_on && !this->has_requested_high_power_) {
|
||||
this->power_supply_->request_high_power();
|
||||
this->has_requested_high_power_ = true;
|
||||
}
|
||||
if (!is_on && this->has_requested_high_power_) {
|
||||
this->power_supply_->unrequest_high_power();
|
||||
this->has_requested_high_power_ = false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
this->controller_->showLeds();
|
||||
}
|
||||
|
||||
|
||||
@@ -4,10 +4,6 @@
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/components/light/addressable_light.h"
|
||||
|
||||
#ifdef USE_POWER_SUPPLY
|
||||
#include "esphome/components/power_supply/power_supply.h"
|
||||
#endif
|
||||
|
||||
#define FASTLED_ESP8266_RAW_PIN_ORDER
|
||||
#define FASTLED_ESP32_RAW_PIN_ORDER
|
||||
#define FASTLED_RMT_BUILTIN_DRIVER true
|
||||
@@ -30,10 +26,6 @@ class FastLEDLightOutput : public Component, public light::AddressableLight {
|
||||
/// Set a maximum refresh rate in µs as some lights do not like being updated too often.
|
||||
void set_max_refresh_rate(uint32_t interval_us) { this->max_refresh_rate_ = interval_us; }
|
||||
|
||||
#ifdef USE_POWER_SUPPLY
|
||||
void set_power_supply(power_supply::PowerSupply *power_supply) { this->power_supply_ = power_supply; }
|
||||
#endif
|
||||
|
||||
/// Add some LEDS, can only be called once.
|
||||
CLEDController &add_leds(CLEDController *controller, int num_leds) {
|
||||
this->controller_ = controller;
|
||||
@@ -242,10 +234,6 @@ class FastLEDLightOutput : public Component, public light::AddressableLight {
|
||||
int num_leds_{0};
|
||||
uint32_t last_refresh_{0};
|
||||
optional<uint32_t> max_refresh_rate_{};
|
||||
#ifdef USE_POWER_SUPPLY
|
||||
power_supply::PowerSupply *power_supply_{nullptr};
|
||||
bool has_requested_high_power_{false};
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace fastled_base
|
||||
|
||||
@@ -70,7 +70,7 @@ FONT_SCHEMA = cv.Schema({
|
||||
cv.Required(CONF_ID): cv.declare_id(Font),
|
||||
cv.Required(CONF_FILE): validate_truetype_file,
|
||||
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),
|
||||
})
|
||||
|
||||
|
||||
@@ -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')
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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))
|
||||
@@ -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
|
||||
@@ -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...");
|
||||
this->sel_pin_->setup();
|
||||
this->sel_pin_->digital_write(this->current_mode_);
|
||||
this->cf_store_.setup(this->cf_pin_);
|
||||
this->cf1_store_.setup(this->cf1_pin_);
|
||||
this->cf_store_.pulse_counter_setup(this->cf_pin_);
|
||||
this->cf1_store_.pulse_counter_setup(this->cf1_pin_);
|
||||
}
|
||||
void HLW8012Component::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "HLW8012:");
|
||||
@@ -32,18 +32,18 @@ void HLW8012Component::dump_config() {
|
||||
float HLW8012Component::get_setup_priority() const { return setup_priority::DATA; }
|
||||
void HLW8012Component::update() {
|
||||
// HLW8012 has 50% duty cycle
|
||||
const uint32_t last_rise_cf = this->cf_store_.get_last_rise();
|
||||
const uint32_t last_rise_cf1 = this->cf1_store_.get_last_rise();
|
||||
const uint32_t now = micros();
|
||||
float full_cycle_cf = this->cf_store_.get_pulse_width_s() * 2;
|
||||
float full_cycle_cf1 = this->cf1_store_.get_pulse_width_s() * 2;
|
||||
float cf_hz = 0.0f, cf1_hz = 0.0f;
|
||||
auto update_interval_micros = static_cast<uint32_t>(this->update_interval_ * 1e3f);
|
||||
|
||||
if (full_cycle_cf != 0.0f && now - last_rise_cf < update_interval_micros * 3)
|
||||
cf_hz = 1.0f / full_cycle_cf;
|
||||
if (full_cycle_cf1 != 0.0f && now - last_rise_cf1 < update_interval_micros * 3)
|
||||
cf1_hz = 1.0f / full_cycle_cf1;
|
||||
pulse_counter::pulse_counter_t raw_cf = this->cf_store_.read_raw_value();
|
||||
pulse_counter::pulse_counter_t raw_cf1 = this->cf1_store_.read_raw_value();
|
||||
float cf_hz = raw_cf / (this->get_update_interval() / 1000.0f);
|
||||
if (raw_cf <= 1) {
|
||||
// don't count single pulse as power
|
||||
cf_hz = 0.0f;
|
||||
}
|
||||
float cf1_hz = raw_cf1 / (this->get_update_interval() / 1000.0f);
|
||||
if (raw_cf1 <= 1) {
|
||||
// don't count single pulse as anything
|
||||
cf1_hz = 0.0f;
|
||||
}
|
||||
|
||||
if (this->nth_value_++ < 2) {
|
||||
return;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/esphal.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 hlw8012 {
|
||||
@@ -34,9 +34,9 @@ class HLW8012Component : public PollingComponent {
|
||||
float voltage_divider_{2351};
|
||||
GPIOPin *sel_pin_;
|
||||
GPIOPin *cf_pin_;
|
||||
pulse_width::PulseWidthSensorStore cf_store_;
|
||||
pulse_counter::PulseCounterStorage cf_store_;
|
||||
GPIOPin *cf1_pin_;
|
||||
pulse_width::PulseWidthSensorStore cf1_store_;
|
||||
pulse_counter::PulseCounterStorage cf1_store_;
|
||||
sensor::Sensor *voltage_sensor_{nullptr};
|
||||
sensor::Sensor *current_sensor_{nullptr};
|
||||
sensor::Sensor *power_sensor_{nullptr};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user