Merge branch 'dev' into optolink

This commit is contained in:
j0ta29 2023-03-16 08:28:23 +01:00 committed by GitHub
commit 9d7063cce4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 187 additions and 57 deletions

View file

@ -32,9 +32,11 @@ void Rect::extend(Rect rect) {
this->h = rect.h; this->h = rect.h;
} else { } else {
if (this->x > rect.x) { if (this->x > rect.x) {
this->w = this->w + (this->x - rect.x);
this->x = rect.x; this->x = rect.x;
} }
if (this->y > rect.y) { if (this->y > rect.y) {
this->h = this->h + (this->y - rect.y);
this->y = rect.y; this->y = rect.y;
} }
if (this->x2() < rect.x2()) { if (this->x2() < rect.x2()) {
@ -49,29 +51,35 @@ void Rect::shrink(Rect rect) {
if (!this->inside(rect)) { if (!this->inside(rect)) {
(*this) = Rect(); (*this) = Rect();
} else { } else {
if (this->x < rect.x) {
this->x = rect.x;
}
if (this->y < rect.y) {
this->y = rect.y;
}
if (this->x2() > rect.x2()) { if (this->x2() > rect.x2()) {
this->w = rect.x2() - this->x; this->w = rect.x2() - this->x;
} }
if (this->x < rect.x) {
this->w = this->w + (this->x - rect.x);
this->x = rect.x;
}
if (this->y2() > rect.y2()) { if (this->y2() > rect.y2()) {
this->h = rect.y2() - this->y; this->h = rect.y2() - this->y;
} }
if (this->y < rect.y) {
this->h = this->h + (this->y - rect.y);
this->y = rect.y;
}
} }
} }
bool Rect::inside(int16_t x, int16_t y, bool absolute) { // NOLINT bool Rect::equal(Rect rect) {
return (rect.x == this->x) && (rect.w == this->w) && (rect.y == this->y) && (rect.h == this->h);
}
bool Rect::inside(int16_t test_x, int16_t test_y, bool absolute) { // NOLINT
if (!this->is_set()) { if (!this->is_set()) {
return true; return true;
} }
if (absolute) { if (absolute) {
return ((x >= 0) && (x <= this->w) && (y >= 0) && (y <= this->h)); return ((test_x >= this->x) && (test_x <= this->x2()) && (test_y >= this->y) && (test_y <= this->y2()));
} else { } else {
return ((x >= this->x) && (x <= this->x2()) && (y >= this->y) && (y <= this->y2())); return ((test_x >= 0) && (test_x <= this->w) && (test_y >= 0) && (test_y <= this->h));
} }
} }
@ -80,15 +88,16 @@ bool Rect::inside(Rect rect, bool absolute) {
return true; return true;
} }
if (absolute) { if (absolute) {
return ((rect.x <= this->w) && (rect.w >= 0) && (rect.y <= this->h) && (rect.h >= 0));
} else {
return ((rect.x <= this->x2()) && (rect.x2() >= this->x) && (rect.y <= this->y2()) && (rect.y2() >= this->y)); return ((rect.x <= this->x2()) && (rect.x2() >= this->x) && (rect.y <= this->y2()) && (rect.y2() >= this->y));
} else {
return ((rect.x <= this->w) && (rect.w >= 0) && (rect.y <= this->h) && (rect.h >= 0));
} }
} }
void Rect::info(const std::string &prefix) { void Rect::info(const std::string &prefix) {
if (this->is_set()) { if (this->is_set()) {
ESP_LOGI(TAG, "%s [%3d,%3d,%3d,%3d]", prefix.c_str(), this->x, this->y, this->w, this->h); ESP_LOGI(TAG, "%s [%3d,%3d,%3d,%3d] (%3d,%3d)", prefix.c_str(), this->x, this->y, this->w, this->h, this->x2(),
this->y2());
} else } else
ESP_LOGI(TAG, "%s ** IS NOT SET **", prefix.c_str()); ESP_LOGI(TAG, "%s ** IS NOT SET **", prefix.c_str());
} }

View file

@ -120,8 +120,9 @@ class Rect {
void extend(Rect rect); void extend(Rect rect);
void shrink(Rect rect); void shrink(Rect rect);
bool inside(Rect rect, bool absolute = false); bool inside(Rect rect, bool absolute = true);
bool inside(int16_t x, int16_t y, bool absolute = false); bool inside(int16_t test_x, int16_t test_y, bool absolute = true);
bool equal(Rect rect);
void info(const std::string &prefix = "rect info:"); void info(const std::string &prefix = "rect info:");
}; };

View file

@ -7,6 +7,7 @@ CODEOWNERS = ["@rspaargaren"]
DEPENDENCIES = ["spi"] DEPENDENCIES = ["spi"]
CONF_ROTATE_CHIP = "rotate_chip" CONF_ROTATE_CHIP = "rotate_chip"
CONF_FLIP_X = "flip_x"
CONF_SCROLL_SPEED = "scroll_speed" CONF_SCROLL_SPEED = "scroll_speed"
CONF_SCROLL_DWELL = "scroll_dwell" CONF_SCROLL_DWELL = "scroll_dwell"
CONF_SCROLL_DELAY = "scroll_delay" CONF_SCROLL_DELAY = "scroll_delay"
@ -67,6 +68,7 @@ CONFIG_SCHEMA = (
CONF_SCROLL_DWELL, default="1000ms" CONF_SCROLL_DWELL, default="1000ms"
): cv.positive_time_period_milliseconds, ): cv.positive_time_period_milliseconds,
cv.Optional(CONF_REVERSE_ENABLE, default=False): cv.boolean, cv.Optional(CONF_REVERSE_ENABLE, default=False): cv.boolean,
cv.Optional(CONF_FLIP_X, default=False): cv.boolean,
} }
) )
.extend(cv.polling_component_schema("500ms")) .extend(cv.polling_component_schema("500ms"))
@ -91,6 +93,7 @@ async def to_code(config):
cg.add(var.set_scroll(config[CONF_SCROLL_ENABLE])) cg.add(var.set_scroll(config[CONF_SCROLL_ENABLE]))
cg.add(var.set_scroll_mode(config[CONF_SCROLL_MODE])) cg.add(var.set_scroll_mode(config[CONF_SCROLL_MODE]))
cg.add(var.set_reverse(config[CONF_REVERSE_ENABLE])) cg.add(var.set_reverse(config[CONF_REVERSE_ENABLE]))
cg.add(var.set_flip_x([CONF_FLIP_X]))
if CONF_LAMBDA in config: if CONF_LAMBDA in config:
lambda_ = await cg.process_lambda( lambda_ = await cg.process_lambda(

View file

@ -261,14 +261,22 @@ void MAX7219Component::send64pixels(uint8_t chip, const uint8_t pixels[8]) {
if (this->orientation_ == 0) { if (this->orientation_ == 0) {
for (uint8_t i = 0; i < 8; i++) { for (uint8_t i = 0; i < 8; i++) {
// run this loop 8 times for all the pixels[8] received // run this loop 8 times for all the pixels[8] received
if (this->flip_x_) {
b |= ((pixels[i] >> col) & 1) << i; // change the column bits into row bits
} else {
b |= ((pixels[i] >> col) & 1) << (7 - i); // change the column bits into row bits b |= ((pixels[i] >> col) & 1) << (7 - i); // change the column bits into row bits
} }
}
} else if (this->orientation_ == 1) { } else if (this->orientation_ == 1) {
b = pixels[col]; b = pixels[col];
} else if (this->orientation_ == 2) { } else if (this->orientation_ == 2) {
for (uint8_t i = 0; i < 8; i++) { for (uint8_t i = 0; i < 8; i++) {
if (this->flip_x_) {
b |= ((pixels[i] >> (7 - col)) & 1) << (7 - i);
} else {
b |= ((pixels[i] >> (7 - col)) & 1) << i; b |= ((pixels[i] >> (7 - col)) & 1) << i;
} }
}
} else { } else {
b = pixels[7 - col]; b = pixels[7 - col];
} }

View file

@ -67,6 +67,7 @@ class MAX7219Component : public PollingComponent,
void set_scroll(bool on_off) { this->scroll_ = on_off; }; void set_scroll(bool on_off) { this->scroll_ = on_off; };
void set_scroll_mode(ScrollMode mode) { this->scroll_mode_ = mode; }; void set_scroll_mode(ScrollMode mode) { this->scroll_mode_ = mode; };
void set_reverse(bool on_off) { this->reverse_ = on_off; }; void set_reverse(bool on_off) { this->reverse_ = on_off; };
void set_flip_x(bool flip_x) { this->flip_x_ = flip_x; };
void send_char(uint8_t chip, uint8_t data); void send_char(uint8_t chip, uint8_t data);
void send64pixels(uint8_t chip, const uint8_t pixels[8]); void send64pixels(uint8_t chip, const uint8_t pixels[8]);
@ -108,6 +109,7 @@ class MAX7219Component : public PollingComponent,
ChipLinesStyle chip_lines_style_; ChipLinesStyle chip_lines_style_;
bool scroll_; bool scroll_;
bool reverse_; bool reverse_;
bool flip_x_;
bool update_{false}; bool update_{false};
uint16_t scroll_speed_; uint16_t scroll_speed_;
uint16_t scroll_delay_; uint16_t scroll_delay_;

View file

@ -264,13 +264,52 @@ void PMSX003Component::parse_data_() {
break; break;
} }
case PMSX003_TYPE_5003T: { case PMSX003_TYPE_5003T: {
uint16_t pm_1_0_std_concentration = this->get_16_bit_uint_(4);
uint16_t pm_2_5_std_concentration = this->get_16_bit_uint_(6);
uint16_t pm_10_0_std_concentration = this->get_16_bit_uint_(8);
uint16_t pm_1_0_concentration = this->get_16_bit_uint_(10);
uint16_t pm_2_5_concentration = this->get_16_bit_uint_(12); uint16_t pm_2_5_concentration = this->get_16_bit_uint_(12);
uint16_t pm_10_0_concentration = this->get_16_bit_uint_(14);
uint16_t pm_particles_03um = this->get_16_bit_uint_(16);
uint16_t pm_particles_05um = this->get_16_bit_uint_(18);
uint16_t pm_particles_10um = this->get_16_bit_uint_(20);
uint16_t pm_particles_25um = this->get_16_bit_uint_(22);
// Note the pm particles 50um & 100um are not returned,
// as PMS5003T uses those data values for temperature and humidity.
float temperature = this->get_16_bit_uint_(24) / 10.0f; float temperature = this->get_16_bit_uint_(24) / 10.0f;
float humidity = this->get_16_bit_uint_(26) / 10.0f; float humidity = this->get_16_bit_uint_(26) / 10.0f;
ESP_LOGD(TAG, "Got PM2.5 Concentration: %u µg/m^3, Temperature: %.1f°C, Humidity: %.1f%%", pm_2_5_concentration,
temperature, humidity); ESP_LOGD(TAG,
"Got PM1.0 Concentration: %u µg/m^3, PM2.5 Concentration %u µg/m^3, PM10.0 Concentration: %u µg/m^3, "
"Temperature: %.1f°C, Humidity: %.1f%%",
pm_1_0_concentration, pm_2_5_concentration, pm_10_0_concentration, temperature, humidity);
if (this->pm_1_0_std_sensor_ != nullptr)
this->pm_1_0_std_sensor_->publish_state(pm_1_0_std_concentration);
if (this->pm_2_5_std_sensor_ != nullptr)
this->pm_2_5_std_sensor_->publish_state(pm_2_5_std_concentration);
if (this->pm_10_0_std_sensor_ != nullptr)
this->pm_10_0_std_sensor_->publish_state(pm_10_0_std_concentration);
if (this->pm_1_0_sensor_ != nullptr)
this->pm_1_0_sensor_->publish_state(pm_1_0_concentration);
if (this->pm_2_5_sensor_ != nullptr) if (this->pm_2_5_sensor_ != nullptr)
this->pm_2_5_sensor_->publish_state(pm_2_5_concentration); this->pm_2_5_sensor_->publish_state(pm_2_5_concentration);
if (this->pm_10_0_sensor_ != nullptr)
this->pm_10_0_sensor_->publish_state(pm_10_0_concentration);
if (this->pm_particles_03um_sensor_ != nullptr)
this->pm_particles_03um_sensor_->publish_state(pm_particles_03um);
if (this->pm_particles_05um_sensor_ != nullptr)
this->pm_particles_05um_sensor_->publish_state(pm_particles_05um);
if (this->pm_particles_10um_sensor_ != nullptr)
this->pm_particles_10um_sensor_->publish_state(pm_particles_10um);
if (this->pm_particles_25um_sensor_ != nullptr)
this->pm_particles_25um_sensor_->publish_state(pm_particles_25um);
if (this->temperature_sensor_ != nullptr) if (this->temperature_sensor_ != nullptr)
this->temperature_sensor_->publish_state(temperature); this->temperature_sensor_->publish_state(temperature);
if (this->humidity_sensor_ != nullptr) if (this->humidity_sensor_ != nullptr)

View file

@ -55,9 +55,9 @@ PMSX003_TYPES = {
} }
SENSORS_TO_TYPE = { SENSORS_TO_TYPE = {
CONF_PM_1_0: [TYPE_PMSX003, TYPE_PMS5003ST, TYPE_PMS5003S], CONF_PM_1_0: [TYPE_PMSX003, TYPE_PMS5003T, TYPE_PMS5003ST, TYPE_PMS5003S],
CONF_PM_2_5: [TYPE_PMSX003, TYPE_PMS5003T, TYPE_PMS5003ST, TYPE_PMS5003S], CONF_PM_2_5: [TYPE_PMSX003, TYPE_PMS5003T, TYPE_PMS5003ST, TYPE_PMS5003S],
CONF_PM_10_0: [TYPE_PMSX003, TYPE_PMS5003ST, TYPE_PMS5003S], CONF_PM_10_0: [TYPE_PMSX003, TYPE_PMS5003T, TYPE_PMS5003ST, TYPE_PMS5003S],
CONF_TEMPERATURE: [TYPE_PMS5003T, TYPE_PMS5003ST], CONF_TEMPERATURE: [TYPE_PMS5003T, TYPE_PMS5003ST],
CONF_HUMIDITY: [TYPE_PMS5003T, TYPE_PMS5003ST], CONF_HUMIDITY: [TYPE_PMS5003T, TYPE_PMS5003ST],
CONF_FORMALDEHYDE: [TYPE_PMS5003ST, TYPE_PMS5003S], CONF_FORMALDEHYDE: [TYPE_PMS5003ST, TYPE_PMS5003S],

View file

@ -466,9 +466,21 @@ async def lambda_filter_to_code(config, filter_id):
return cg.new_Pvariable(filter_id, lambda_) return cg.new_Pvariable(filter_id, lambda_)
@FILTER_REGISTRY.register("delta", DeltaFilter, cv.float_) def validate_delta(config):
try:
return (cv.positive_float(config), False)
except cv.Invalid:
pass
try:
return (cv.percentage(config), True)
except cv.Invalid:
pass
raise cv.Invalid("Delta filter requires a positive number or percentage value.")
@FILTER_REGISTRY.register("delta", DeltaFilter, validate_delta)
async def delta_filter_to_code(config, filter_id): async def delta_filter_to_code(config, filter_id):
return cg.new_Pvariable(filter_id, config) return cg.new_Pvariable(filter_id, *config)
@FILTER_REGISTRY.register("or", OrFilter, validate_filters) @FILTER_REGISTRY.register("or", OrFilter, validate_filters)

View file

@ -315,19 +315,23 @@ optional<float> ThrottleFilter::new_value(float value) {
} }
// DeltaFilter // DeltaFilter
DeltaFilter::DeltaFilter(float min_delta) : min_delta_(min_delta), last_value_(NAN) {} DeltaFilter::DeltaFilter(float delta, bool percentage_mode)
: delta_(delta), current_delta_(delta), percentage_mode_(percentage_mode), last_value_(NAN) {}
optional<float> DeltaFilter::new_value(float value) { optional<float> DeltaFilter::new_value(float value) {
if (std::isnan(value)) { if (std::isnan(value)) {
if (std::isnan(this->last_value_)) { if (std::isnan(this->last_value_)) {
return {}; return {};
} else { } else {
if (this->percentage_mode_) {
this->current_delta_ = fabsf(value * this->delta_);
}
return this->last_value_ = value; return this->last_value_ = value;
} }
} }
if (std::isnan(this->last_value_)) { if (std::isnan(this->last_value_) || fabsf(value - this->last_value_) >= this->current_delta_) {
return this->last_value_ = value; if (this->percentage_mode_) {
this->current_delta_ = fabsf(value * this->delta_);
} }
if (fabsf(value - this->last_value_) >= this->min_delta_) {
return this->last_value_ = value; return this->last_value_ = value;
} }
return {}; return {};

View file

@ -325,12 +325,14 @@ class HeartbeatFilter : public Filter, public Component {
class DeltaFilter : public Filter { class DeltaFilter : public Filter {
public: public:
explicit DeltaFilter(float min_delta); explicit DeltaFilter(float delta, bool percentage_mode);
optional<float> new_value(float value) override; optional<float> new_value(float value) override;
protected: protected:
float min_delta_; float delta_;
float current_delta_;
bool percentage_mode_;
float last_value_{NAN}; float last_value_{NAN};
}; };

View file

@ -17,6 +17,7 @@ spi_ns = cg.esphome_ns.namespace("spi")
SPIComponent = spi_ns.class_("SPIComponent", cg.Component) SPIComponent = spi_ns.class_("SPIComponent", cg.Component)
SPIDevice = spi_ns.class_("SPIDevice") SPIDevice = spi_ns.class_("SPIDevice")
MULTI_CONF = True MULTI_CONF = True
CONF_FORCE_SW = "force_sw"
CONFIG_SCHEMA = cv.All( CONFIG_SCHEMA = cv.All(
cv.Schema( cv.Schema(
@ -25,6 +26,7 @@ CONFIG_SCHEMA = cv.All(
cv.Required(CONF_CLK_PIN): pins.gpio_output_pin_schema, cv.Required(CONF_CLK_PIN): pins.gpio_output_pin_schema,
cv.Optional(CONF_MISO_PIN): pins.gpio_input_pin_schema, cv.Optional(CONF_MISO_PIN): pins.gpio_input_pin_schema,
cv.Optional(CONF_MOSI_PIN): pins.gpio_output_pin_schema, cv.Optional(CONF_MOSI_PIN): pins.gpio_output_pin_schema,
cv.Optional(CONF_FORCE_SW, default=False): cv.boolean,
} }
), ),
cv.has_at_least_one_key(CONF_MISO_PIN, CONF_MOSI_PIN), cv.has_at_least_one_key(CONF_MISO_PIN, CONF_MOSI_PIN),
@ -39,6 +41,7 @@ async def to_code(config):
clk = await cg.gpio_pin_expression(config[CONF_CLK_PIN]) clk = await cg.gpio_pin_expression(config[CONF_CLK_PIN])
cg.add(var.set_clk(clk)) cg.add(var.set_clk(clk))
cg.add(var.set_force_sw(config[CONF_FORCE_SW]))
if CONF_MISO_PIN in config: if CONF_MISO_PIN in config:
miso = await cg.gpio_pin_expression(config[CONF_MISO_PIN]) miso = await cg.gpio_pin_expression(config[CONF_MISO_PIN])
cg.add(var.set_miso(miso)) cg.add(var.set_miso(miso))

View file

@ -25,7 +25,7 @@ void SPIComponent::setup() {
this->clk_->digital_write(true); this->clk_->digital_write(true);
#ifdef USE_SPI_ARDUINO_BACKEND #ifdef USE_SPI_ARDUINO_BACKEND
bool use_hw_spi = true; bool use_hw_spi = !this->force_sw_;
const bool has_miso = this->miso_ != nullptr; const bool has_miso = this->miso_ != nullptr;
const bool has_mosi = this->mosi_ != nullptr; const bool has_mosi = this->mosi_ != nullptr;
int8_t clk_pin = -1, miso_pin = -1, mosi_pin = -1; int8_t clk_pin = -1, miso_pin = -1, mosi_pin = -1;

View file

@ -74,6 +74,7 @@ class SPIComponent : public Component {
void set_clk(GPIOPin *clk) { clk_ = clk; } void set_clk(GPIOPin *clk) { clk_ = clk; }
void set_miso(GPIOPin *miso) { miso_ = miso; } void set_miso(GPIOPin *miso) { miso_ = miso; }
void set_mosi(GPIOPin *mosi) { mosi_ = mosi; } void set_mosi(GPIOPin *mosi) { mosi_ = mosi; }
void set_force_sw(bool force_sw) { force_sw_ = force_sw; }
void setup() override; void setup() override;
@ -260,6 +261,7 @@ class SPIComponent : public Component {
GPIOPin *miso_{nullptr}; GPIOPin *miso_{nullptr};
GPIOPin *mosi_{nullptr}; GPIOPin *mosi_{nullptr};
GPIOPin *active_cs_{nullptr}; GPIOPin *active_cs_{nullptr};
bool force_sw_{false};
#ifdef USE_SPI_ARDUINO_BACKEND #ifdef USE_SPI_ARDUINO_BACKEND
SPIClass *hw_spi_{nullptr}; SPIClass *hw_spi_{nullptr};
#endif // USE_SPI_ARDUINO_BACKEND #endif // USE_SPI_ARDUINO_BACKEND

View file

@ -1,26 +1,35 @@
from esphome import pins from esphome import pins
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import light from esphome.components import light, output
from esphome.const import CONF_OUTPUT_ID, CONF_PIN from esphome.const import CONF_OUTPUT, CONF_OUTPUT_ID, CONF_PIN
from .. import status_led_ns from .. import status_led_ns
AUTO_LOAD = ["output"]
StatusLEDLightOutput = status_led_ns.class_( StatusLEDLightOutput = status_led_ns.class_(
"StatusLEDLightOutput", light.LightOutput, cg.Component "StatusLEDLightOutput", light.LightOutput, cg.Component
) )
CONFIG_SCHEMA = light.BINARY_LIGHT_SCHEMA.extend( CONFIG_SCHEMA = cv.All(
light.BINARY_LIGHT_SCHEMA.extend(
{ {
cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(StatusLEDLightOutput), cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(StatusLEDLightOutput),
cv.Required(CONF_PIN): pins.gpio_output_pin_schema, cv.Optional(CONF_PIN): pins.gpio_output_pin_schema,
cv.Optional(CONF_OUTPUT): cv.use_id(output.BinaryOutput),
} }
),
cv.has_at_least_one_key(CONF_PIN, CONF_OUTPUT),
) )
async def to_code(config): async def to_code(config):
var = cg.new_Pvariable(config[CONF_OUTPUT_ID]) var = cg.new_Pvariable(config[CONF_OUTPUT_ID])
if CONF_PIN in config:
pin = await cg.gpio_pin_expression(config[CONF_PIN]) pin = await cg.gpio_pin_expression(config[CONF_PIN])
cg.add(var.set_pin(pin)) cg.add(var.set_pin(pin))
if CONF_OUTPUT in config:
out = await cg.get_variable(config[CONF_OUTPUT])
cg.add(var.set_output(out))
await cg.register_component(var, config) await cg.register_component(var, config)
# cg.add(cg.App.register_component(var))
await light.register_light(var, config) await light.register_light(var, config)

View file

@ -15,10 +15,10 @@ void StatusLEDLightOutput::loop() {
} }
if ((new_state & STATUS_LED_ERROR) != 0u) { if ((new_state & STATUS_LED_ERROR) != 0u) {
this->pin_->digital_write(millis() % 250u < 150u); this->output_state_(millis() % 250u < 150u);
this->last_app_state_ = new_state; this->last_app_state_ = new_state;
} else if ((new_state & STATUS_LED_WARNING) != 0u) { } else if ((new_state & STATUS_LED_WARNING) != 0u) {
this->pin_->digital_write(millis() % 1500u < 250u); this->output_state_(millis() % 1500u < 250u);
this->last_app_state_ = new_state; this->last_app_state_ = new_state;
} else if (new_state != this->last_app_state_) { } else if (new_state != this->last_app_state_) {
// if no error/warning -> restore light state or turn off // if no error/warning -> restore light state or turn off
@ -26,17 +26,16 @@ void StatusLEDLightOutput::loop() {
if (lightstate_) if (lightstate_)
lightstate_->current_values_as_binary(&state); lightstate_->current_values_as_binary(&state);
this->pin_->digital_write(state);
this->last_app_state_ = new_state;
ESP_LOGD(TAG, "Restoring light state %s", ONOFF(state)); ESP_LOGD(TAG, "Restoring light state %s", ONOFF(state));
this->output_state_(state);
this->last_app_state_ = new_state;
} }
} }
void StatusLEDLightOutput::setup_state(light::LightState *state) { void StatusLEDLightOutput::setup_state(light::LightState *state) {
lightstate_ = state; lightstate_ = state;
ESP_LOGD(TAG, "'%s': Setting initital state", state->get_name().c_str()); ESP_LOGD(TAG, "'%s': Setting initial state", state->get_name().c_str());
this->write_state(state); this->write_state(state);
} }
@ -47,22 +46,31 @@ void StatusLEDLightOutput::write_state(light::LightState *state) {
// if in warning/error, don't overwrite the status_led // if in warning/error, don't overwrite the status_led
// once it is back to OK, the loop will restore the state // once it is back to OK, the loop will restore the state
if ((App.get_app_state() & (STATUS_LED_ERROR | STATUS_LED_WARNING)) == 0u) { if ((App.get_app_state() & (STATUS_LED_ERROR | STATUS_LED_WARNING)) == 0u) {
this->pin_->digital_write(binary);
ESP_LOGD(TAG, "'%s': Setting state %s", state->get_name().c_str(), ONOFF(binary)); ESP_LOGD(TAG, "'%s': Setting state %s", state->get_name().c_str(), ONOFF(binary));
this->output_state_(binary);
} }
} }
void StatusLEDLightOutput::setup() { void StatusLEDLightOutput::setup() {
ESP_LOGCONFIG(TAG, "Setting up Status LED..."); ESP_LOGCONFIG(TAG, "Setting up Status LED...");
if (this->pin_ != nullptr) {
this->pin_->setup(); this->pin_->setup();
this->pin_->digital_write(false); this->pin_->digital_write(false);
} }
}
void StatusLEDLightOutput::dump_config() { void StatusLEDLightOutput::dump_config() {
ESP_LOGCONFIG(TAG, "Status Led Light:"); ESP_LOGCONFIG(TAG, "Status Led Light:");
LOG_PIN(" Pin: ", this->pin_); LOG_PIN(" Pin: ", this->pin_);
} }
void StatusLEDLightOutput::output_state_(bool state) {
if (this->pin_ != nullptr)
this->pin_->digital_write(state);
if (this->output_ != nullptr)
this->output_->set_state(state);
}
} // namespace status_led } // namespace status_led
} // namespace esphome } // namespace esphome

View file

@ -3,6 +3,7 @@
#include "esphome/core/component.h" #include "esphome/core/component.h"
#include "esphome/core/hal.h" #include "esphome/core/hal.h"
#include "esphome/components/light/light_output.h" #include "esphome/components/light/light_output.h"
#include "esphome/components/output/binary_output.h"
namespace esphome { namespace esphome {
namespace status_led { namespace status_led {
@ -10,6 +11,7 @@ namespace status_led {
class StatusLEDLightOutput : public light::LightOutput, public Component { class StatusLEDLightOutput : public light::LightOutput, public Component {
public: public:
void set_pin(GPIOPin *pin) { pin_ = pin; } void set_pin(GPIOPin *pin) { pin_ = pin; }
void set_output(output::BinaryOutput *output) { output_ = output; }
light::LightTraits get_traits() override { light::LightTraits get_traits() override {
auto traits = light::LightTraits(); auto traits = light::LightTraits();
@ -31,9 +33,11 @@ class StatusLEDLightOutput : public light::LightOutput, public Component {
float get_loop_priority() const override { return 50.0f; } float get_loop_priority() const override { return 50.0f; }
protected: protected:
GPIOPin *pin_; GPIOPin *pin_{nullptr};
output::BinaryOutput *output_{nullptr};
light::LightState *lightstate_{}; light::LightState *lightstate_{};
uint32_t last_app_state_{0xFFFF}; uint32_t last_app_state_{0xFFFF};
void output_state_(bool state);
}; };
} // namespace status_led } // namespace status_led

View file

@ -1097,7 +1097,7 @@ def possibly_negative_percentage(value):
if isinstance(value, str): if isinstance(value, str):
try: try:
if value.endswith("%"): if value.endswith("%"):
has_percent_sign = False has_percent_sign = True
value = float(value[:-1].rstrip()) / 100.0 value = float(value[:-1].rstrip()) / 100.0
else: else:
value = float(value) value = float(value)

View file

@ -23,8 +23,13 @@ bool EntityBase::is_disabled_by_default() const { return this->disabled_by_defau
void EntityBase::set_disabled_by_default(bool disabled_by_default) { this->disabled_by_default_ = disabled_by_default; } void EntityBase::set_disabled_by_default(bool disabled_by_default) { this->disabled_by_default_ = disabled_by_default; }
// Entity Icon // Entity Icon
const std::string &EntityBase::get_icon() const { return this->icon_; } std::string EntityBase::get_icon() const {
void EntityBase::set_icon(const std::string &name) { this->icon_ = name; } if (this->icon_c_str_ == nullptr) {
return "";
}
return this->icon_c_str_;
}
void EntityBase::set_icon(const char *icon) { this->icon_c_str_ = icon; }
// Entity Category // Entity Category
EntityCategory EntityBase::get_entity_category() const { return this->entity_category_; } EntityCategory EntityBase::get_entity_category() const { return this->entity_category_; }

View file

@ -42,8 +42,8 @@ class EntityBase {
void set_entity_category(EntityCategory entity_category); void set_entity_category(EntityCategory entity_category);
// Get/set this entity's icon // Get/set this entity's icon
const std::string &get_icon() const; std::string get_icon() const;
void set_icon(const std::string &name); void set_icon(const char *icon);
protected: protected:
/// The hash_base() function has been deprecated. It is kept in this /// The hash_base() function has been deprecated. It is kept in this
@ -53,7 +53,7 @@ class EntityBase {
std::string name_; std::string name_;
std::string object_id_; std::string object_id_;
std::string icon_; const char *icon_c_str_{nullptr};
uint32_t object_id_hash_; uint32_t object_id_hash_;
bool internal_{false}; bool internal_{false};
bool disabled_by_default_{false}; bool disabled_by_default_{false};

View file

@ -400,6 +400,7 @@ sensor:
- heartbeat: 5s - heartbeat: 5s
- debounce: 0.1s - debounce: 0.1s
- delta: 5.0 - delta: 5.0
- delta: 1%
- or: - or:
- throttle: 1s - throttle: 1s
- delta: 5.0 - delta: 5.0

View file

@ -630,8 +630,26 @@ sensor:
- platform: pmsx003 - platform: pmsx003
uart_id: uart5 uart_id: uart5
type: PMS5003T type: PMS5003T
pm_1_0:
name: PM 1.0 Concentration
pm_2_5: pm_2_5:
name: PM 2.5 Concentration name: PM 2.5 Concentration
pm_10_0:
name: PM 10.0 Concentration
pm_1_0_std:
name: PM 1.0 Standard Atmospher Concentration
pm_2_5_std:
name: PM 2.5 Standard Atmospher Concentration
pm_10_0_std:
name: PM 10.0 Standard Atmospher Concentration
pm_0_3um:
name: Particulate Count >0.3um
pm_0_5um:
name: Particulate Count >0.5um
pm_1_0um:
name: Particulate Count >1.0um
pm_2_5um:
name: Particulate Count >2.5um
temperature: temperature:
name: PMS Temperature name: PMS Temperature
humidity: humidity: