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;
} else {
if (this->x > rect.x) {
this->w = this->w + (this->x - rect.x);
this->x = rect.x;
}
if (this->y > rect.y) {
this->h = this->h + (this->y - rect.y);
this->y = rect.y;
}
if (this->x2() < rect.x2()) {
@ -49,29 +51,35 @@ void Rect::shrink(Rect rect) {
if (!this->inside(rect)) {
(*this) = Rect();
} else {
if (this->x < rect.x) {
this->x = rect.x;
}
if (this->y < rect.y) {
this->y = rect.y;
}
if (this->x2() > rect.x2()) {
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()) {
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()) {
return true;
}
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 {
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;
}
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));
} else {
return ((rect.x <= this->w) && (rect.w >= 0) && (rect.y <= this->h) && (rect.h >= 0));
}
}
void Rect::info(const std::string &prefix) {
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
ESP_LOGI(TAG, "%s ** IS NOT SET **", prefix.c_str());
}

View file

@ -120,8 +120,9 @@ class Rect {
void extend(Rect rect);
void shrink(Rect rect);
bool inside(Rect rect, bool absolute = false);
bool inside(int16_t x, int16_t y, bool absolute = false);
bool inside(Rect rect, bool absolute = true);
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:");
};

View file

@ -7,6 +7,7 @@ CODEOWNERS = ["@rspaargaren"]
DEPENDENCIES = ["spi"]
CONF_ROTATE_CHIP = "rotate_chip"
CONF_FLIP_X = "flip_x"
CONF_SCROLL_SPEED = "scroll_speed"
CONF_SCROLL_DWELL = "scroll_dwell"
CONF_SCROLL_DELAY = "scroll_delay"
@ -67,6 +68,7 @@ CONFIG_SCHEMA = (
CONF_SCROLL_DWELL, default="1000ms"
): cv.positive_time_period_milliseconds,
cv.Optional(CONF_REVERSE_ENABLE, default=False): cv.boolean,
cv.Optional(CONF_FLIP_X, default=False): cv.boolean,
}
)
.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_mode(config[CONF_SCROLL_MODE]))
cg.add(var.set_reverse(config[CONF_REVERSE_ENABLE]))
cg.add(var.set_flip_x([CONF_FLIP_X]))
if CONF_LAMBDA in config:
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) {
for (uint8_t i = 0; i < 8; i++) {
// 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
}
}
} else if (this->orientation_ == 1) {
b = pixels[col];
} else if (this->orientation_ == 2) {
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;
}
}
} else {
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_mode(ScrollMode mode) { this->scroll_mode_ = mode; };
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 send64pixels(uint8_t chip, const uint8_t pixels[8]);
@ -108,6 +109,7 @@ class MAX7219Component : public PollingComponent,
ChipLinesStyle chip_lines_style_;
bool scroll_;
bool reverse_;
bool flip_x_;
bool update_{false};
uint16_t scroll_speed_;
uint16_t scroll_delay_;

View file

@ -264,13 +264,52 @@ void PMSX003Component::parse_data_() {
break;
}
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_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 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)
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)
this->temperature_sensor_->publish_state(temperature);
if (this->humidity_sensor_ != nullptr)

View file

@ -55,9 +55,9 @@ PMSX003_TYPES = {
}
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_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_HUMIDITY: [TYPE_PMS5003T, TYPE_PMS5003ST],
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_)
@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):
return cg.new_Pvariable(filter_id, config)
return cg.new_Pvariable(filter_id, *config)
@FILTER_REGISTRY.register("or", OrFilter, validate_filters)

View file

@ -315,19 +315,23 @@ optional<float> ThrottleFilter::new_value(float value) {
}
// 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) {
if (std::isnan(value)) {
if (std::isnan(this->last_value_)) {
return {};
} else {
if (this->percentage_mode_) {
this->current_delta_ = fabsf(value * this->delta_);
}
return this->last_value_ = value;
}
}
if (std::isnan(this->last_value_)) {
return this->last_value_ = value;
if (std::isnan(this->last_value_) || fabsf(value - this->last_value_) >= this->current_delta_) {
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 {};

View file

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

View file

@ -17,6 +17,7 @@ spi_ns = cg.esphome_ns.namespace("spi")
SPIComponent = spi_ns.class_("SPIComponent", cg.Component)
SPIDevice = spi_ns.class_("SPIDevice")
MULTI_CONF = True
CONF_FORCE_SW = "force_sw"
CONFIG_SCHEMA = cv.All(
cv.Schema(
@ -25,6 +26,7 @@ CONFIG_SCHEMA = cv.All(
cv.Required(CONF_CLK_PIN): pins.gpio_output_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_FORCE_SW, default=False): cv.boolean,
}
),
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])
cg.add(var.set_clk(clk))
cg.add(var.set_force_sw(config[CONF_FORCE_SW]))
if CONF_MISO_PIN in config:
miso = await cg.gpio_pin_expression(config[CONF_MISO_PIN])
cg.add(var.set_miso(miso))

