mirror of
https://github.com/esphome/esphome.git
synced 2026-02-08 08:41:59 +00:00
Merge branch 'dev' into combine_logs
This commit is contained in:
@@ -3,6 +3,7 @@ import esphome.codegen as cg
|
||||
from esphome.components import output
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID, CONF_METHOD, CONF_MIN_POWER
|
||||
from esphome.core import CORE
|
||||
|
||||
CODEOWNERS = ["@glmnet"]
|
||||
|
||||
@@ -36,6 +37,12 @@ CONFIG_SCHEMA = cv.All(
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
if CORE.is_esp8266:
|
||||
# ac_dimmer uses setTimer1Callback which requires the waveform generator
|
||||
from esphome.components.esp8266.const import require_waveform
|
||||
|
||||
require_waveform()
|
||||
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
|
||||
|
||||
@@ -644,6 +644,7 @@ CONF_DISABLE_VFS_SUPPORT_SELECT = "disable_vfs_support_select"
|
||||
CONF_DISABLE_VFS_SUPPORT_DIR = "disable_vfs_support_dir"
|
||||
CONF_FREERTOS_IN_IRAM = "freertos_in_iram"
|
||||
CONF_RINGBUF_IN_IRAM = "ringbuf_in_iram"
|
||||
CONF_HEAP_IN_IRAM = "heap_in_iram"
|
||||
CONF_LOOP_TASK_STACK_SIZE = "loop_task_stack_size"
|
||||
|
||||
# VFS requirement tracking
|
||||
@@ -745,6 +746,7 @@ FRAMEWORK_SCHEMA = cv.Schema(
|
||||
cv.Optional(CONF_DISABLE_VFS_SUPPORT_DIR, default=True): cv.boolean,
|
||||
cv.Optional(CONF_FREERTOS_IN_IRAM, default=False): cv.boolean,
|
||||
cv.Optional(CONF_RINGBUF_IN_IRAM, default=False): cv.boolean,
|
||||
cv.Optional(CONF_HEAP_IN_IRAM, default=False): cv.boolean,
|
||||
cv.Optional(CONF_EXECUTE_FROM_PSRAM, default=False): cv.boolean,
|
||||
cv.Optional(CONF_LOOP_TASK_STACK_SIZE, default=8192): cv.int_range(
|
||||
min=8192, max=32768
|
||||
@@ -1090,6 +1092,12 @@ async def to_code(config):
|
||||
# Place in flash to save IRAM (default)
|
||||
add_idf_sdkconfig_option("CONFIG_RINGBUF_PLACE_FUNCTIONS_INTO_FLASH", True)
|
||||
|
||||
# Place heap functions into flash to save IRAM (~4-6KB savings)
|
||||
# Safe as long as heap functions are not called from ISRs (which they shouldn't be)
|
||||
# Users can set heap_in_iram: true as an escape hatch if needed
|
||||
if not conf[CONF_ADVANCED][CONF_HEAP_IN_IRAM]:
|
||||
add_idf_sdkconfig_option("CONFIG_HEAP_PLACE_FUNCTION_INTO_FLASH", True)
|
||||
|
||||
# Setup watchdog
|
||||
add_idf_sdkconfig_option("CONFIG_ESP_TASK_WDT", True)
|
||||
add_idf_sdkconfig_option("CONFIG_ESP_TASK_WDT_PANIC", True)
|
||||
|
||||
@@ -98,7 +98,7 @@ void ESP32RMTLEDStripLightOutput::setup() {
|
||||
channel.trans_queue_depth = 1;
|
||||
channel.flags.io_loop_back = 0;
|
||||
channel.flags.io_od_mode = 0;
|
||||
channel.flags.invert_out = 0;
|
||||
channel.flags.invert_out = this->invert_out_;
|
||||
channel.flags.with_dma = this->use_dma_;
|
||||
channel.intr_priority = 0;
|
||||
if (rmt_new_tx_channel(&channel, &this->channel_) != ESP_OK) {
|
||||
|
||||
@@ -49,6 +49,7 @@ class ESP32RMTLEDStripLightOutput : public light::AddressableLight {
|
||||
}
|
||||
|
||||
void set_pin(uint8_t pin) { this->pin_ = pin; }
|
||||
void set_inverted(bool inverted) { this->invert_out_ = inverted; }
|
||||
void set_num_leds(uint16_t num_leds) { this->num_leds_ = num_leds; }
|
||||
void set_is_rgbw(bool is_rgbw) { this->is_rgbw_ = is_rgbw; }
|
||||
void set_is_wrgb(bool is_wrgb) { this->is_wrgb_ = is_wrgb; }
|
||||
@@ -93,6 +94,7 @@ class ESP32RMTLEDStripLightOutput : public light::AddressableLight {
|
||||
bool is_wrgb_{false};
|
||||
bool use_dma_{false};
|
||||
bool use_psram_{false};
|
||||
bool invert_out_{false};
|
||||
|
||||
RGBOrder rgb_order_{ORDER_RGB};
|
||||
|
||||
|
||||
@@ -8,9 +8,11 @@ from esphome.components.const import CONF_USE_PSRAM
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_CHIPSET,
|
||||
CONF_INVERTED,
|
||||
CONF_IS_RGBW,
|
||||
CONF_MAX_REFRESH_RATE,
|
||||
CONF_NUM_LEDS,
|
||||
CONF_NUMBER,
|
||||
CONF_OUTPUT_ID,
|
||||
CONF_PIN,
|
||||
CONF_RGB_ORDER,
|
||||
@@ -71,7 +73,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
light.ADDRESSABLE_LIGHT_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(ESP32RMTLEDStripLightOutput),
|
||||
cv.Required(CONF_PIN): pins.internal_gpio_output_pin_number,
|
||||
cv.Required(CONF_PIN): pins.internal_gpio_output_pin_schema,
|
||||
cv.Required(CONF_NUM_LEDS): cv.positive_not_null_int,
|
||||
cv.Required(CONF_RGB_ORDER): cv.enum(RGB_ORDERS, upper=True),
|
||||
cv.SplitDefault(
|
||||
@@ -132,7 +134,9 @@ async def to_code(config):
|
||||
await cg.register_component(var, config)
|
||||
|
||||
cg.add(var.set_num_leds(config[CONF_NUM_LEDS]))
|
||||
cg.add(var.set_pin(config[CONF_PIN]))
|
||||
cg.add(var.set_pin(config[CONF_PIN][CONF_NUMBER]))
|
||||
if config[CONF_PIN][CONF_INVERTED]:
|
||||
cg.add(var.set_inverted(True))
|
||||
|
||||
if CONF_MAX_REFRESH_RATE in config:
|
||||
cg.add(var.set_max_refresh_rate(config[CONF_MAX_REFRESH_RATE]))
|
||||
|
||||
@@ -66,6 +66,8 @@ void Logger::pre_setup() {
|
||||
void HOT Logger::write_msg_(const char *msg, size_t len) {
|
||||
// Single write with newline already in buffer (added by caller)
|
||||
#ifdef CONFIG_PRINTK
|
||||
// Requires the debug component and an active SWD connection.
|
||||
// It is used for pyocd rtt -t nrf52840
|
||||
k_str_out(const_cast<char *>(msg), len);
|
||||
#endif
|
||||
if (this->uart_dev_ == nullptr) {
|
||||
|
||||
123
esphome/components/template/water_heater/__init__.py
Normal file
123
esphome/components/template/water_heater/__init__.py
Normal file
@@ -0,0 +1,123 @@
|
||||
from esphome import automation
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import water_heater
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_MODE,
|
||||
CONF_OPTIMISTIC,
|
||||
CONF_RESTORE_MODE,
|
||||
CONF_SET_ACTION,
|
||||
CONF_SUPPORTED_MODES,
|
||||
CONF_TARGET_TEMPERATURE,
|
||||
)
|
||||
from esphome.core import ID
|
||||
from esphome.cpp_generator import MockObj, TemplateArgsType
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from .. import template_ns
|
||||
|
||||
CONF_CURRENT_TEMPERATURE = "current_temperature"
|
||||
|
||||
TemplateWaterHeater = template_ns.class_(
|
||||
"TemplateWaterHeater", water_heater.WaterHeater
|
||||
)
|
||||
|
||||
TemplateWaterHeaterPublishAction = template_ns.class_(
|
||||
"TemplateWaterHeaterPublishAction",
|
||||
automation.Action,
|
||||
cg.Parented.template(TemplateWaterHeater),
|
||||
)
|
||||
|
||||
TemplateWaterHeaterRestoreMode = template_ns.enum("TemplateWaterHeaterRestoreMode")
|
||||
RESTORE_MODES = {
|
||||
"NO_RESTORE": TemplateWaterHeaterRestoreMode.WATER_HEATER_NO_RESTORE,
|
||||
"RESTORE": TemplateWaterHeaterRestoreMode.WATER_HEATER_RESTORE,
|
||||
"RESTORE_AND_CALL": TemplateWaterHeaterRestoreMode.WATER_HEATER_RESTORE_AND_CALL,
|
||||
}
|
||||
|
||||
CONFIG_SCHEMA = water_heater.water_heater_schema(TemplateWaterHeater).extend(
|
||||
{
|
||||
cv.Optional(CONF_OPTIMISTIC, default=True): cv.boolean,
|
||||
cv.Optional(CONF_SET_ACTION): automation.validate_automation(single=True),
|
||||
cv.Optional(CONF_RESTORE_MODE, default="NO_RESTORE"): cv.enum(
|
||||
RESTORE_MODES, upper=True
|
||||
),
|
||||
cv.Optional(CONF_CURRENT_TEMPERATURE): cv.returning_lambda,
|
||||
cv.Optional(CONF_MODE): cv.returning_lambda,
|
||||
cv.Optional(CONF_SUPPORTED_MODES): cv.ensure_list(
|
||||
water_heater.validate_water_heater_mode
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await water_heater.register_water_heater(var, config)
|
||||
|
||||
cg.add(var.set_optimistic(config[CONF_OPTIMISTIC]))
|
||||
|
||||
if CONF_SET_ACTION in config:
|
||||
await automation.build_automation(
|
||||
var.get_set_trigger(), [], config[CONF_SET_ACTION]
|
||||
)
|
||||
|
||||
cg.add(var.set_restore_mode(config[CONF_RESTORE_MODE]))
|
||||
|
||||
if CONF_CURRENT_TEMPERATURE in config:
|
||||
template_ = await cg.process_lambda(
|
||||
config[CONF_CURRENT_TEMPERATURE],
|
||||
[],
|
||||
return_type=cg.optional.template(cg.float_),
|
||||
)
|
||||
cg.add(var.set_current_temperature_lambda(template_))
|
||||
|
||||
if CONF_MODE in config:
|
||||
template_ = await cg.process_lambda(
|
||||
config[CONF_MODE],
|
||||
[],
|
||||
return_type=cg.optional.template(water_heater.WaterHeaterMode),
|
||||
)
|
||||
cg.add(var.set_mode_lambda(template_))
|
||||
|
||||
if CONF_SUPPORTED_MODES in config:
|
||||
cg.add(var.set_supported_modes(config[CONF_SUPPORTED_MODES]))
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"water_heater.template.publish",
|
||||
TemplateWaterHeaterPublishAction,
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(TemplateWaterHeater),
|
||||
cv.Optional(CONF_CURRENT_TEMPERATURE): cv.templatable(cv.temperature),
|
||||
cv.Optional(CONF_TARGET_TEMPERATURE): cv.templatable(cv.temperature),
|
||||
cv.Optional(CONF_MODE): cv.templatable(
|
||||
water_heater.validate_water_heater_mode
|
||||
),
|
||||
}
|
||||
),
|
||||
)
|
||||
async def water_heater_template_publish_to_code(
|
||||
config: ConfigType,
|
||||
action_id: ID,
|
||||
template_arg: cg.TemplateArguments,
|
||||
args: TemplateArgsType,
|
||||
) -> MockObj:
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
|
||||
if current_temp := config.get(CONF_CURRENT_TEMPERATURE):
|
||||
template_ = await cg.templatable(current_temp, args, float)
|
||||
cg.add(var.set_current_temperature(template_))
|
||||
|
||||
if target_temp := config.get(CONF_TARGET_TEMPERATURE):
|
||||
template_ = await cg.templatable(target_temp, args, float)
|
||||
cg.add(var.set_target_temperature(template_))
|
||||
|
||||
if mode := config.get(CONF_MODE):
|
||||
template_ = await cg.templatable(mode, args, water_heater.WaterHeaterMode)
|
||||
cg.add(var.set_mode(template_))
|
||||
|
||||
return var
|
||||
35
esphome/components/template/water_heater/automation.h
Normal file
35
esphome/components/template/water_heater/automation.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include "template_water_heater.h"
|
||||
#include "esphome/core/automation.h"
|
||||
|
||||
namespace esphome::template_ {
|
||||
|
||||
template<typename... Ts>
|
||||
class TemplateWaterHeaterPublishAction : public Action<Ts...>, public Parented<TemplateWaterHeater> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(float, current_temperature)
|
||||
TEMPLATABLE_VALUE(float, target_temperature)
|
||||
TEMPLATABLE_VALUE(water_heater::WaterHeaterMode, mode)
|
||||
|
||||
void play(const Ts &...x) override {
|
||||
if (this->current_temperature_.has_value()) {
|
||||
this->parent_->set_current_temperature(this->current_temperature_.value(x...));
|
||||
}
|
||||
bool needs_call = this->target_temperature_.has_value() || this->mode_.has_value();
|
||||
if (needs_call) {
|
||||
auto call = this->parent_->make_call();
|
||||
if (this->target_temperature_.has_value()) {
|
||||
call.set_target_temperature(this->target_temperature_.value(x...));
|
||||
}
|
||||
if (this->mode_.has_value()) {
|
||||
call.set_mode(this->mode_.value(x...));
|
||||
}
|
||||
call.perform();
|
||||
} else {
|
||||
this->parent_->publish_state();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace esphome::template_
|
||||
@@ -0,0 +1,88 @@
|
||||
#include "template_water_heater.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome::template_ {
|
||||
|
||||
static const char *const TAG = "template.water_heater";
|
||||
|
||||
TemplateWaterHeater::TemplateWaterHeater() : set_trigger_(new Trigger<>()) {}
|
||||
|
||||
void TemplateWaterHeater::setup() {
|
||||
if (this->restore_mode_ == TemplateWaterHeaterRestoreMode::WATER_HEATER_RESTORE ||
|
||||
this->restore_mode_ == TemplateWaterHeaterRestoreMode::WATER_HEATER_RESTORE_AND_CALL) {
|
||||
auto restore = this->restore_state();
|
||||
|
||||
if (restore.has_value()) {
|
||||
restore->perform();
|
||||
}
|
||||
}
|
||||
if (!this->current_temperature_f_.has_value() && !this->mode_f_.has_value())
|
||||
this->disable_loop();
|
||||
}
|
||||
|
||||
water_heater::WaterHeaterTraits TemplateWaterHeater::traits() {
|
||||
auto traits = water_heater::WaterHeater::get_traits();
|
||||
|
||||
if (!this->supported_modes_.empty()) {
|
||||
traits.set_supported_modes(this->supported_modes_);
|
||||
}
|
||||
|
||||
traits.set_supports_current_temperature(true);
|
||||
return traits;
|
||||
}
|
||||
|
||||
void TemplateWaterHeater::loop() {
|
||||
bool changed = false;
|
||||
|
||||
auto curr_temp = this->current_temperature_f_.call();
|
||||
if (curr_temp.has_value()) {
|
||||
if (*curr_temp != this->current_temperature_) {
|
||||
this->current_temperature_ = *curr_temp;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
auto new_mode = this->mode_f_.call();
|
||||
if (new_mode.has_value()) {
|
||||
if (*new_mode != this->mode_) {
|
||||
this->mode_ = *new_mode;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
this->publish_state();
|
||||
}
|
||||
}
|
||||
|
||||
void TemplateWaterHeater::dump_config() {
|
||||
LOG_WATER_HEATER("", "Template Water Heater", this);
|
||||
ESP_LOGCONFIG(TAG, " Optimistic: %s", YESNO(this->optimistic_));
|
||||
}
|
||||
|
||||
float TemplateWaterHeater::get_setup_priority() const { return setup_priority::HARDWARE; }
|
||||
|
||||
water_heater::WaterHeaterCallInternal TemplateWaterHeater::make_call() {
|
||||
return water_heater::WaterHeaterCallInternal(this);
|
||||
}
|
||||
|
||||
void TemplateWaterHeater::control(const water_heater::WaterHeaterCall &call) {
|
||||
if (call.get_mode().has_value()) {
|
||||
if (this->optimistic_) {
|
||||
this->mode_ = *call.get_mode();
|
||||
}
|
||||
}
|
||||
if (!std::isnan(call.get_target_temperature())) {
|
||||
if (this->optimistic_) {
|
||||
this->target_temperature_ = call.get_target_temperature();
|
||||
}
|
||||
}
|
||||
|
||||
this->set_trigger_->trigger();
|
||||
|
||||
if (this->optimistic_) {
|
||||
this->publish_state();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace esphome::template_
|
||||
@@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/template_lambda.h"
|
||||
#include "esphome/components/water_heater/water_heater.h"
|
||||
|
||||
namespace esphome::template_ {
|
||||
|
||||
enum TemplateWaterHeaterRestoreMode {
|
||||
WATER_HEATER_NO_RESTORE,
|
||||
WATER_HEATER_RESTORE,
|
||||
WATER_HEATER_RESTORE_AND_CALL,
|
||||
};
|
||||
|
||||
class TemplateWaterHeater : public water_heater::WaterHeater {
|
||||
public:
|
||||
TemplateWaterHeater();
|
||||
|
||||
template<typename F> void set_current_temperature_lambda(F &&f) {
|
||||
this->current_temperature_f_.set(std::forward<F>(f));
|
||||
}
|
||||
template<typename F> void set_mode_lambda(F &&f) { this->mode_f_.set(std::forward<F>(f)); }
|
||||
|
||||
void set_optimistic(bool optimistic) { this->optimistic_ = optimistic; }
|
||||
void set_restore_mode(TemplateWaterHeaterRestoreMode restore_mode) { this->restore_mode_ = restore_mode; }
|
||||
void set_supported_modes(const std::initializer_list<water_heater::WaterHeaterMode> &modes) {
|
||||
this->supported_modes_ = modes;
|
||||
}
|
||||
|
||||
Trigger<> *get_set_trigger() const { return this->set_trigger_; }
|
||||
|
||||
void setup() override;
|
||||
void loop() override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override;
|
||||
|
||||
water_heater::WaterHeaterCallInternal make_call() override;
|
||||
|
||||
protected:
|
||||
void control(const water_heater::WaterHeaterCall &call) override;
|
||||
water_heater::WaterHeaterTraits traits() override;
|
||||
|
||||
// Ordered to minimize padding on 32-bit: 4-byte members first, then smaller
|
||||
Trigger<> *set_trigger_;
|
||||
TemplateLambda<float> current_temperature_f_;
|
||||
TemplateLambda<water_heater::WaterHeaterMode> mode_f_;
|
||||
TemplateWaterHeaterRestoreMode restore_mode_{WATER_HEATER_NO_RESTORE};
|
||||
water_heater::WaterHeaterModeMask supported_modes_;
|
||||
bool optimistic_{true};
|
||||
};
|
||||
|
||||
} // namespace esphome::template_
|
||||
@@ -794,13 +794,15 @@ void WiFiComponent::start_connecting(const WiFiAP &ap) {
|
||||
#ifdef USE_WIFI_WPA2_EAP
|
||||
if (ap.get_eap().has_value()) {
|
||||
EAPAuth eap_config = ap.get_eap().value();
|
||||
// clang-format off
|
||||
ESP_LOGV(
|
||||
TAG,
|
||||
" WPA2 Enterprise authentication configured:\n"
|
||||
" Identity: " LOG_SECRET("'%s'") "\n"
|
||||
" Username: " LOG_SECRET("'%s'") "\n"
|
||||
" Password: " LOG_SECRET("'%s'"),
|
||||
" Username: " LOG_SECRET("'%s'") "\n"
|
||||
" Password: " LOG_SECRET("'%s'"),
|
||||
eap_config.identity.c_str(), eap_config.username.c_str(), eap_config.password.c_str());
|
||||
// clang-format on
|
||||
#if defined(USE_ESP32) && defined(USE_WIFI_WPA2_EAP) && ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
|
||||
ESP_LOGV(TAG, " TTLS Phase 2: " LOG_SECRET("'%s'"), eap_phase2_to_str(eap_config.ttls_phase_2));
|
||||
#endif
|
||||
|
||||
@@ -12,14 +12,20 @@ platformio==6.1.18 # When updating platformio, also update /docker/Dockerfile
|
||||
esptool==5.1.0
|
||||
click==8.1.7
|
||||
esphome-dashboard==20251013.0
|
||||
aioesphomeapi==43.10.0
|
||||
aioesphomeapi==43.10.1
|
||||
zeroconf==0.148.0
|
||||
puremagic==1.30
|
||||
ruamel.yaml==0.19.1 # dashboard_import
|
||||
ruamel.yaml.clib==0.2.15 # dashboard_import
|
||||
esphome-glyphsets==0.2.0
|
||||
pillow==11.3.0
|
||||
cairosvg==2.8.2
|
||||
|
||||
# pycairo fork for Windows
|
||||
cairosvg @ git+https://github.com/clydebarrow/cairosvg.git@release ; sys_platform == 'win32'
|
||||
|
||||
# Original for everything else
|
||||
cairosvg==2.8.2 ; sys_platform != 'win32'
|
||||
|
||||
freetype-py==2.5.1
|
||||
jinja2==3.1.6
|
||||
bleak==2.1.1
|
||||
|
||||
@@ -9,6 +9,18 @@ esphome:
|
||||
id: template_sens
|
||||
state: !lambda "return 42.0;"
|
||||
|
||||
- water_heater.template.publish:
|
||||
id: template_water_heater
|
||||
target_temperature: 50.0
|
||||
mode: ECO
|
||||
|
||||
# Templated
|
||||
- water_heater.template.publish:
|
||||
id: template_water_heater
|
||||
current_temperature: !lambda "return 45.0;"
|
||||
target_temperature: !lambda "return 55.0;"
|
||||
mode: !lambda "return water_heater::WATER_HEATER_MODE_GAS;"
|
||||
|
||||
# Test C++ API: set_template() with stateless lambda (no captures)
|
||||
# NOTE: set_template() is not intended to be a public API, but we test it to ensure it doesn't break.
|
||||
- lambda: |-
|
||||
@@ -299,6 +311,24 @@ alarm_control_panel:
|
||||
codes:
|
||||
- "1234"
|
||||
|
||||
water_heater:
|
||||
- platform: template
|
||||
id: template_water_heater
|
||||
name: "Template Water Heater"
|
||||
optimistic: true
|
||||
current_temperature: !lambda "return 42.0f;"
|
||||
mode: !lambda "return water_heater::WATER_HEATER_MODE_ECO;"
|
||||
supported_modes:
|
||||
- "OFF"
|
||||
- ECO
|
||||
- GAS
|
||||
- ELECTRIC
|
||||
- HEAT_PUMP
|
||||
- HIGH_DEMAND
|
||||
- PERFORMANCE
|
||||
set_action:
|
||||
- logger.log: "set_action"
|
||||
|
||||
datetime:
|
||||
- platform: template
|
||||
name: Date
|
||||
|
||||
Reference in New Issue
Block a user