mirror of
https://github.com/esphome/esphome.git
synced 2025-08-10 20:29:24 +00:00
Compare commits
120 Commits
jesserockz
...
2021.11.0b
Author | SHA1 | Date | |
---|---|---|---|
![]() |
29a7d32f77 | ||
![]() |
687a7e9b2f | ||
![]() |
09e8782318 | ||
![]() |
f2aea02210 | ||
![]() |
194f922312 | ||
![]() |
fea3c48098 | ||
![]() |
c2f57baec2 | ||
![]() |
f4a140e126 | ||
![]() |
ab506b09fe | ||
![]() |
87e1cdeedb | ||
![]() |
81a36146ef | ||
![]() |
7fa4a68a27 | ||
![]() |
f1c5e2ef81 | ||
![]() |
b526155cce | ||
![]() |
62c3f301e7 | ||
![]() |
38cb988809 | ||
![]() |
b976ac54c8 | ||
![]() |
78026e766f | ||
![]() |
b4cd8d21a5 | ||
![]() |
7552893311 | ||
![]() |
21c896d8f8 | ||
![]() |
4b7fe202ec | ||
![]() |
9f4519210f | ||
![]() |
b0506afa5b | ||
![]() |
8cbb379898 | ||
![]() |
b226215593 | ||
![]() |
19970729a9 | ||
![]() |
d2ebfd2833 | ||
![]() |
bd782fc828 | ||
![]() |
23560e608c | ||
![]() |
f1377b560e | ||
![]() |
72108684ea | ||
![]() |
c6adaaea97 | ||
![]() |
91999a38ca | ||
![]() |
b34eed125d | ||
![]() |
2abe09529a | ||
![]() |
9aaaf4dd4b | ||
![]() |
cbfbcf7f1b | ||
![]() |
c7651dc40d | ||
![]() |
eda1c471ad | ||
![]() |
c7ef18fbc4 | ||
![]() |
901ec918b1 | ||
![]() |
6bdae55ee1 | ||
![]() |
dfb96e4b7f | ||
![]() |
ff2c316b18 | ||
![]() |
5be52f71f9 | ||
![]() |
42873dd37c | ||
![]() |
f93e7d4e3a | ||
![]() |
bbcd523967 | ||
![]() |
68cbe58d00 | ||
![]() |
115bca98f1 | ||
![]() |
ed0b34b2fe | ||
![]() |
ab34401421 | ||
![]() |
eed0c18d65 | ||
![]() |
e5a38ce748 | ||
![]() |
7d9d9fcf36 | ||
![]() |
f0aba6ceb2 | ||
![]() |
ab07ee57c6 | ||
![]() |
eae3d72a4d | ||
![]() |
7b8d826704 | ||
![]() |
e7baa42e63 | ||
![]() |
2f32833a22 | ||
![]() |
f6935a4b4b | ||
![]() |
332c9e891b | ||
![]() |
b91ee4847f | ||
![]() |
625463d871 | ||
![]() |
6433a01e07 | ||
![]() |
56cc31e8e7 | ||
![]() |
3af297aa76 | ||
![]() |
996ec59d28 | ||
![]() |
95593eeeab | ||
![]() |
dad244fb7a | ||
![]() |
adb5d27d95 | ||
![]() |
8456a8cecb | ||
![]() |
b9f66373c1 | ||
![]() |
9ac365feef | ||
![]() |
43bbd58a44 | ||
![]() |
7feffa64f3 | ||
![]() |
ea0977abb4 | ||
![]() |
4c83dc7c28 | ||
![]() |
e10ab1da78 | ||
![]() |
1b0e60374b | ||
![]() |
3a760fbb44 | ||
![]() |
6ef57a2973 | ||
![]() |
3e9c7f2e9f | ||
![]() |
430598b7a1 | ||
![]() |
91611b09b4 | ||
![]() |
ecd115851f | ||
![]() |
4a1e50fed1 | ||
![]() |
d6d037047b | ||
![]() |
b5734c2b20 | ||
![]() |
723fb7eaac | ||
![]() |
63a9acaa19 | ||
![]() |
0524f8c677 | ||
![]() |
70b62f272e | ||
![]() |
f0089b7940 | ||
![]() |
4b44280d53 | ||
![]() |
f045382d20 | ||
![]() |
db3fa1ade7 | ||
![]() |
f83950fd75 | ||
![]() |
4dd1bf920d | ||
![]() |
98755f3621 | ||
![]() |
c3a8a044b9 | ||
![]() |
15b5ea43a7 | ||
![]() |
ec683fc227 | ||
![]() |
d4e65eb82a | ||
![]() |
10c6601b0a | ||
![]() |
73940bc1bd | ||
![]() |
9b7fb829f9 | ||
![]() |
c51d8c9021 | ||
![]() |
d8a6dfe5ce | ||
![]() |
5f7cef0b06 | ||
![]() |
48ff2ffc68 | ||
![]() |
b3b9ccd314 | ||
![]() |
e63c7b483b | ||
![]() |
f57980b069 | ||
![]() |
7006aa0d2a | ||
![]() |
8051c1ca99 | ||
![]() |
a779592414 | ||
![]() |
112215848d |
@@ -44,10 +44,11 @@ from esphome.const import (
|
||||
DEVICE_CLASS_POWER,
|
||||
DEVICE_CLASS_PRESENCE,
|
||||
DEVICE_CLASS_PROBLEM,
|
||||
DEVICE_CLASS_RUNNING,
|
||||
DEVICE_CLASS_SAFETY,
|
||||
DEVICE_CLASS_SMOKE,
|
||||
DEVICE_CLASS_SOUND,
|
||||
DEVICE_CLASS_UPDATE,
|
||||
DEVICE_CLASS_TAMPER,
|
||||
DEVICE_CLASS_VIBRATION,
|
||||
DEVICE_CLASS_WINDOW,
|
||||
)
|
||||
@@ -76,10 +77,11 @@ DEVICE_CLASSES = [
|
||||
DEVICE_CLASS_POWER,
|
||||
DEVICE_CLASS_PRESENCE,
|
||||
DEVICE_CLASS_PROBLEM,
|
||||
DEVICE_CLASS_RUNNING,
|
||||
DEVICE_CLASS_SAFETY,
|
||||
DEVICE_CLASS_SMOKE,
|
||||
DEVICE_CLASS_SOUND,
|
||||
DEVICE_CLASS_UPDATE,
|
||||
DEVICE_CLASS_TAMPER,
|
||||
DEVICE_CLASS_VIBRATION,
|
||||
DEVICE_CLASS_WINDOW,
|
||||
]
|
||||
|
@@ -11,6 +11,7 @@ namespace esphome {
|
||||
namespace esp32_improv {
|
||||
|
||||
static const char *const TAG = "esp32_improv.component";
|
||||
static const char *const ESPHOME_MY_LINK = "https://my.home-assistant.io/redirect/config_flow_start?domain=esphome";
|
||||
|
||||
ESP32ImprovComponent::ESP32ImprovComponent() { global_improv_component = this; }
|
||||
|
||||
@@ -124,8 +125,13 @@ void ESP32ImprovComponent::loop() {
|
||||
this->cancel_timeout("wifi-connect-timeout");
|
||||
this->set_state_(improv::STATE_PROVISIONED);
|
||||
|
||||
std::string url = "https://my.home-assistant.io/redirect/config_flow_start?domain=esphome";
|
||||
std::vector<uint8_t> data = improv::build_rpc_response(improv::WIFI_SETTINGS, {url});
|
||||
std::vector<std::string> urls = {ESPHOME_MY_LINK};
|
||||
#ifdef USE_WEBSERVER
|
||||
auto ip = wifi::global_wifi_component->wifi_sta_ip();
|
||||
std::string webserver_url = "http://" + ip.str() + ":" + to_string(WEBSERVER_PORT);
|
||||
urls.push_back(webserver_url);
|
||||
#endif
|
||||
std::vector<uint8_t> data = improv::build_rpc_response(improv::WIFI_SETTINGS, urls);
|
||||
this->send_response_(data);
|
||||
this->set_timeout("end-service", 1000, [this] {
|
||||
this->service_->stop();
|
||||
|
@@ -1,6 +1,7 @@
|
||||
#ifdef USE_ESP32
|
||||
|
||||
#include "esp32_touch.h"
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/hal.h"
|
||||
|
||||
@@ -93,7 +94,6 @@ void ESP32TouchComponent::dump_config() {
|
||||
|
||||
if (this->iir_filter_enabled_()) {
|
||||
ESP_LOGCONFIG(TAG, " IIR Filter: %ums", this->iir_filter_);
|
||||
touch_pad_filter_start(this->iir_filter_);
|
||||
} else {
|
||||
ESP_LOGCONFIG(TAG, " IIR Filter DISABLED");
|
||||
}
|
||||
@@ -125,6 +125,8 @@ void ESP32TouchComponent::loop() {
|
||||
if (should_print) {
|
||||
ESP_LOGD(TAG, "Touch Pad '%s' (T%u): %u", child->get_name().c_str(), child->get_touch_pad(), value);
|
||||
}
|
||||
|
||||
App.feed_wdt();
|
||||
}
|
||||
|
||||
if (should_print) {
|
||||
|
@@ -92,8 +92,7 @@ void ImprovSerialComponent::loop() {
|
||||
}
|
||||
|
||||
std::vector<uint8_t> ImprovSerialComponent::build_rpc_settings_response_(improv::Command command) {
|
||||
std::string url = "https://my.home-assistant.io/redirect/config_flow_start?domain=esphome";
|
||||
std::vector<std::string> urls = {url};
|
||||
std::vector<std::string> urls;
|
||||
#ifdef USE_WEBSERVER
|
||||
auto ip = wifi::global_wifi_component->wifi_sta_ip();
|
||||
std::string webserver_url = "http://" + ip.str() + ":" + to_string(WEBSERVER_PORT);
|
||||
|
@@ -87,7 +87,7 @@ class AddressableLight : public LightOutput, public Component {
|
||||
void mark_shown_() {
|
||||
#ifdef USE_POWER_SUPPLY
|
||||
for (const auto &c : *this) {
|
||||
if (c.get().is_on()) {
|
||||
if (c.get_red_raw() > 0 || c.get_green_raw() > 0 || c.get_blue_raw() > 0 || c.get_white_raw() > 0) {
|
||||
this->power_.request();
|
||||
return;
|
||||
}
|
||||
|
@@ -1,10 +1,11 @@
|
||||
#ifdef USE_ARDUINO
|
||||
|
||||
#include "esphome/core/log.h"
|
||||
#include "adapter.h"
|
||||
#include "ac_adapter.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace midea {
|
||||
namespace ac {
|
||||
|
||||
const char *const Constants::TAG = "midea";
|
||||
const std::string Constants::FREEZE_PROTECTION = "freeze protection";
|
||||
@@ -171,6 +172,7 @@ void Converters::to_climate_traits(ClimateTraits &traits, const dudanov::midea::
|
||||
traits.add_supported_custom_preset(Constants::FREEZE_PROTECTION);
|
||||
}
|
||||
|
||||
} // namespace ac
|
||||
} // namespace midea
|
||||
} // namespace esphome
|
||||
|
@@ -2,12 +2,15 @@
|
||||
|
||||
#ifdef USE_ARDUINO
|
||||
|
||||
// MideaUART
|
||||
#include <Appliance/AirConditioner/AirConditioner.h>
|
||||
|
||||
#include "esphome/components/climate/climate_traits.h"
|
||||
#include "appliance_base.h"
|
||||
#include "air_conditioner.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace midea {
|
||||
namespace ac {
|
||||
|
||||
using MideaMode = dudanov::midea::ac::Mode;
|
||||
using MideaSwingMode = dudanov::midea::ac::SwingMode;
|
||||
@@ -41,6 +44,7 @@ class Converters {
|
||||
static void to_climate_traits(ClimateTraits &traits, const dudanov::midea::ac::Capabilities &capabilities);
|
||||
};
|
||||
|
||||
} // namespace ac
|
||||
} // namespace midea
|
||||
} // namespace esphome
|
||||
|
@@ -7,6 +7,7 @@
|
||||
|
||||
namespace esphome {
|
||||
namespace midea {
|
||||
namespace ac {
|
||||
|
||||
template<typename... Ts> class MideaActionBase : public Action<Ts...> {
|
||||
public:
|
||||
@@ -55,6 +56,7 @@ template<typename... Ts> class PowerOffAction : public MideaActionBase<Ts...> {
|
||||
void play(Ts... x) override { this->parent_->do_power_off(); }
|
||||
};
|
||||
|
||||
} // namespace ac
|
||||
} // namespace midea
|
||||
} // namespace esphome
|
||||
|
@@ -2,13 +2,11 @@
|
||||
|
||||
#include "esphome/core/log.h"
|
||||
#include "air_conditioner.h"
|
||||
#include "adapter.h"
|
||||
#ifdef USE_REMOTE_TRANSMITTER
|
||||
#include "midea_ir.h"
|
||||
#endif
|
||||
#include "ac_adapter.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace midea {
|
||||
namespace ac {
|
||||
|
||||
static void set_sensor(Sensor *sensor, float value) {
|
||||
if (sensor != nullptr && (!sensor->has_state() || sensor->get_raw_state() != value))
|
||||
@@ -122,7 +120,7 @@ void AirConditioner::dump_config() {
|
||||
void AirConditioner::do_follow_me(float temperature, bool beeper) {
|
||||
#ifdef USE_REMOTE_TRANSMITTER
|
||||
IrFollowMeData data(static_cast<uint8_t>(lroundf(temperature)), beeper);
|
||||
this->transmit_ir(data);
|
||||
this->transmitter_.transmit(data);
|
||||
#else
|
||||
ESP_LOGW(Constants::TAG, "Action needs remote_transmitter component");
|
||||
#endif
|
||||
@@ -131,7 +129,7 @@ void AirConditioner::do_follow_me(float temperature, bool beeper) {
|
||||
void AirConditioner::do_swing_step() {
|
||||
#ifdef USE_REMOTE_TRANSMITTER
|
||||
IrSpecialData data(0x01);
|
||||
this->transmit_ir(data);
|
||||
this->transmitter_.transmit(data);
|
||||
#else
|
||||
ESP_LOGW(Constants::TAG, "Action needs remote_transmitter component");
|
||||
#endif
|
||||
@@ -143,13 +141,14 @@ void AirConditioner::do_display_toggle() {
|
||||
} else {
|
||||
#ifdef USE_REMOTE_TRANSMITTER
|
||||
IrSpecialData data(0x08);
|
||||
this->transmit_ir(data);
|
||||
this->transmitter_.transmit(data);
|
||||
#else
|
||||
ESP_LOGW(Constants::TAG, "Action needs remote_transmitter component");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ac
|
||||
} // namespace midea
|
||||
} // namespace esphome
|
||||
|
||||
|
@@ -2,17 +2,25 @@
|
||||
|
||||
#ifdef USE_ARDUINO
|
||||
|
||||
// MideaUART
|
||||
#include <Appliance/AirConditioner/AirConditioner.h>
|
||||
|
||||
#include "appliance_base.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace midea {
|
||||
namespace ac {
|
||||
|
||||
using sensor::Sensor;
|
||||
using climate::ClimateCall;
|
||||
using climate::ClimatePreset;
|
||||
using climate::ClimateTraits;
|
||||
using climate::ClimateMode;
|
||||
using climate::ClimateSwingMode;
|
||||
using climate::ClimateFanMode;
|
||||
|
||||
class AirConditioner : public ApplianceBase<dudanov::midea::ac::AirConditioner> {
|
||||
class AirConditioner : public ApplianceBase<dudanov::midea::ac::AirConditioner>, public climate::Climate {
|
||||
public:
|
||||
void dump_config() override;
|
||||
void set_outdoor_temperature_sensor(Sensor *sensor) { this->outdoor_sensor_ = sensor; }
|
||||
@@ -31,15 +39,26 @@ class AirConditioner : public ApplianceBase<dudanov::midea::ac::AirConditioner>
|
||||
void do_beeper_off() { this->set_beeper_feedback(false); }
|
||||
void do_power_on() { this->base_.setPowerState(true); }
|
||||
void do_power_off() { this->base_.setPowerState(false); }
|
||||
void set_supported_modes(const std::set<ClimateMode> &modes) { this->supported_modes_ = modes; }
|
||||
void set_supported_swing_modes(const std::set<ClimateSwingMode> &modes) { this->supported_swing_modes_ = modes; }
|
||||
void set_supported_presets(const std::set<ClimatePreset> &presets) { this->supported_presets_ = presets; }
|
||||
void set_custom_presets(const std::set<std::string> &presets) { this->supported_custom_presets_ = presets; }
|
||||
void set_custom_fan_modes(const std::set<std::string> &modes) { this->supported_custom_fan_modes_ = modes; }
|
||||
|
||||
protected:
|
||||
void control(const ClimateCall &call) override;
|
||||
ClimateTraits traits() override;
|
||||
std::set<ClimateMode> supported_modes_{};
|
||||
std::set<ClimateSwingMode> supported_swing_modes_{};
|
||||
std::set<ClimatePreset> supported_presets_{};
|
||||
std::set<std::string> supported_custom_presets_{};
|
||||
std::set<std::string> supported_custom_fan_modes_{};
|
||||
Sensor *outdoor_sensor_{nullptr};
|
||||
Sensor *humidity_sensor_{nullptr};
|
||||
Sensor *power_sensor_{nullptr};
|
||||
};
|
||||
|
||||
} // namespace ac
|
||||
} // namespace midea
|
||||
} // namespace esphome
|
||||
|
||||
|
@@ -2,84 +2,97 @@
|
||||
|
||||
#ifdef USE_ARDUINO
|
||||
|
||||
// MideaUART
|
||||
#include <Appliance/ApplianceBase.h>
|
||||
#include <Helpers/Logger.h>
|
||||
|
||||
// Include global defines
|
||||
#include "esphome/core/defines.h"
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/components/uart/uart.h"
|
||||
#include "esphome/components/climate/climate.h"
|
||||
#ifdef USE_REMOTE_TRANSMITTER
|
||||
#include "esphome/components/remote_base/midea_protocol.h"
|
||||
#include "esphome/components/remote_transmitter/remote_transmitter.h"
|
||||
#endif
|
||||
#include <Appliance/ApplianceBase.h>
|
||||
#include <Helpers/Logger.h>
|
||||
#include "ir_transmitter.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace midea {
|
||||
|
||||
using climate::ClimatePreset;
|
||||
using climate::ClimateTraits;
|
||||
using climate::ClimateMode;
|
||||
using climate::ClimateSwingMode;
|
||||
using climate::ClimateFanMode;
|
||||
/* Stream from UART component */
|
||||
class UARTStream : public Stream {
|
||||
public:
|
||||
void set_uart(uart::UARTComponent *uart) { this->uart_ = uart; }
|
||||
|
||||
template<typename T>
|
||||
class ApplianceBase : public Component, public uart::UARTDevice, public climate::Climate, public Stream {
|
||||
/* Stream interface implementation */
|
||||
|
||||
int available() override { return this->uart_->available(); }
|
||||
int read() override {
|
||||
uint8_t data;
|
||||
this->uart_->read_byte(&data);
|
||||
return data;
|
||||
}
|
||||
int peek() override {
|
||||
uint8_t data;
|
||||
this->uart_->peek_byte(&data);
|
||||
return data;
|
||||
}
|
||||
size_t write(uint8_t data) override {
|
||||
this->uart_->write_byte(data);
|
||||
return 1;
|
||||
}
|
||||
size_t write(const uint8_t *data, size_t size) override {
|
||||
this->uart_->write_array(data, size);
|
||||
return size;
|
||||
}
|
||||
void flush() override { this->uart_->flush(); }
|
||||
|
||||
protected:
|
||||
uart::UARTComponent *uart_;
|
||||
};
|
||||
|
||||
template<typename T> class ApplianceBase : public Component {
|
||||
static_assert(std::is_base_of<dudanov::midea::ApplianceBase, T>::value,
|
||||
"T must derive from dudanov::midea::ApplianceBase class");
|
||||
|
||||
public:
|
||||
ApplianceBase() {
|
||||
this->base_.setStream(this);
|
||||
this->base_.setStream(&this->stream_);
|
||||
this->base_.addOnStateCallback(std::bind(&ApplianceBase::on_status_change, this));
|
||||
dudanov::midea::ApplianceBase::setLogger(
|
||||
[](int level, const char *tag, int line, const String &format, va_list args) {
|
||||
esp_log_vprintf_(level, tag, line, format.c_str(), args);
|
||||
});
|
||||
}
|
||||
bool can_proceed() override {
|
||||
return this->base_.getAutoconfStatus() != dudanov::midea::AutoconfStatus::AUTOCONF_PROGRESS;
|
||||
}
|
||||
float get_setup_priority() const override { return setup_priority::BEFORE_CONNECTION; }
|
||||
void setup() override { this->base_.setup(); }
|
||||
void loop() override { this->base_.loop(); }
|
||||
|
||||
#ifdef USE_REMOTE_TRANSMITTER
|
||||
void set_transmitter(RemoteTransmitterBase *transmitter) { this->transmitter_.set_transmitter(transmitter); }
|
||||
#endif
|
||||
|
||||
/* UART communication */
|
||||
|
||||
void set_uart_parent(uart::UARTComponent *parent) { this->stream_.set_uart(parent); }
|
||||
void set_period(uint32_t ms) { this->base_.setPeriod(ms); }
|
||||
void set_response_timeout(uint32_t ms) { this->base_.setTimeout(ms); }
|
||||
void set_request_attempts(uint32_t attempts) { this->base_.setNumAttempts(attempts); }
|
||||
|
||||
/* Component methods */
|
||||
|
||||
void setup() override { this->base_.setup(); }
|
||||
void loop() override { this->base_.loop(); }
|
||||
float get_setup_priority() const override { return setup_priority::BEFORE_CONNECTION; }
|
||||
bool can_proceed() override {
|
||||
return this->base_.getAutoconfStatus() != dudanov::midea::AutoconfStatus::AUTOCONF_PROGRESS;
|
||||
}
|
||||
|
||||
void set_beeper_feedback(bool state) { this->base_.setBeeper(state); }
|
||||
void set_autoconf(bool value) { this->base_.setAutoconf(value); }
|
||||
void set_supported_modes(const std::set<ClimateMode> &modes) { this->supported_modes_ = modes; }
|
||||
void set_supported_swing_modes(const std::set<ClimateSwingMode> &modes) { this->supported_swing_modes_ = modes; }
|
||||
void set_supported_presets(const std::set<ClimatePreset> &presets) { this->supported_presets_ = presets; }
|
||||
void set_custom_presets(const std::set<std::string> &presets) { this->supported_custom_presets_ = presets; }
|
||||
void set_custom_fan_modes(const std::set<std::string> &modes) { this->supported_custom_fan_modes_ = modes; }
|
||||
virtual void on_status_change() = 0;
|
||||
#ifdef USE_REMOTE_TRANSMITTER
|
||||
void set_transmitter(remote_transmitter::RemoteTransmitterComponent *transmitter) {
|
||||
this->transmitter_ = transmitter;
|
||||
}
|
||||
void transmit_ir(remote_base::MideaData &data) {
|
||||
data.finalize();
|
||||
auto transmit = this->transmitter_->transmit();
|
||||
remote_base::MideaProtocol().encode(transmit.get_data(), data);
|
||||
transmit.perform();
|
||||
}
|
||||
#endif
|
||||
|
||||
int available() override { return uart::UARTDevice::available(); }
|
||||
int read() override { return uart::UARTDevice::read(); }
|
||||
int peek() override { return uart::UARTDevice::peek(); }
|
||||
void flush() override { uart::UARTDevice::flush(); }
|
||||
size_t write(uint8_t data) override { return uart::UARTDevice::write(data); }
|
||||
|
||||
protected:
|
||||
T base_;
|
||||
std::set<ClimateMode> supported_modes_{};
|
||||
std::set<ClimateSwingMode> supported_swing_modes_{};
|
||||
std::set<ClimatePreset> supported_presets_{};
|
||||
std::set<std::string> supported_custom_presets_{};
|
||||
std::set<std::string> supported_custom_fan_modes_{};
|
||||
UARTStream stream_;
|
||||
#ifdef USE_REMOTE_TRANSMITTER
|
||||
remote_transmitter::RemoteTransmitterComponent *transmitter_{nullptr};
|
||||
IrTransmitter transmitter_;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
@@ -40,9 +40,9 @@ AUTO_LOAD = ["sensor"]
|
||||
CONF_OUTDOOR_TEMPERATURE = "outdoor_temperature"
|
||||
CONF_POWER_USAGE = "power_usage"
|
||||
CONF_HUMIDITY_SETPOINT = "humidity_setpoint"
|
||||
midea_ns = cg.esphome_ns.namespace("midea")
|
||||
AirConditioner = midea_ns.class_("AirConditioner", climate.Climate, cg.Component)
|
||||
Capabilities = midea_ns.namespace("Constants")
|
||||
midea_ac_ns = cg.esphome_ns.namespace("midea").namespace("ac")
|
||||
AirConditioner = midea_ac_ns.class_("AirConditioner", climate.Climate, cg.Component)
|
||||
Capabilities = midea_ac_ns.namespace("Constants")
|
||||
|
||||
|
||||
def templatize(value):
|
||||
@@ -156,13 +156,13 @@ CONFIG_SCHEMA = cv.All(
|
||||
)
|
||||
|
||||
# Actions
|
||||
FollowMeAction = midea_ns.class_("FollowMeAction", automation.Action)
|
||||
DisplayToggleAction = midea_ns.class_("DisplayToggleAction", automation.Action)
|
||||
SwingStepAction = midea_ns.class_("SwingStepAction", automation.Action)
|
||||
BeeperOnAction = midea_ns.class_("BeeperOnAction", automation.Action)
|
||||
BeeperOffAction = midea_ns.class_("BeeperOffAction", automation.Action)
|
||||
PowerOnAction = midea_ns.class_("PowerOnAction", automation.Action)
|
||||
PowerOffAction = midea_ns.class_("PowerOffAction", automation.Action)
|
||||
FollowMeAction = midea_ac_ns.class_("FollowMeAction", automation.Action)
|
||||
DisplayToggleAction = midea_ac_ns.class_("DisplayToggleAction", automation.Action)
|
||||
SwingStepAction = midea_ac_ns.class_("SwingStepAction", automation.Action)
|
||||
BeeperOnAction = midea_ac_ns.class_("BeeperOnAction", automation.Action)
|
||||
BeeperOffAction = midea_ac_ns.class_("BeeperOffAction", automation.Action)
|
||||
PowerOnAction = midea_ac_ns.class_("PowerOnAction", automation.Action)
|
||||
PowerOffAction = midea_ac_ns.class_("PowerOffAction", automation.Action)
|
||||
|
||||
MIDEA_ACTION_BASE_SCHEMA = cv.Schema(
|
||||
{
|
||||
|
@@ -7,6 +7,7 @@
|
||||
namespace esphome {
|
||||
namespace midea {
|
||||
|
||||
using remote_base::RemoteTransmitterBase;
|
||||
using IrData = remote_base::MideaData;
|
||||
|
||||
class IrFollowMeData : public IrData {
|
||||
@@ -38,6 +39,20 @@ class IrSpecialData : public IrData {
|
||||
IrSpecialData(uint8_t code) : IrData({MIDEA_TYPE_SPECIAL, code, 0xFF, 0xFF, 0xFF}) {}
|
||||
};
|
||||
|
||||
class IrTransmitter {
|
||||
public:
|
||||
void set_transmitter(RemoteTransmitterBase *transmitter) { this->transmitter_ = transmitter; }
|
||||
void transmit(IrData &data) {
|
||||
data.finalize();
|
||||
auto transmit = this->transmitter_->transmit();
|
||||
remote_base::MideaProtocol().encode(transmit.get_data(), data);
|
||||
transmit.perform();
|
||||
}
|
||||
|
||||
protected:
|
||||
RemoteTransmitterBase *transmitter_{nullptr};
|
||||
};
|
||||
|
||||
} // namespace midea
|
||||
} // namespace esphome
|
||||
|
@@ -129,14 +129,14 @@ async def to_code(config):
|
||||
return_type=cg.optional.template(float),
|
||||
)
|
||||
cg.add(var.set_template(template_))
|
||||
if CONF_WRITE_LAMBDA in config:
|
||||
template_ = await cg.process_lambda(
|
||||
config[CONF_WRITE_LAMBDA],
|
||||
[
|
||||
(ModbusNumber.operator("ptr"), "item"),
|
||||
(cg.float_, "x"),
|
||||
(cg.std_vector.template(cg.uint16).operator("ref"), "payload"),
|
||||
],
|
||||
return_type=cg.optional.template(float),
|
||||
)
|
||||
cg.add(var.set_write_template(template_))
|
||||
if CONF_WRITE_LAMBDA in config:
|
||||
template_ = await cg.process_lambda(
|
||||
config[CONF_WRITE_LAMBDA],
|
||||
[
|
||||
(ModbusNumber.operator("ptr"), "item"),
|
||||
(cg.float_, "x"),
|
||||
(cg.std_vector.template(cg.uint16).operator("ref"), "payload"),
|
||||
],
|
||||
return_type=cg.optional.template(float),
|
||||
)
|
||||
cg.add(var.set_write_template(template_))
|
||||
|
@@ -41,7 +41,7 @@ NumberInRangeCondition = number_ns.class_(
|
||||
|
||||
icon = cv.icon
|
||||
|
||||
NUMBER_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend(
|
||||
NUMBER_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
|
||||
{
|
||||
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTNumberComponent),
|
||||
cv.GenerateID(): cv.declare_id(Number),
|
||||
|
@@ -15,7 +15,8 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
cv.GenerateID(CONF_WEB_SERVER_BASE_ID): cv.use_id(
|
||||
web_server_base.WebServerBase
|
||||
),
|
||||
}
|
||||
},
|
||||
cv.only_with_arduino,
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
|
||||
|
@@ -32,6 +32,9 @@ class RemoteTransmitterComponent : public remote_base::RemoteTransmitterBase,
|
||||
void mark_(uint32_t on_time, uint32_t off_time, uint32_t usec);
|
||||
|
||||
void space_(uint32_t usec);
|
||||
|
||||
void await_target_time_();
|
||||
uint32_t target_time_;
|
||||
#endif
|
||||
|
||||
#ifdef USE_ESP32
|
||||
|
@@ -33,56 +33,64 @@ void RemoteTransmitterComponent::calculate_on_off_time_(uint32_t carrier_frequen
|
||||
*off_time_period = period - *on_time_period;
|
||||
}
|
||||
|
||||
void RemoteTransmitterComponent::await_target_time_() {
|
||||
const uint32_t current_time = micros();
|
||||
if (this->target_time_ == 0)
|
||||
this->target_time_ = current_time;
|
||||
else if (this->target_time_ > current_time)
|
||||
delayMicroseconds(this->target_time_ - current_time);
|
||||
}
|
||||
|
||||
void RemoteTransmitterComponent::mark_(uint32_t on_time, uint32_t off_time, uint32_t usec) {
|
||||
if (this->carrier_duty_percent_ == 100 || (on_time == 0 && off_time == 0)) {
|
||||
this->pin_->digital_write(true);
|
||||
delayMicroseconds(usec);
|
||||
this->pin_->digital_write(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const uint32_t start_time = micros();
|
||||
uint32_t current_time = start_time;
|
||||
|
||||
while (current_time - start_time < usec) {
|
||||
const uint32_t elapsed = current_time - start_time;
|
||||
this->pin_->digital_write(true);
|
||||
|
||||
delayMicroseconds(std::min(on_time, usec - elapsed));
|
||||
this->pin_->digital_write(false);
|
||||
if (elapsed + on_time >= usec)
|
||||
return;
|
||||
|
||||
delayMicroseconds(std::min(usec - elapsed - on_time, off_time));
|
||||
|
||||
current_time = micros();
|
||||
this->await_target_time_();
|
||||
this->pin_->digital_write(true);
|
||||
|
||||
const uint32_t target = this->target_time_ + usec;
|
||||
if (this->carrier_duty_percent_ < 100 && (on_time > 0 || off_time > 0)) {
|
||||
while (true) { // Modulate with carrier frequency
|
||||
this->target_time_ += on_time;
|
||||
if (this->target_time_ >= target)
|
||||
break;
|
||||
this->await_target_time_();
|
||||
this->pin_->digital_write(false);
|
||||
|
||||
this->target_time_ += off_time;
|
||||
if (this->target_time_ >= target)
|
||||
break;
|
||||
this->await_target_time_();
|
||||
this->pin_->digital_write(true);
|
||||
}
|
||||
}
|
||||
this->target_time_ = target;
|
||||
}
|
||||
|
||||
void RemoteTransmitterComponent::space_(uint32_t usec) {
|
||||
this->await_target_time_();
|
||||
this->pin_->digital_write(false);
|
||||
delayMicroseconds(usec);
|
||||
this->target_time_ += usec;
|
||||
}
|
||||
|
||||
void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t send_wait) {
|
||||
ESP_LOGD(TAG, "Sending remote code...");
|
||||
uint32_t on_time, off_time;
|
||||
this->calculate_on_off_time_(this->temp_.get_carrier_frequency(), &on_time, &off_time);
|
||||
this->target_time_ = 0;
|
||||
for (uint32_t i = 0; i < send_times; i++) {
|
||||
{
|
||||
InterruptLock lock;
|
||||
for (int32_t item : this->temp_.get_data()) {
|
||||
if (item > 0) {
|
||||
const auto length = uint32_t(item);
|
||||
this->mark_(on_time, off_time, length);
|
||||
} else {
|
||||
const auto length = uint32_t(-item);
|
||||
this->space_(length);
|
||||
}
|
||||
App.feed_wdt();
|
||||
for (int32_t item : this->temp_.get_data()) {
|
||||
if (item > 0) {
|
||||
const auto length = uint32_t(item);
|
||||
this->mark_(on_time, off_time, length);
|
||||
} else {
|
||||
const auto length = uint32_t(-item);
|
||||
this->space_(length);
|
||||
}
|
||||
App.feed_wdt();
|
||||
}
|
||||
this->await_target_time_(); // wait for duration of last pulse
|
||||
this->pin_->digital_write(false);
|
||||
|
||||
if (i + 1 < send_times)
|
||||
delayMicroseconds(send_wait);
|
||||
this->target_time_ += send_wait;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -30,7 +30,7 @@ SelectSetAction = select_ns.class_("SelectSetAction", automation.Action)
|
||||
|
||||
icon = cv.icon
|
||||
|
||||
SELECT_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend(
|
||||
SELECT_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
|
||||
{
|
||||
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTSelectComponent),
|
||||
cv.GenerateID(): cv.declare_id(Select),
|
||||
|
@@ -35,6 +35,9 @@ def validate(config):
|
||||
raise cv.Invalid("initial_value cannot be used with lambda")
|
||||
if CONF_RESTORE_VALUE in config:
|
||||
raise cv.Invalid("restore_value cannot be used with lambda")
|
||||
elif CONF_INITIAL_VALUE not in config:
|
||||
config[CONF_INITIAL_VALUE] = config[CONF_MIN_VALUE]
|
||||
|
||||
if not config[CONF_OPTIMISTIC] and CONF_SET_ACTION not in config:
|
||||
raise cv.Invalid(
|
||||
"Either optimistic mode must be enabled, or set_action must be set, to handle the number being set."
|
||||
@@ -80,8 +83,7 @@ async def to_code(config):
|
||||
|
||||
else:
|
||||
cg.add(var.set_optimistic(config[CONF_OPTIMISTIC]))
|
||||
if CONF_INITIAL_VALUE in config:
|
||||
cg.add(var.set_initial_value(config[CONF_INITIAL_VALUE]))
|
||||
cg.add(var.set_initial_value(config[CONF_INITIAL_VALUE]))
|
||||
if CONF_RESTORE_VALUE in config:
|
||||
cg.add(var.set_restore_value(config[CONF_RESTORE_VALUE]))
|
||||
|
||||
|
@@ -14,6 +14,16 @@ from esphome.const import (
|
||||
CONF_DATA,
|
||||
CONF_RX_BUFFER_SIZE,
|
||||
CONF_INVERT,
|
||||
CONF_TRIGGER_ID,
|
||||
CONF_SEQUENCE,
|
||||
CONF_TIMEOUT,
|
||||
CONF_DEBUG,
|
||||
CONF_DIRECTION,
|
||||
CONF_AFTER,
|
||||
CONF_BYTES,
|
||||
CONF_DELIMITER,
|
||||
CONF_DUMMY_RECEIVER,
|
||||
CONF_DUMMY_RECEIVER_ID,
|
||||
)
|
||||
from esphome.core import CORE
|
||||
|
||||
@@ -31,6 +41,8 @@ ESP8266UartComponent = uart_ns.class_(
|
||||
|
||||
UARTDevice = uart_ns.class_("UARTDevice")
|
||||
UARTWriteAction = uart_ns.class_("UARTWriteAction", automation.Action)
|
||||
UARTDebugger = uart_ns.class_("UARTDebugger", cg.Component, automation.Action)
|
||||
UARTDummyReceiver = uart_ns.class_("UARTDummyReceiver", cg.Component)
|
||||
MULTI_CONF = True
|
||||
|
||||
|
||||
@@ -75,6 +87,34 @@ CONF_STOP_BITS = "stop_bits"
|
||||
CONF_DATA_BITS = "data_bits"
|
||||
CONF_PARITY = "parity"
|
||||
|
||||
UARTDirection = uart_ns.enum("UARTDirection")
|
||||
UART_DIRECTIONS = {
|
||||
"RX": UARTDirection.UART_DIRECTION_RX,
|
||||
"TX": UARTDirection.UART_DIRECTION_TX,
|
||||
"BOTH": UARTDirection.UART_DIRECTION_BOTH,
|
||||
}
|
||||
|
||||
DEBUG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(UARTDebugger),
|
||||
cv.Optional(CONF_DIRECTION, default="BOTH"): cv.enum(
|
||||
UART_DIRECTIONS, upper=True
|
||||
),
|
||||
cv.Optional(CONF_AFTER): cv.Schema(
|
||||
{
|
||||
cv.Optional(CONF_BYTES, default=256): cv.validate_bytes,
|
||||
cv.Optional(
|
||||
CONF_TIMEOUT, default="100ms"
|
||||
): cv.positive_time_period_milliseconds,
|
||||
cv.Optional(CONF_DELIMITER): cv.templatable(validate_raw_data),
|
||||
}
|
||||
),
|
||||
cv.Required(CONF_SEQUENCE): automation.validate_automation(),
|
||||
cv.Optional(CONF_DUMMY_RECEIVER, default=False): cv.boolean,
|
||||
cv.GenerateID(CONF_DUMMY_RECEIVER_ID): cv.declare_id(UARTDummyReceiver),
|
||||
}
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
cv.Schema(
|
||||
{
|
||||
@@ -91,12 +131,38 @@ CONFIG_SCHEMA = cv.All(
|
||||
cv.Optional(CONF_INVERT): cv.invalid(
|
||||
"This option has been removed. Please instead use invert in the tx/rx pin schemas."
|
||||
),
|
||||
cv.Optional(CONF_DEBUG): DEBUG_SCHEMA,
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA),
|
||||
cv.has_at_least_one_key(CONF_TX_PIN, CONF_RX_PIN),
|
||||
)
|
||||
|
||||
|
||||
async def debug_to_code(config, parent):
|
||||
trigger = cg.new_Pvariable(config[CONF_TRIGGER_ID], parent)
|
||||
await cg.register_component(trigger, config)
|
||||
for action in config[CONF_SEQUENCE]:
|
||||
await automation.build_automation(
|
||||
trigger,
|
||||
[(UARTDirection, "direction"), (cg.std_vector.template(cg.uint8), "bytes")],
|
||||
action,
|
||||
)
|
||||
cg.add(trigger.set_direction(config[CONF_DIRECTION]))
|
||||
after = config[CONF_AFTER]
|
||||
cg.add(trigger.set_after_bytes(after[CONF_BYTES]))
|
||||
cg.add(trigger.set_after_timeout(after[CONF_TIMEOUT]))
|
||||
if CONF_DELIMITER in after:
|
||||
data = after[CONF_DELIMITER]
|
||||
if isinstance(data, bytes):
|
||||
data = list(data)
|
||||
for byte in after[CONF_DELIMITER]:
|
||||
cg.add(trigger.add_delimiter_byte(byte))
|
||||
if config[CONF_DUMMY_RECEIVER]:
|
||||
dummy = cg.new_Pvariable(config[CONF_DUMMY_RECEIVER_ID], parent)
|
||||
await cg.register_component(dummy, {})
|
||||
cg.add_define("USE_UART_DEBUGGER")
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
cg.add_global(uart_ns.using)
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
@@ -115,6 +181,9 @@ async def to_code(config):
|
||||
cg.add(var.set_data_bits(config[CONF_DATA_BITS]))
|
||||
cg.add(var.set_parity(config[CONF_PARITY]))
|
||||
|
||||
if CONF_DEBUG in config:
|
||||
await debug_to_code(config[CONF_DEBUG], var)
|
||||
|
||||
|
||||
# A schema to use for all UART devices, all UART integrations must extend this!
|
||||
UART_DEVICE_SCHEMA = cv.Schema(
|
||||
|
@@ -45,17 +45,17 @@ class UARTDevice {
|
||||
// Compat APIs
|
||||
int read() {
|
||||
uint8_t data;
|
||||
if (!read_byte(&data))
|
||||
if (!this->read_byte(&data))
|
||||
return -1;
|
||||
return data;
|
||||
}
|
||||
size_t write(uint8_t data) {
|
||||
write_byte(data);
|
||||
this->write_byte(data);
|
||||
return 1;
|
||||
}
|
||||
int peek() {
|
||||
uint8_t data;
|
||||
if (!peek_byte(&data))
|
||||
if (!this->peek_byte(&data))
|
||||
return -1;
|
||||
return data;
|
||||
}
|
||||
|
@@ -2,9 +2,13 @@
|
||||
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/log.h"
|
||||
#ifdef USE_UART_DEBUGGER
|
||||
#include "esphome/core/automation.h"
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace uart {
|
||||
@@ -15,6 +19,14 @@ enum UARTParityOptions {
|
||||
UART_CONFIG_PARITY_ODD,
|
||||
};
|
||||
|
||||
#ifdef USE_UART_DEBUGGER
|
||||
enum UARTDirection {
|
||||
UART_DIRECTION_RX,
|
||||
UART_DIRECTION_TX,
|
||||
UART_DIRECTION_BOTH,
|
||||
};
|
||||
#endif
|
||||
|
||||
const LogString *parity_to_str(UARTParityOptions parity);
|
||||
|
||||
class UARTComponent {
|
||||
@@ -50,6 +62,12 @@ class UARTComponent {
|
||||
void set_baud_rate(uint32_t baud_rate) { baud_rate_ = baud_rate; }
|
||||
uint32_t get_baud_rate() const { return baud_rate_; }
|
||||
|
||||
#ifdef USE_UART_DEBUGGER
|
||||
void add_debug_callback(std::function<void(UARTDirection, uint8_t)> &&callback) {
|
||||
this->debug_callback_.add(std::move(callback));
|
||||
}
|
||||
#endif
|
||||
|
||||
protected:
|
||||
virtual void check_logger_conflict() = 0;
|
||||
bool check_read_timeout_(size_t len = 1);
|
||||
@@ -61,6 +79,9 @@ class UARTComponent {
|
||||
uint8_t stop_bits_;
|
||||
uint8_t data_bits_;
|
||||
UARTParityOptions parity_;
|
||||
#ifdef USE_UART_DEBUGGER
|
||||
CallbackManager<void(UARTDirection, uint8_t)> debug_callback_{};
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace uart
|
||||
|
@@ -117,26 +117,32 @@ void ESP32ArduinoUARTComponent::dump_config() {
|
||||
|
||||
void ESP32ArduinoUARTComponent::write_array(const uint8_t *data, size_t len) {
|
||||
this->hw_serial_->write(data, len);
|
||||
#ifdef USE_UART_DEBUGGER
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
ESP_LOGVV(TAG, " Wrote 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(data[i]), data[i]);
|
||||
this->debug_callback_.call(UART_DIRECTION_TX, data[i]);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool ESP32ArduinoUARTComponent::peek_byte(uint8_t *data) {
|
||||
if (!this->check_read_timeout_())
|
||||
return false;
|
||||
*data = this->hw_serial_->peek();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ESP32ArduinoUARTComponent::read_array(uint8_t *data, size_t len) {
|
||||
if (!this->check_read_timeout_(len))
|
||||
return false;
|
||||
this->hw_serial_->readBytes(data, len);
|
||||
#ifdef USE_UART_DEBUGGER
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
ESP_LOGVV(TAG, " Read 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(data[i]), data[i]);
|
||||
this->debug_callback_.call(UART_DIRECTION_RX, data[i]);
|
||||
}
|
||||
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
int ESP32ArduinoUARTComponent::available() { return this->hw_serial_->available(); }
|
||||
void ESP32ArduinoUARTComponent::flush() {
|
||||
ESP_LOGVV(TAG, " Flushing...");
|
||||
|
@@ -130,9 +130,11 @@ void ESP8266UartComponent::write_array(const uint8_t *data, size_t len) {
|
||||
for (size_t i = 0; i < len; i++)
|
||||
this->sw_serial_->write_byte(data[i]);
|
||||
}
|
||||
#ifdef USE_UART_DEBUGGER
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
ESP_LOGVV(TAG, " Wrote 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(data[i]), data[i]);
|
||||
this->debug_callback_.call(UART_DIRECTION_TX, data[i]);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
bool ESP8266UartComponent::peek_byte(uint8_t *data) {
|
||||
if (!this->check_read_timeout_())
|
||||
@@ -153,10 +155,11 @@ bool ESP8266UartComponent::read_array(uint8_t *data, size_t len) {
|
||||
for (size_t i = 0; i < len; i++)
|
||||
data[i] = this->sw_serial_->read_byte();
|
||||
}
|
||||
#ifdef USE_UART_DEBUGGER
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
ESP_LOGVV(TAG, " Read 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(data[i]), data[i]);
|
||||
this->debug_callback_.call(UART_DIRECTION_RX, data[i]);
|
||||
}
|
||||
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
int ESP8266UartComponent::available() {
|
||||
|
@@ -130,10 +130,13 @@ void IDFUARTComponent::write_array(const uint8_t *data, size_t len) {
|
||||
xSemaphoreTake(this->lock_, portMAX_DELAY);
|
||||
uart_write_bytes(this->uart_num_, data, len);
|
||||
xSemaphoreGive(this->lock_);
|
||||
#ifdef USE_UART_DEBUGGER
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
ESP_LOGVV(TAG, " Wrote 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(data[i]), data[i]);
|
||||
this->debug_callback_.call(UART_DIRECTION_TX, data[i]);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool IDFUARTComponent::peek_byte(uint8_t *data) {
|
||||
if (!this->check_read_timeout_())
|
||||
return false;
|
||||
@@ -152,6 +155,7 @@ bool IDFUARTComponent::peek_byte(uint8_t *data) {
|
||||
xSemaphoreGive(this->lock_);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IDFUARTComponent::read_array(uint8_t *data, size_t len) {
|
||||
size_t length_to_read = len;
|
||||
if (!this->check_read_timeout_(len))
|
||||
@@ -165,12 +169,12 @@ bool IDFUARTComponent::read_array(uint8_t *data, size_t len) {
|
||||
}
|
||||
if (length_to_read > 0)
|
||||
uart_read_bytes(this->uart_num_, data, length_to_read, 20 / portTICK_RATE_MS);
|
||||
|
||||
xSemaphoreGive(this->lock_);
|
||||
#ifdef USE_UART_DEBUGGER
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
ESP_LOGVV(TAG, " Read 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(data[i]), data[i]);
|
||||
this->debug_callback_.call(UART_DIRECTION_RX, data[i]);
|
||||
}
|
||||
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
|
193
esphome/components/uart/uart_debugger.cpp
Normal file
193
esphome/components/uart/uart_debugger.cpp
Normal file
@@ -0,0 +1,193 @@
|
||||
#include "esphome/core/defines.h"
|
||||
#ifdef USE_UART_DEBUGGER
|
||||
|
||||
#include <vector>
|
||||
#include "uart_debugger.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace uart {
|
||||
|
||||
static const char *const TAG = "uart_debug";
|
||||
|
||||
UARTDebugger::UARTDebugger(UARTComponent *parent) {
|
||||
parent->add_debug_callback([this](UARTDirection direction, uint8_t byte) {
|
||||
if (!this->is_my_direction_(direction) || this->is_recursive_()) {
|
||||
return;
|
||||
}
|
||||
this->trigger_after_direction_change_(direction);
|
||||
this->store_byte_(direction, byte);
|
||||
this->trigger_after_delimiter_(byte);
|
||||
this->trigger_after_bytes_();
|
||||
});
|
||||
}
|
||||
|
||||
void UARTDebugger::loop() { this->trigger_after_timeout_(); }
|
||||
|
||||
bool UARTDebugger::is_my_direction_(UARTDirection direction) {
|
||||
return this->for_direction_ == UART_DIRECTION_BOTH || this->for_direction_ == direction;
|
||||
}
|
||||
|
||||
bool UARTDebugger::is_recursive_() { return this->is_triggering_; }
|
||||
|
||||
void UARTDebugger::trigger_after_direction_change_(UARTDirection direction) {
|
||||
if (this->has_buffered_bytes_() && this->for_direction_ == UART_DIRECTION_BOTH &&
|
||||
this->last_direction_ != direction) {
|
||||
this->fire_trigger_();
|
||||
}
|
||||
}
|
||||
|
||||
void UARTDebugger::store_byte_(UARTDirection direction, uint8_t byte) {
|
||||
this->bytes_.push_back(byte);
|
||||
this->last_direction_ = direction;
|
||||
this->last_time_ = millis();
|
||||
}
|
||||
|
||||
void UARTDebugger::trigger_after_delimiter_(uint8_t byte) {
|
||||
if (this->after_delimiter_.empty() || !this->has_buffered_bytes_()) {
|
||||
return;
|
||||
}
|
||||
if (this->after_delimiter_[this->after_delimiter_pos_] != byte) {
|
||||
this->after_delimiter_pos_ = 0;
|
||||
return;
|
||||
}
|
||||
this->after_delimiter_pos_++;
|
||||
if (this->after_delimiter_pos_ == this->after_delimiter_.size()) {
|
||||
this->fire_trigger_();
|
||||
this->after_delimiter_pos_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void UARTDebugger::trigger_after_bytes_() {
|
||||
if (this->has_buffered_bytes_() && this->after_bytes_ > 0 && this->bytes_.size() >= this->after_bytes_) {
|
||||
this->fire_trigger_();
|
||||
}
|
||||
}
|
||||
|
||||
void UARTDebugger::trigger_after_timeout_() {
|
||||
if (this->has_buffered_bytes_() && this->after_timeout_ > 0 && millis() - this->last_time_ >= this->after_timeout_) {
|
||||
this->fire_trigger_();
|
||||
}
|
||||
}
|
||||
|
||||
bool UARTDebugger::has_buffered_bytes_() { return !this->bytes_.empty(); }
|
||||
|
||||
void UARTDebugger::fire_trigger_() {
|
||||
this->is_triggering_ = true;
|
||||
trigger(this->last_direction_, this->bytes_);
|
||||
this->bytes_.clear();
|
||||
this->is_triggering_ = false;
|
||||
}
|
||||
|
||||
void UARTDummyReceiver::loop() {
|
||||
// Reading up to a limited number of bytes, to make sure that this loop()
|
||||
// won't lock up the system on a continuous incoming stream of bytes.
|
||||
uint8_t data;
|
||||
int count = 50;
|
||||
while (this->available() && count--) {
|
||||
this->read_byte(&data);
|
||||
}
|
||||
}
|
||||
|
||||
void UARTDebug::log_hex(UARTDirection direction, std::vector<uint8_t> bytes, uint8_t separator) {
|
||||
std::string res;
|
||||
if (direction == UART_DIRECTION_RX) {
|
||||
res += "<<< ";
|
||||
} else {
|
||||
res += ">>> ";
|
||||
}
|
||||
size_t len = bytes.size();
|
||||
char buf[5];
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (i > 0) {
|
||||
res += separator;
|
||||
}
|
||||
sprintf(buf, "%02X", bytes[i]);
|
||||
res += buf;
|
||||
}
|
||||
ESP_LOGD(TAG, "%s", res.c_str());
|
||||
}
|
||||
|
||||
void UARTDebug::log_string(UARTDirection direction, std::vector<uint8_t> bytes) {
|
||||
std::string res;
|
||||
if (direction == UART_DIRECTION_RX) {
|
||||
res += "<<< \"";
|
||||
} else {
|
||||
res += ">>> \"";
|
||||
}
|
||||
size_t len = bytes.size();
|
||||
char buf[5];
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (bytes[i] == 7) {
|
||||
res += "\\a";
|
||||
} else if (bytes[i] == 8) {
|
||||
res += "\\b";
|
||||
} else if (bytes[i] == 9) {
|
||||
res += "\\t";
|
||||
} else if (bytes[i] == 10) {
|
||||
res += "\\n";
|
||||
} else if (bytes[i] == 11) {
|
||||
res += "\\v";
|
||||
} else if (bytes[i] == 12) {
|
||||
res += "\\f";
|
||||
} else if (bytes[i] == 13) {
|
||||
res += "\\r";
|
||||
} else if (bytes[i] == 27) {
|
||||
res += "\\e";
|
||||
} else if (bytes[i] == 34) {
|
||||
res += "\\\"";
|
||||
} else if (bytes[i] == 39) {
|
||||
res += "\\'";
|
||||
} else if (bytes[i] == 92) {
|
||||
res += "\\\\";
|
||||
} else if (bytes[i] < 32 || bytes[i] > 127) {
|
||||
sprintf(buf, "\\x%02X", bytes[i]);
|
||||
res += buf;
|
||||
} else {
|
||||
res += bytes[i];
|
||||
}
|
||||
}
|
||||
res += '"';
|
||||
ESP_LOGD(TAG, "%s", res.c_str());
|
||||
}
|
||||
|
||||
void UARTDebug::log_int(UARTDirection direction, std::vector<uint8_t> bytes, uint8_t separator) {
|
||||
std::string res;
|
||||
size_t len = bytes.size();
|
||||
if (direction == UART_DIRECTION_RX) {
|
||||
res += "<<< ";
|
||||
} else {
|
||||
res += ">>> ";
|
||||
}
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (i > 0) {
|
||||
res += separator;
|
||||
}
|
||||
res += to_string(bytes[i]);
|
||||
}
|
||||
ESP_LOGD(TAG, "%s", res.c_str());
|
||||
}
|
||||
|
||||
void UARTDebug::log_binary(UARTDirection direction, std::vector<uint8_t> bytes, uint8_t separator) {
|
||||
std::string res;
|
||||
size_t len = bytes.size();
|
||||
if (direction == UART_DIRECTION_RX) {
|
||||
res += "<<< ";
|
||||
} else {
|
||||
res += ">>> ";
|
||||
}
|
||||
char buf[20];
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (i > 0) {
|
||||
res += separator;
|
||||
}
|
||||
sprintf(buf, "0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", BYTE_TO_BINARY(bytes[i]), bytes[i]);
|
||||
res += buf;
|
||||
}
|
||||
ESP_LOGD(TAG, "%s", res.c_str());
|
||||
}
|
||||
|
||||
} // namespace uart
|
||||
} // namespace esphome
|
||||
#endif
|
101
esphome/components/uart/uart_debugger.h
Normal file
101
esphome/components/uart/uart_debugger.h
Normal file
@@ -0,0 +1,101 @@
|
||||
#pragma once
|
||||
#include "esphome/core/defines.h"
|
||||
#ifdef USE_UART_DEBUGGER
|
||||
|
||||
#include <vector>
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "uart.h"
|
||||
#include "uart_component.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace uart {
|
||||
|
||||
/// The UARTDebugger class adds debugging support to a UART bus.
|
||||
///
|
||||
/// It accumulates bytes that travel over the UART bus and triggers one or
|
||||
/// more actions that can log the data at an appropriate time. What
|
||||
/// 'appropriate time' means exactly, is determined by a number of
|
||||
/// configurable constraints. E.g. when a given number of bytes is gathered
|
||||
/// and/or when no more data has been seen for a given time interval.
|
||||
class UARTDebugger : public Component, public Trigger<UARTDirection, std::vector<uint8_t>> {
|
||||
public:
|
||||
explicit UARTDebugger(UARTComponent *parent);
|
||||
void loop() override;
|
||||
|
||||
/// Set the direction in which to inspect the bytes: incoming, outgoing
|
||||
/// or both. When debugging in both directions, logging will be triggered
|
||||
/// when the direction of the data stream changes.
|
||||
void set_direction(UARTDirection direction) { this->for_direction_ = direction; }
|
||||
|
||||
/// Set the maximum number of bytes to accumulate. When the number of bytes
|
||||
/// is reached, logging will be triggered.
|
||||
void set_after_bytes(size_t size) { this->after_bytes_ = size; }
|
||||
|
||||
/// Set a timeout for the data stream. When no new bytes are seen during
|
||||
/// this timeout, logging will be triggered.
|
||||
void set_after_timeout(uint32_t timeout) { this->after_timeout_ = timeout; }
|
||||
|
||||
/// Add a delimiter byte. This can be called multiple times to setup a
|
||||
/// multi-byte delimiter (a typical example would be '\r\n').
|
||||
/// When the constructued byte sequence is found in the data stream,
|
||||
/// logging will be triggered.
|
||||
void add_delimiter_byte(uint8_t byte) { this->after_delimiter_.push_back(byte); }
|
||||
|
||||
protected:
|
||||
UARTDirection for_direction_;
|
||||
UARTDirection last_direction_{};
|
||||
std::vector<uint8_t> bytes_{};
|
||||
size_t after_bytes_;
|
||||
uint32_t after_timeout_;
|
||||
uint32_t last_time_{};
|
||||
std::vector<uint8_t> after_delimiter_{};
|
||||
size_t after_delimiter_pos_{};
|
||||
bool is_triggering_{false};
|
||||
|
||||
bool is_my_direction_(UARTDirection direction);
|
||||
bool is_recursive_();
|
||||
void store_byte_(UARTDirection direction, uint8_t byte);
|
||||
void trigger_after_direction_change_(UARTDirection direction);
|
||||
void trigger_after_delimiter_(uint8_t byte);
|
||||
void trigger_after_bytes_();
|
||||
void trigger_after_timeout_();
|
||||
bool has_buffered_bytes_();
|
||||
void fire_trigger_();
|
||||
};
|
||||
|
||||
/// This UARTDevice is used by the serial debugger to read data from a
|
||||
/// serial interface when the 'dummy_receiver' option is enabled.
|
||||
/// The data are not stored, nor processed. This is most useful when the
|
||||
/// debugger is used to reverse engineer a serial protocol, for which no
|
||||
/// specific UARTDevice implementation exists (yet), but for which the
|
||||
/// incoming bytes must be read to drive the debugger.
|
||||
class UARTDummyReceiver : public Component, public UARTDevice {
|
||||
public:
|
||||
UARTDummyReceiver(UARTComponent *parent) : UARTDevice(parent) {}
|
||||
void loop() override;
|
||||
};
|
||||
|
||||
/// This class contains some static methods, that can be used to easily
|
||||
/// create a logging action for the debugger.
|
||||
class UARTDebug {
|
||||
public:
|
||||
/// Log the bytes as hex values, separated by the provided separator
|
||||
/// character.
|
||||
static void log_hex(UARTDirection direction, std::vector<uint8_t> bytes, uint8_t separator);
|
||||
|
||||
/// Log the bytes as string values, escaping unprintable characters.
|
||||
static void log_string(UARTDirection direction, std::vector<uint8_t> bytes);
|
||||
|
||||
/// Log the bytes as integer values, separated by the provided separator
|
||||
/// character.
|
||||
static void log_int(UARTDirection direction, std::vector<uint8_t> bytes, uint8_t separator);
|
||||
|
||||
/// Log the bytes as '<binary> (<hex>)' values, separated by the provided
|
||||
/// separator.
|
||||
static void log_binary(UARTDirection direction, std::vector<uint8_t> bytes, uint8_t separator);
|
||||
};
|
||||
|
||||
} // namespace uart
|
||||
} // namespace esphome
|
||||
#endif
|
@@ -28,4 +28,4 @@ async def to_code(config):
|
||||
cg.add_library("FS", None)
|
||||
cg.add_library("Update", None)
|
||||
# https://github.com/esphome/ESPAsyncWebServer/blob/master/library.json
|
||||
cg.add_library("esphome/ESPAsyncWebServer-esphome", "2.0.1")
|
||||
cg.add_library("esphome/ESPAsyncWebServer-esphome", "2.1.0")
|
||||
|
@@ -1,6 +1,6 @@
|
||||
"""Constants used by esphome."""
|
||||
|
||||
__version__ = "2021.11.0-dev"
|
||||
__version__ = "2021.11.0b5"
|
||||
|
||||
ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_"
|
||||
|
||||
@@ -34,6 +34,7 @@ ARDUINO_VERSION_ESP8266 = {
|
||||
SOURCE_FILE_EXTENSIONS = {".cpp", ".hpp", ".h", ".c", ".tcc", ".ino"}
|
||||
HEADER_FILE_EXTENSIONS = {".h", ".hpp", ".tcc"}
|
||||
|
||||
|
||||
CONF_ABOVE = "above"
|
||||
CONF_ACCELERATION = "acceleration"
|
||||
CONF_ACCELERATION_X = "acceleration_x"
|
||||
@@ -47,6 +48,7 @@ CONF_ACTIVE_POWER = "active_power"
|
||||
CONF_ADDRESS = "address"
|
||||
CONF_ADDRESSABLE_LIGHT_ID = "addressable_light_id"
|
||||
CONF_ADVANCED = "advanced"
|
||||
CONF_AFTER = "after"
|
||||
CONF_ALPHA = "alpha"
|
||||
CONF_ALTITUDE = "altitude"
|
||||
CONF_AND = "and"
|
||||
@@ -93,6 +95,7 @@ CONF_BUFFER_SIZE = "buffer_size"
|
||||
CONF_BUILD_PATH = "build_path"
|
||||
CONF_BUS_VOLTAGE = "bus_voltage"
|
||||
CONF_BUSY_PIN = "busy_pin"
|
||||
CONF_BYTES = "bytes"
|
||||
CONF_CALCULATED_LUX = "calculated_lux"
|
||||
CONF_CALIBRATE_LINEAR = "calibrate_linear"
|
||||
CONF_CALIBRATION = "calibration"
|
||||
@@ -164,6 +167,7 @@ CONF_DAYS_OF_WEEK = "days_of_week"
|
||||
CONF_DC_PIN = "dc_pin"
|
||||
CONF_DEASSERT_RTS_DTR = "deassert_rts_dtr"
|
||||
CONF_DEBOUNCE = "debounce"
|
||||
CONF_DEBUG = "debug"
|
||||
CONF_DECAY_MODE = "decay_mode"
|
||||
CONF_DECELERATION = "deceleration"
|
||||
CONF_DEFAULT_MODE = "default_mode"
|
||||
@@ -171,6 +175,7 @@ CONF_DEFAULT_TARGET_TEMPERATURE_HIGH = "default_target_temperature_high"
|
||||
CONF_DEFAULT_TARGET_TEMPERATURE_LOW = "default_target_temperature_low"
|
||||
CONF_DEFAULT_TRANSITION_LENGTH = "default_transition_length"
|
||||
CONF_DELAY = "delay"
|
||||
CONF_DELIMITER = "delimiter"
|
||||
CONF_DELTA = "delta"
|
||||
CONF_DEVICE = "device"
|
||||
CONF_DEVICE_CLASS = "device_class"
|
||||
@@ -192,6 +197,8 @@ CONF_DNS2 = "dns2"
|
||||
CONF_DOMAIN = "domain"
|
||||
CONF_DRY_ACTION = "dry_action"
|
||||
CONF_DRY_MODE = "dry_mode"
|
||||
CONF_DUMMY_RECEIVER = "dummy_receiver"
|
||||
CONF_DUMMY_RECEIVER_ID = "dummy_receiver_id"
|
||||
CONF_DUMP = "dump"
|
||||
CONF_DURATION = "duration"
|
||||
CONF_EAP = "eap"
|
||||
@@ -871,10 +878,11 @@ DEVICE_CLASS_OPENING = "opening"
|
||||
DEVICE_CLASS_PLUG = "plug"
|
||||
DEVICE_CLASS_PRESENCE = "presence"
|
||||
DEVICE_CLASS_PROBLEM = "problem"
|
||||
DEVICE_CLASS_RUNNING = "running"
|
||||
DEVICE_CLASS_SAFETY = "safety"
|
||||
DEVICE_CLASS_SMOKE = "smoke"
|
||||
DEVICE_CLASS_SOUND = "sound"
|
||||
DEVICE_CLASS_UPDATE = "update"
|
||||
DEVICE_CLASS_TAMPER = "tamper"
|
||||
DEVICE_CLASS_VIBRATION = "vibration"
|
||||
DEVICE_CLASS_WINDOW = "window"
|
||||
# device classes of both binary_sensor and sensor component
|
||||
|
@@ -37,6 +37,7 @@
|
||||
#define USE_SWITCH
|
||||
#define USE_TEXT_SENSOR
|
||||
#define USE_TIME
|
||||
#define USE_UART_DEBUGGER
|
||||
#define USE_WEBSERVER
|
||||
#define USE_WIFI
|
||||
|
||||
|
@@ -9,6 +9,7 @@
|
||||
#ifdef USE_WIFI
|
||||
#include <ESP8266WiFi.h>
|
||||
#endif
|
||||
#include <Arduino.h>
|
||||
#include <osapi.h>
|
||||
#elif defined(USE_ESP32_FRAMEWORK_ARDUINO)
|
||||
#include <Esp.h>
|
||||
@@ -430,13 +431,8 @@ void hsv_to_rgb(int hue, float saturation, float value, float &red, float &green
|
||||
}
|
||||
|
||||
#ifdef USE_ESP8266
|
||||
#ifdef USE_WIFI
|
||||
IRAM_ATTR InterruptLock::InterruptLock() { xt_state_ = xt_rsil(15); }
|
||||
IRAM_ATTR InterruptLock::~InterruptLock() { xt_wsr_ps(xt_state_); }
|
||||
#else
|
||||
IRAM_ATTR InterruptLock::InterruptLock() {}
|
||||
IRAM_ATTR InterruptLock::~InterruptLock() {}
|
||||
#endif
|
||||
#endif
|
||||
#ifdef USE_ESP32
|
||||
IRAM_ATTR InterruptLock::InterruptLock() { portDISABLE_INTERRUPTS(); }
|
||||
|
@@ -41,7 +41,7 @@ lib_deps =
|
||||
${common.lib_deps}
|
||||
ottowinter/AsyncMqttClient-esphome@0.8.6 ; mqtt
|
||||
ottowinter/ArduinoJson-esphomelib@5.13.3 ; json
|
||||
esphome/ESPAsyncWebServer-esphome@2.0.1 ; web_server_base
|
||||
esphome/ESPAsyncWebServer-esphome@2.1.0 ; web_server_base
|
||||
fastled/FastLED@3.3.2 ; fastled_base
|
||||
mikalhart/TinyGPSPlus@1.0.2 ; gps
|
||||
freekode/TM1651@1.0.1 ; tm1651
|
||||
|
@@ -193,6 +193,18 @@ uart:
|
||||
data_bits: 8
|
||||
stop_bits: 1
|
||||
rx_buffer_size: 512
|
||||
debug:
|
||||
dummy_receiver: true
|
||||
direction: both
|
||||
after:
|
||||
bytes: 50
|
||||
timeout: 500ms
|
||||
delimiter: "\r\n"
|
||||
sequence:
|
||||
- lambda: UARTDebug::log_hex(direction, bytes, ':');
|
||||
- lambda: UARTDebug::log_string(direction, bytes);
|
||||
- lambda: UARTDebug::log_int(direction, bytes, ',');
|
||||
- lambda: UARTDebug::log_binary(direction, bytes, ';');
|
||||
|
||||
- id: adalight_uart
|
||||
tx_pin: GPIO25
|
||||
@@ -2500,3 +2512,23 @@ teleinfo:
|
||||
uart_id: uart0
|
||||
update_interval: 60s
|
||||
historical_mode: true
|
||||
|
||||
number:
|
||||
- platform: template
|
||||
id: test_number
|
||||
state_topic: livingroom/custom_state_topic
|
||||
command_topic: livingroom/custom_command_topic
|
||||
min_value: 0
|
||||
step: 1
|
||||
max_value: 10
|
||||
optimistic: true
|
||||
|
||||
select:
|
||||
- platform: template
|
||||
id: test_select
|
||||
state_topic: livingroom/custom_state_topic
|
||||
command_topic: livingroom/custom_command_topic
|
||||
options:
|
||||
- one
|
||||
- two
|
||||
optimistic: true
|
||||
|
Reference in New Issue
Block a user