mirror of
https://github.com/esphome/esphome.git
synced 2026-10-22 05:46:12 +00:00
Compare commits
10
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff36d3a6c8 | ||
|
|
ba3bc70bbb | ||
|
|
6c1a7642f3 | ||
|
|
8b0d1d31db | ||
|
|
653686e11e | ||
|
|
d17dcaaee4 | ||
|
|
1ce0db00b3 | ||
|
|
6409611e1c | ||
|
|
86dcbe4645 | ||
|
|
a50a30414e |
@@ -1,28 +1,24 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import output
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from .. import CONF_BH1745_ID, BH1745Component, bh1745_ns
|
||||
from .. import CONF_BH1745_ID, BH1745Component
|
||||
|
||||
DEPENDENCIES = ["bh1745"]
|
||||
|
||||
BH1745InterruptPinOutput = bh1745_ns.class_(
|
||||
"BH1745InterruptPinOutput", output.BinaryOutput
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = output.BINARY_OUTPUT_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(BH1745InterruptPinOutput),
|
||||
cv.GenerateID(): cv.declare_id(output.ApplyBinaryOutput),
|
||||
cv.GenerateID(CONF_BH1745_ID): cv.use_id(BH1745Component),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await output.register_output(var, config)
|
||||
hub = await cg.get_variable(config[CONF_BH1745_ID])
|
||||
await cg.register_parented(var, hub)
|
||||
await output.sub_binary_outputs(config, parent=hub).create(
|
||||
config, None, apply="set_interrupt_state(state)"
|
||||
)
|
||||
cg.add(hub.enable_interrupt_output())
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/output/binary_output.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
#include "../bh1745.h"
|
||||
|
||||
namespace esphome::bh1745 {
|
||||
|
||||
/// Drives the BH1745 INT pin, which switches the LEDs on breakout boards such as the Pimoroni one.
|
||||
class BH1745InterruptPinOutput : public output::BinaryOutput, public Parented<BH1745Component> {
|
||||
protected:
|
||||
void write_state(bool state) override { this->parent_->set_interrupt_state(state); }
|
||||
};
|
||||
|
||||
} // namespace esphome::bh1745
|
||||
@@ -80,7 +80,8 @@ void BP1658CJ::loop() {
|
||||
this->update_ = false;
|
||||
}
|
||||
|
||||
void BP1658CJ::set_channel_value_(uint8_t channel, uint16_t value) {
|
||||
void BP1658CJ::write_channel(uint8_t channel, float state) {
|
||||
auto value = static_cast<uint16_t>(state * 0x3FF);
|
||||
if (this->pwm_amounts_[channel] != value) {
|
||||
this->update_ = true;
|
||||
this->update_channel_ = channel;
|
||||
|
||||
@@ -2,15 +2,12 @@
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/components/output/float_output.h"
|
||||
#include <vector>
|
||||
|
||||
namespace esphome::bp1658cj {
|
||||
|
||||
class BP1658CJ final : public Component {
|
||||
public:
|
||||
class Channel;
|
||||
|
||||
void set_data_pin(GPIOPin *data_pin) { data_pin_ = data_pin; }
|
||||
void set_clock_pin(GPIOPin *clock_pin) { clock_pin_ = clock_pin; }
|
||||
void set_max_power_color_channels(uint8_t max_power_color_channels) {
|
||||
@@ -29,23 +26,9 @@ class BP1658CJ final : public Component {
|
||||
/// Send new values if they were updated.
|
||||
void loop() override;
|
||||
|
||||
class Channel final : public output::FloatOutput {
|
||||
public:
|
||||
void set_parent(BP1658CJ *parent) { parent_ = parent; }
|
||||
void set_channel(uint8_t channel) { channel_ = channel; }
|
||||
|
||||
protected:
|
||||
void write_state(float state) override {
|
||||
auto amount = static_cast<uint16_t>(state * 0x3FF);
|
||||
this->parent_->set_channel_value_(this->channel_, amount);
|
||||
}
|
||||
|
||||
BP1658CJ *parent_;
|
||||
uint8_t channel_;
|
||||
};
|
||||
void write_channel(uint8_t channel, float state);
|
||||
|
||||
protected:
|
||||
void set_channel_value_(uint8_t channel, uint16_t value);
|
||||
void write_bit_(bool value);
|
||||
void write_byte_(uint8_t data);
|
||||
void write_buffer_(uint8_t *buffer, uint8_t size);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import output
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_CHANNEL, CONF_ID
|
||||
@@ -8,21 +7,18 @@ from . import BP1658CJ
|
||||
|
||||
DEPENDENCIES = ["bp1658cj"]
|
||||
|
||||
Channel = BP1658CJ.class_("Channel", output.FloatOutput)
|
||||
|
||||
CONF_BP1658CJ_ID = "bp1658cj_id"
|
||||
CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(CONF_BP1658CJ_ID): cv.use_id(BP1658CJ),
|
||||
cv.Required(CONF_ID): cv.declare_id(Channel),
|
||||
cv.Required(CONF_ID): cv.declare_id(output.ApplyFloatOutput),
|
||||
cv.Required(CONF_CHANNEL): cv.int_range(min=0, max=4),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await output.register_output(var, config)
|
||||
|
||||
await cg.register_parented(var, config[CONF_BP1658CJ_ID])
|
||||
cg.add(var.set_channel(config[CONF_CHANNEL]))
|
||||
await output.sub_float_outputs(config, parent=config[CONF_BP1658CJ_ID]).create(
|
||||
config, None, config[CONF_CHANNEL], apply="write_channel(index, state)"
|
||||
)
|
||||
|
||||
@@ -48,15 +48,14 @@ void DAC7678Output::dump_config() {
|
||||
}
|
||||
}
|
||||
|
||||
void DAC7678Output::register_channel(DAC7678Channel *channel) {
|
||||
auto c = channel->channel_;
|
||||
this->min_channel_ = std::min(this->min_channel_, c);
|
||||
this->max_channel_ = std::max(this->max_channel_, c);
|
||||
channel->set_parent(this);
|
||||
ESP_LOGV(TAG, "Registered channel: %01u", channel->channel_);
|
||||
void DAC7678Output::register_channel(uint8_t channel) {
|
||||
this->min_channel_ = std::min(this->min_channel_, channel);
|
||||
this->max_channel_ = std::max(this->max_channel_, channel);
|
||||
ESP_LOGV(TAG, "Registered channel: %01u", channel);
|
||||
}
|
||||
|
||||
void DAC7678Output::set_channel_value_(uint8_t channel, uint16_t value) {
|
||||
void DAC7678Output::write_channel(uint8_t channel, float state) {
|
||||
auto value = static_cast<uint16_t>(roundf(state * 0xFFF));
|
||||
if (channel >= std::size(this->dac_input_reg_))
|
||||
return;
|
||||
if (this->dac_input_reg_[channel] != value) {
|
||||
@@ -71,10 +70,4 @@ void DAC7678Output::set_channel_value_(uint8_t channel, uint16_t value) {
|
||||
this->status_clear_warning();
|
||||
}
|
||||
|
||||
void DAC7678Channel::write_state(float state) {
|
||||
const float input_rounded = roundf(state * this->full_scale_);
|
||||
auto input = static_cast<uint16_t>(input_rounded);
|
||||
this->parent_->set_channel_value_(this->channel_, input);
|
||||
}
|
||||
|
||||
} // namespace esphome::dac7678
|
||||
|
||||
@@ -1,34 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/components/output/float_output.h"
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
|
||||
namespace esphome::dac7678 {
|
||||
|
||||
class DAC7678Output;
|
||||
|
||||
class DAC7678Channel final : public output::FloatOutput, public Parented<DAC7678Output> {
|
||||
public:
|
||||
void set_channel(uint8_t channel) { channel_ = channel; }
|
||||
|
||||
protected:
|
||||
friend class DAC7678Output;
|
||||
|
||||
const uint16_t full_scale_ = 0xFFF;
|
||||
|
||||
void write_state(float state) override;
|
||||
|
||||
uint8_t channel_;
|
||||
};
|
||||
|
||||
/// DAC7678 float output component.
|
||||
class DAC7678Output final : public Component, public i2c::I2CDevice {
|
||||
public:
|
||||
DAC7678Output() {}
|
||||
|
||||
void register_channel(DAC7678Channel *channel);
|
||||
void register_channel(uint8_t channel);
|
||||
void write_channel(uint8_t channel, float state);
|
||||
|
||||
void set_internal_reference(const bool value) { this->internal_reference_ = value; }
|
||||
|
||||
@@ -37,12 +20,8 @@ class DAC7678Output final : public Component, public i2c::I2CDevice {
|
||||
float get_setup_priority() const override { return setup_priority::HARDWARE; }
|
||||
|
||||
protected:
|
||||
friend DAC7678Channel;
|
||||
|
||||
bool internal_reference_;
|
||||
|
||||
void set_channel_value_(uint8_t channel, uint16_t value);
|
||||
|
||||
uint8_t min_channel_{0xFF};
|
||||
uint8_t max_channel_{0x00};
|
||||
uint16_t dac_input_reg_[8] = {
|
||||
|
||||
@@ -2,29 +2,26 @@ import esphome.codegen as cg
|
||||
from esphome.components import output
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_CHANNEL, CONF_ID
|
||||
from esphome.cpp_generator import MockObj
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from . import DAC7678Output, dac7678_ns
|
||||
from . import DAC7678Output
|
||||
|
||||
DEPENDENCIES = ["dac7678"]
|
||||
|
||||
DAC7678Channel = dac7678_ns.class_("DAC7678Channel", output.FloatOutput)
|
||||
CONF_DAC7678_ID = "dac7678_id"
|
||||
|
||||
CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.declare_id(DAC7678Channel),
|
||||
cv.Required(CONF_ID): cv.declare_id(output.ApplyFloatOutput),
|
||||
cv.GenerateID(CONF_DAC7678_ID): cv.use_id(DAC7678Output),
|
||||
cv.Required(CONF_CHANNEL): cv.int_range(min=0, max=7),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> MockObj:
|
||||
paren = await cg.get_variable(config[CONF_DAC7678_ID])
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
cg.add(var.set_channel(config[CONF_CHANNEL]))
|
||||
cg.add(paren.register_channel(var))
|
||||
await output.register_output(var, config)
|
||||
return var
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
hub = await cg.get_variable(config[CONF_DAC7678_ID])
|
||||
await output.sub_float_outputs(config, parent=hub).create(
|
||||
config, None, config[CONF_CHANNEL], apply="write_channel(index, state)"
|
||||
)
|
||||
cg.add(hub.register_channel(config[CONF_CHANNEL]))
|
||||
|
||||
@@ -1,23 +1,21 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import output
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from .. import CONF_EMC2101_ID, EMC2101_COMPONENT_SCHEMA, emc2101_ns
|
||||
from .. import CONF_EMC2101_ID, EMC2101_COMPONENT_SCHEMA
|
||||
|
||||
DEPENDENCIES = ["emc2101"]
|
||||
|
||||
EMC2101Output = emc2101_ns.class_("EMC2101Output", output.FloatOutput)
|
||||
|
||||
CONFIG_SCHEMA = EMC2101_COMPONENT_SCHEMA.extend(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.declare_id(EMC2101Output),
|
||||
cv.Required(CONF_ID): cv.declare_id(output.ApplyFloatOutput),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
paren = await cg.get_variable(config[CONF_EMC2101_ID])
|
||||
var = cg.new_Pvariable(config[CONF_ID], paren)
|
||||
await output.register_output(var, config)
|
||||
await output.sub_float_outputs(config, parent=config[CONF_EMC2101_ID]).create(
|
||||
config, None, apply="set_duty_cycle(state)"
|
||||
)
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
#include "emc2101_output.h"
|
||||
|
||||
namespace esphome::emc2101 {
|
||||
|
||||
void EMC2101Output::write_state(float state) { this->parent_->set_duty_cycle(state); }
|
||||
|
||||
} // namespace esphome::emc2101
|
||||
@@ -1,20 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../emc2101.h"
|
||||
#include "esphome/components/output/float_output.h"
|
||||
|
||||
namespace esphome::emc2101 {
|
||||
|
||||
/// This class allows to control the EMC2101 output.
|
||||
class EMC2101Output final : public output::FloatOutput {
|
||||
public:
|
||||
EMC2101Output(Emc2101Component *parent) : parent_(parent) {}
|
||||
|
||||
protected:
|
||||
/** Used by ESPHome framework. */
|
||||
void write_state(float state) override;
|
||||
|
||||
Emc2101Component *parent_;
|
||||
};
|
||||
|
||||
} // namespace esphome::emc2101
|
||||
@@ -1,31 +1,23 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import i2c, output
|
||||
from esphome.components import output
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_CHANNEL, CONF_ID
|
||||
from esphome.const import CONF_CHANNEL
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from .. import CONF_GP8403_ID, GP8403Component, gp8403_ns
|
||||
from .. import CONF_GP8403_ID, GP8403Component
|
||||
|
||||
DEPENDENCIES = ["gp8403"]
|
||||
|
||||
GP8403Output = gp8403_ns.class_(
|
||||
"GP8403Output", cg.Component, i2c.I2CDevice, output.FloatOutput
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(GP8403Output),
|
||||
cv.GenerateID(): cv.declare_id(output.ApplyFloatOutput),
|
||||
cv.GenerateID(CONF_GP8403_ID): cv.use_id(GP8403Component),
|
||||
cv.Required(CONF_CHANNEL): cv.int_range(min=0, max=1),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await output.register_output(var, config)
|
||||
|
||||
await cg.register_parented(var, config[CONF_GP8403_ID])
|
||||
|
||||
cg.add(var.set_channel(config[CONF_CHANNEL]))
|
||||
await output.sub_float_outputs(config, parent=config[CONF_GP8403_ID]).create(
|
||||
config, None, config[CONF_CHANNEL], apply="write_state(state, index)"
|
||||
)
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
#include "gp8403_output.h"
|
||||
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome::gp8403 {
|
||||
|
||||
ESPHOME_LOG_TAG(TAG, "gp8403.output");
|
||||
|
||||
void GP8403Output::dump_config() {
|
||||
ESP_LOGCONFIG(TAG,
|
||||
"GP8403 Output:\n"
|
||||
" Channel: %u",
|
||||
this->channel_);
|
||||
}
|
||||
|
||||
void GP8403Output::write_state(float state) { this->parent_->write_state(state, this->channel_); }
|
||||
|
||||
} // namespace esphome::gp8403
|
||||
@@ -1,21 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/output/float_output.h"
|
||||
#include "esphome/core/component.h"
|
||||
|
||||
#include "esphome/components/gp8403/gp8403.h"
|
||||
|
||||
namespace esphome::gp8403 {
|
||||
|
||||
class GP8403Output final : public Component, public output::FloatOutput, public Parented<GP8403Component> {
|
||||
public:
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::DATA - 1; }
|
||||
void set_channel(uint8_t channel) { this->channel_ = channel; }
|
||||
void write_state(float state) override;
|
||||
|
||||
protected:
|
||||
uint8_t channel_;
|
||||
};
|
||||
|
||||
} // namespace esphome::gp8403
|
||||
@@ -70,16 +70,11 @@ void LP5562Output::dump_config() {
|
||||
LOG_I2C_DEVICE(this);
|
||||
}
|
||||
|
||||
void LP5562Output::set_channel_value_(uint8_t channel, uint8_t value) {
|
||||
void LP5562Output::write_channel(uint8_t channel, float state) {
|
||||
if (this->is_failed())
|
||||
return;
|
||||
this->status_comm_result(this->write_byte(progmem_read_byte(&LP5562_REG_PWM[channel]), value));
|
||||
}
|
||||
|
||||
void LP5562Channel::write_state(float state) {
|
||||
const uint8_t max_duty = 255;
|
||||
const auto duty = static_cast<uint8_t>(roundf(state * max_duty));
|
||||
this->parent_->set_channel_value_(this->channel_, duty);
|
||||
const auto duty = static_cast<uint8_t>(roundf(state * 255));
|
||||
this->status_comm_result(this->write_byte(progmem_read_byte(&LP5562_REG_PWM[channel]), duty));
|
||||
}
|
||||
|
||||
} // namespace esphome::lp5562
|
||||
|
||||
@@ -3,24 +3,10 @@
|
||||
#include <array>
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
#include "esphome/components/output/float_output.h"
|
||||
|
||||
namespace esphome::lp5562 {
|
||||
|
||||
class LP5562Output;
|
||||
|
||||
class LP5562Channel final : public output::FloatOutput, public Parented<LP5562Output> {
|
||||
public:
|
||||
void set_channel(uint8_t channel) { this->channel_ = channel; }
|
||||
|
||||
protected:
|
||||
void write_state(float state) override;
|
||||
|
||||
uint8_t channel_{0};
|
||||
};
|
||||
|
||||
/// LP5562 4-channel (RGBW) I2C LED driver.
|
||||
class LP5562Output final : public Component, public i2c::I2CDevice {
|
||||
public:
|
||||
@@ -31,12 +17,9 @@ class LP5562Output final : public Component, public i2c::I2CDevice {
|
||||
void set_logarithmic_dimming(bool logarithmic_dimming) { this->logarithmic_dimming_ = logarithmic_dimming; }
|
||||
/// Current limit of a channel in steps of 0.1 mA, written to the chip in setup().
|
||||
void set_channel_current(uint8_t channel, uint8_t current) { this->currents_[channel] = current; }
|
||||
void write_channel(uint8_t channel, float state);
|
||||
|
||||
protected:
|
||||
friend class LP5562Channel;
|
||||
|
||||
void set_channel_value_(uint8_t channel, uint8_t value);
|
||||
|
||||
// 17.5 mA, the chip's reset value
|
||||
std::array<uint8_t, 4> currents_{175, 175, 175, 175};
|
||||
bool logarithmic_dimming_{false};
|
||||
|
||||
@@ -5,11 +5,10 @@ from esphome.const import CONF_CHANNEL, CONF_CURRENT, CONF_ID
|
||||
import esphome.final_validate as fv
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from . import LP5562Output, lp5562_ns
|
||||
from . import LP5562Output
|
||||
|
||||
DEPENDENCIES = ["lp5562"]
|
||||
|
||||
LP5562Channel = lp5562_ns.class_("LP5562Channel", output.FloatOutput)
|
||||
CONF_LP5562_ID = "lp5562_id"
|
||||
# Chip reset value of the current registers, 17.5 mA; matches currents_ in lp5562_output.h
|
||||
CURRENT_RESET_TENTHS = 175
|
||||
@@ -38,7 +37,7 @@ def _validate_current(amps: float) -> float:
|
||||
|
||||
CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.declare_id(LP5562Channel),
|
||||
cv.Required(CONF_ID): cv.declare_id(output.ApplyFloatOutput),
|
||||
cv.GenerateID(CONF_LP5562_ID): cv.use_id(LP5562Output),
|
||||
cv.Required(CONF_CHANNEL): cv.enum(LP5562_CHANNELS, lower=True),
|
||||
cv.Optional(CONF_CURRENT, default="17.5mA"): cv.All(
|
||||
@@ -65,11 +64,11 @@ FINAL_VALIDATE_SCHEMA = _final_validate
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_parented(var, config[CONF_LP5562_ID])
|
||||
parent = await cg.get_variable(config[CONF_LP5562_ID])
|
||||
cg.add(var.set_channel(config[CONF_CHANNEL]))
|
||||
channel = config[CONF_CHANNEL]
|
||||
hub = await cg.get_variable(config[CONF_LP5562_ID])
|
||||
await output.sub_float_outputs(config, parent=hub).create(
|
||||
config, None, channel, apply="write_channel(index, state)"
|
||||
)
|
||||
# The hub already holds the chip's reset value, 17.5 mA
|
||||
if (current := _tenths(config[CONF_CURRENT])) != CURRENT_RESET_TENTHS:
|
||||
cg.add(parent.set_channel_current(config[CONF_CHANNEL], current))
|
||||
await output.register_output(var, config)
|
||||
cg.add(hub.set_channel_current(channel, current))
|
||||
|
||||
@@ -39,19 +39,10 @@ void MCP4728Component::loop() {
|
||||
}
|
||||
}
|
||||
|
||||
void MCP4728Component::set_channel_value_(MCP4728ChannelIdx channel, uint16_t value) {
|
||||
uint8_t cn = 0;
|
||||
if (channel == MCP4728_CHANNEL_A) {
|
||||
cn = 'A';
|
||||
} else if (channel == MCP4728_CHANNEL_B) {
|
||||
cn = 'B';
|
||||
} else if (channel == MCP4728_CHANNEL_C) {
|
||||
cn = 'C';
|
||||
} else {
|
||||
cn = 'D';
|
||||
}
|
||||
ESP_LOGV(TAG, "Setting MCP4728 channel %c to %d!", cn, value);
|
||||
reg_[channel].data = value;
|
||||
void MCP4728Component::write_channel(uint8_t channel, float state) {
|
||||
auto value = static_cast<uint16_t>(roundf(state * 4095));
|
||||
ESP_LOGV(TAG, "Setting MCP4728 channel %c to %d!", 'A' + channel, value);
|
||||
this->reg_[channel].data = value;
|
||||
this->update_ = true;
|
||||
}
|
||||
|
||||
@@ -87,21 +78,11 @@ bool MCP4728Component::seq_write_() {
|
||||
return err == i2c::ERROR_OK;
|
||||
}
|
||||
|
||||
void MCP4728Component::select_vref_(MCP4728ChannelIdx channel, MCP4728Vref vref) {
|
||||
reg_[channel].vref = vref;
|
||||
|
||||
this->update_ = true;
|
||||
}
|
||||
|
||||
void MCP4728Component::select_power_down_(MCP4728ChannelIdx channel, MCP4728PwrDown pd) {
|
||||
reg_[channel].pd = pd;
|
||||
|
||||
this->update_ = true;
|
||||
}
|
||||
|
||||
void MCP4728Component::select_gain_(MCP4728ChannelIdx channel, MCP4728Gain gain) {
|
||||
reg_[channel].gain = gain;
|
||||
|
||||
void MCP4728Component::configure_channel(MCP4728ChannelIdx channel, MCP4728Vref vref, MCP4728Gain gain,
|
||||
MCP4728PwrDown pd) {
|
||||
this->reg_[channel].vref = vref;
|
||||
this->reg_[channel].pd = pd;
|
||||
this->reg_[channel].gain = gain;
|
||||
this->update_ = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -35,8 +35,6 @@ struct DACInputData {
|
||||
uint16_t data;
|
||||
};
|
||||
|
||||
class MCP4728Channel;
|
||||
|
||||
/// MCP4728 float output component.
|
||||
class MCP4728Component final : public Component, public i2c::I2CDevice {
|
||||
public:
|
||||
@@ -47,14 +45,12 @@ class MCP4728Component final : public Component, public i2c::I2CDevice {
|
||||
float get_setup_priority() const override { return setup_priority::HARDWARE; }
|
||||
void loop() override;
|
||||
|
||||
void configure_channel(MCP4728ChannelIdx channel, MCP4728Vref vref, MCP4728Gain gain, MCP4728PwrDown pd);
|
||||
void write_channel(uint8_t channel, float state);
|
||||
|
||||
protected:
|
||||
friend MCP4728Channel;
|
||||
void set_channel_value_(MCP4728ChannelIdx channel, uint16_t value);
|
||||
bool multi_write_();
|
||||
bool seq_write_();
|
||||
void select_vref_(MCP4728ChannelIdx channel, MCP4728Vref vref);
|
||||
void select_power_down_(MCP4728ChannelIdx channel, MCP4728PwrDown pd);
|
||||
void select_gain_(MCP4728ChannelIdx channel, MCP4728Gain gain);
|
||||
|
||||
private:
|
||||
DACInputData reg_[4]{};
|
||||
|
||||
@@ -8,7 +8,6 @@ from .. import CONF_MCP4728_ID, MCP4728Component, mcp4728_ns
|
||||
|
||||
DEPENDENCIES = ["mcp4728"]
|
||||
|
||||
MCP4728Channel = mcp4728_ns.class_("MCP4728Channel", output.FloatOutput)
|
||||
CONF_VREF = "vref"
|
||||
CONF_POWER_DOWN = "power_down"
|
||||
|
||||
@@ -36,7 +35,7 @@ CHANNEL_OPTIONS = MCP4728ChannelIdx.options(
|
||||
|
||||
CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.declare_id(MCP4728Channel),
|
||||
cv.Required(CONF_ID): cv.declare_id(output.ApplyFloatOutput),
|
||||
cv.GenerateID(CONF_MCP4728_ID): cv.use_id(MCP4728Component),
|
||||
cv.Required(CONF_CHANNEL): cv.enum(CHANNEL_OPTIONS, upper=True),
|
||||
cv.Optional(CONF_VREF, default="vdd"): cv.enum(VREF_OPTIONS, upper=False),
|
||||
@@ -49,13 +48,16 @@ CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
paren = await cg.get_variable(config[CONF_MCP4728_ID])
|
||||
var = cg.new_Pvariable(
|
||||
config[CONF_ID],
|
||||
paren,
|
||||
config[CONF_CHANNEL],
|
||||
config[CONF_VREF],
|
||||
config[CONF_GAIN],
|
||||
config[CONF_POWER_DOWN],
|
||||
channel = config[CONF_CHANNEL]
|
||||
hub = await cg.get_variable(config[CONF_MCP4728_ID])
|
||||
await output.sub_float_outputs(config, parent=hub).create(
|
||||
config, None, channel, apply="write_channel(index, state)"
|
||||
)
|
||||
cg.add(
|
||||
hub.configure_channel(
|
||||
channel,
|
||||
config[CONF_VREF],
|
||||
config[CONF_GAIN],
|
||||
config[CONF_POWER_DOWN],
|
||||
)
|
||||
)
|
||||
await output.register_output(var, config)
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
#include "mcp4728_output.h"
|
||||
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome::mcp4728 {
|
||||
|
||||
void MCP4728Channel::write_state(float state) {
|
||||
const uint16_t max_duty = 4095;
|
||||
const float duty_rounded = roundf(state * max_duty);
|
||||
auto duty = static_cast<uint16_t>(duty_rounded);
|
||||
this->parent_->set_channel_value_(this->channel_, duty);
|
||||
}
|
||||
|
||||
} // namespace esphome::mcp4728
|
||||
@@ -1,30 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../mcp4728.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/output/float_output.h"
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
|
||||
namespace esphome::mcp4728 {
|
||||
|
||||
class MCP4728Channel final : public output::FloatOutput {
|
||||
public:
|
||||
MCP4728Channel(MCP4728Component *parent, MCP4728ChannelIdx channel, MCP4728Vref vref, MCP4728Gain gain,
|
||||
MCP4728PwrDown pwrdown)
|
||||
: parent_(parent), channel_(channel) {
|
||||
// update VREF
|
||||
parent->select_vref_(channel, vref);
|
||||
// update PD
|
||||
parent->select_power_down_(channel, pwrdown);
|
||||
// update GAIN
|
||||
parent->select_gain_(channel, gain);
|
||||
}
|
||||
|
||||
protected:
|
||||
void write_state(float state) override;
|
||||
|
||||
MCP4728Component *parent_;
|
||||
MCP4728ChannelIdx channel_;
|
||||
};
|
||||
|
||||
} // namespace esphome::mcp4728
|
||||
@@ -80,7 +80,8 @@ void MY9231OutputComponent::loop() {
|
||||
}
|
||||
this->update_ = false;
|
||||
}
|
||||
void MY9231OutputComponent::set_channel_value_(uint16_t channel, uint16_t value) {
|
||||
void MY9231OutputComponent::write_channel(uint16_t channel, float state) {
|
||||
auto value = uint16_t(state * this->get_max_amount_());
|
||||
ESP_LOGV(TAG, "set channels %u to %u", channel, value);
|
||||
uint16_t index = this->num_channels_ - channel - 1;
|
||||
if (this->pwm_amounts_[index] != value) {
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/components/output/float_output.h"
|
||||
#include <vector>
|
||||
|
||||
namespace esphome::my9231 {
|
||||
@@ -10,7 +9,6 @@ namespace esphome::my9231 {
|
||||
/// MY9231 float output component.
|
||||
class MY9231OutputComponent final : public Component {
|
||||
public:
|
||||
class Channel;
|
||||
void set_pin_di(GPIOPin *pin_di) { pin_di_ = pin_di; }
|
||||
void set_pin_dcki(GPIOPin *pin_dcki) { pin_dcki_ = pin_dcki; }
|
||||
|
||||
@@ -26,25 +24,11 @@ class MY9231OutputComponent final : public Component {
|
||||
/// Send new values if they were updated.
|
||||
void loop() override;
|
||||
|
||||
class Channel final : public output::FloatOutput {
|
||||
public:
|
||||
void set_parent(MY9231OutputComponent *parent) { parent_ = parent; }
|
||||
void set_channel(uint16_t channel) { channel_ = channel; }
|
||||
|
||||
protected:
|
||||
void write_state(float state) override {
|
||||
auto amount = uint16_t(state * this->parent_->get_max_amount_());
|
||||
this->parent_->set_channel_value_(this->channel_, amount);
|
||||
}
|
||||
|
||||
MY9231OutputComponent *parent_;
|
||||
uint16_t channel_;
|
||||
};
|
||||
void write_channel(uint16_t channel, float state);
|
||||
|
||||
protected:
|
||||
uint16_t get_max_amount_() const { return (uint32_t(1) << this->bit_depth_) - 1; }
|
||||
|
||||
void set_channel_value_(uint16_t channel, uint16_t value);
|
||||
void init_chips_(uint8_t command);
|
||||
void write_word_(uint16_t value, uint8_t bits);
|
||||
void send_di_pulses_(uint8_t count);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import output
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_CHANNEL, CONF_ID
|
||||
@@ -8,21 +7,18 @@ from . import MY9231OutputComponent
|
||||
|
||||
DEPENDENCIES = ["my9231"]
|
||||
|
||||
Channel = MY9231OutputComponent.class_("Channel", output.FloatOutput)
|
||||
|
||||
CONF_MY9231_ID = "my9231_id"
|
||||
CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(CONF_MY9231_ID): cv.use_id(MY9231OutputComponent),
|
||||
cv.Required(CONF_ID): cv.declare_id(Channel),
|
||||
cv.Required(CONF_ID): cv.declare_id(output.ApplyFloatOutput),
|
||||
cv.Required(CONF_CHANNEL): cv.uint16_t,
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await output.register_output(var, config)
|
||||
|
||||
await cg.register_parented(var, config[CONF_MY9231_ID])
|
||||
cg.add(var.set_channel(config[CONF_CHANNEL]))
|
||||
await output.sub_float_outputs(config, parent=config[CONF_MY9231_ID]).create(
|
||||
config, None, config[CONF_CHANNEL], apply="write_channel(index, state)"
|
||||
)
|
||||
|
||||
@@ -13,7 +13,9 @@ from esphome.const import (
|
||||
CONF_MIN_POWER,
|
||||
CONF_POWER_SUPPLY,
|
||||
)
|
||||
from esphome.core import CORE
|
||||
from esphome.core import CORE, ID
|
||||
from esphome.core.entity_helpers import ApplyEntity, SubEntities
|
||||
from esphome.cpp_generator import MockObj, SafeExpType
|
||||
from esphome.types import ConfigType
|
||||
|
||||
CODEOWNERS = ["@esphome/core"]
|
||||
@@ -54,6 +56,8 @@ BinaryOutput = output_ns.class_("BinaryOutput")
|
||||
BinaryOutputPtr = BinaryOutput.operator("ptr")
|
||||
FloatOutput = output_ns.class_("FloatOutput", BinaryOutput)
|
||||
FloatOutputPtr = FloatOutput.operator("ptr")
|
||||
ApplyBinaryOutput = output_ns.class_("ApplyBinaryOutput", BinaryOutput)
|
||||
ApplyFloatOutput = output_ns.class_("ApplyFloatOutput", FloatOutput)
|
||||
|
||||
|
||||
async def setup_output_platform_(obj, config):
|
||||
@@ -84,6 +88,39 @@ async def register_output(var, config):
|
||||
await setup_output_platform_(var, config)
|
||||
|
||||
|
||||
async def new_output(config: ConfigType, *args: SafeExpType) -> MockObj:
|
||||
var = cg.new_Pvariable(config[CONF_ID], *args)
|
||||
await register_output(var, config)
|
||||
return var
|
||||
|
||||
|
||||
BINARY_APPLY_ENTITY = ApplyEntity(
|
||||
ApplyBinaryOutput,
|
||||
((BinaryOutputPtr, "entity"), (cg.uint16, "index"), (cg.bool_, "state")),
|
||||
)
|
||||
FLOAT_APPLY_ENTITY = ApplyEntity(
|
||||
ApplyFloatOutput,
|
||||
((FloatOutputPtr, "entity"), (cg.uint16, "index"), (cg.float_, "state")),
|
||||
)
|
||||
|
||||
|
||||
def sub_binary_outputs(
|
||||
config: ConfigType, *, parent: MockObj | ID | None = None
|
||||
) -> SubEntities:
|
||||
"""Return a SubEntities bound to new_output; ``apply`` can use ``entity``, ``index`` and ``state``.
|
||||
|
||||
``index`` is the constructor argument (default 0), so a hub's channels share one class.
|
||||
"""
|
||||
return SubEntities(new_output, config, parent, BINARY_APPLY_ENTITY)
|
||||
|
||||
|
||||
def sub_float_outputs(
|
||||
config: ConfigType, *, parent: MockObj | ID | None = None
|
||||
) -> SubEntities:
|
||||
"""Like sub_binary_outputs; ``apply`` gets the scaled level as ``state``."""
|
||||
return SubEntities(new_output, config, parent, FLOAT_APPLY_ENTITY)
|
||||
|
||||
|
||||
BINARY_OUTPUT_ACTION_SCHEMA = maybe_simple_id(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.use_id(BinaryOutput),
|
||||
|
||||
@@ -68,4 +68,16 @@ class BinaryOutput {
|
||||
#endif
|
||||
};
|
||||
|
||||
/// Calls the codegen-generated Fn(this, index, state) on a state write; the hub is baked into Fn.
|
||||
/// The index (a hub's channel) is data, so all channels of a hub share one instantiation.
|
||||
template<auto Fn> class ApplyBinaryOutput final : public BinaryOutput {
|
||||
public:
|
||||
explicit ApplyBinaryOutput(uint16_t index = 0) : index_(index) {}
|
||||
|
||||
protected:
|
||||
void write_state(bool state) override { Fn(this, this->index_, state); }
|
||||
|
||||
uint16_t index_; // Fits in the base's tail padding
|
||||
};
|
||||
|
||||
} // namespace esphome::output
|
||||
|
||||
@@ -130,4 +130,16 @@ class FloatOutput : public BinaryOutput {
|
||||
#endif
|
||||
};
|
||||
|
||||
/// Calls the codegen-generated Fn(this, index, state) with the scaled, inverted level; the hub is baked into Fn.
|
||||
/// The index (a hub's channel) is data, so all channels of a hub share one instantiation.
|
||||
template<auto Fn> class ApplyFloatOutput final : public FloatOutput {
|
||||
public:
|
||||
explicit ApplyFloatOutput(uint16_t index = 0) : index_(index) {}
|
||||
|
||||
protected:
|
||||
void write_state(float state) override { Fn(this, this->index_, state); }
|
||||
|
||||
uint16_t index_; // Fits in the base's tail padding
|
||||
};
|
||||
|
||||
} // namespace esphome::output
|
||||
|
||||
@@ -4,16 +4,15 @@ import esphome.config_validation as cv
|
||||
from esphome.const import CONF_CHANNEL, CONF_ID
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from . import PCA9685Output, pca9685_ns
|
||||
from . import PCA9685Output
|
||||
|
||||
DEPENDENCIES = ["pca9685"]
|
||||
|
||||
PCA9685Channel = pca9685_ns.class_("PCA9685Channel", output.FloatOutput)
|
||||
CONF_PCA9685_ID = "pca9685_id"
|
||||
|
||||
CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.declare_id(PCA9685Channel),
|
||||
cv.Required(CONF_ID): cv.declare_id(output.ApplyFloatOutput),
|
||||
cv.GenerateID(CONF_PCA9685_ID): cv.use_id(PCA9685Output),
|
||||
cv.Required(CONF_CHANNEL): cv.int_range(min=0, max=15),
|
||||
}
|
||||
@@ -21,8 +20,8 @@ CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
paren = await cg.get_variable(config[CONF_PCA9685_ID])
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
cg.add(var.set_channel(config[CONF_CHANNEL]))
|
||||
cg.add(paren.register_channel(var))
|
||||
await output.register_output(var, config)
|
||||
hub = await cg.get_variable(config[CONF_PCA9685_ID])
|
||||
await output.sub_float_outputs(config, parent=hub).create(
|
||||
config, None, config[CONF_CHANNEL], apply="write_channel(index, state)"
|
||||
)
|
||||
cg.add(hub.register_channel(config[CONF_CHANNEL]))
|
||||
|
||||
@@ -140,18 +140,16 @@ void PCA9685Output::loop() {
|
||||
this->update_ = false;
|
||||
}
|
||||
|
||||
void PCA9685Output::register_channel(PCA9685Channel *channel) {
|
||||
auto c = channel->channel_;
|
||||
this->min_channel_ = std::min(this->min_channel_, c);
|
||||
this->max_channel_ = std::max(this->max_channel_, c);
|
||||
channel->set_parent(this);
|
||||
void PCA9685Output::register_channel(uint8_t channel) {
|
||||
this->min_channel_ = std::min(this->min_channel_, channel);
|
||||
this->max_channel_ = std::max(this->max_channel_, channel);
|
||||
}
|
||||
|
||||
void PCA9685Channel::write_state(float state) {
|
||||
const uint16_t max_duty = 4096;
|
||||
const float duty_rounded = roundf(state * max_duty);
|
||||
auto duty = static_cast<uint16_t>(duty_rounded);
|
||||
this->parent_->set_channel_value_(this->channel_, duty);
|
||||
void PCA9685Output::write_channel(uint8_t channel, float state) {
|
||||
auto value = static_cast<uint16_t>(roundf(state * 4096));
|
||||
if (this->pwm_amounts_[channel] != value)
|
||||
this->update_ = true;
|
||||
this->pwm_amounts_[channel] = value;
|
||||
}
|
||||
|
||||
} // namespace esphome::pca9685
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/output/float_output.h"
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
|
||||
namespace esphome::pca9685 {
|
||||
@@ -22,28 +21,13 @@ inline constexpr uint8_t PCA9685_MODE_OUTNE_HIGHZ = 0x02;
|
||||
/// Similarly, sets channel output to high if in totem-pole mode, otherwise
|
||||
inline constexpr uint8_t PCA9685_MODE_OUTNE_LOW = 0x01;
|
||||
|
||||
class PCA9685Output;
|
||||
|
||||
class PCA9685Channel final : public output::FloatOutput {
|
||||
public:
|
||||
void set_channel(uint8_t channel) { channel_ = channel; }
|
||||
void set_parent(PCA9685Output *parent) { parent_ = parent; }
|
||||
|
||||
protected:
|
||||
friend class PCA9685Output;
|
||||
|
||||
void write_state(float state) override;
|
||||
|
||||
uint8_t channel_;
|
||||
PCA9685Output *parent_;
|
||||
};
|
||||
|
||||
/// PCA9685 float output component.
|
||||
class PCA9685Output final : public Component, public i2c::I2CDevice {
|
||||
public:
|
||||
PCA9685Output(uint8_t mode = PCA9685_MODE_OUTPUT_ONACK | PCA9685_MODE_OUTPUT_TOTEM_POLE) : mode_(mode) {}
|
||||
|
||||
void register_channel(PCA9685Channel *channel);
|
||||
void register_channel(uint8_t channel);
|
||||
void write_channel(uint8_t channel, float state);
|
||||
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
@@ -56,14 +40,6 @@ class PCA9685Output final : public Component, public i2c::I2CDevice {
|
||||
protected:
|
||||
/// Configure the chip; false on an I2C failure.
|
||||
bool setup_registers_();
|
||||
friend PCA9685Channel;
|
||||
|
||||
void set_channel_value_(uint8_t channel, uint16_t value) {
|
||||
if (this->pwm_amounts_[channel] != value)
|
||||
this->update_ = true;
|
||||
this->pwm_amounts_[channel] = value;
|
||||
}
|
||||
|
||||
float frequency_;
|
||||
uint8_t mode_;
|
||||
bool extclk_ = false;
|
||||
|
||||
@@ -80,7 +80,8 @@ void Sm10BitBase::loop() {
|
||||
this->update_ = false;
|
||||
}
|
||||
|
||||
void Sm10BitBase::set_channel_value_(uint8_t channel, uint16_t value) {
|
||||
void Sm10BitBase::write_channel(uint8_t channel, float state) {
|
||||
auto value = static_cast<uint16_t>(state * 0x3FF);
|
||||
if (this->pwm_amounts_[channel] != value) {
|
||||
this->update_ = true;
|
||||
this->update_channel_ = channel;
|
||||
|
||||
@@ -2,15 +2,12 @@
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/components/output/float_output.h"
|
||||
#include <vector>
|
||||
|
||||
namespace esphome::sm10bit_base {
|
||||
|
||||
class Sm10BitBase : public Component {
|
||||
public:
|
||||
class Channel;
|
||||
|
||||
void set_model(uint8_t model_id) { model_id_ = model_id; }
|
||||
void set_data_pin(GPIOPin *data_pin) { data_pin_ = data_pin; }
|
||||
void set_clock_pin(GPIOPin *clock_pin) { clock_pin_ = clock_pin; }
|
||||
@@ -27,23 +24,9 @@ class Sm10BitBase : public Component {
|
||||
void dump_config() override;
|
||||
void loop() override;
|
||||
|
||||
class Channel final : public output::FloatOutput {
|
||||
public:
|
||||
void set_parent(Sm10BitBase *parent) { parent_ = parent; }
|
||||
void set_channel(uint8_t channel) { channel_ = channel; }
|
||||
|
||||
protected:
|
||||
void write_state(float state) override {
|
||||
auto amount = static_cast<uint16_t>(state * 0x3FF);
|
||||
this->parent_->set_channel_value_(this->channel_, amount);
|
||||
}
|
||||
|
||||
Sm10BitBase *parent_;
|
||||
uint8_t channel_;
|
||||
};
|
||||
void write_channel(uint8_t channel, float state);
|
||||
|
||||
protected:
|
||||
void set_channel_value_(uint8_t channel, uint16_t value);
|
||||
void write_bit_(bool value);
|
||||
void write_byte_(uint8_t data);
|
||||
void write_buffer_(uint8_t *buffer, uint8_t size);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import output
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_CHANNEL, CONF_ID
|
||||
@@ -8,21 +7,18 @@ from . import SM16716
|
||||
|
||||
DEPENDENCIES = ["sm16716"]
|
||||
|
||||
Channel = SM16716.class_("Channel", output.FloatOutput)
|
||||
|
||||
CONF_SM16716_ID = "sm16716_id"
|
||||
CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(CONF_SM16716_ID): cv.use_id(SM16716),
|
||||
cv.Required(CONF_ID): cv.declare_id(Channel),
|
||||
cv.Required(CONF_ID): cv.declare_id(output.ApplyFloatOutput),
|
||||
cv.Required(CONF_CHANNEL): cv.int_range(min=0, max=254),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await output.register_output(var, config)
|
||||
|
||||
await cg.register_parented(var, config[CONF_SM16716_ID])
|
||||
cg.add(var.set_channel(config[CONF_CHANNEL]))
|
||||
await output.sub_float_outputs(config, parent=config[CONF_SM16716_ID]).create(
|
||||
config, None, config[CONF_CHANNEL], apply="write_channel(index, state)"
|
||||
)
|
||||
|
||||
@@ -2,15 +2,12 @@
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/components/output/float_output.h"
|
||||
#include <vector>
|
||||
|
||||
namespace esphome::sm16716 {
|
||||
|
||||
class SM16716 final : public Component {
|
||||
public:
|
||||
class Channel;
|
||||
|
||||
void set_data_pin(GPIOPin *data_pin) { data_pin_ = data_pin; }
|
||||
void set_clock_pin(GPIOPin *clock_pin) { clock_pin_ = clock_pin; }
|
||||
void set_num_channels(uint8_t num_channels) { num_channels_ = num_channels; }
|
||||
@@ -25,29 +22,16 @@ class SM16716 final : public Component {
|
||||
/// Send new values if they were updated.
|
||||
void loop() override;
|
||||
|
||||
class Channel final : public output::FloatOutput {
|
||||
public:
|
||||
void set_parent(SM16716 *parent) { parent_ = parent; }
|
||||
void set_channel(uint8_t channel) { channel_ = channel; }
|
||||
|
||||
protected:
|
||||
void write_state(float state) override {
|
||||
auto amount = uint8_t(state * 0xFF);
|
||||
this->parent_->set_channel_value_(this->channel_, amount);
|
||||
}
|
||||
|
||||
SM16716 *parent_;
|
||||
uint8_t channel_;
|
||||
};
|
||||
|
||||
protected:
|
||||
void set_channel_value_(uint8_t channel, uint8_t value) {
|
||||
void write_channel(uint8_t channel, float state) {
|
||||
auto value = uint8_t(state * 0xFF);
|
||||
uint8_t index = this->num_channels_ - channel - 1;
|
||||
if (this->pwm_amounts_[index] != value) {
|
||||
this->update_ = true;
|
||||
}
|
||||
this->pwm_amounts_[index] = value;
|
||||
}
|
||||
|
||||
protected:
|
||||
void write_bit_(bool value) {
|
||||
this->data_pin_->digital_write(value);
|
||||
this->clock_pin_->digital_write(true);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import output
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_CHANNEL, CONF_ID
|
||||
@@ -9,21 +8,18 @@ from . import SM2135
|
||||
DEPENDENCIES = ["sm2135"]
|
||||
CODEOWNERS = ["@BoukeHaarsma23"]
|
||||
|
||||
Channel = SM2135.class_("Channel", output.FloatOutput)
|
||||
|
||||
CONF_SM2135_ID = "sm2135_id"
|
||||
CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(CONF_SM2135_ID): cv.use_id(SM2135),
|
||||
cv.Required(CONF_ID): cv.declare_id(Channel),
|
||||
cv.Required(CONF_ID): cv.declare_id(output.ApplyFloatOutput),
|
||||
cv.Required(CONF_CHANNEL): cv.int_range(min=0, max=4),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await output.register_output(var, config)
|
||||
|
||||
await cg.register_parented(var, config[CONF_SM2135_ID])
|
||||
cg.add(var.set_channel(config[CONF_CHANNEL]))
|
||||
await output.sub_float_outputs(config, parent=config[CONF_SM2135_ID]).create(
|
||||
config, None, config[CONF_CHANNEL], apply="write_channel(index, state)"
|
||||
)
|
||||
|
||||
@@ -130,7 +130,8 @@ void SM2135::loop() {
|
||||
this->update_ = false;
|
||||
}
|
||||
|
||||
void SM2135::set_channel_value_(uint8_t channel, uint8_t value) {
|
||||
void SM2135::write_channel(uint8_t channel, float state) {
|
||||
auto value = static_cast<uint8_t>(state * 0xff);
|
||||
if (this->pwm_amounts_[channel] != value) {
|
||||
this->update_ = true;
|
||||
this->update_channel_ = channel;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "esphome/components/output/float_output.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/hal.h"
|
||||
|
||||
@@ -23,8 +22,6 @@ enum SM2135Current : uint8_t {
|
||||
|
||||
class SM2135 final : public Component {
|
||||
public:
|
||||
class Channel;
|
||||
|
||||
void set_data_pin(GPIOPin *data_pin) { this->data_pin_ = data_pin; }
|
||||
void set_clock_pin(GPIOPin *clock_pin) { this->clock_pin_ = clock_pin; }
|
||||
|
||||
@@ -49,23 +46,9 @@ class SM2135 final : public Component {
|
||||
/// Send new values if they were updated.
|
||||
void loop() override;
|
||||
|
||||
class Channel final : public output::FloatOutput {
|
||||
public:
|
||||
void set_parent(SM2135 *parent) { parent_ = parent; }
|
||||
void set_channel(uint8_t channel) { channel_ = channel; }
|
||||
|
||||
protected:
|
||||
void write_state(float state) override {
|
||||
auto amount = static_cast<uint8_t>(state * 0xff);
|
||||
this->parent_->set_channel_value_(this->channel_, amount);
|
||||
}
|
||||
|
||||
SM2135 *parent_;
|
||||
uint8_t channel_;
|
||||
};
|
||||
void write_channel(uint8_t channel, float state);
|
||||
|
||||
protected:
|
||||
void set_channel_value_(uint8_t channel, uint8_t value);
|
||||
void sm2135_set_low_(GPIOPin *pin);
|
||||
void sm2135_set_high_(GPIOPin *pin);
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import output
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_CHANNEL, CONF_ID
|
||||
@@ -9,21 +8,18 @@ from . import SM2235
|
||||
DEPENDENCIES = ["sm2235"]
|
||||
CODEOWNERS = ["@Cossid"]
|
||||
|
||||
Channel = SM2235.class_("Channel", output.FloatOutput)
|
||||
|
||||
CONF_SM2235_ID = "sm2235_id"
|
||||
CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(CONF_SM2235_ID): cv.use_id(SM2235),
|
||||
cv.Required(CONF_ID): cv.declare_id(Channel),
|
||||
cv.Required(CONF_ID): cv.declare_id(output.ApplyFloatOutput),
|
||||
cv.Required(CONF_CHANNEL): cv.int_range(min=0, max=4),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await output.register_output(var, config)
|
||||
|
||||
await cg.register_parented(var, config[CONF_SM2235_ID])
|
||||
cg.add(var.set_channel(config[CONF_CHANNEL]))
|
||||
await output.sub_float_outputs(config, parent=config[CONF_SM2235_ID]).create(
|
||||
config, None, config[CONF_CHANNEL], apply="write_channel(index, state)"
|
||||
)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import output
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_CHANNEL, CONF_ID
|
||||
@@ -9,21 +8,18 @@ from . import SM2335
|
||||
DEPENDENCIES = ["sm2335"]
|
||||
CODEOWNERS = ["@Cossid"]
|
||||
|
||||
Channel = SM2335.class_("Channel", output.FloatOutput)
|
||||
|
||||
CONF_SM2335_ID = "sm2335_id"
|
||||
CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(CONF_SM2335_ID): cv.use_id(SM2335),
|
||||
cv.Required(CONF_ID): cv.declare_id(Channel),
|
||||
cv.Required(CONF_ID): cv.declare_id(output.ApplyFloatOutput),
|
||||
cv.Required(CONF_CHANNEL): cv.int_range(min=0, max=4),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await output.register_output(var, config)
|
||||
|
||||
await cg.register_parented(var, config[CONF_SM2335_ID])
|
||||
cg.add(var.set_channel(config[CONF_CHANNEL]))
|
||||
await output.sub_float_outputs(config, parent=config[CONF_SM2335_ID]).create(
|
||||
config, None, config[CONF_CHANNEL], apply="write_channel(index, state)"
|
||||
)
|
||||
|
||||
@@ -4,16 +4,15 @@ import esphome.config_validation as cv
|
||||
from esphome.const import CONF_CHANNEL, CONF_ID
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from . import TLC59208FOutput, tlc59208f_ns
|
||||
from . import TLC59208FOutput
|
||||
|
||||
DEPENDENCIES = ["tlc59208f"]
|
||||
|
||||
TLC59208FChannel = tlc59208f_ns.class_("TLC59208FChannel", output.FloatOutput)
|
||||
CONF_TLC59208F_ID = "tlc59208f_id"
|
||||
|
||||
CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.declare_id(TLC59208FChannel),
|
||||
cv.Required(CONF_ID): cv.declare_id(output.ApplyFloatOutput),
|
||||
cv.GenerateID(CONF_TLC59208F_ID): cv.use_id(TLC59208FOutput),
|
||||
cv.Required(CONF_CHANNEL): cv.int_range(min=0, max=7),
|
||||
}
|
||||
@@ -21,8 +20,8 @@ CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
paren = await cg.get_variable(config[CONF_TLC59208F_ID])
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
cg.add(var.set_channel(config[CONF_CHANNEL]))
|
||||
cg.add(paren.register_channel(var))
|
||||
await output.register_output(var, config)
|
||||
hub = await cg.get_variable(config[CONF_TLC59208F_ID])
|
||||
await output.sub_float_outputs(config, parent=hub).create(
|
||||
config, None, config[CONF_CHANNEL], apply="write_channel(index, state)"
|
||||
)
|
||||
cg.add(hub.register_channel(config[CONF_CHANNEL]))
|
||||
|
||||
@@ -119,18 +119,16 @@ void TLC59208FOutput::loop() {
|
||||
this->update_ = false;
|
||||
}
|
||||
|
||||
void TLC59208FOutput::register_channel(TLC59208FChannel *channel) {
|
||||
auto c = channel->channel_;
|
||||
this->min_channel_ = std::min(this->min_channel_, c);
|
||||
this->max_channel_ = std::max(this->max_channel_, c);
|
||||
channel->set_parent(this);
|
||||
void TLC59208FOutput::register_channel(uint8_t channel) {
|
||||
this->min_channel_ = std::min(this->min_channel_, channel);
|
||||
this->max_channel_ = std::max(this->max_channel_, channel);
|
||||
}
|
||||
|
||||
void TLC59208FChannel::write_state(float state) {
|
||||
const uint8_t max_duty = 255;
|
||||
const float duty_rounded = roundf(state * max_duty);
|
||||
auto duty = static_cast<uint8_t>(duty_rounded);
|
||||
this->parent_->set_channel_value_(this->channel_, duty);
|
||||
void TLC59208FOutput::write_channel(uint8_t channel, float state) {
|
||||
auto duty = static_cast<uint8_t>(roundf(state * 255));
|
||||
if (this->pwm_amounts_[channel] != duty)
|
||||
this->update_ = true;
|
||||
this->pwm_amounts_[channel] = duty;
|
||||
}
|
||||
|
||||
} // namespace esphome::tlc59208f
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/components/output/float_output.h"
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
|
||||
namespace esphome::tlc59208f {
|
||||
@@ -19,26 +17,13 @@ inline constexpr uint8_t TLC59208F_MODE2_WDT_15MS = (1 << 0);
|
||||
inline constexpr uint8_t TLC59208F_MODE2_WDT_25MS = (2 << 0);
|
||||
inline constexpr uint8_t TLC59208F_MODE2_WDT_35MS = (3 << 0);
|
||||
|
||||
class TLC59208FOutput;
|
||||
|
||||
class TLC59208FChannel final : public output::FloatOutput, public Parented<TLC59208FOutput> {
|
||||
public:
|
||||
void set_channel(uint8_t channel) { channel_ = channel; }
|
||||
|
||||
protected:
|
||||
friend class TLC59208FOutput;
|
||||
|
||||
void write_state(float state) override;
|
||||
|
||||
uint8_t channel_;
|
||||
};
|
||||
|
||||
/// TLC59208F float output component.
|
||||
class TLC59208FOutput final : public Component, public i2c::I2CDevice {
|
||||
public:
|
||||
TLC59208FOutput(uint8_t mode = TLC59208F_MODE2_OCH) : mode_(mode) {}
|
||||
|
||||
void register_channel(TLC59208FChannel *channel);
|
||||
void register_channel(uint8_t channel);
|
||||
void write_channel(uint8_t channel, float state);
|
||||
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
@@ -46,14 +31,6 @@ class TLC59208FOutput final : public Component, public i2c::I2CDevice {
|
||||
void loop() override;
|
||||
|
||||
protected:
|
||||
friend TLC59208FChannel;
|
||||
|
||||
void set_channel_value_(uint8_t channel, uint8_t value) {
|
||||
if (this->pwm_amounts_[channel] != value)
|
||||
this->update_ = true;
|
||||
this->pwm_amounts_[channel] = value;
|
||||
}
|
||||
|
||||
uint8_t mode_;
|
||||
|
||||
uint8_t min_channel_{0xFF};
|
||||
|
||||
@@ -1,29 +1,24 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import output
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_CHANNEL, CONF_ID
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from .. import TLC5947, tlc5947_ns
|
||||
from .. import TLC5947
|
||||
|
||||
DEPENDENCIES = ["tlc5947"]
|
||||
|
||||
TLC5947Channel = tlc5947_ns.class_(
|
||||
"TLC5947Channel", output.FloatOutput, cg.Parented.template(TLC5947)
|
||||
)
|
||||
|
||||
CONF_TLC5947_ID = "tlc5947_id"
|
||||
CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(CONF_TLC5947_ID): cv.use_id(TLC5947),
|
||||
cv.Required(CONF_ID): cv.declare_id(TLC5947Channel),
|
||||
cv.Required(CONF_ID): cv.declare_id(output.ApplyFloatOutput),
|
||||
cv.Required(CONF_CHANNEL): cv.uint16_t,
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await output.register_output(var, config)
|
||||
await cg.register_parented(var, config[CONF_TLC5947_ID])
|
||||
cg.add(var.set_channel(config[CONF_CHANNEL]))
|
||||
await output.sub_float_outputs(config, parent=config[CONF_TLC5947_ID]).create(
|
||||
config, None, config[CONF_CHANNEL], apply="write_channel(index, state)"
|
||||
)
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
#include "tlc5947_output.h"
|
||||
|
||||
namespace esphome::tlc5947 {
|
||||
|
||||
void TLC5947Channel::write_state(float state) {
|
||||
auto amount = static_cast<uint16_t>(state * 0xfff);
|
||||
this->parent_->set_channel_value(this->channel_, amount);
|
||||
}
|
||||
|
||||
} // namespace esphome::tlc5947
|
||||
@@ -1,20 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
#include "esphome/components/output/float_output.h"
|
||||
|
||||
#include "../tlc5947.h"
|
||||
|
||||
namespace esphome::tlc5947 {
|
||||
|
||||
class TLC5947Channel final : public output::FloatOutput, public Parented<TLC5947> {
|
||||
public:
|
||||
void set_channel(uint16_t channel) { this->channel_ = channel; }
|
||||
|
||||
protected:
|
||||
void write_state(float state) override;
|
||||
uint16_t channel_;
|
||||
};
|
||||
|
||||
} // namespace esphome::tlc5947
|
||||
@@ -3,7 +3,6 @@
|
||||
// https://www.ti.com/lit/ds/symlink/tlc5947.pdf
|
||||
|
||||
#include <vector>
|
||||
#include "esphome/components/output/float_output.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/hal.h"
|
||||
|
||||
@@ -29,6 +28,10 @@ class TLC5947 final : public Component {
|
||||
void loop() override;
|
||||
|
||||
void set_channel_value(uint16_t channel, uint16_t value);
|
||||
/// Set a channel from an output level, 0.0 to 1.0.
|
||||
void write_channel(uint16_t channel, float state) {
|
||||
this->set_channel_value(channel, static_cast<uint16_t>(state * 0xfff));
|
||||
}
|
||||
|
||||
protected:
|
||||
GPIOPin *data_pin_;
|
||||
|
||||
@@ -1,29 +1,24 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import output
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_CHANNEL, CONF_ID
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from .. import TLC5971, tlc5971_ns
|
||||
from .. import TLC5971
|
||||
|
||||
DEPENDENCIES = ["tlc5971"]
|
||||
|
||||
TLC5971Channel = tlc5971_ns.class_(
|
||||
"TLC5971Channel", output.FloatOutput, cg.Parented.template(TLC5971)
|
||||
)
|
||||
|
||||
CONF_TLC5971_ID = "tlc5971_id"
|
||||
CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(CONF_TLC5971_ID): cv.use_id(TLC5971),
|
||||
cv.Required(CONF_ID): cv.declare_id(TLC5971Channel),
|
||||
cv.Required(CONF_ID): cv.declare_id(output.ApplyFloatOutput),
|
||||
cv.Required(CONF_CHANNEL): cv.uint16_t,
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await output.register_output(var, config)
|
||||
await cg.register_parented(var, config[CONF_TLC5971_ID])
|
||||
cg.add(var.set_channel(config[CONF_CHANNEL]))
|
||||
await output.sub_float_outputs(config, parent=config[CONF_TLC5971_ID]).create(
|
||||
config, None, config[CONF_CHANNEL], apply="write_channel(index, state)"
|
||||
)
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
#include "tlc5971_output.h"
|
||||
|
||||
namespace esphome::tlc5971 {
|
||||
|
||||
void TLC5971Channel::write_state(float state) {
|
||||
auto amount = static_cast<uint16_t>(state * 0xffff);
|
||||
this->parent_->set_channel_value(this->channel_, amount);
|
||||
}
|
||||
|
||||
} // namespace esphome::tlc5971
|
||||
@@ -1,20 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
#include "esphome/components/output/float_output.h"
|
||||
|
||||
#include "../tlc5971.h"
|
||||
|
||||
namespace esphome::tlc5971 {
|
||||
|
||||
class TLC5971Channel final : public output::FloatOutput, public Parented<TLC5971> {
|
||||
public:
|
||||
void set_channel(uint16_t channel) { this->channel_ = channel; }
|
||||
|
||||
protected:
|
||||
void write_state(float state) override;
|
||||
uint16_t channel_;
|
||||
};
|
||||
|
||||
} // namespace esphome::tlc5971
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/components/output/float_output.h"
|
||||
#include <vector>
|
||||
|
||||
namespace esphome::tlc5971 {
|
||||
@@ -27,6 +26,10 @@ class TLC5971 final : public Component {
|
||||
void loop() override;
|
||||
|
||||
void set_channel_value(uint16_t channel, uint16_t value);
|
||||
/// Set a channel from an output level, 0.0 to 1.0.
|
||||
void write_channel(uint16_t channel, float state) {
|
||||
this->set_channel_value(channel, static_cast<uint16_t>(state * 0xffff));
|
||||
}
|
||||
|
||||
protected:
|
||||
void transfer_(uint8_t send);
|
||||
|
||||
@@ -1,26 +1,20 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import output
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID, CONF_LED
|
||||
from esphome.const import CONF_LED
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from ..display import CONF_TM1638_ID, TM1638Component, tm1638_ns
|
||||
|
||||
TM1638OutputLed = tm1638_ns.class_("TM1638OutputLed", output.BinaryOutput, cg.Component)
|
||||
|
||||
from ..display import CONF_TM1638_ID, TM1638Component
|
||||
|
||||
CONFIG_SCHEMA = output.BINARY_OUTPUT_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(TM1638OutputLed),
|
||||
cv.GenerateID(): cv.declare_id(output.ApplyBinaryOutput),
|
||||
cv.GenerateID(CONF_TM1638_ID): cv.use_id(TM1638Component),
|
||||
cv.Required(CONF_LED): cv.int_range(min=0, max=7),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await output.register_output(var, config)
|
||||
await cg.register_component(var, config)
|
||||
cg.add(var.set_lednum(config[CONF_LED]))
|
||||
await cg.add_variable(var.set_tm1638, config[CONF_TM1638_ID])
|
||||
await output.sub_binary_outputs(config, parent=config[CONF_TM1638_ID]).create(
|
||||
config, None, config[CONF_LED], apply="set_led(index, state)"
|
||||
)
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
#include "tm1638_output_led.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome::tm1638 {
|
||||
|
||||
ESPHOME_LOG_TAG(TAG, "tm1638.led");
|
||||
|
||||
void TM1638OutputLed::write_state(bool state) { tm1638_->set_led(led_, state); }
|
||||
|
||||
void TM1638OutputLed::dump_config() {
|
||||
LOG_BINARY_OUTPUT(this);
|
||||
ESP_LOGCONFIG(TAG, " LED: %d", led_);
|
||||
}
|
||||
|
||||
} // namespace esphome::tm1638
|
||||
@@ -1,23 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/output/binary_output.h"
|
||||
#include "../tm1638.h"
|
||||
|
||||
namespace esphome::tm1638 {
|
||||
|
||||
class TM1638OutputLed final : public output::BinaryOutput, public Component {
|
||||
public:
|
||||
void dump_config() override;
|
||||
|
||||
void set_tm1638(TM1638Component *tm1638) { tm1638_ = tm1638; }
|
||||
void set_lednum(int led) { led_ = led; }
|
||||
|
||||
protected:
|
||||
void write_state(bool state) override;
|
||||
|
||||
TM1638Component *tm1638_;
|
||||
int led_;
|
||||
};
|
||||
|
||||
} // namespace esphome::tm1638
|
||||
@@ -1,22 +1,12 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import output
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID, CONF_MAX_VALUE, CONF_MIN_VALUE
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from .. import (
|
||||
CONF_WAVESHARE_IO_CH32V003_ID,
|
||||
WaveshareIOCH32V003Component,
|
||||
waveshare_io_ch32v003_ns,
|
||||
)
|
||||
from .. import CONF_WAVESHARE_IO_CH32V003_ID, WaveshareIOCH32V003Component
|
||||
|
||||
DEPENDENCIES = ["waveshare_io_ch32v003"]
|
||||
|
||||
WaveshareIOCH32V003Output = waveshare_io_ch32v003_ns.class_(
|
||||
"WaveshareIOCH32V003Output",
|
||||
output.FloatOutput,
|
||||
cg.Parented.template(WaveshareIOCH32V003Component),
|
||||
)
|
||||
|
||||
CONF_SAFE_PWM_LEVELS = "safe_pwm_levels"
|
||||
|
||||
@@ -47,7 +37,7 @@ CONF_SAFE_PWM_LEVELS_SCHEMA = cv.Schema(
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
output.FLOAT_OUTPUT_SCHEMA.extend(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.declare_id(WaveshareIOCH32V003Output),
|
||||
cv.Required(CONF_ID): cv.declare_id(output.ApplyFloatOutput),
|
||||
cv.GenerateID(CONF_WAVESHARE_IO_CH32V003_ID): cv.use_id(
|
||||
WaveshareIOCH32V003Component
|
||||
),
|
||||
@@ -59,9 +49,12 @@ CONFIG_SCHEMA = cv.All(
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await output.register_output(var, config)
|
||||
await cg.register_parented(var, config[CONF_WAVESHARE_IO_CH32V003_ID])
|
||||
min_val = config.get(CONF_SAFE_PWM_LEVELS, {}).get(CONF_MIN_VALUE, DUTY_DEFAULT_MIN)
|
||||
max_val = config.get(CONF_SAFE_PWM_LEVELS, {}).get(CONF_MAX_VALUE, DUTY_DEFAULT_MAX)
|
||||
cg.add(var.set_pwm_safe_range(min_val, max_val))
|
||||
await output.sub_float_outputs(
|
||||
config, parent=config[CONF_WAVESHARE_IO_CH32V003_ID]
|
||||
).create(
|
||||
config,
|
||||
None,
|
||||
apply=f"write_pwm_level(state, {min_val}, {max_val})",
|
||||
)
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
#include "waveshare_io_ch32v003_output.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include <algorithm>
|
||||
|
||||
namespace esphome::waveshare_io_ch32v003 {
|
||||
|
||||
ESPHOME_LOG_TAG(TAG, "waveshare_io_ch32v003.output");
|
||||
|
||||
void WaveshareIOCH32V003Output::write_state(float state) {
|
||||
uint8_t pwm_value = static_cast<uint8_t>(state * 255.0f);
|
||||
uint8_t final_pwm_value = std::clamp(pwm_value, this->pwm_min_value_, this->pwm_max_value_);
|
||||
if (final_pwm_value != pwm_value) {
|
||||
ESP_LOGVV(TAG, "Clamping PWM value %u to safe range [%u, %u]", pwm_value, this->pwm_min_value_,
|
||||
this->pwm_max_value_);
|
||||
}
|
||||
this->parent_->set_pwm_value(final_pwm_value);
|
||||
}
|
||||
|
||||
} // namespace esphome::waveshare_io_ch32v003
|
||||
@@ -1,22 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../waveshare_io_ch32v003.h"
|
||||
#include "esphome/components/output/float_output.h"
|
||||
|
||||
namespace esphome::waveshare_io_ch32v003 {
|
||||
|
||||
class WaveshareIOCH32V003Output : public output::FloatOutput, public Parented<WaveshareIOCH32V003Component> {
|
||||
public:
|
||||
void set_pwm_safe_range(uint8_t min_value, uint8_t max_value) {
|
||||
this->pwm_min_value_ = min_value;
|
||||
this->pwm_max_value_ = max_value;
|
||||
}
|
||||
|
||||
protected:
|
||||
void write_state(float state) override;
|
||||
|
||||
uint8_t pwm_min_value_{1};
|
||||
uint8_t pwm_max_value_{247};
|
||||
};
|
||||
|
||||
} // namespace esphome::waveshare_io_ch32v003
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "waveshare_io_ch32v003.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include <algorithm>
|
||||
|
||||
namespace esphome::waveshare_io_ch32v003 {
|
||||
|
||||
@@ -73,13 +74,20 @@ void WaveshareIOCH32V003Component::set_pwm_value(uint8_t value) {
|
||||
if (this->is_failed())
|
||||
return;
|
||||
|
||||
// PWM limits are enforced at the output component level to protect hardware
|
||||
// based on circuit schematic requirements. This follows the pattern from the
|
||||
// original Waveshare IO library function "void IO_EXTENSION_Pwm_Output(uint8_t Value)".
|
||||
|
||||
// The safe PWM range is enforced by write_pwm_level(), as in the original Waveshare IO
|
||||
// library function "void IO_EXTENSION_Pwm_Output(uint8_t Value)".
|
||||
this->status_comm_result(this->write_byte(IO_EXTENSION_PWM_ADDR, value), LOG_STR("Failed to set PWM duty cycle"));
|
||||
}
|
||||
|
||||
void WaveshareIOCH32V003Component::write_pwm_level(float state, uint8_t min_value, uint8_t max_value) {
|
||||
auto pwm_value = static_cast<uint8_t>(state * 255.0f);
|
||||
uint8_t final_pwm_value = std::clamp(pwm_value, min_value, max_value);
|
||||
if (final_pwm_value != pwm_value) {
|
||||
ESP_LOGVV(TAG, "Clamping PWM value %u to safe range [%u, %u]", pwm_value, min_value, max_value);
|
||||
}
|
||||
this->set_pwm_value(final_pwm_value);
|
||||
}
|
||||
|
||||
bool WaveshareIOCH32V003Component::write_gpio_modes_() {
|
||||
if (this->is_failed())
|
||||
return false;
|
||||
|
||||
@@ -26,6 +26,8 @@ class WaveshareIOCH32V003Component : public Component,
|
||||
uint16_t get_adc_value();
|
||||
uint8_t get_rtc_interrupt_status();
|
||||
void set_pwm_value(uint8_t value); // 0 - 255
|
||||
/// Write an output level, 0.0 to 1.0, clamped to the board's safe PWM range.
|
||||
void write_pwm_level(float state, uint8_t min_value, uint8_t max_value);
|
||||
|
||||
protected:
|
||||
bool digital_read_hw(uint8_t pin) override;
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.types import ConfigType
|
||||
from tests.testing_helpers import ComponentManifestOverride
|
||||
|
||||
|
||||
def override_manifest(manifest: ComponentManifestOverride) -> None:
|
||||
# Builds the min/max power scaling so the Apply output tests cover it
|
||||
async def to_code_testing(config: ConfigType) -> None:
|
||||
cg.add_define("USE_OUTPUT_FLOAT_POWER_SCALING")
|
||||
|
||||
manifest.to_code = to_code_testing
|
||||
@@ -0,0 +1,81 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "esphome/components/output/binary_output.h"
|
||||
#include "esphome/components/output/float_output.h"
|
||||
|
||||
namespace esphome::output::testing {
|
||||
|
||||
static void *last_output = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
static uint16_t last_index = 0; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
static std::vector<bool> bool_writes; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
static std::vector<float> levels; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
|
||||
static void on_binary_write(BinaryOutput *output, uint16_t index, bool state) {
|
||||
last_output = output;
|
||||
last_index = index;
|
||||
bool_writes.push_back(state);
|
||||
}
|
||||
|
||||
static void on_float_write(FloatOutput *output, uint16_t index, float state) {
|
||||
last_output = output;
|
||||
last_index = index;
|
||||
levels.push_back(state);
|
||||
}
|
||||
|
||||
// The index sits in the base's tail padding; only the power supply requester leaves none.
|
||||
#ifndef USE_POWER_SUPPLY
|
||||
static_assert(sizeof(ApplyBinaryOutput<on_binary_write>) == sizeof(BinaryOutput));
|
||||
static_assert(sizeof(ApplyFloatOutput<on_float_write>) == sizeof(FloatOutput));
|
||||
#endif
|
||||
|
||||
static void reset() {
|
||||
last_output = nullptr;
|
||||
last_index = 0;
|
||||
bool_writes.clear();
|
||||
levels.clear();
|
||||
}
|
||||
|
||||
TEST(ApplyBinaryOutput, WritesReachTheFunctionWithIndexAndInversion) {
|
||||
reset();
|
||||
ApplyBinaryOutput<on_binary_write> out(7);
|
||||
out.turn_on();
|
||||
out.turn_off();
|
||||
out.set_inverted(true);
|
||||
out.turn_on();
|
||||
EXPECT_EQ(bool_writes, (std::vector<bool>{true, false, false}));
|
||||
EXPECT_EQ(last_output, &out);
|
||||
EXPECT_EQ(last_index, 7);
|
||||
}
|
||||
|
||||
TEST(ApplyFloatOutput, WritesReachTheFunctionWithIndexInversionAndClamp) {
|
||||
reset();
|
||||
ApplyFloatOutput<on_float_write> out(3);
|
||||
out.set_level(0.25f);
|
||||
out.turn_on();
|
||||
out.set_inverted(true);
|
||||
out.set_level(0.25f);
|
||||
out.set_level(2.0f);
|
||||
EXPECT_EQ(levels, (std::vector<float>{0.25f, 1.0f, 0.75f, 0.0f}));
|
||||
EXPECT_EQ(last_output, &out);
|
||||
EXPECT_EQ(last_index, 3);
|
||||
}
|
||||
|
||||
#ifdef USE_OUTPUT_FLOAT_POWER_SCALING
|
||||
TEST(ApplyFloatOutput, PowerScalingIsAppliedBeforeTheFunction) {
|
||||
reset();
|
||||
ApplyFloatOutput<on_float_write> out;
|
||||
out.set_min_power(0.2f);
|
||||
out.set_max_power(0.8f);
|
||||
out.set_zero_means_zero(true);
|
||||
out.set_level(0.5f);
|
||||
out.turn_off();
|
||||
ASSERT_EQ(levels.size(), 2u);
|
||||
EXPECT_FLOAT_EQ(levels[0], 0.5f);
|
||||
EXPECT_EQ(levels[1], 0.0f);
|
||||
EXPECT_EQ(last_index, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace esphome::output::testing
|
||||
@@ -8,7 +8,7 @@ from typing import Any
|
||||
import pytest
|
||||
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import button, number, select, switch
|
||||
from esphome.components import button, number, output, select, switch
|
||||
from esphome.config_validation import Invalid
|
||||
from esphome.const import (
|
||||
CONF_DEVICE_CLASS,
|
||||
@@ -1464,6 +1464,30 @@ async def test_sub_entities_apply_shares_equal_functions(
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_sub_outputs_share_one_function_per_hub(
|
||||
setup_test_environment: list[str], apply_functions: list[tuple[str, str, str]]
|
||||
) -> None:
|
||||
"""A hub's channels pass their channel as the index, so they share one function."""
|
||||
calls: list[tuple[Any, ...]] = []
|
||||
cls = output.ApplyFloatOutput
|
||||
config = {"a": _apply_conf("ch0", cls), "b": _apply_conf("ch5", cls)}
|
||||
sub = _apply_sub(calls, config, output.FLOAT_APPLY_ENTITY)
|
||||
await sub("a", None, 0, apply="write_channel(index, state)")
|
||||
await sub("b", None, 5, apply="write_channel(index, state)")
|
||||
assert calls == [
|
||||
("ch0", "<esphome__ch0__fn>", "0"),
|
||||
("ch5", "<esphome__ch0__fn>", "5"),
|
||||
]
|
||||
assert apply_functions == [
|
||||
(
|
||||
"esphome__ch0__fn",
|
||||
"output::FloatOutput * entity, uint16_t index, float state",
|
||||
"::hub->write_channel(index, state);",
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_sub_entities_apply_parent_id(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
@@ -1565,6 +1589,18 @@ async def test_sub_entities_apply_needs_parent(
|
||||
"select::Select * entity, size_t index",
|
||||
"entity->publish_state(index);",
|
||||
),
|
||||
(
|
||||
output.sub_binary_outputs,
|
||||
output.ApplyBinaryOutput,
|
||||
"output::BinaryOutput * entity, uint16_t index, bool state",
|
||||
None,
|
||||
),
|
||||
(
|
||||
output.sub_float_outputs,
|
||||
output.ApplyFloatOutput,
|
||||
"output::FloatOutput * entity, uint16_t index, float state",
|
||||
None,
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_domain_apply_entities(
|
||||
|
||||
Reference in New Issue
Block a user