mirror of
https://github.com/esphome/esphome.git
synced 2026-05-27 11:56:11 +08:00
[output] Gate FloatOutput power scaling fields behind USE_OUTPUT_FLOAT_POWER_SCALING (#15998)
This commit is contained in:
@@ -54,10 +54,16 @@ async def setup_output_platform_(obj, config):
|
|||||||
power_supply_ = await cg.get_variable(config[CONF_POWER_SUPPLY])
|
power_supply_ = await cg.get_variable(config[CONF_POWER_SUPPLY])
|
||||||
cg.add(obj.set_power_supply(power_supply_))
|
cg.add(obj.set_power_supply(power_supply_))
|
||||||
if CONF_MAX_POWER in config:
|
if CONF_MAX_POWER in config:
|
||||||
|
cg.add_define("USE_OUTPUT_FLOAT_POWER_SCALING")
|
||||||
cg.add(obj.set_max_power(config[CONF_MAX_POWER]))
|
cg.add(obj.set_max_power(config[CONF_MAX_POWER]))
|
||||||
if CONF_MIN_POWER in config:
|
if CONF_MIN_POWER in config:
|
||||||
|
cg.add_define("USE_OUTPUT_FLOAT_POWER_SCALING")
|
||||||
cg.add(obj.set_min_power(config[CONF_MIN_POWER]))
|
cg.add(obj.set_min_power(config[CONF_MIN_POWER]))
|
||||||
if CONF_ZERO_MEANS_ZERO in config:
|
# Only emit when zero_means_zero is actually enabled. The schema defaults to False
|
||||||
|
# so this key is always present; emitting unconditionally would force
|
||||||
|
# USE_OUTPUT_FLOAT_POWER_SCALING on for every output, defeating the gate.
|
||||||
|
if config.get(CONF_ZERO_MEANS_ZERO):
|
||||||
|
cg.add_define("USE_OUTPUT_FLOAT_POWER_SCALING")
|
||||||
cg.add(obj.set_zero_means_zero(config[CONF_ZERO_MEANS_ZERO]))
|
cg.add(obj.set_zero_means_zero(config[CONF_ZERO_MEANS_ZERO]))
|
||||||
|
|
||||||
|
|
||||||
@@ -121,6 +127,7 @@ async def output_set_level_to_code(config, action_id, template_arg, args):
|
|||||||
synchronous=True,
|
synchronous=True,
|
||||||
)
|
)
|
||||||
async def output_set_min_power_to_code(config, action_id, template_arg, args):
|
async def output_set_min_power_to_code(config, action_id, template_arg, args):
|
||||||
|
cg.add_define("USE_OUTPUT_FLOAT_POWER_SCALING")
|
||||||
paren = await cg.get_variable(config[CONF_ID])
|
paren = await cg.get_variable(config[CONF_ID])
|
||||||
var = cg.new_Pvariable(action_id, template_arg, paren)
|
var = cg.new_Pvariable(action_id, template_arg, paren)
|
||||||
template_ = await cg.templatable(config[CONF_MIN_POWER], args, cg.float_)
|
template_ = await cg.templatable(config[CONF_MIN_POWER], args, cg.float_)
|
||||||
@@ -140,6 +147,7 @@ async def output_set_min_power_to_code(config, action_id, template_arg, args):
|
|||||||
synchronous=True,
|
synchronous=True,
|
||||||
)
|
)
|
||||||
async def output_set_max_power_to_code(config, action_id, template_arg, args):
|
async def output_set_max_power_to_code(config, action_id, template_arg, args):
|
||||||
|
cg.add_define("USE_OUTPUT_FLOAT_POWER_SCALING")
|
||||||
paren = await cg.get_variable(config[CONF_ID])
|
paren = await cg.get_variable(config[CONF_ID])
|
||||||
var = cg.new_Pvariable(action_id, template_arg, paren)
|
var = cg.new_Pvariable(action_id, template_arg, paren)
|
||||||
template_ = await cg.templatable(config[CONF_MAX_POWER], args, cg.float_)
|
template_ = await cg.templatable(config[CONF_MAX_POWER], args, cg.float_)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
#include "esphome/core/automation.h"
|
#include "esphome/core/automation.h"
|
||||||
|
#include "esphome/core/defines.h"
|
||||||
#include "esphome/components/output/binary_output.h"
|
#include "esphome/components/output/binary_output.h"
|
||||||
#include "esphome/components/output/float_output.h"
|
#include "esphome/components/output/float_output.h"
|
||||||
|
|
||||||
@@ -40,6 +41,7 @@ template<typename... Ts> class SetLevelAction : public Action<Ts...> {
|
|||||||
FloatOutput *output_;
|
FloatOutput *output_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef USE_OUTPUT_FLOAT_POWER_SCALING
|
||||||
template<typename... Ts> class SetMinPowerAction : public Action<Ts...> {
|
template<typename... Ts> class SetMinPowerAction : public Action<Ts...> {
|
||||||
public:
|
public:
|
||||||
SetMinPowerAction(FloatOutput *output) : output_(output) {}
|
SetMinPowerAction(FloatOutput *output) : output_(output) {}
|
||||||
@@ -63,6 +65,7 @@ template<typename... Ts> class SetMaxPowerAction : public Action<Ts...> {
|
|||||||
protected:
|
protected:
|
||||||
FloatOutput *output_;
|
FloatOutput *output_;
|
||||||
};
|
};
|
||||||
|
#endif // USE_OUTPUT_FLOAT_POWER_SCALING
|
||||||
|
|
||||||
} // namespace output
|
} // namespace output
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|||||||
@@ -7,13 +7,15 @@ namespace output {
|
|||||||
|
|
||||||
static const char *const TAG = "output.float";
|
static const char *const TAG = "output.float";
|
||||||
|
|
||||||
|
#ifdef USE_OUTPUT_FLOAT_POWER_SCALING
|
||||||
void FloatOutput::set_max_power(float max_power) {
|
void FloatOutput::set_max_power(float max_power) {
|
||||||
this->max_power_ = clamp(max_power, this->min_power_, 1.0f); // Clamp to MIN>=MAX>=1.0
|
this->max_power_ = clamp(max_power, this->min_power_, 1.0f); // Clamp to min_power <= max <= 1.0
|
||||||
}
|
}
|
||||||
|
|
||||||
void FloatOutput::set_min_power(float min_power) {
|
void FloatOutput::set_min_power(float min_power) {
|
||||||
this->min_power_ = clamp(min_power, 0.0f, this->max_power_); // Clamp to 0.0>=MIN>=MAX
|
this->min_power_ = clamp(min_power, 0.0f, this->max_power_); // Clamp to 0.0 <= min <= max_power
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void FloatOutput::set_level(float state) {
|
void FloatOutput::set_level(float state) {
|
||||||
state = clamp(state, 0.0f, 1.0f);
|
state = clamp(state, 0.0f, 1.0f);
|
||||||
@@ -26,8 +28,10 @@ void FloatOutput::set_level(float state) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_OUTPUT_FLOAT_POWER_SCALING
|
||||||
if (state != 0.0f || !this->zero_means_zero_) // regardless of min_power_, 0.0 means off
|
if (state != 0.0f || !this->zero_means_zero_) // regardless of min_power_, 0.0 means off
|
||||||
state = (state * (this->max_power_ - this->min_power_)) + this->min_power_;
|
state = (state * (this->max_power_ - this->min_power_)) + this->min_power_;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (this->is_inverted())
|
if (this->is_inverted())
|
||||||
state = 1.0f - state;
|
state = 1.0f - state;
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/core/defines.h"
|
||||||
#include "binary_output.h"
|
#include "binary_output.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace output {
|
namespace output {
|
||||||
|
|
||||||
|
#ifdef USE_OUTPUT_FLOAT_POWER_SCALING
|
||||||
#define LOG_FLOAT_OUTPUT(this) \
|
#define LOG_FLOAT_OUTPUT(this) \
|
||||||
LOG_BINARY_OUTPUT(this) \
|
LOG_BINARY_OUTPUT(this) \
|
||||||
if (this->max_power_ != 1.0f) { \
|
if (this->max_power_ != 1.0f) { \
|
||||||
@@ -14,6 +16,9 @@ namespace output {
|
|||||||
if (this->min_power_ != 0.0f) { \
|
if (this->min_power_ != 0.0f) { \
|
||||||
ESP_LOGCONFIG(TAG, " Min Power: %.1f%%", this->min_power_ * 100.0f); \
|
ESP_LOGCONFIG(TAG, " Min Power: %.1f%%", this->min_power_ * 100.0f); \
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
#define LOG_FLOAT_OUTPUT(this) LOG_BINARY_OUTPUT(this)
|
||||||
|
#endif
|
||||||
|
|
||||||
/** Base class for all output components that can output a variable level, like PWM.
|
/** Base class for all output components that can output a variable level, like PWM.
|
||||||
*
|
*
|
||||||
@@ -22,14 +27,18 @@ namespace output {
|
|||||||
* makes using maths much easier and (in theory) supports all possible bit depths.
|
* makes using maths much easier and (in theory) supports all possible bit depths.
|
||||||
*
|
*
|
||||||
* If you want to create a FloatOutput yourself, you essentially just have to override write_state(float).
|
* If you want to create a FloatOutput yourself, you essentially just have to override write_state(float).
|
||||||
* That method will be called for you with inversion and max-min power and offset to min power already applied.
|
* That method will be called for you with inversion already applied. When USE_OUTPUT_FLOAT_POWER_SCALING is
|
||||||
|
* enabled (set automatically by Python codegen if any output uses min_power/max_power/zero_means_zero or the
|
||||||
|
* matching runtime actions), the value will additionally have max-min power scaling and offset to min_power
|
||||||
|
* applied; otherwise only inversion is applied.
|
||||||
*
|
*
|
||||||
* This interface is compatible with BinaryOutput (and will automatically convert the binary states to floating
|
* This interface is compatible with BinaryOutput (and will automatically convert the binary states to floating
|
||||||
* point states for you). Additionally, this class provides a way for users to set a minimum and/or maximum power
|
* point states for you). Additionally, this class provides a way for users to set a minimum and/or maximum power
|
||||||
* output
|
* output (gated on USE_OUTPUT_FLOAT_POWER_SCALING).
|
||||||
*/
|
*/
|
||||||
class FloatOutput : public BinaryOutput {
|
class FloatOutput : public BinaryOutput {
|
||||||
public:
|
public:
|
||||||
|
#ifdef USE_OUTPUT_FLOAT_POWER_SCALING
|
||||||
/** Set the maximum power output of this component.
|
/** Set the maximum power output of this component.
|
||||||
*
|
*
|
||||||
* All values are multiplied by max_power - min_power and offset to min_power to get the adjusted value.
|
* All values are multiplied by max_power - min_power and offset to min_power to get the adjusted value.
|
||||||
@@ -51,6 +60,32 @@ class FloatOutput : public BinaryOutput {
|
|||||||
* @param zero_means_zero True if a 0 state should mean 0 and not min_power.
|
* @param zero_means_zero True if a 0 state should mean 0 and not min_power.
|
||||||
*/
|
*/
|
||||||
void set_zero_means_zero(bool zero_means_zero) { this->zero_means_zero_ = zero_means_zero; }
|
void set_zero_means_zero(bool zero_means_zero) { this->zero_means_zero_ = zero_means_zero; }
|
||||||
|
#else
|
||||||
|
// Compile-time guards for users calling these methods from lambdas (documented usage at
|
||||||
|
// https://esphome.io/components/output/#output-set_min_power_action). When power scaling
|
||||||
|
// is compiled out, these template stubs fail to compile with an actionable error pointing
|
||||||
|
// at the user's lambda. Templating on a default-false bool means static_assert only fires
|
||||||
|
// on instantiation (i.e. when the user actually calls the method), not on every parse.
|
||||||
|
template<bool _use_output_float_power_scaling = false> void set_max_power(float max_power) {
|
||||||
|
static_assert(_use_output_float_power_scaling,
|
||||||
|
"set_max_power() requires USE_OUTPUT_FLOAT_POWER_SCALING. "
|
||||||
|
"To enable it, add 'max_power: 100%' (or any value) to one output entry in your YAML — "
|
||||||
|
"the codegen will then keep the scaling fields. "
|
||||||
|
"See https://esphome.io/components/output/ for details.");
|
||||||
|
}
|
||||||
|
template<bool _use_output_float_power_scaling = false> void set_min_power(float min_power) {
|
||||||
|
static_assert(_use_output_float_power_scaling,
|
||||||
|
"set_min_power() requires USE_OUTPUT_FLOAT_POWER_SCALING. "
|
||||||
|
"To enable it, add 'min_power: 0%' (or any value) to one output entry in your YAML — "
|
||||||
|
"the codegen will then keep the scaling fields. "
|
||||||
|
"See https://esphome.io/components/output/ for details.");
|
||||||
|
}
|
||||||
|
template<bool _use_output_float_power_scaling = false> void set_zero_means_zero(bool zero_means_zero) {
|
||||||
|
static_assert(_use_output_float_power_scaling,
|
||||||
|
"set_zero_means_zero() requires USE_OUTPUT_FLOAT_POWER_SCALING. "
|
||||||
|
"To enable it, add 'zero_means_zero: true' to one output entry in your YAML.");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/** Set the level of this float output, this is called from the front-end.
|
/** Set the level of this float output, this is called from the front-end.
|
||||||
*
|
*
|
||||||
@@ -69,20 +104,30 @@ class FloatOutput : public BinaryOutput {
|
|||||||
// ========== INTERNAL METHODS ==========
|
// ========== INTERNAL METHODS ==========
|
||||||
// (In most use cases you won't need these)
|
// (In most use cases you won't need these)
|
||||||
|
|
||||||
|
#ifdef USE_OUTPUT_FLOAT_POWER_SCALING
|
||||||
/// Get the maximum power output.
|
/// Get the maximum power output.
|
||||||
float get_max_power() const { return this->max_power_; }
|
float get_max_power() const { return this->max_power_; }
|
||||||
|
|
||||||
/// Get the minimum power output.
|
/// Get the minimum power output.
|
||||||
float get_min_power() const { return this->min_power_; }
|
float get_min_power() const { return this->min_power_; }
|
||||||
|
#else
|
||||||
|
/// Get the maximum power output.
|
||||||
|
float get_max_power() const { return 1.0f; }
|
||||||
|
|
||||||
|
/// Get the minimum power output.
|
||||||
|
float get_min_power() const { return 0.0f; }
|
||||||
|
#endif
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/// Implement BinarySensor's write_enabled; this should never be called.
|
/// Implement BinarySensor's write_enabled; this should never be called.
|
||||||
void write_state(bool state) override;
|
void write_state(bool state) override;
|
||||||
virtual void write_state(float state) = 0;
|
virtual void write_state(float state) = 0;
|
||||||
|
|
||||||
|
#ifdef USE_OUTPUT_FLOAT_POWER_SCALING
|
||||||
float max_power_{1.0f};
|
float max_power_{1.0f};
|
||||||
float min_power_{0.0f};
|
float min_power_{0.0f};
|
||||||
bool zero_means_zero_;
|
bool zero_means_zero_{false};
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace output
|
} // namespace output
|
||||||
|
|||||||
@@ -146,6 +146,7 @@
|
|||||||
#define USE_NEXTION_WAVEFORM
|
#define USE_NEXTION_WAVEFORM
|
||||||
#define USE_NUMBER
|
#define USE_NUMBER
|
||||||
#define USE_OUTPUT
|
#define USE_OUTPUT
|
||||||
|
#define USE_OUTPUT_FLOAT_POWER_SCALING
|
||||||
#define USE_POWER_SUPPLY
|
#define USE_POWER_SUPPLY
|
||||||
#define USE_PREFERENCES_SYNC_EVERY_LOOP
|
#define USE_PREFERENCES_SYNC_EVERY_LOOP
|
||||||
#define USE_QR_CODE
|
#define USE_QR_CODE
|
||||||
|
|||||||
Reference in New Issue
Block a user