View file

@ -25,7 +25,7 @@ void SPIComponent::setup() {
this->clk_->digital_write(true);
#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_mosi = this->mosi_ != nullptr;
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_miso(GPIOPin *miso) { miso_ = miso; }
void set_mosi(GPIOPin *mosi) { mosi_ = mosi; }
void set_force_sw(bool force_sw) { force_sw_ = force_sw; }
void setup() override;
@ -260,6 +261,7 @@ class SPIComponent : public Component {
GPIOPin *miso_{nullptr};
GPIOPin *mosi_{nullptr};
GPIOPin *active_cs_{nullptr};
bool force_sw_{false};
#ifdef USE_SPI_ARDUINO_BACKEND
SPIClass *hw_spi_{nullptr};
#endif // USE_SPI_ARDUINO_BACKEND

View file

@ -1,26 +1,35 @@
from esphome import pins
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import light
from esphome.const import CONF_OUTPUT_ID, CONF_PIN
from esphome.components import light, output
from esphome.const import CONF_OUTPUT, CONF_OUTPUT_ID, CONF_PIN
from .. import status_led_ns
AUTO_LOAD = ["output"]
StatusLEDLightOutput = status_led_ns.class_(
"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.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):
var = cg.new_Pvariable(config[CONF_OUTPUT_ID])
if CONF_PIN in config:
pin = await cg.gpio_pin_expression(config[CONF_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)
# cg.add(cg.App.register_component(var))
await light.register_light(var, config)

View file

@ -15,10 +15,10 @@ void StatusLEDLightOutput::loop() {
}
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;
} 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;
} else if (new_state != this->last_app_state_) {
// if no error/warning -> restore light state or turn off
@ -26,17 +26,16 @@ void StatusLEDLightOutput::loop() {
if (lightstate_)
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));
this->output_state_(state);
this->last_app_state_ = new_state;
}
}
void StatusLEDLightOutput::setup_state(light::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);
}
@ -47,16 +46,18 @@ void StatusLEDLightOutput::write_state(light::LightState *state) {
// if in warning/error, don't overwrite the status_led
// once it is back to OK, the loop will restore the state
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));
this->output_state_(binary);
}
}
void StatusLEDLightOutput::setup() {
ESP_LOGCONFIG(TAG, "Setting up Status LED...");
if (this->pin_ != nullptr) {
this->pin_->setup();
this->pin_->digital_write(false);
}
}
void StatusLEDLightOutput::dump_config() {
@ -64,5 +65,12 @@ void StatusLEDLightOutput::dump_config() {
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 esphome

View file

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

View file

@ -1097,7 +1097,7 @@ def possibly_negative_percentage(value):
if isinstance(value, str):
try:
if value.endswith("%"):
has_percent_sign = False
has_percent_sign = True
value = float(value[:-1].rstrip()) / 100.0
else:
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; }
// Entity Icon
const std::string &EntityBase::get_icon() const { return this->icon_; }
void EntityBase::set_icon(const std::string &name) { this->icon_ = name; }
std::string EntityBase::get_icon() const {
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
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);
// Get/set this entity's icon
const std::string &get_icon() const;
void set_icon(const std::string &name);
std::string get_icon() const;
void set_icon(const char *icon);
protected:
/// The hash_base() function has been deprecated. It is kept in this
@ -53,7 +53,7 @@ class EntityBase {
std::string name_;
std::string object_id_;
std::string icon_;
const char *icon_c_str_{nullptr};
uint32_t object_id_hash_;
bool internal_{false};
bool disabled_by_default_{false};

View file

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

View file

@ -630,8 +630,26 @@ sensor:
- platform: pmsx003
uart_id: uart5
type: PMS5003T
pm_1_0:
name: PM 1.0 Concentration
pm_2_5:
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:
name: PMS Temperature
humidity: