mirror of
https://github.com/esphome/esphome.git
synced 2024-11-10 09:17:46 +01:00
Moar Custom platforms
This commit is contained in:
parent
6306d44955
commit
8be444a25e
14 changed files with 253 additions and 44 deletions
|
@ -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({
|
||||
|
|
|
@ -111,7 +111,7 @@ void CCS811Component::dump_config() {
|
|||
ESP_LOGCONFIG(TAG, "CCS811");
|
||||
LOG_I2C_DEVICE(this)
|
||||
LOG_UPDATE_INTERVAL(this)
|
||||
LOG_SENSOR(" ", "CO2 Sesnor", this->co2_)
|
||||
LOG_SENSOR(" ", "CO2 Sensor", this->co2_)
|
||||
LOG_SENSOR(" ", "TVOC Sensor", this->tvoc_)
|
||||
if (this->baseline_) {
|
||||
ESP_LOGCONFIG(TAG, " Baseline: %04X", *this->baseline_);
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import i2c, sensor
|
||||
from esphome.const import CONF_ID, ICON_GAS_CYLINDER, ICON_RADIATOR, UNIT_PARTS_PER_MILLION, \
|
||||
UNIT_PARTS_PER_BILLION, CONF_TEMPERATURE, CONF_HUMIDITY
|
||||
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']
|
||||
|
||||
|
@ -15,7 +15,8 @@ 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_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.Optional(CONF_BASELINE): cv.hex_uint16_t,
|
||||
|
|
|
@ -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),
|
||||
|
|
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')
|
||||
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.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({
|
||||
}),
|
||||
CONF_FLOAT: cv.Schema({
|
||||
cv.GenerateID(): cv.declare_id(CustomFloatOutputConstructor),
|
||||
cv.Required(CONF_LAMBDA): cv.returning_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
|
||||
})
|
||||
}, lower=True)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
|
|
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
|
|
@ -16,7 +16,7 @@ from esphome import core
|
|||
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_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, \
|
||||
TimePeriodMilliseconds, TimePeriodSeconds, TimePeriodMinutes
|
||||
from esphome.helpers import list_starts_with
|
||||
|
@ -1078,6 +1078,24 @@ def extract_keys(schema):
|
|||
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):
|
||||
"""Mark this key as being an auto-generated ID key."""
|
||||
def __init__(self, key=CONF_ID):
|
||||
|
|
Loading…
Reference in a new issue