Merge branch 'output-power-scaling-optional' into integration

This commit is contained in:
J. Nick Koston
2026-04-25 13:10:27 -05:00
3 changed files with 35 additions and 6 deletions
+3 -2
View File
@@ -59,8 +59,9 @@ async def setup_output_platform_(obj, 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]))
# Only emit (and pull in the scaling field) when explicitly enabled — the
# C++ default initializer covers the False case, saving 4 B per instance.
# 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]))
+2 -2
View File
@@ -9,11 +9,11 @@ static const char *const TAG = "output.float";
#ifdef USE_OUTPUT_FLOAT_POWER_SCALING
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) {
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
+30 -2
View File
@@ -27,11 +27,14 @@ namespace output {
* 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).
* 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
* 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 {
public:
@@ -57,6 +60,31 @@ class FloatOutput : public BinaryOutput {
* @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; }
#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.