Moar Custom platforms

This commit is contained in:
Otto Winter 2019-05-15 10:55:35 +02:00
parent 6306d44955
commit 8be444a25e
No known key found for this signature in database
GPG key ID: DB66C0BE6013F97E
14 changed files with 253 additions and 44 deletions

View file

@ -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({

View file

@ -111,7 +111,7 @@ void CCS811Component::dump_config() {
ESP_LOGCONFIG(TAG, "CCS811"); ESP_LOGCONFIG(TAG, "CCS811");
LOG_I2C_DEVICE(this) LOG_I2C_DEVICE(this)
LOG_UPDATE_INTERVAL(this) LOG_UPDATE_INTERVAL(this)
LOG_SENSOR(" ", "CO2 Sesnor", this->co2_) LOG_SENSOR(" ", "CO2 Sensor", this->co2_)
LOG_SENSOR(" ", "TVOC Sensor", this->tvoc_) LOG_SENSOR(" ", "TVOC Sensor", this->tvoc_)
if (this->baseline_) { if (this->baseline_) {
ESP_LOGCONFIG(TAG, " Baseline: %04X", *this->baseline_); ESP_LOGCONFIG(TAG, " Baseline: %04X", *this->baseline_);

View file

@ -1,8 +1,8 @@
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 i2c, sensor from esphome.components import i2c, sensor
from esphome.const import CONF_ID, ICON_GAS_CYLINDER, ICON_RADIATOR, UNIT_PARTS_PER_MILLION, \ from esphome.const import CONF_ID, ICON_RADIATOR, UNIT_PARTS_PER_MILLION, \
UNIT_PARTS_PER_BILLION, CONF_TEMPERATURE, CONF_HUMIDITY UNIT_PARTS_PER_BILLION, CONF_TEMPERATURE, CONF_HUMIDITY, ICON_PERIODIC_TABLE_CO2
DEPENDENCIES = ['i2c'] DEPENDENCIES = ['i2c']
@ -15,7 +15,8 @@ CONF_BASELINE = 'baseline'
CONFIG_SCHEMA = cv.Schema({ CONFIG_SCHEMA = cv.Schema({
cv.GenerateID(): cv.declare_id(CCS811Component), cv.GenerateID(): cv.declare_id(CCS811Component),
cv.Required(CONF_ECO2): sensor.sensor_schema(UNIT_PARTS_PER_MILLION, ICON_GAS_CYLINDER, 0), 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.Required(CONF_TVOC): sensor.sensor_schema(UNIT_PARTS_PER_BILLION, ICON_RADIATOR, 0),
cv.Optional(CONF_BASELINE): cv.hex_uint16_t, cv.Optional(CONF_BASELINE): cv.hex_uint16_t,

View file

@ -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),

View 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)

View 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

View 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)

View 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

View 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)

View 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

View file

@ -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'
cv.GenerateID(): cv.declare_id(CustomBinaryOutputConstructor), CONF_FLOAT = 'float'
cv.Required(CONF_LAMBDA): cv.returning_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),
})),
})
FLOAT_SCHEMA = cv.Schema({ CONFIG_SCHEMA = cv.typed_schema({
cv.GenerateID(): cv.declare_id(CustomFloatOutputConstructor), CONF_BINARY: cv.Schema({
cv.Required(CONF_LAMBDA): cv.returning_lambda, cv.GenerateID(): cv.declare_id(CustomBinaryOutputConstructor),
cv.Required(CONF_TYPE): 'float', cv.Required(CONF_LAMBDA): cv.returning_lambda,
cv.Required(CONF_OUTPUTS): cv.Required(CONF_OUTPUTS):
cv.ensure_list(output.FLOAT_OUTPUT_SCHEMA.extend({ cv.ensure_list(output.BINARY_OUTPUT_SCHEMA.extend({
cv.GenerateID(): cv.declare_id(output.FloatOutput), cv.GenerateID(): cv.declare_id(output.BinaryOutput),
})), })),
}) }),
CONF_FLOAT: cv.Schema({
cv.GenerateID(): cv.declare_id(CustomFloatOutputConstructor),
def validate_custom_output(value): cv.Required(CONF_LAMBDA): cv.returning_lambda,
if not isinstance(value, dict): cv.Required(CONF_OUTPUTS):
raise cv.Invalid("Value must be dict") cv.ensure_list(output.FLOAT_OUTPUT_SCHEMA.extend({
if CONF_TYPE not in value: cv.GenerateID(): cv.declare_id(output.FloatOutput),
raise cv.Invalid("type not specified!") })),
type = cv.string_strict(value[CONF_TYPE]).lower() })
value[CONF_TYPE] = type }, lower=True)
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):

View 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)

View 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

View file

@ -16,7 +16,7 @@ from esphome import core
from esphome.const import CONF_AVAILABILITY, CONF_COMMAND_TOPIC, CONF_DISCOVERY, CONF_ID, \ from esphome.const import CONF_AVAILABILITY, CONF_COMMAND_TOPIC, CONF_DISCOVERY, CONF_ID, \
CONF_INTERNAL, CONF_NAME, CONF_PAYLOAD_AVAILABLE, CONF_PAYLOAD_NOT_AVAILABLE, \ CONF_INTERNAL, CONF_NAME, CONF_PAYLOAD_AVAILABLE, CONF_PAYLOAD_NOT_AVAILABLE, \
CONF_RETAIN, CONF_SETUP_PRIORITY, CONF_STATE_TOPIC, CONF_TOPIC, \ CONF_RETAIN, CONF_SETUP_PRIORITY, CONF_STATE_TOPIC, CONF_TOPIC, \
CONF_HOUR, CONF_MINUTE, CONF_SECOND, CONF_VALUE, CONF_UPDATE_INTERVAL, CONF_TYPE_ID CONF_HOUR, CONF_MINUTE, CONF_SECOND, CONF_VALUE, CONF_UPDATE_INTERVAL, CONF_TYPE_ID, CONF_TYPE
from esphome.core import CORE, HexInt, IPAddress, Lambda, TimePeriod, TimePeriodMicroseconds, \ from esphome.core import CORE, HexInt, IPAddress, Lambda, TimePeriod, TimePeriodMicroseconds, \
TimePeriodMilliseconds, TimePeriodSeconds, TimePeriodMinutes TimePeriodMilliseconds, TimePeriodSeconds, TimePeriodMinutes
from esphome.helpers import list_starts_with from esphome.helpers import list_starts_with
@ -1078,6 +1078,24 @@ def extract_keys(schema):
return keys return keys
def typed_schema(schemas, **kwargs):
"""Create a schema that has a key to distinguish between schemas"""
key = kwargs.pop('key', CONF_TYPE)
key_validator = one_of(*schemas, **kwargs)
def validator(value):
if not isinstance(value, dict):
raise Invalid("Value must be dict")
if CONF_TYPE not in value:
raise Invalid("type not specified!")
value = value.copy()
key_v = key_validator(value.pop(key))
value = schemas[key_v](value)
value[key] = key_v
return validator
class GenerateID(Optional): class GenerateID(Optional):
"""Mark this key as being an auto-generated ID key.""" """Mark this key as being an auto-generated ID key."""
def __init__(self, key=CONF_ID): def __init__(self, key=CONF_ID):