mirror of
https://github.com/esphome/esphome.git
synced 2025-11-01 15:41:52 +00:00
Compare commits
149 Commits
ble-server
...
2021.11.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
21db43db06 | ||
|
|
5009b3029f | ||
|
|
57a029189c | ||
|
|
0cb715bb76 | ||
|
|
7d03823afd | ||
|
|
8e1c9f5042 | ||
|
|
980b7cda8f | ||
|
|
3a72dd5cb6 | ||
|
|
3178243811 | ||
|
|
d30e2f2a4f | ||
|
|
6226dae05c | ||
|
|
9c6a475a6e | ||
|
|
8294d10d5b | ||
|
|
67558bec47 | ||
|
|
84873d4074 | ||
|
|
58a0b28a39 | ||
|
|
b37d3a66cc | ||
|
|
7e495a5e27 | ||
|
|
c41547fd4a | ||
|
|
0d47d41c85 | ||
|
|
41a3a17456 | ||
|
|
cbbafbcca2 | ||
|
|
c75566b374 | ||
|
|
7279f1fcc1 | ||
|
|
d7432f7c10 | ||
|
|
b0a0a153f3 | ||
|
|
024632dbd0 | ||
|
|
0a545a28b9 | ||
|
|
0f2df59998 | ||
|
|
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 |
@@ -60,6 +60,10 @@ async def to_code(config):
|
||||
image.seek(frameIndex)
|
||||
frame = image.convert("L", dither=Image.NONE)
|
||||
pixels = list(frame.getdata())
|
||||
if len(pixels) != height * width:
|
||||
raise core.EsphomeError(
|
||||
f"Unexpected number of pixels in frame {frameIndex}: {len(pixels)} != {height*width}"
|
||||
)
|
||||
for pix in pixels:
|
||||
data[pos] = pix
|
||||
pos += 1
|
||||
@@ -69,8 +73,14 @@ async def to_code(config):
|
||||
pos = 0
|
||||
for frameIndex in range(frames):
|
||||
image.seek(frameIndex)
|
||||
if CONF_RESIZE in config:
|
||||
image.thumbnail(config[CONF_RESIZE])
|
||||
frame = image.convert("RGB")
|
||||
pixels = list(frame.getdata())
|
||||
if len(pixels) != height * width:
|
||||
raise core.EsphomeError(
|
||||
f"Unexpected number of pixels in frame {frameIndex}: {len(pixels)} != {height*width}"
|
||||
)
|
||||
for pix in pixels:
|
||||
data[pos] = pix[0]
|
||||
pos += 1
|
||||
|
||||
@@ -73,51 +73,52 @@ AnovaPacket *AnovaCodec::get_stop_request() {
|
||||
}
|
||||
|
||||
void AnovaCodec::decode(const uint8_t *data, uint16_t length) {
|
||||
memset(this->buf_, 0, 32);
|
||||
strncpy(this->buf_, (char *) data, length);
|
||||
char buf[32];
|
||||
memset(buf, 0, sizeof(buf));
|
||||
strncpy(buf, (char *) data, std::min<uint16_t>(length, sizeof(buf) - 1));
|
||||
this->has_target_temp_ = this->has_current_temp_ = this->has_unit_ = this->has_running_ = false;
|
||||
switch (this->current_query_) {
|
||||
case READ_DEVICE_STATUS: {
|
||||
if (!strncmp(this->buf_, "stopped", 7)) {
|
||||
if (!strncmp(buf, "stopped", 7)) {
|
||||
this->has_running_ = true;
|
||||
this->running_ = false;
|
||||
}
|
||||
if (!strncmp(this->buf_, "running", 7)) {
|
||||
if (!strncmp(buf, "running", 7)) {
|
||||
this->has_running_ = true;
|
||||
this->running_ = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case START: {
|
||||
if (!strncmp(this->buf_, "start", 5)) {
|
||||
if (!strncmp(buf, "start", 5)) {
|
||||
this->has_running_ = true;
|
||||
this->running_ = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case STOP: {
|
||||
if (!strncmp(this->buf_, "stop", 4)) {
|
||||
if (!strncmp(buf, "stop", 4)) {
|
||||
this->has_running_ = true;
|
||||
this->running_ = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case READ_TARGET_TEMPERATURE: {
|
||||
this->target_temp_ = parse_number<float>(this->buf_, sizeof(this->buf_)).value_or(0.0f);
|
||||
this->target_temp_ = parse_number<float>(buf, sizeof(buf)).value_or(0.0f);
|
||||
if (this->fahrenheit_)
|
||||
this->target_temp_ = ftoc(this->target_temp_);
|
||||
this->has_target_temp_ = true;
|
||||
break;
|
||||
}
|
||||
case SET_TARGET_TEMPERATURE: {
|
||||
this->target_temp_ = parse_number<float>(this->buf_, sizeof(this->buf_)).value_or(0.0f);
|
||||
this->target_temp_ = parse_number<float>(buf, sizeof(buf)).value_or(0.0f);
|
||||
if (this->fahrenheit_)
|
||||
this->target_temp_ = ftoc(this->target_temp_);
|
||||
this->has_target_temp_ = true;
|
||||
break;
|
||||
}
|
||||
case READ_CURRENT_TEMPERATURE: {
|
||||
this->current_temp_ = parse_number<float>(this->buf_, sizeof(this->buf_)).value_or(0.0f);
|
||||
this->current_temp_ = parse_number<float>(buf, sizeof(buf)).value_or(0.0f);
|
||||
if (this->fahrenheit_)
|
||||
this->current_temp_ = ftoc(this->current_temp_);
|
||||
this->has_current_temp_ = true;
|
||||
@@ -125,8 +126,8 @@ void AnovaCodec::decode(const uint8_t *data, uint16_t length) {
|
||||
}
|
||||
case SET_UNIT:
|
||||
case READ_UNIT: {
|
||||
this->unit_ = this->buf_[0];
|
||||
this->fahrenheit_ = this->buf_[0] == 'f';
|
||||
this->unit_ = buf[0];
|
||||
this->fahrenheit_ = buf[0] == 'f';
|
||||
this->has_unit_ = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -70,7 +70,6 @@ class AnovaCodec {
|
||||
bool has_current_temp_;
|
||||
bool has_unit_;
|
||||
bool has_running_;
|
||||
char buf_[32];
|
||||
bool fahrenheit_;
|
||||
|
||||
CurrentQuery current_query_;
|
||||
|
||||
@@ -44,9 +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_TAMPER,
|
||||
DEVICE_CLASS_UPDATE,
|
||||
DEVICE_CLASS_VIBRATION,
|
||||
DEVICE_CLASS_WINDOW,
|
||||
@@ -76,9 +78,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_TAMPER,
|
||||
DEVICE_CLASS_UPDATE,
|
||||
DEVICE_CLASS_VIBRATION,
|
||||
DEVICE_CLASS_WINDOW,
|
||||
|
||||
@@ -67,7 +67,7 @@ async def to_code(config):
|
||||
var.set_service_uuid32(esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID]))
|
||||
)
|
||||
elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid128_format):
|
||||
uuid128 = esp32_ble_tracker.as_hex_array(config[CONF_SERVICE_UUID])
|
||||
uuid128 = esp32_ble_tracker.as_reversed_hex_array(config[CONF_SERVICE_UUID])
|
||||
cg.add(var.set_service_uuid128(uuid128))
|
||||
|
||||
if len(config[CONF_CHARACTERISTIC_UUID]) == len(esp32_ble_tracker.bt_uuid16_format):
|
||||
@@ -87,7 +87,9 @@ async def to_code(config):
|
||||
elif len(config[CONF_CHARACTERISTIC_UUID]) == len(
|
||||
esp32_ble_tracker.bt_uuid128_format
|
||||
):
|
||||
uuid128 = esp32_ble_tracker.as_hex_array(config[CONF_CHARACTERISTIC_UUID])
|
||||
uuid128 = esp32_ble_tracker.as_reversed_hex_array(
|
||||
config[CONF_CHARACTERISTIC_UUID]
|
||||
)
|
||||
cg.add(var.set_char_uuid128(uuid128))
|
||||
|
||||
if CONF_DESCRIPTOR_UUID in config:
|
||||
@@ -108,7 +110,9 @@ async def to_code(config):
|
||||
elif len(config[CONF_DESCRIPTOR_UUID]) == len(
|
||||
esp32_ble_tracker.bt_uuid128_format
|
||||
):
|
||||
uuid128 = esp32_ble_tracker.as_hex_array(config[CONF_DESCRIPTOR_UUID])
|
||||
uuid128 = esp32_ble_tracker.as_reversed_hex_array(
|
||||
config[CONF_DESCRIPTOR_UUID]
|
||||
)
|
||||
cg.add(var.set_descr_uuid128(uuid128))
|
||||
|
||||
if CONF_LAMBDA in config:
|
||||
|
||||
@@ -76,6 +76,7 @@ class ESP32Preferences : public ESPPreferences {
|
||||
uint32_t current_offset = 0;
|
||||
|
||||
void open() {
|
||||
nvs_flash_init();
|
||||
esp_err_t err = nvs_open("esphome", NVS_READWRITE, &nvs_handle);
|
||||
if (err == 0)
|
||||
return;
|
||||
|
||||
@@ -21,12 +21,19 @@ static const char *const TAG = "esp32_camera_web_server";
|
||||
#define CONTENT_TYPE "image/jpeg"
|
||||
#define CONTENT_LENGTH "Content-Length"
|
||||
|
||||
static const char *const STREAM_HEADER =
|
||||
"HTTP/1.1 200\r\nAccess-Control-Allow-Origin: *\r\nContent-Type: multipart/x-mixed-replace;boundary=" PART_BOUNDARY
|
||||
"\r\n";
|
||||
static const char *const STREAM_500 = "HTTP/1.1 500\r\nContent-Type: text/plain\r\n\r\nNo frames send.\r\n";
|
||||
static const char *const STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n";
|
||||
static const char *const STREAM_HEADER = "HTTP/1.0 200 OK\r\n"
|
||||
"Access-Control-Allow-Origin: *\r\n"
|
||||
"Connection: close\r\n"
|
||||
"Content-Type: multipart/x-mixed-replace;boundary=" PART_BOUNDARY "\r\n"
|
||||
"\r\n"
|
||||
"--" PART_BOUNDARY "\r\n";
|
||||
static const char *const STREAM_ERROR = "Content-Type: text/plain\r\n"
|
||||
"\r\n"
|
||||
"No frames send.\r\n"
|
||||
"--" PART_BOUNDARY "\r\n";
|
||||
static const char *const STREAM_PART = "Content-Type: " CONTENT_TYPE "\r\n" CONTENT_LENGTH ": %u\r\n\r\n";
|
||||
static const char *const STREAM_BOUNDARY = "\r\n"
|
||||
"--" PART_BOUNDARY "\r\n";
|
||||
|
||||
CameraWebServer::CameraWebServer() {}
|
||||
|
||||
@@ -45,6 +52,7 @@ void CameraWebServer::setup() {
|
||||
config.ctrl_port = this->port_;
|
||||
config.max_open_sockets = 1;
|
||||
config.backlog_conn = 2;
|
||||
config.lru_purge_enable = true;
|
||||
|
||||
if (httpd_start(&this->httpd_, &config) != ESP_OK) {
|
||||
mark_failed();
|
||||
@@ -172,9 +180,6 @@ esp_err_t CameraWebServer::streaming_handler_(struct httpd_req *req) {
|
||||
ESP_LOGW(TAG, "STREAM: failed to acquire frame");
|
||||
res = ESP_FAIL;
|
||||
}
|
||||
if (res == ESP_OK) {
|
||||
res = httpd_send_all(req, STREAM_BOUNDARY, strlen(STREAM_BOUNDARY));
|
||||
}
|
||||
if (res == ESP_OK) {
|
||||
size_t hlen = snprintf(part_buf, 64, STREAM_PART, image->get_data_length());
|
||||
res = httpd_send_all(req, part_buf, hlen);
|
||||
@@ -182,6 +187,9 @@ esp_err_t CameraWebServer::streaming_handler_(struct httpd_req *req) {
|
||||
if (res == ESP_OK) {
|
||||
res = httpd_send_all(req, (const char *) image->get_data_buffer(), image->get_data_length());
|
||||
}
|
||||
if (res == ESP_OK) {
|
||||
res = httpd_send_all(req, STREAM_BOUNDARY, strlen(STREAM_BOUNDARY));
|
||||
}
|
||||
if (res == ESP_OK) {
|
||||
frames++;
|
||||
int64_t frame_time = millis() - last_frame;
|
||||
@@ -193,7 +201,7 @@ esp_err_t CameraWebServer::streaming_handler_(struct httpd_req *req) {
|
||||
}
|
||||
|
||||
if (!frames) {
|
||||
res = httpd_send_all(req, STREAM_500, strlen(STREAM_500));
|
||||
res = httpd_send_all(req, STREAM_ERROR, strlen(STREAM_ERROR));
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "STREAM: closed. Frames: %u", frames);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -32,7 +32,7 @@ void EZOSensor::update() {
|
||||
}
|
||||
|
||||
void EZOSensor::loop() {
|
||||
uint8_t buf[20];
|
||||
uint8_t buf[21];
|
||||
if (!(this->state_ & EZO_STATE_WAIT)) {
|
||||
if (this->state_ & EZO_STATE_SEND_TEMP) {
|
||||
int len = sprintf((char *) buf, "T,%0.3f", this->tempcomp_);
|
||||
@@ -74,7 +74,7 @@ void EZOSensor::loop() {
|
||||
if (buf[0] != 1)
|
||||
return;
|
||||
|
||||
float val = parse_number<float>((char *) &buf[1], sizeof(buf) - 1).value_or(0);
|
||||
float val = parse_number<float>((char *) &buf[1], sizeof(buf) - 2).value_or(0);
|
||||
this->publish_state(val);
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ class AQICalculator : public AbstractAQICalculator {
|
||||
int conc_lo = array[grid_index][0];
|
||||
int conc_hi = array[grid_index][1];
|
||||
|
||||
return ((aqi_hi - aqi_lo) / (conc_hi - conc_lo)) * (value - conc_lo) + aqi_lo;
|
||||
return (value - conc_lo) * (aqi_hi - aqi_lo) / (conc_hi - conc_lo) + aqi_lo;
|
||||
}
|
||||
|
||||
int get_grid_index_(uint16_t value, int array[AMOUNT_OF_LEVELS][2]) {
|
||||
|
||||
@@ -37,9 +37,7 @@ class CAQICalculator : public AbstractAQICalculator {
|
||||
int conc_lo = array[grid_index][0];
|
||||
int conc_hi = array[grid_index][1];
|
||||
|
||||
int aqi = ((aqi_hi - aqi_lo) / (conc_hi - conc_lo)) * (value - conc_lo) + aqi_lo;
|
||||
|
||||
return aqi;
|
||||
return (value - conc_lo) * (aqi_hi - aqi_lo) / (conc_hi - conc_lo) + aqi_lo;
|
||||
}
|
||||
|
||||
int get_grid_index_(uint16_t value, int array[AMOUNT_OF_LEVELS][2]) {
|
||||
|
||||
@@ -2,30 +2,32 @@
|
||||
|
||||
namespace improv {
|
||||
|
||||
ImprovCommand parse_improv_data(const std::vector<uint8_t> &data) {
|
||||
return parse_improv_data(data.data(), data.size());
|
||||
ImprovCommand parse_improv_data(const std::vector<uint8_t> &data, bool check_checksum) {
|
||||
return parse_improv_data(data.data(), data.size(), check_checksum);
|
||||
}
|
||||
|
||||
ImprovCommand parse_improv_data(const uint8_t *data, size_t length) {
|
||||
ImprovCommand parse_improv_data(const uint8_t *data, size_t length, bool check_checksum) {
|
||||
ImprovCommand improv_command;
|
||||
Command command = (Command) data[0];
|
||||
uint8_t data_length = data[1];
|
||||
|
||||
if (data_length != length - 3) {
|
||||
if (data_length != length - 2 - check_checksum) {
|
||||
improv_command.command = UNKNOWN;
|
||||
return improv_command;
|
||||
}
|
||||
|
||||
uint8_t checksum = data[length - 1];
|
||||
if (check_checksum) {
|
||||
uint8_t checksum = data[length - 1];
|
||||
|
||||
uint32_t calculated_checksum = 0;
|
||||
for (uint8_t i = 0; i < length - 1; i++) {
|
||||
calculated_checksum += data[i];
|
||||
}
|
||||
uint32_t calculated_checksum = 0;
|
||||
for (uint8_t i = 0; i < length - 1; i++) {
|
||||
calculated_checksum += data[i];
|
||||
}
|
||||
|
||||
if ((uint8_t) calculated_checksum != checksum) {
|
||||
improv_command.command = BAD_CHECKSUM;
|
||||
return improv_command;
|
||||
if ((uint8_t) calculated_checksum != checksum) {
|
||||
improv_command.command = BAD_CHECKSUM;
|
||||
return improv_command;
|
||||
}
|
||||
}
|
||||
|
||||
if (command == WIFI_SETTINGS) {
|
||||
@@ -46,7 +48,7 @@ ImprovCommand parse_improv_data(const uint8_t *data, size_t length) {
|
||||
return improv_command;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> build_rpc_response(Command command, const std::vector<std::string> &datum) {
|
||||
std::vector<uint8_t> build_rpc_response(Command command, const std::vector<std::string> &datum, bool add_checksum) {
|
||||
std::vector<uint8_t> out;
|
||||
uint32_t length = 0;
|
||||
out.push_back(command);
|
||||
@@ -58,17 +60,19 @@ std::vector<uint8_t> build_rpc_response(Command command, const std::vector<std::
|
||||
}
|
||||
out.insert(out.begin() + 1, length);
|
||||
|
||||
uint32_t calculated_checksum = 0;
|
||||
if (add_checksum) {
|
||||
uint32_t calculated_checksum = 0;
|
||||
|
||||
for (uint8_t byte : out) {
|
||||
calculated_checksum += byte;
|
||||
for (uint8_t byte : out) {
|
||||
calculated_checksum += byte;
|
||||
}
|
||||
out.push_back(calculated_checksum);
|
||||
}
|
||||
out.push_back(calculated_checksum);
|
||||
return out;
|
||||
}
|
||||
|
||||
#ifdef USE_ARDUINO
|
||||
std::vector<uint8_t> build_rpc_response(Command command, const std::vector<String> &datum) {
|
||||
#ifdef ARDUINO
|
||||
std::vector<uint8_t> build_rpc_response(Command command, const std::vector<String> &datum, bool add_checksum) {
|
||||
std::vector<uint8_t> out;
|
||||
uint32_t length = 0;
|
||||
out.push_back(command);
|
||||
@@ -80,14 +84,16 @@ std::vector<uint8_t> build_rpc_response(Command command, const std::vector<Strin
|
||||
}
|
||||
out.insert(out.begin() + 1, length);
|
||||
|
||||
uint32_t calculated_checksum = 0;
|
||||
if (add_checksum) {
|
||||
uint32_t calculated_checksum = 0;
|
||||
|
||||
for (uint8_t byte : out) {
|
||||
calculated_checksum += byte;
|
||||
for (uint8_t byte : out) {
|
||||
calculated_checksum += byte;
|
||||
}
|
||||
out.push_back(calculated_checksum);
|
||||
}
|
||||
out.push_back(calculated_checksum);
|
||||
return out;
|
||||
}
|
||||
#endif // USE_ARDUINO
|
||||
#endif // ARDUINO
|
||||
|
||||
} // namespace improv
|
||||
|
||||
@@ -51,12 +51,13 @@ struct ImprovCommand {
|
||||
std::string password;
|
||||
};
|
||||
|
||||
ImprovCommand parse_improv_data(const std::vector<uint8_t> &data);
|
||||
ImprovCommand parse_improv_data(const uint8_t *data, size_t length);
|
||||
ImprovCommand parse_improv_data(const std::vector<uint8_t> &data, bool check_checksum = true);
|
||||
ImprovCommand parse_improv_data(const uint8_t *data, size_t length, bool check_checksum = true);
|
||||
|
||||
std::vector<uint8_t> build_rpc_response(Command command, const std::vector<std::string> &datum);
|
||||
std::vector<uint8_t> build_rpc_response(Command command, const std::vector<std::string> &datum,
|
||||
bool add_checksum = true);
|
||||
#ifdef ARDUINO
|
||||
std::vector<uint8_t> build_rpc_response(Command command, const std::vector<String> &datum);
|
||||
std::vector<uint8_t> build_rpc_response(Command command, const std::vector<String> &datum, bool add_checksum = true);
|
||||
#endif // ARDUINO
|
||||
|
||||
} // namespace improv
|
||||
|
||||
@@ -92,20 +92,19 @@ 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);
|
||||
urls.push_back(webserver_url);
|
||||
#endif
|
||||
std::vector<uint8_t> data = improv::build_rpc_response(command, urls);
|
||||
std::vector<uint8_t> data = improv::build_rpc_response(command, urls, false);
|
||||
return data;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> ImprovSerialComponent::build_version_info_() {
|
||||
std::vector<std::string> infos = {"ESPHome", ESPHOME_VERSION, ESPHOME_VARIANT, App.get_name()};
|
||||
std::vector<uint8_t> data = improv::build_rpc_response(improv::GET_DEVICE_INFO, infos);
|
||||
std::vector<uint8_t> data = improv::build_rpc_response(improv::GET_DEVICE_INFO, infos, false);
|
||||
return data;
|
||||
};
|
||||
|
||||
@@ -141,22 +140,33 @@ bool ImprovSerialComponent::parse_improv_serial_byte_(uint8_t byte) {
|
||||
if (at < 8 + data_len)
|
||||
return true;
|
||||
|
||||
if (at == 8 + data_len) {
|
||||
if (at == 8 + data_len)
|
||||
return true;
|
||||
|
||||
if (at == 8 + data_len + 1) {
|
||||
uint8_t checksum = 0x00;
|
||||
for (uint8_t i = 0; i < at; i++)
|
||||
checksum += raw[i];
|
||||
|
||||
if (checksum != byte) {
|
||||
ESP_LOGW(TAG, "Error decoding Improv payload");
|
||||
this->set_error_(improv::ERROR_INVALID_RPC);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (type == TYPE_RPC) {
|
||||
this->set_error_(improv::ERROR_NONE);
|
||||
auto command = improv::parse_improv_data(&raw[9], data_len);
|
||||
auto command = improv::parse_improv_data(&raw[9], data_len, false);
|
||||
return this->parse_improv_payload_(command);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
// If we got here then the command coming is is improv, but not an RPC command
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ImprovSerialComponent::parse_improv_payload_(improv::ImprovCommand &command) {
|
||||
switch (command.command) {
|
||||
case improv::BAD_CHECKSUM:
|
||||
ESP_LOGW(TAG, "Error decoding Improv payload");
|
||||
this->set_error_(improv::ERROR_INVALID_RPC);
|
||||
return false;
|
||||
case improv::WIFI_SETTINGS: {
|
||||
wifi::WiFiAP sta{};
|
||||
sta.set_ssid(command.ssid);
|
||||
@@ -233,6 +243,12 @@ void ImprovSerialComponent::send_response_(std::vector<uint8_t> &response) {
|
||||
data[7] = TYPE_RPC_RESPONSE;
|
||||
data[8] = response.size();
|
||||
data.insert(data.end(), response.begin(), response.end());
|
||||
|
||||
uint8_t checksum = 0x00;
|
||||
for (uint8_t d : data)
|
||||
checksum += d;
|
||||
data.push_back(checksum);
|
||||
|
||||
this->write_data_(data);
|
||||
}
|
||||
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -141,12 +141,16 @@ void SenseAirComponent::abc_get_period() {
|
||||
}
|
||||
|
||||
bool SenseAirComponent::senseair_write_command_(const uint8_t *command, uint8_t *response, uint8_t response_length) {
|
||||
// Verify we have somewhere to store the response
|
||||
if (response == nullptr) {
|
||||
return false;
|
||||
}
|
||||
// Write wake up byte required by some S8 sensor models
|
||||
this->write_byte(0);
|
||||
this->flush();
|
||||
delay(5);
|
||||
this->write_array(command, SENSEAIR_REQUEST_LENGTH);
|
||||
|
||||
if (response == nullptr)
|
||||
return true;
|
||||
|
||||
bool ret = this->read_array(response, response_length);
|
||||
this->flush();
|
||||
return ret;
|
||||
|
||||
@@ -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,38 @@ 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,
|
||||
}
|
||||
|
||||
AFTER_DEFAULTS = {CONF_BYTES: 256, CONF_TIMEOUT: "100ms"}
|
||||
|
||||
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, default=AFTER_DEFAULTS): cv.Schema(
|
||||
{
|
||||
cv.Optional(
|
||||
CONF_BYTES, default=AFTER_DEFAULTS[CONF_BYTES]
|
||||
): cv.validate_bytes,
|
||||
cv.Optional(
|
||||
CONF_TIMEOUT, default=AFTER_DEFAULTS[CONF_TIMEOUT]
|
||||
): 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 +135,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 +185,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")
|
||||
|
||||
@@ -57,38 +57,46 @@ void IRAM_ATTR ZaSensorStore::interrupt(ZaSensorStore *arg) {
|
||||
void IRAM_ATTR ZaSensorStore::set_data_(ZaMessage *message) {
|
||||
switch (message->type) {
|
||||
case HUMIDITY:
|
||||
this->humidity = (message->value > 10000) ? NAN : (message->value / 100.0f);
|
||||
this->humidity = message->value;
|
||||
break;
|
||||
|
||||
case TEMPERATURE:
|
||||
this->temperature = (message->value > 5970) ? NAN : (message->value / 16.0f - 273.15f);
|
||||
this->temperature = message->value;
|
||||
break;
|
||||
|
||||
case CO2:
|
||||
this->co2 = (message->value > 10000) ? NAN : message->value;
|
||||
break;
|
||||
|
||||
default:
|
||||
this->co2 = message->value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool ZyAuraSensor::publish_state_(sensor::Sensor *sensor, float *value) {
|
||||
// Sensor doesn't added to configuration
|
||||
bool ZyAuraSensor::publish_state_(ZaDataType data_type, sensor::Sensor *sensor, uint16_t *data_value) {
|
||||
// Sensor wasn't added to configuration
|
||||
if (sensor == nullptr) {
|
||||
return true;
|
||||
}
|
||||
|
||||
sensor->publish_state(*value);
|
||||
float value = NAN;
|
||||
switch (data_type) {
|
||||
case HUMIDITY:
|
||||
value = (*data_value > 10000) ? NAN : (*data_value / 100.0f);
|
||||
break;
|
||||
case TEMPERATURE:
|
||||
value = (*data_value > 5970) ? NAN : (*data_value / 16.0f - 273.15f);
|
||||
break;
|
||||
case CO2:
|
||||
value = (*data_value > 10000) ? NAN : *data_value;
|
||||
break;
|
||||
}
|
||||
|
||||
sensor->publish_state(value);
|
||||
|
||||
// Sensor reported wrong value
|
||||
if (std::isnan(*value)) {
|
||||
if (std::isnan(value)) {
|
||||
ESP_LOGW(TAG, "Sensor reported invalid data. Is the update interval too small?");
|
||||
this->status_set_warning();
|
||||
return false;
|
||||
}
|
||||
|
||||
*value = NAN;
|
||||
*data_value = -1;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -104,9 +112,9 @@ void ZyAuraSensor::dump_config() {
|
||||
}
|
||||
|
||||
void ZyAuraSensor::update() {
|
||||
bool co2_result = this->publish_state_(this->co2_sensor_, &this->store_.co2);
|
||||
bool temperature_result = this->publish_state_(this->temperature_sensor_, &this->store_.temperature);
|
||||
bool humidity_result = this->publish_state_(this->humidity_sensor_, &this->store_.humidity);
|
||||
bool co2_result = this->publish_state_(CO2, this->co2_sensor_, &this->store_.co2);
|
||||
bool temperature_result = this->publish_state_(TEMPERATURE, this->temperature_sensor_, &this->store_.temperature);
|
||||
bool humidity_result = this->publish_state_(HUMIDITY, this->humidity_sensor_, &this->store_.humidity);
|
||||
|
||||
if (co2_result && temperature_result && humidity_result) {
|
||||
this->status_clear_warning();
|
||||
|
||||
@@ -42,9 +42,9 @@ class ZaDataProcessor {
|
||||
|
||||
class ZaSensorStore {
|
||||
public:
|
||||
float co2 = NAN;
|
||||
float temperature = NAN;
|
||||
float humidity = NAN;
|
||||
uint16_t co2 = -1;
|
||||
uint16_t temperature = -1;
|
||||
uint16_t humidity = -1;
|
||||
|
||||
void setup(InternalGPIOPin *pin_clock, InternalGPIOPin *pin_data);
|
||||
static void interrupt(ZaSensorStore *arg);
|
||||
@@ -79,7 +79,7 @@ class ZyAuraSensor : public PollingComponent {
|
||||
sensor::Sensor *temperature_sensor_{nullptr};
|
||||
sensor::Sensor *humidity_sensor_{nullptr};
|
||||
|
||||
bool publish_state_(sensor::Sensor *sensor, float *value);
|
||||
bool publish_state_(ZaDataType data_type, sensor::Sensor *sensor, uint16_t *data_value);
|
||||
};
|
||||
|
||||
} // namespace zyaura
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""Constants used by esphome."""
|
||||
|
||||
__version__ = "2021.11.0-dev"
|
||||
__version__ = "2021.11.3"
|
||||
|
||||
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,9 +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_TAMPER = "tamper"
|
||||
DEVICE_CLASS_UPDATE = "update"
|
||||
DEVICE_CLASS_VIBRATION = "vibration"
|
||||
DEVICE_CLASS_WINDOW = "window"
|
||||
|
||||
@@ -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(); }
|
||||
|
||||
@@ -362,45 +362,47 @@ std::string str_sanitize(const std::string &str);
|
||||
/// @name Parsing & formatting
|
||||
///@{
|
||||
|
||||
/// Parse a unsigned decimal number.
|
||||
/// Parse an unsigned decimal number (requires null-terminated string).
|
||||
template<typename T, enable_if_t<(std::is_integral<T>::value && std::is_unsigned<T>::value), int> = 0>
|
||||
optional<T> parse_number(const char *str, size_t len) {
|
||||
char *end = nullptr;
|
||||
unsigned long value = ::strtoul(str, &end, 10); // NOLINT(google-runtime-int)
|
||||
if (end == nullptr || end != str + len || value > std::numeric_limits<T>::max())
|
||||
if (end == str || *end != '\0' || value > std::numeric_limits<T>::max())
|
||||
return {};
|
||||
return value;
|
||||
}
|
||||
/// Parse an unsigned decimal number.
|
||||
template<typename T, enable_if_t<(std::is_integral<T>::value && std::is_unsigned<T>::value), int> = 0>
|
||||
optional<T> parse_number(const std::string &str) {
|
||||
return parse_number<T>(str.c_str(), str.length());
|
||||
return parse_number<T>(str.c_str(), str.length() + 1);
|
||||
}
|
||||
/// Parse a signed decimal number.
|
||||
/// Parse a signed decimal number (requires null-terminated string).
|
||||
template<typename T, enable_if_t<(std::is_integral<T>::value && std::is_signed<T>::value), int> = 0>
|
||||
optional<T> parse_number(const char *str, size_t len) {
|
||||
char *end = nullptr;
|
||||
signed long value = ::strtol(str, &end, 10); // NOLINT(google-runtime-int)
|
||||
if (end == nullptr || end != str + len || value < std::numeric_limits<T>::min() ||
|
||||
value > std::numeric_limits<T>::max())
|
||||
if (end == str || *end != '\0' || value < std::numeric_limits<T>::min() || value > std::numeric_limits<T>::max())
|
||||
return {};
|
||||
return value;
|
||||
}
|
||||
/// Parse a signed decimal number.
|
||||
template<typename T, enable_if_t<(std::is_integral<T>::value && std::is_signed<T>::value), int> = 0>
|
||||
optional<T> parse_number(const std::string &str) {
|
||||
return parse_number<T>(str.c_str(), str.length());
|
||||
return parse_number<T>(str.c_str(), str.length() + 1);
|
||||
}
|
||||
/// Parse a decimal floating-point number.
|
||||
/// Parse a decimal floating-point number (requires null-terminated string).
|
||||
template<typename T, enable_if_t<(std::is_same<T, float>::value), int> = 0>
|
||||
optional<T> parse_number(const char *str, size_t len) {
|
||||
char *end = nullptr;
|
||||
float value = ::strtof(str, &end);
|
||||
if (end == nullptr || end != str + len || value == HUGE_VALF)
|
||||
if (end == str || *end != '\0' || value == HUGE_VALF)
|
||||
return {};
|
||||
return value;
|
||||
}
|
||||
/// Parse a decimal floating-point number.
|
||||
template<typename T, enable_if_t<(std::is_same<T, float>::value), int> = 0>
|
||||
optional<T> parse_number(const std::string &str) {
|
||||
return parse_number<T>(str.c_str(), str.length());
|
||||
return parse_number<T>(str.c_str(), str.length() + 1);
|
||||
}
|
||||
|
||||
///@}
|
||||
|
||||
@@ -13,8 +13,9 @@ from zeroconf import (
|
||||
RecordUpdateListener,
|
||||
Zeroconf,
|
||||
ServiceBrowser,
|
||||
ServiceStateChange,
|
||||
current_time_millis,
|
||||
)
|
||||
from zeroconf._services import ServiceStateChange
|
||||
|
||||
_CLASS_IN = 1
|
||||
_FLAGS_QR_QUERY = 0x0000 # query
|
||||
@@ -88,7 +89,7 @@ class DashboardStatus(threading.Thread):
|
||||
entries = self.zc.cache.entries_with_name(key)
|
||||
if not entries:
|
||||
return False
|
||||
now = time.time() * 1000
|
||||
now = current_time_millis()
|
||||
|
||||
return any(
|
||||
(entry.created + DashboardStatus.OFFLINE_AFTER) >= now for entry in entries
|
||||
@@ -99,7 +100,7 @@ class DashboardStatus(threading.Thread):
|
||||
self.on_update(
|
||||
{key: self.host_status(host) for key, host in self.key_to_host.items()}
|
||||
)
|
||||
now = time.time() * 1000
|
||||
now = current_time_millis()
|
||||
for host in self.query_hosts:
|
||||
entries = self.zc.cache.entries_with_name(host)
|
||||
if not entries or all(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -11,6 +11,7 @@ esptool==3.2
|
||||
click==8.0.3
|
||||
esphome-dashboard==20211021.1
|
||||
aioesphomeapi==10.2.0
|
||||
zeroconf==0.36.13
|
||||
|
||||
# esp-idf requires this, but doesn't bundle it by default
|
||||
# https://github.com/espressif/esp-idf/blob/220590d599e134d7a5e7f1e683cc4550349ffbf8/requirements.txt#L24
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -39,6 +39,12 @@ uart:
|
||||
tx_pin: GPIO22
|
||||
rx_pin: GPIO23
|
||||
baud_rate: 115200
|
||||
# Specifically added for testing debug with no after: definition.
|
||||
debug:
|
||||
dummy_receiver: false
|
||||
direction: rx
|
||||
sequence:
|
||||
- lambda: UARTDebug::log_hex(direction, bytes, ':');
|
||||
|
||||
ota:
|
||||
safe_mode: True
|
||||
|
||||
Reference in New Issue
Block a user