mirror of
https://github.com/esphome/esphome.git
synced 2025-11-02 16:11:53 +00:00
Compare commits
133 Commits
improv_ser
...
2021.11.0b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
@@ -72,6 +72,7 @@ esphome/components/hitachi_ac424/* @sourabhjaiswal
|
||||
esphome/components/homeassistant/* @OttoWinter
|
||||
esphome/components/hrxl_maxsonar_wr/* @netmikey
|
||||
esphome/components/i2c/* @esphome/core
|
||||
esphome/components/improv/* @jesserockz
|
||||
esphome/components/improv_serial/* @esphome/core
|
||||
esphome/components/inkbird_ibsth1_mini/* @fkirill
|
||||
esphome/components/inkplate6/* @jesserockz
|
||||
|
||||
@@ -147,9 +147,9 @@ RUN \
|
||||
/var/{cache,log}/* \
|
||||
/var/lib/apt/lists/*
|
||||
|
||||
COPY requirements.txt requirements_optional.txt requirements_test.txt docker/platformio_install_deps.py platformio.ini /
|
||||
COPY requirements.txt requirements_optional.txt docker/platformio_install_deps.py platformio.ini /
|
||||
RUN \
|
||||
pip3 install --no-cache-dir -r /requirements.txt -r /requirements_optional.txt -r /requirements_test.txt \
|
||||
pip3 install --no-cache-dir -r /requirements.txt -r /requirements_optional.txt \
|
||||
&& /platformio_install_deps.py /platformio.ini
|
||||
|
||||
VOLUME ["/esphome"]
|
||||
|
||||
@@ -18,7 +18,6 @@ from esphome.const import (
|
||||
CONF_PORT,
|
||||
CONF_ESPHOME,
|
||||
CONF_PLATFORMIO_OPTIONS,
|
||||
SECRETS_FILES,
|
||||
)
|
||||
from esphome.core import CORE, EsphomeError, coroutine
|
||||
from esphome.helpers import indent
|
||||
@@ -201,7 +200,8 @@ def upload_using_esptool(config, port):
|
||||
firmware_offset = "0x10000" if CORE.is_esp32 else "0x0"
|
||||
flash_images = [
|
||||
platformio_api.FlashImage(
|
||||
path=idedata.firmware_bin_path, offset=firmware_offset
|
||||
path=idedata.firmware_bin_path,
|
||||
offset=firmware_offset,
|
||||
),
|
||||
*idedata.extra_flash_images,
|
||||
]
|
||||
@@ -607,7 +607,10 @@ def parse_args(argv):
|
||||
"wizard",
|
||||
help="A helpful setup wizard that will guide you through setting up ESPHome.",
|
||||
)
|
||||
parser_wizard.add_argument("configuration", help="Your YAML configuration file.")
|
||||
parser_wizard.add_argument(
|
||||
"configuration",
|
||||
help="Your YAML configuration file.",
|
||||
)
|
||||
|
||||
parser_fingerprint = subparsers.add_parser(
|
||||
"mqtt-fingerprint", help="Get the SSL fingerprint from a MQTT broker."
|
||||
@@ -629,7 +632,8 @@ def parse_args(argv):
|
||||
"dashboard", help="Create a simple web server for a dashboard."
|
||||
)
|
||||
parser_dashboard.add_argument(
|
||||
"configuration", help="Your YAML configuration file directory."
|
||||
"configuration",
|
||||
help="Your YAML configuration file directory.",
|
||||
)
|
||||
parser_dashboard.add_argument(
|
||||
"--port",
|
||||
@@ -637,12 +641,6 @@ def parse_args(argv):
|
||||
type=int,
|
||||
default=6052,
|
||||
)
|
||||
parser_dashboard.add_argument(
|
||||
"--address",
|
||||
help="The address to bind to.",
|
||||
type=str,
|
||||
default="0.0.0.0",
|
||||
)
|
||||
parser_dashboard.add_argument(
|
||||
"--username",
|
||||
help="The optional username to require for authentication.",
|
||||
@@ -791,10 +789,6 @@ def run_esphome(argv):
|
||||
return 1
|
||||
|
||||
for conf_path in args.configuration:
|
||||
if any(os.path.basename(conf_path) == x for x in SECRETS_FILES):
|
||||
_LOGGER.warning("Skipping secrets file %s", conf_path)
|
||||
continue
|
||||
|
||||
CORE.config_path = conf_path
|
||||
CORE.dashboard = args.dashboard
|
||||
|
||||
|
||||
@@ -73,6 +73,13 @@ void AHT10Component::update() {
|
||||
bool success = false;
|
||||
for (int i = 0; i < AHT10_ATTEMPTS; ++i) {
|
||||
ESP_LOGVV(TAG, "Attempt %d at %6u", i, millis());
|
||||
delayMicroseconds(4);
|
||||
|
||||
uint8_t reg = 0;
|
||||
if (this->write(®, 1) != i2c::ERROR_OK) {
|
||||
ESP_LOGD(TAG, "Communication with AHT10 failed, waiting...");
|
||||
continue;
|
||||
}
|
||||
delay(delay_ms);
|
||||
if (this->read(data, 6) != i2c::ERROR_OK) {
|
||||
ESP_LOGD(TAG, "Communication with AHT10 failed, waiting...");
|
||||
|
||||
@@ -44,9 +44,8 @@ async def to_code(config):
|
||||
width, height = image.size
|
||||
frames = image.n_frames
|
||||
if CONF_RESIZE in config:
|
||||
new_width_max, new_height_max = config[CONF_RESIZE]
|
||||
ratio = min(new_width_max / width, new_height_max / height)
|
||||
width, height = int(width * ratio), int(height * ratio)
|
||||
image.thumbnail(config[CONF_RESIZE])
|
||||
width, height = image.size
|
||||
else:
|
||||
if width > 500 or height > 500:
|
||||
_LOGGER.warning(
|
||||
@@ -60,13 +59,7 @@ async def to_code(config):
|
||||
for frameIndex in range(frames):
|
||||
image.seek(frameIndex)
|
||||
frame = image.convert("L", dither=Image.NONE)
|
||||
if CONF_RESIZE in config:
|
||||
frame = frame.resize([width, height])
|
||||
pixels = list(frame.getdata())
|
||||
if len(pixels) != height * width:
|
||||
raise core.EsphomeError(
|
||||
f"Unexpected number of pixels in {path} frame {frameIndex}: ({len(pixels)} != {height*width})"
|
||||
)
|
||||
for pix in pixels:
|
||||
data[pos] = pix
|
||||
pos += 1
|
||||
@@ -77,13 +70,7 @@ async def to_code(config):
|
||||
for frameIndex in range(frames):
|
||||
image.seek(frameIndex)
|
||||
frame = image.convert("RGB")
|
||||
if CONF_RESIZE in config:
|
||||
frame = frame.resize([width, height])
|
||||
pixels = list(frame.getdata())
|
||||
if len(pixels) != height * width:
|
||||
raise core.EsphomeError(
|
||||
f"Unexpected number of pixels in {path} frame {frameIndex}: ({len(pixels)} != {height*width})"
|
||||
)
|
||||
for pix in pixels:
|
||||
data[pos] = pix[0]
|
||||
pos += 1
|
||||
@@ -98,8 +85,6 @@ async def to_code(config):
|
||||
for frameIndex in range(frames):
|
||||
image.seek(frameIndex)
|
||||
frame = image.convert("1", dither=Image.NONE)
|
||||
if CONF_RESIZE in config:
|
||||
frame = frame.resize([width, height])
|
||||
for y in range(height):
|
||||
for x in range(width):
|
||||
if frame.getpixel((x, y)):
|
||||
|
||||
@@ -49,7 +49,6 @@ from esphome.const import (
|
||||
DEVICE_CLASS_SMOKE,
|
||||
DEVICE_CLASS_SOUND,
|
||||
DEVICE_CLASS_TAMPER,
|
||||
DEVICE_CLASS_UPDATE,
|
||||
DEVICE_CLASS_VIBRATION,
|
||||
DEVICE_CLASS_WINDOW,
|
||||
)
|
||||
@@ -83,7 +82,6 @@ DEVICE_CLASSES = [
|
||||
DEVICE_CLASS_SMOKE,
|
||||
DEVICE_CLASS_SOUND,
|
||||
DEVICE_CLASS_TAMPER,
|
||||
DEVICE_CLASS_UPDATE,
|
||||
DEVICE_CLASS_VIBRATION,
|
||||
DEVICE_CLASS_WINDOW,
|
||||
]
|
||||
|
||||
@@ -388,15 +388,6 @@ BLEDescriptor *BLECharacteristic::get_descriptor(uint16_t uuid) {
|
||||
return this->get_descriptor(espbt::ESPBTUUID::from_uint16(uuid));
|
||||
}
|
||||
|
||||
void BLECharacteristic::write_value(uint8_t *new_val, int16_t new_val_size) {
|
||||
auto client = this->service->client;
|
||||
auto status = esp_ble_gattc_write_char(client->gattc_if, client->conn_id, this->handle, new_val_size, new_val,
|
||||
ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
|
||||
if (status) {
|
||||
ESP_LOGW(TAG, "Error sending write value to BLE gattc server, status=%d", status);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ble_client
|
||||
} // namespace esphome
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ class BLECharacteristic {
|
||||
void parse_descriptors();
|
||||
BLEDescriptor *get_descriptor(espbt::ESPBTUUID uuid);
|
||||
BLEDescriptor *get_descriptor(uint16_t uuid);
|
||||
void write_value(uint8_t *new_val, int16_t new_val_size);
|
||||
|
||||
BLEService *service;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import output, ble_client, esp32_ble_tracker
|
||||
from esphome.const import CONF_ID, CONF_SERVICE_UUID
|
||||
from .. import ble_client_ns
|
||||
|
||||
|
||||
DEPENDENCIES = ["ble_client"]
|
||||
|
||||
CONF_CHARACTERISTIC_UUID = "characteristic_uuid"
|
||||
|
||||
BLEBinaryOutput = ble_client_ns.class_(
|
||||
"BLEBinaryOutput", output.BinaryOutput, ble_client.BLEClientNode, cg.Component
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
output.BINARY_OUTPUT_SCHEMA.extend(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.declare_id(BLEBinaryOutput),
|
||||
cv.Required(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid,
|
||||
cv.Required(CONF_CHARACTERISTIC_UUID): esp32_ble_tracker.bt_uuid,
|
||||
}
|
||||
)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
.extend(ble_client.BLE_CLIENT_SCHEMA)
|
||||
)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
if len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid16_format):
|
||||
cg.add(
|
||||
var.set_service_uuid16(esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID]))
|
||||
)
|
||||
elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid32_format):
|
||||
cg.add(
|
||||
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_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):
|
||||
cg.add(
|
||||
var.set_char_uuid16(
|
||||
esp32_ble_tracker.as_hex(config[CONF_CHARACTERISTIC_UUID])
|
||||
)
|
||||
)
|
||||
elif len(config[CONF_CHARACTERISTIC_UUID]) == len(
|
||||
esp32_ble_tracker.bt_uuid32_format
|
||||
):
|
||||
cg.add(
|
||||
var.set_char_uuid32(
|
||||
esp32_ble_tracker.as_hex(config[CONF_CHARACTERISTIC_UUID])
|
||||
)
|
||||
)
|
||||
elif len(config[CONF_CHARACTERISTIC_UUID]) == len(
|
||||
esp32_ble_tracker.bt_uuid128_format
|
||||
):
|
||||
uuid128 = esp32_ble_tracker.as_reversed_hex_array(
|
||||
config[CONF_CHARACTERISTIC_UUID]
|
||||
)
|
||||
cg.add(var.set_char_uuid128(uuid128))
|
||||
|
||||
yield output.register_output(var, config)
|
||||
yield ble_client.register_ble_node(var, config)
|
||||
yield cg.register_component(var, config)
|
||||
@@ -1,71 +0,0 @@
|
||||
#include "ble_binary_output.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||
|
||||
#ifdef USE_ESP32
|
||||
namespace esphome {
|
||||
namespace ble_client {
|
||||
|
||||
static const char *const TAG = "ble_binary_output";
|
||||
|
||||
void BLEBinaryOutput::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "BLE Binary Output:");
|
||||
ESP_LOGCONFIG(TAG, " MAC address : %s", this->parent_->address_str().c_str());
|
||||
ESP_LOGCONFIG(TAG, " Service UUID : %s", this->service_uuid_.to_string().c_str());
|
||||
ESP_LOGCONFIG(TAG, " Characteristic UUID: %s", this->char_uuid_.to_string().c_str());
|
||||
LOG_BINARY_OUTPUT(this);
|
||||
}
|
||||
|
||||
void BLEBinaryOutput::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||
esp_ble_gattc_cb_param_t *param) {
|
||||
switch (event) {
|
||||
case ESP_GATTC_OPEN_EVT:
|
||||
this->client_state_ = espbt::ClientState::ESTABLISHED;
|
||||
ESP_LOGW(TAG, "[%s] Connected successfully!", this->char_uuid_.to_string().c_str());
|
||||
break;
|
||||
case ESP_GATTC_DISCONNECT_EVT:
|
||||
ESP_LOGW(TAG, "[%s] Disconnected", this->char_uuid_.to_string().c_str());
|
||||
this->client_state_ = espbt::ClientState::IDLE;
|
||||
break;
|
||||
case ESP_GATTC_WRITE_CHAR_EVT: {
|
||||
if (param->write.status == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
auto chr = this->parent()->get_characteristic(this->service_uuid_, this->char_uuid_);
|
||||
if (chr == nullptr) {
|
||||
ESP_LOGW(TAG, "[%s] Characteristic not found.", this->char_uuid_.to_string().c_str());
|
||||
break;
|
||||
}
|
||||
if (param->write.handle == chr->handle) {
|
||||
ESP_LOGW(TAG, "[%s] Write error, status=%d", this->char_uuid_.to_string().c_str(), param->write.status);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void BLEBinaryOutput::write_state(bool state) {
|
||||
if (this->client_state_ != espbt::ClientState::ESTABLISHED) {
|
||||
ESP_LOGW(TAG, "[%s] Not connected to BLE client. State update can not be written.",
|
||||
this->char_uuid_.to_string().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
auto chr = this->parent()->get_characteristic(this->service_uuid_, this->char_uuid_);
|
||||
if (chr == nullptr) {
|
||||
ESP_LOGW(TAG, "[%s] Characteristic not found. State update can not be written.",
|
||||
this->char_uuid_.to_string().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t state_as_uint = (uint8_t) state;
|
||||
ESP_LOGV(TAG, "[%s] Write State: %d", this->char_uuid_.to_string().c_str(), state_as_uint);
|
||||
chr->write_value(&state_as_uint, sizeof(state_as_uint));
|
||||
}
|
||||
|
||||
} // namespace ble_client
|
||||
} // namespace esphome
|
||||
#endif
|
||||
@@ -1,39 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/ble_client/ble_client.h"
|
||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||
#include "esphome/components/output/binary_output.h"
|
||||
|
||||
#ifdef USE_ESP32
|
||||
#include <esp_gattc_api.h>
|
||||
namespace esphome {
|
||||
namespace ble_client {
|
||||
|
||||
namespace espbt = esphome::esp32_ble_tracker;
|
||||
|
||||
class BLEBinaryOutput : public output::BinaryOutput, public BLEClientNode, public Component {
|
||||
public:
|
||||
void dump_config() override;
|
||||
void loop() override {}
|
||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||
void set_service_uuid16(uint16_t uuid) { this->service_uuid_ = espbt::ESPBTUUID::from_uint16(uuid); }
|
||||
void set_service_uuid32(uint32_t uuid) { this->service_uuid_ = espbt::ESPBTUUID::from_uint32(uuid); }
|
||||
void set_service_uuid128(uint8_t *uuid) { this->service_uuid_ = espbt::ESPBTUUID::from_raw(uuid); }
|
||||
void set_char_uuid16(uint16_t uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_uint16(uuid); }
|
||||
void set_char_uuid32(uint32_t uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_uint32(uuid); }
|
||||
void set_char_uuid128(uint8_t *uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_raw(uuid); }
|
||||
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||
esp_ble_gattc_cb_param_t *param) override;
|
||||
|
||||
protected:
|
||||
void write_state(bool state) override;
|
||||
espbt::ESPBTUUID service_uuid_;
|
||||
espbt::ESPBTUUID char_uuid_;
|
||||
espbt::ClientState client_state_;
|
||||
};
|
||||
|
||||
} // namespace ble_client
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
||||
@@ -20,7 +20,6 @@ from esphome.const import (
|
||||
CONF_MODE,
|
||||
CONF_MODE_COMMAND_TOPIC,
|
||||
CONF_MODE_STATE_TOPIC,
|
||||
CONF_ON_STATE,
|
||||
CONF_PRESET,
|
||||
CONF_SWING_MODE,
|
||||
CONF_SWING_MODE_COMMAND_TOPIC,
|
||||
@@ -35,7 +34,6 @@ from esphome.const import (
|
||||
CONF_TARGET_TEMPERATURE_LOW_COMMAND_TOPIC,
|
||||
CONF_TARGET_TEMPERATURE_LOW_STATE_TOPIC,
|
||||
CONF_TEMPERATURE_STEP,
|
||||
CONF_TRIGGER_ID,
|
||||
CONF_VISUAL,
|
||||
CONF_MQTT_ID,
|
||||
)
|
||||
@@ -103,7 +101,6 @@ validate_climate_swing_mode = cv.enum(CLIMATE_SWING_MODES, upper=True)
|
||||
|
||||
# Actions
|
||||
ControlAction = climate_ns.class_("ControlAction", automation.Action)
|
||||
StateTrigger = climate_ns.class_("StateTrigger", automation.Trigger.template())
|
||||
|
||||
CLIMATE_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
|
||||
{
|
||||
@@ -164,11 +161,6 @@ CLIMATE_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).
|
||||
cv.Optional(CONF_TARGET_TEMPERATURE_LOW_STATE_TOPIC): cv.All(
|
||||
cv.requires_component("mqtt"), cv.publish_topic
|
||||
),
|
||||
cv.Optional(CONF_ON_STATE): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StateTrigger),
|
||||
}
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -264,10 +256,6 @@ async def setup_climate_core_(var, config):
|
||||
)
|
||||
)
|
||||
|
||||
for conf in config.get(CONF_ON_STATE, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
|
||||
|
||||
async def register_climate(var, config):
|
||||
if not CORE.has_id(config[CONF_ID]):
|
||||
|
||||
@@ -42,12 +42,5 @@ template<typename... Ts> class ControlAction : public Action<Ts...> {
|
||||
Climate *climate_;
|
||||
};
|
||||
|
||||
class StateTrigger : public Trigger<> {
|
||||
public:
|
||||
StateTrigger(Climate *climate) {
|
||||
climate->add_on_state_callback([this]() { this->trigger(); });
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace climate
|
||||
} // namespace esphome
|
||||
|
||||
@@ -4,23 +4,20 @@
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/version.h"
|
||||
|
||||
#ifdef USE_ESP_IDF
|
||||
#include <esp_heap_caps.h>
|
||||
#include <esp_system.h>
|
||||
#endif
|
||||
|
||||
#ifdef USE_ESP32
|
||||
#if ESP_IDF_VERSION_MAJOR >= 4
|
||||
#include <esp32/rom/rtc.h>
|
||||
#else
|
||||
#include <rom/rtc.h>
|
||||
#endif
|
||||
#include <esp_idf_version.h>
|
||||
#endif
|
||||
|
||||
#ifdef USE_ARDUINO
|
||||
#include <Esp.h>
|
||||
#endif
|
||||
|
||||
#ifdef USE_ESP_IDF
|
||||
#include <esp_heap_caps.h>
|
||||
#include <esp_system.h>
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace debug {
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome import pins
|
||||
from esphome.components import uart
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
@@ -12,13 +11,10 @@ CODEOWNERS = ["@glmnet", "@zuidwijk"]
|
||||
DEPENDENCIES = ["uart"]
|
||||
AUTO_LOAD = ["sensor", "text_sensor"]
|
||||
|
||||
CONF_CRC_CHECK = "crc_check"
|
||||
CONF_DECRYPTION_KEY = "decryption_key"
|
||||
CONF_DSMR_ID = "dsmr_id"
|
||||
CONF_DECRYPTION_KEY = "decryption_key"
|
||||
CONF_CRC_CHECK = "crc_check"
|
||||
CONF_GAS_MBUS_ID = "gas_mbus_id"
|
||||
CONF_MAX_TELEGRAM_LENGTH = "max_telegram_length"
|
||||
CONF_REQUEST_INTERVAL = "request_interval"
|
||||
CONF_REQUEST_PIN = "request_pin"
|
||||
|
||||
# Hack to prevent compile error due to ambiguity with lib namespace
|
||||
dsmr_ns = cg.esphome_ns.namespace("esphome::dsmr")
|
||||
@@ -50,9 +46,6 @@ CONFIG_SCHEMA = cv.All(
|
||||
cv.Optional(CONF_DECRYPTION_KEY): _validate_key,
|
||||
cv.Optional(CONF_CRC_CHECK, default=True): cv.boolean,
|
||||
cv.Optional(CONF_GAS_MBUS_ID, default=1): cv.int_,
|
||||
cv.Optional(CONF_MAX_TELEGRAM_LENGTH, default=1500): cv.int_,
|
||||
cv.Optional(CONF_REQUEST_PIN): pins.gpio_output_pin_schema,
|
||||
cv.Optional(CONF_REQUEST_INTERVAL): cv.positive_time_period_milliseconds,
|
||||
}
|
||||
).extend(uart.UART_DEVICE_SCHEMA),
|
||||
cv.only_with_arduino,
|
||||
@@ -62,19 +55,10 @@ CONFIG_SCHEMA = cv.All(
|
||||
async def to_code(config):
|
||||
uart_component = await cg.get_variable(config[CONF_UART_ID])
|
||||
var = cg.new_Pvariable(config[CONF_ID], uart_component, config[CONF_CRC_CHECK])
|
||||
cg.add(var.set_max_telegram_length(config[CONF_MAX_TELEGRAM_LENGTH]))
|
||||
if CONF_DECRYPTION_KEY in config:
|
||||
cg.add(var.set_decryption_key(config[CONF_DECRYPTION_KEY]))
|
||||
await cg.register_component(var, config)
|
||||
|
||||
if CONF_REQUEST_PIN in config:
|
||||
request_pin = await cg.gpio_pin_expression(config[CONF_REQUEST_PIN])
|
||||
cg.add(var.set_request_pin(request_pin))
|
||||
if CONF_REQUEST_INTERVAL in config:
|
||||
cg.add(
|
||||
var.set_request_interval(config[CONF_REQUEST_INTERVAL].total_milliseconds)
|
||||
)
|
||||
|
||||
cg.add_define("DSMR_GAS_MBUS_ID", config[CONF_GAS_MBUS_ID])
|
||||
|
||||
# DSMR Parser
|
||||
|
||||
@@ -12,218 +12,146 @@ namespace dsmr {
|
||||
|
||||
static const char *const TAG = "dsmr";
|
||||
|
||||
void Dsmr::setup() {
|
||||
this->telegram_ = new char[this->max_telegram_len_]; // NOLINT
|
||||
if (this->request_pin_ != nullptr) {
|
||||
this->request_pin_->setup();
|
||||
}
|
||||
}
|
||||
|
||||
void Dsmr::loop() {
|
||||
if (this->ready_to_request_data_()) {
|
||||
if (this->decryption_key_.empty()) {
|
||||
this->receive_telegram_();
|
||||
} else {
|
||||
this->receive_encrypted_();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Dsmr::ready_to_request_data_() {
|
||||
// When using a request pin, then wait for the next request interval.
|
||||
if (this->request_pin_ != nullptr) {
|
||||
if (!this->requesting_data_ && this->request_interval_reached_()) {
|
||||
this->start_requesting_data_();
|
||||
}
|
||||
}
|
||||
// Otherwise, sink serial data until next request interval.
|
||||
else {
|
||||
if (this->request_interval_reached_()) {
|
||||
this->start_requesting_data_();
|
||||
}
|
||||
if (!this->requesting_data_) {
|
||||
while (this->available()) {
|
||||
this->read();
|
||||
}
|
||||
}
|
||||
}
|
||||
return this->requesting_data_;
|
||||
}
|
||||
|
||||
bool Dsmr::request_interval_reached_() {
|
||||
if (this->last_request_time_ == 0) {
|
||||
return true;
|
||||
}
|
||||
return millis() - this->last_request_time_ > this->request_interval_;
|
||||
if (this->decryption_key_.empty())
|
||||
this->receive_telegram_();
|
||||
else
|
||||
this->receive_encrypted_();
|
||||
}
|
||||
|
||||
bool Dsmr::available_within_timeout_() {
|
||||
uint8_t tries = READ_TIMEOUT_MS / 5;
|
||||
while (tries--) {
|
||||
delay(5);
|
||||
if (this->available()) {
|
||||
if (available()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Dsmr::start_requesting_data_() {
|
||||
if (!this->requesting_data_) {
|
||||
if (this->request_pin_ != nullptr) {
|
||||
ESP_LOGV(TAG, "Start requesting data from P1 port");
|
||||
this->request_pin_->digital_write(true);
|
||||
} else {
|
||||
ESP_LOGV(TAG, "Start reading data from P1 port");
|
||||
}
|
||||
this->requesting_data_ = true;
|
||||
this->last_request_time_ = millis();
|
||||
}
|
||||
}
|
||||
|
||||
void Dsmr::stop_requesting_data_() {
|
||||
if (this->requesting_data_) {
|
||||
if (this->request_pin_ != nullptr) {
|
||||
ESP_LOGV(TAG, "Stop requesting data from P1 port");
|
||||
this->request_pin_->digital_write(false);
|
||||
} else {
|
||||
ESP_LOGV(TAG, "Stop reading data from P1 port");
|
||||
}
|
||||
while (this->available()) {
|
||||
this->read();
|
||||
}
|
||||
this->requesting_data_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
void Dsmr::receive_telegram_() {
|
||||
while (true) {
|
||||
if (!this->available()) {
|
||||
if (!this->header_found_ || !this->available_within_timeout_()) {
|
||||
if (!available()) {
|
||||
if (!header_found_ || !available_within_timeout_()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const char c = this->read();
|
||||
const char c = read();
|
||||
|
||||
// Find a new telegram header, i.e. forward slash.
|
||||
if (c == '/') {
|
||||
ESP_LOGV(TAG, "Header of telegram found");
|
||||
this->header_found_ = true;
|
||||
this->footer_found_ = false;
|
||||
this->telegram_len_ = 0;
|
||||
header_found_ = true;
|
||||
footer_found_ = false;
|
||||
telegram_len_ = 0;
|
||||
}
|
||||
if (!this->header_found_)
|
||||
if (!header_found_)
|
||||
continue;
|
||||
|
||||
// Check for buffer overflow.
|
||||
if (this->telegram_len_ >= this->max_telegram_len_) {
|
||||
this->header_found_ = false;
|
||||
this->footer_found_ = false;
|
||||
ESP_LOGE(TAG, "Error: telegram larger than buffer (%d bytes)", this->max_telegram_len_);
|
||||
if (telegram_len_ >= MAX_TELEGRAM_LENGTH) {
|
||||
header_found_ = false;
|
||||
footer_found_ = false;
|
||||
ESP_LOGE(TAG, "Error: telegram larger than buffer (%d bytes)", MAX_TELEGRAM_LENGTH);
|
||||
return;
|
||||
}
|
||||
|
||||
// Some v2.2 or v3 meters will send a new value which starts with '('
|
||||
// in a new line, while the value belongs to the previous ObisId. For
|
||||
// proper parsing, remove these new line characters.
|
||||
if (c == '(') {
|
||||
while (true) {
|
||||
auto previous_char = this->telegram_[this->telegram_len_ - 1];
|
||||
if (previous_char == '\n' || previous_char == '\r') {
|
||||
this->telegram_len_--;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// in a new line while the value belongs to the previous ObisId. For
|
||||
// proper parsing remove these new line characters
|
||||
while (c == '(' && (telegram_[telegram_len_ - 1] == '\n' || telegram_[telegram_len_ - 1] == '\r'))
|
||||
telegram_len_--;
|
||||
|
||||
// Store the byte in the buffer.
|
||||
this->telegram_[this->telegram_len_] = c;
|
||||
this->telegram_len_++;
|
||||
telegram_[telegram_len_] = c;
|
||||
telegram_len_++;
|
||||
|
||||
// Check for a footer, i.e. exlamation mark, followed by a hex checksum.
|
||||
if (c == '!') {
|
||||
ESP_LOGV(TAG, "Footer of telegram found");
|
||||
this->footer_found_ = true;
|
||||
footer_found_ = true;
|
||||
continue;
|
||||
}
|
||||
// Check for the end of the hex checksum, i.e. a newline.
|
||||
if (this->footer_found_ && c == '\n') {
|
||||
if (footer_found_ && c == '\n') {
|
||||
// Parse the telegram and publish sensor values.
|
||||
this->parse_telegram();
|
||||
parse_telegram();
|
||||
|
||||
this->header_found_ = false;
|
||||
header_found_ = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Dsmr::receive_encrypted_() {
|
||||
this->encrypted_telegram_len_ = 0;
|
||||
// Encrypted buffer
|
||||
uint8_t buffer[MAX_TELEGRAM_LENGTH];
|
||||
size_t buffer_length = 0;
|
||||
size_t packet_size = 0;
|
||||
|
||||
while (true) {
|
||||
if (!this->available()) {
|
||||
if (!this->header_found_) {
|
||||
if (!available()) {
|
||||
if (!header_found_) {
|
||||
return;
|
||||
}
|
||||
if (!this->available_within_timeout_()) {
|
||||
if (!available_within_timeout_()) {
|
||||
ESP_LOGW(TAG, "Timeout while reading data for encrypted telegram");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const char c = this->read();
|
||||
const char c = read();
|
||||
|
||||
// Find a new telegram start byte.
|
||||
if (!this->header_found_) {
|
||||
if (!header_found_) {
|
||||
if ((uint8_t) c != 0xDB) {
|
||||
continue;
|
||||
}
|
||||
ESP_LOGV(TAG, "Start byte 0xDB of encrypted telegram found");
|
||||
this->header_found_ = true;
|
||||
header_found_ = true;
|
||||
}
|
||||
|
||||
// Check for buffer overflow.
|
||||
if (this->encrypted_telegram_len_ >= this->max_telegram_len_) {
|
||||
this->header_found_ = false;
|
||||
ESP_LOGE(TAG, "Error: encrypted telegram larger than buffer (%d bytes)", this->max_telegram_len_);
|
||||
if (buffer_length >= MAX_TELEGRAM_LENGTH) {
|
||||
header_found_ = false;
|
||||
ESP_LOGE(TAG, "Error: encrypted telegram larger than buffer (%d bytes)", MAX_TELEGRAM_LENGTH);
|
||||
return;
|
||||
}
|
||||
|
||||
this->encrypted_telegram_[this->encrypted_telegram_len_++] = c;
|
||||
buffer[buffer_length++] = c;
|
||||
|
||||
if (packet_size == 0 && this->encrypted_telegram_len_ > 20) {
|
||||
if (packet_size == 0 && buffer_length > 20) {
|
||||
// Complete header + data bytes
|
||||
packet_size = 13 + (this->encrypted_telegram_[11] << 8 | this->encrypted_telegram_[12]);
|
||||
packet_size = 13 + (buffer[11] << 8 | buffer[12]);
|
||||
ESP_LOGV(TAG, "Encrypted telegram size: %d bytes", packet_size);
|
||||
}
|
||||
if (this->encrypted_telegram_len_ == packet_size && packet_size > 0) {
|
||||
if (buffer_length == packet_size && packet_size > 0) {
|
||||
ESP_LOGV(TAG, "End of encrypted telegram found");
|
||||
GCM<AES128> *gcmaes128{new GCM<AES128>()};
|
||||
gcmaes128->setKey(this->decryption_key_.data(), gcmaes128->keySize());
|
||||
// the iv is 8 bytes of the system title + 4 bytes frame counter
|
||||
// system title is at byte 2 and frame counter at byte 15
|
||||
for (int i = 10; i < 14; i++)
|
||||
this->encrypted_telegram_[i] = this->encrypted_telegram_[i + 4];
|
||||
buffer[i] = buffer[i + 4];
|
||||
constexpr uint16_t iv_size{12};
|
||||
gcmaes128->setIV(&this->encrypted_telegram_[2], iv_size);
|
||||
gcmaes128->setIV(&buffer[2], iv_size);
|
||||
gcmaes128->decrypt(reinterpret_cast<uint8_t *>(this->telegram_),
|
||||
// the ciphertext start at byte 18
|
||||
&this->encrypted_telegram_[18],
|
||||
&buffer[18],
|
||||
// cipher size
|
||||
this->encrypted_telegram_len_ - 17);
|
||||
buffer_length - 17);
|
||||
delete gcmaes128; // NOLINT(cppcoreguidelines-owning-memory)
|
||||
|
||||
this->telegram_len_ = strnlen(this->telegram_, this->max_telegram_len_);
|
||||
ESP_LOGV(TAG, "Decrypted telegram size: %d bytes", this->telegram_len_);
|
||||
telegram_len_ = strnlen(this->telegram_, sizeof(this->telegram_));
|
||||
ESP_LOGV(TAG, "Decrypted telegram size: %d bytes", telegram_len_);
|
||||
ESP_LOGVV(TAG, "Decrypted telegram: %s", this->telegram_);
|
||||
|
||||
this->parse_telegram();
|
||||
parse_telegram();
|
||||
|
||||
this->header_found_ = false;
|
||||
this->telegram_len_ = 0;
|
||||
header_found_ = false;
|
||||
telegram_len_ = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -232,32 +160,23 @@ void Dsmr::receive_encrypted_() {
|
||||
bool Dsmr::parse_telegram() {
|
||||
MyData data;
|
||||
ESP_LOGV(TAG, "Trying to parse telegram");
|
||||
this->stop_requesting_data_();
|
||||
::dsmr::ParseResult<void> res =
|
||||
::dsmr::P1Parser::parse(&data, this->telegram_, this->telegram_len_, false,
|
||||
::dsmr::P1Parser::parse(&data, telegram_, telegram_len_, false,
|
||||
this->crc_check_); // Parse telegram according to data definition. Ignore unknown values.
|
||||
if (res.err) {
|
||||
// Parsing error, show it
|
||||
auto err_str = res.fullError(this->telegram_, this->telegram_ + this->telegram_len_);
|
||||
auto err_str = res.fullError(telegram_, telegram_ + telegram_len_);
|
||||
ESP_LOGE(TAG, "%s", err_str.c_str());
|
||||
return false;
|
||||
} else {
|
||||
this->status_clear_warning();
|
||||
this->publish_sensors(data);
|
||||
publish_sensors(data);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void Dsmr::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "DSMR:");
|
||||
ESP_LOGCONFIG(TAG, " Max telegram length: %d", this->max_telegram_len_);
|
||||
|
||||
if (this->request_pin_ != nullptr) {
|
||||
LOG_PIN(" Request Pin: ", this->request_pin_);
|
||||
}
|
||||
if (this->request_interval_ > 0) {
|
||||
ESP_LOGCONFIG(TAG, " Request Interval: %.1fs", this->request_interval_ / 1e3f);
|
||||
}
|
||||
|
||||
#define DSMR_LOG_SENSOR(s) LOG_SENSOR(" ", #s, this->s_##s##_);
|
||||
DSMR_SENSOR_LIST(DSMR_LOG_SENSOR, )
|
||||
@@ -270,10 +189,6 @@ void Dsmr::set_decryption_key(const std::string &decryption_key) {
|
||||
if (decryption_key.length() == 0) {
|
||||
ESP_LOGI(TAG, "Disabling decryption");
|
||||
this->decryption_key_.clear();
|
||||
if (this->encrypted_telegram_ != nullptr) {
|
||||
delete[] this->encrypted_telegram_;
|
||||
this->encrypted_telegram_ = nullptr;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -290,16 +205,10 @@ void Dsmr::set_decryption_key(const std::string &decryption_key) {
|
||||
char temp[3] = {0};
|
||||
for (int i = 0; i < 16; i++) {
|
||||
strncpy(temp, &(decryption_key.c_str()[i * 2]), 2);
|
||||
this->decryption_key_.push_back(std::strtoul(temp, nullptr, 16));
|
||||
}
|
||||
|
||||
if (this->encrypted_telegram_ == nullptr) {
|
||||
this->encrypted_telegram_ = new uint8_t[this->max_telegram_len_]; // NOLINT
|
||||
decryption_key_.push_back(std::strtoul(temp, nullptr, 16));
|
||||
}
|
||||
}
|
||||
|
||||
void Dsmr::set_max_telegram_length(size_t length) { max_telegram_len_ = length; }
|
||||
|
||||
} // namespace dsmr
|
||||
} // namespace esphome
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
namespace esphome {
|
||||
namespace dsmr {
|
||||
|
||||
static constexpr uint32_t MAX_TELEGRAM_LENGTH = 1500;
|
||||
static constexpr uint32_t READ_TIMEOUT_MS = 200;
|
||||
|
||||
using namespace ::dsmr::fields;
|
||||
@@ -51,7 +52,6 @@ class Dsmr : public Component, public uart::UARTDevice {
|
||||
public:
|
||||
Dsmr(uart::UARTComponent *uart, bool crc_check) : uart::UARTDevice(uart), crc_check_(crc_check) {}
|
||||
|
||||
void setup() override;
|
||||
void loop() override;
|
||||
|
||||
bool parse_telegram();
|
||||
@@ -72,11 +72,6 @@ class Dsmr : public Component, public uart::UARTDevice {
|
||||
|
||||
void set_decryption_key(const std::string &decryption_key);
|
||||
|
||||
void set_max_telegram_length(size_t length);
|
||||
|
||||
void set_request_pin(GPIOPin *request_pin) { this->request_pin_ = request_pin; }
|
||||
void set_request_interval(uint32_t interval) { this->request_interval_ = interval; }
|
||||
|
||||
// Sensor setters
|
||||
#define DSMR_SET_SENSOR(s) \
|
||||
void set_##s(sensor::Sensor *sensor) { s_##s##_ = sensor; }
|
||||
@@ -101,22 +96,9 @@ class Dsmr : public Component, public uart::UARTDevice {
|
||||
/// lost in the process.
|
||||
bool available_within_timeout_();
|
||||
|
||||
// Data request
|
||||
GPIOPin *request_pin_{nullptr};
|
||||
uint32_t request_interval_{0};
|
||||
uint32_t last_request_time_{0};
|
||||
bool requesting_data_{false};
|
||||
bool ready_to_request_data_();
|
||||
bool request_interval_reached_();
|
||||
void start_requesting_data_();
|
||||
void stop_requesting_data_();
|
||||
|
||||
// Telegram buffer
|
||||
size_t max_telegram_len_;
|
||||
char *telegram_{nullptr};
|
||||
char telegram_[MAX_TELEGRAM_LENGTH];
|
||||
int telegram_len_{0};
|
||||
uint8_t *encrypted_telegram_{nullptr};
|
||||
int encrypted_telegram_len_{0};
|
||||
|
||||
// Serial parser
|
||||
bool header_found_{false};
|
||||
|
||||
@@ -21,19 +21,12 @@ 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.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_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_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() {}
|
||||
|
||||
@@ -52,7 +45,6 @@ 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();
|
||||
@@ -180,6 +172,9 @@ 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);
|
||||
@@ -187,9 +182,6 @@ 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;
|
||||
@@ -201,7 +193,7 @@ esp_err_t CameraWebServer::streaming_handler_(struct httpd_req *req) {
|
||||
}
|
||||
|
||||
if (!frames) {
|
||||
res = httpd_send_all(req, STREAM_ERROR, strlen(STREAM_ERROR));
|
||||
res = httpd_send_all(req, STREAM_500, strlen(STREAM_500));
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "STREAM: closed. Frames: %u", frames);
|
||||
|
||||
@@ -4,7 +4,7 @@ from esphome.components import binary_sensor, output, esp32_ble_server
|
||||
from esphome.const import CONF_ID
|
||||
|
||||
|
||||
AUTO_LOAD = ["binary_sensor", "output", "esp32_ble_server"]
|
||||
AUTO_LOAD = ["binary_sensor", "output", "improv", "esp32_ble_server"]
|
||||
CODEOWNERS = ["@jesserockz"]
|
||||
CONFLICTS_WITH = ["esp32_ble_tracker", "esp32_ble_beacon"]
|
||||
DEPENDENCIES = ["wifi", "esp32"]
|
||||
@@ -56,7 +56,6 @@ async def to_code(config):
|
||||
cg.add(ble_server.register_service_component(var))
|
||||
|
||||
cg.add_define("USE_IMPROV")
|
||||
cg.add_library("esphome/Improv", "1.0.0")
|
||||
|
||||
cg.add(var.set_identify_duration(config[CONF_IDENTIFY_DURATION]))
|
||||
cg.add(var.set_authorized_duration(config[CONF_AUTHORIZED_DURATION]))
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||
#include "esphome/components/esp32_ble_server/ble_characteristic.h"
|
||||
#include "esphome/components/esp32_ble_server/ble_server.h"
|
||||
#include "esphome/components/esp32_ble_server/ble_characteristic.h"
|
||||
#include "esphome/components/improv/improv.h"
|
||||
#include "esphome/components/output/binary_output.h"
|
||||
#include "esphome/components/wifi/wifi_component.h"
|
||||
#include "esphome/core/component.h"
|
||||
@@ -11,8 +12,6 @@
|
||||
|
||||
#ifdef USE_ESP32
|
||||
|
||||
#include <improv.h>
|
||||
|
||||
namespace esphome {
|
||||
namespace esp32_improv {
|
||||
|
||||
|
||||
@@ -206,3 +206,61 @@ ESP8266_BOARD_PINS = {
|
||||
"wio_node": {"LED": 2, "GROVE": 15, "D0": 3, "D1": 5, "BUTTON": 0},
|
||||
"xinabox_cw01": {"SDA": 2, "SCL": 14, "LED": 5, "LED_RED": 12, "LED_GREEN": 13},
|
||||
}
|
||||
|
||||
FLASH_SIZE_1_MB = 2 ** 20
|
||||
FLASH_SIZE_512_KB = FLASH_SIZE_1_MB // 2
|
||||
FLASH_SIZE_2_MB = 2 * FLASH_SIZE_1_MB
|
||||
FLASH_SIZE_4_MB = 4 * FLASH_SIZE_1_MB
|
||||
FLASH_SIZE_16_MB = 16 * FLASH_SIZE_1_MB
|
||||
|
||||
ESP8266_FLASH_SIZES = {
|
||||
"d1": FLASH_SIZE_4_MB,
|
||||
"d1_mini": FLASH_SIZE_4_MB,
|
||||
"d1_mini_lite": FLASH_SIZE_1_MB,
|
||||
"d1_mini_pro": FLASH_SIZE_16_MB,
|
||||
"esp01": FLASH_SIZE_512_KB,
|
||||
"esp01_1m": FLASH_SIZE_1_MB,
|
||||
"esp07": FLASH_SIZE_4_MB,
|
||||
"esp12e": FLASH_SIZE_4_MB,
|
||||
"esp210": FLASH_SIZE_4_MB,
|
||||
"esp8285": FLASH_SIZE_1_MB,
|
||||
"esp_wroom_02": FLASH_SIZE_2_MB,
|
||||
"espduino": FLASH_SIZE_4_MB,
|
||||
"espectro": FLASH_SIZE_4_MB,
|
||||
"espino": FLASH_SIZE_4_MB,
|
||||
"espinotee": FLASH_SIZE_4_MB,
|
||||
"espmxdevkit": FLASH_SIZE_1_MB,
|
||||
"espresso_lite_v1": FLASH_SIZE_4_MB,
|
||||
"espresso_lite_v2": FLASH_SIZE_4_MB,
|
||||
"gen4iod": FLASH_SIZE_512_KB,
|
||||
"heltec_wifi_kit_8": FLASH_SIZE_4_MB,
|
||||
"huzzah": FLASH_SIZE_4_MB,
|
||||
"inventone": FLASH_SIZE_4_MB,
|
||||
"modwifi": FLASH_SIZE_2_MB,
|
||||
"nodemcu": FLASH_SIZE_4_MB,
|
||||
"nodemcuv2": FLASH_SIZE_4_MB,
|
||||
"oak": FLASH_SIZE_4_MB,
|
||||
"phoenix_v1": FLASH_SIZE_4_MB,
|
||||
"phoenix_v2": FLASH_SIZE_4_MB,
|
||||
"sonoff_basic": FLASH_SIZE_1_MB,
|
||||
"sonoff_s20": FLASH_SIZE_1_MB,
|
||||
"sonoff_sv": FLASH_SIZE_1_MB,
|
||||
"sonoff_th": FLASH_SIZE_1_MB,
|
||||
"sparkfunBlynk": FLASH_SIZE_4_MB,
|
||||
"thing": FLASH_SIZE_512_KB,
|
||||
"thingdev": FLASH_SIZE_512_KB,
|
||||
"wifi_slot": FLASH_SIZE_1_MB,
|
||||
"wifiduino": FLASH_SIZE_4_MB,
|
||||
"wifinfo": FLASH_SIZE_1_MB,
|
||||
"wio_link": FLASH_SIZE_4_MB,
|
||||
"wio_node": FLASH_SIZE_4_MB,
|
||||
"xinabox_cw01": FLASH_SIZE_4_MB,
|
||||
}
|
||||
|
||||
ESP8266_LD_SCRIPTS = {
|
||||
FLASH_SIZE_512_KB: ("eagle.flash.512k0.ld", "eagle.flash.512k.ld"),
|
||||
FLASH_SIZE_1_MB: ("eagle.flash.1m0.ld", "eagle.flash.1m.ld"),
|
||||
FLASH_SIZE_2_MB: ("eagle.flash.2m.ld", "eagle.flash.2m.ld"),
|
||||
FLASH_SIZE_4_MB: ("eagle.flash.4m.ld", "eagle.flash.4m.ld"),
|
||||
FLASH_SIZE_16_MB: ("eagle.flash.16m.ld", "eagle.flash.16m14m.ld"),
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ PROTOCOLS = {
|
||||
"gree": Protocol.PROTOCOL_GREE,
|
||||
"greeya": Protocol.PROTOCOL_GREEYAA,
|
||||
"greeyan": Protocol.PROTOCOL_GREEYAN,
|
||||
"greeyac": Protocol.PROTOCOL_GREEYAC,
|
||||
"hisense_aud": Protocol.PROTOCOL_HISENSE_AUD,
|
||||
"hitachi": Protocol.PROTOCOL_HITACHI,
|
||||
"hyundai": Protocol.PROTOCOL_HYUNDAI,
|
||||
@@ -112,6 +111,4 @@ def to_code(config):
|
||||
cg.add(var.set_max_temperature(config[CONF_MIN_TEMPERATURE]))
|
||||
cg.add(var.set_min_temperature(config[CONF_MAX_TEMPERATURE]))
|
||||
|
||||
# PIO isn't updating releases, so referencing the release tag directly. See:
|
||||
# https://github.com/ToniA/arduino-heatpumpir/commit/0948c619d86407a4e50e8db2f3c193e0576c86fd
|
||||
cg.add_library("", "", "https://github.com/ToniA/arduino-heatpumpir.git#1.0.18")
|
||||
cg.add_library("tonia/HeatpumpIR", "1.0.15")
|
||||
|
||||
@@ -25,7 +25,6 @@ const std::map<Protocol, std::function<HeatpumpIR *()>> PROTOCOL_CONSTRUCTOR_MAP
|
||||
{PROTOCOL_GREE, []() { return new GreeGenericHeatpumpIR(); }}, // NOLINT
|
||||
{PROTOCOL_GREEYAA, []() { return new GreeYAAHeatpumpIR(); }}, // NOLINT
|
||||
{PROTOCOL_GREEYAN, []() { return new GreeYANHeatpumpIR(); }}, // NOLINT
|
||||
{PROTOCOL_GREEYAC, []() { return new GreeYACHeatpumpIR(); }}, // NOLINT
|
||||
{PROTOCOL_HISENSE_AUD, []() { return new HisenseHeatpumpIR(); }}, // NOLINT
|
||||
{PROTOCOL_HITACHI, []() { return new HitachiHeatpumpIR(); }}, // NOLINT
|
||||
{PROTOCOL_HYUNDAI, []() { return new HyundaiHeatpumpIR(); }}, // NOLINT
|
||||
@@ -62,19 +61,6 @@ void HeatpumpIRClimate::setup() {
|
||||
}
|
||||
this->heatpump_ir_ = protocol_constructor->second();
|
||||
climate_ir::ClimateIR::setup();
|
||||
if (this->sensor_) {
|
||||
this->sensor_->add_on_state_callback([this](float state) {
|
||||
this->current_temperature = state;
|
||||
|
||||
IRSenderESPHome esp_sender(this->transmitter_);
|
||||
this->heatpump_ir_->send(esp_sender, uint8_t(lround(this->current_temperature + 0.5)));
|
||||
|
||||
// current temperature changed, publish state
|
||||
this->publish_state();
|
||||
});
|
||||
this->current_temperature = this->sensor_->state;
|
||||
} else
|
||||
this->current_temperature = NAN;
|
||||
}
|
||||
|
||||
void HeatpumpIRClimate::transmit_state() {
|
||||
@@ -185,7 +171,8 @@ void HeatpumpIRClimate::transmit_state() {
|
||||
|
||||
temperature_cmd = (uint8_t) clamp(this->target_temperature, this->min_temperature_, this->max_temperature_);
|
||||
|
||||
IRSenderESPHome esp_sender(this->transmitter_);
|
||||
IRSenderESPHome esp_sender(0, this->transmitter_);
|
||||
|
||||
heatpump_ir_->send(esp_sender, power_mode_cmd, operating_mode_cmd, fan_speed_cmd, temperature_cmd, swing_v_cmd,
|
||||
swing_h_cmd);
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ enum Protocol {
|
||||
PROTOCOL_GREE,
|
||||
PROTOCOL_GREEYAA,
|
||||
PROTOCOL_GREEYAN,
|
||||
PROTOCOL_GREEYAC,
|
||||
PROTOCOL_HISENSE_AUD,
|
||||
PROTOCOL_HITACHI,
|
||||
PROTOCOL_HYUNDAI,
|
||||
|
||||
@@ -11,8 +11,8 @@ namespace heatpumpir {
|
||||
|
||||
class IRSenderESPHome : public IRSender {
|
||||
public:
|
||||
IRSenderESPHome(remote_transmitter::RemoteTransmitterComponent *transmitter)
|
||||
: IRSender(0), transmit_(transmitter->transmit()){};
|
||||
IRSenderESPHome(uint8_t pin, remote_transmitter::RemoteTransmitterComponent *transmitter)
|
||||
: IRSender(pin), transmit_(transmitter->transmit()){};
|
||||
void setFrequency(int frequency) override; // NOLINT(readability-identifier-naming)
|
||||
void space(int space_length) override;
|
||||
void mark(int mark_length) override;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_ARDUINO
|
||||
#include <cstdint>
|
||||
|
||||
namespace esphome {
|
||||
@@ -12,3 +13,5 @@ class AbstractAQICalculator {
|
||||
|
||||
} // namespace hm3301
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_ARDUINO
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_ARDUINO
|
||||
|
||||
#include "abstract_aqi_calculator.h"
|
||||
|
||||
namespace esphome {
|
||||
@@ -31,7 +33,7 @@ class AQICalculator : public AbstractAQICalculator {
|
||||
int conc_lo = array[grid_index][0];
|
||||
int conc_hi = array[grid_index][1];
|
||||
|
||||
return (value - conc_lo) * (aqi_hi - aqi_lo) / (conc_hi - conc_lo) + aqi_lo;
|
||||
return ((aqi_hi - aqi_lo) / (conc_hi - conc_lo)) * (value - conc_lo) + aqi_lo;
|
||||
}
|
||||
|
||||
int get_grid_index_(uint16_t value, int array[AMOUNT_OF_LEVELS][2]) {
|
||||
@@ -46,3 +48,5 @@ class AQICalculator : public AbstractAQICalculator {
|
||||
|
||||
} // namespace hm3301
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_ARDUINO
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_ARDUINO
|
||||
|
||||
#include "caqi_calculator.h"
|
||||
#include "aqi_calculator.h"
|
||||
|
||||
@@ -27,3 +29,5 @@ class AQICalculatorFactory {
|
||||
|
||||
} // namespace hm3301
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_ARDUINO
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_ARDUINO
|
||||
|
||||
#include "esphome/core/log.h"
|
||||
#include "abstract_aqi_calculator.h"
|
||||
|
||||
@@ -35,7 +37,9 @@ class CAQICalculator : public AbstractAQICalculator {
|
||||
int conc_lo = array[grid_index][0];
|
||||
int conc_hi = array[grid_index][1];
|
||||
|
||||
return (value - conc_lo) * (aqi_hi - aqi_lo) / (conc_hi - conc_lo) + aqi_lo;
|
||||
int aqi = ((aqi_hi - aqi_lo) / (conc_hi - conc_lo)) * (value - conc_lo) + aqi_lo;
|
||||
|
||||
return aqi;
|
||||
}
|
||||
|
||||
int get_grid_index_(uint16_t value, int array[AMOUNT_OF_LEVELS][2]) {
|
||||
@@ -50,3 +54,5 @@ class CAQICalculator : public AbstractAQICalculator {
|
||||
|
||||
} // namespace hm3301
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_ARDUINO
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#ifdef USE_ARDUINO
|
||||
|
||||
#include "esphome/core/log.h"
|
||||
#include "hm3301.h"
|
||||
|
||||
@@ -12,8 +14,9 @@ static const uint8_t PM_10_0_VALUE_INDEX = 7;
|
||||
|
||||
void HM3301Component::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up HM3301...");
|
||||
if (i2c::ERROR_OK != this->write(&SELECT_COMM_CMD, 1)) {
|
||||
error_code_ = ERROR_COMM;
|
||||
hm3301_ = make_unique<HM330X>();
|
||||
error_code_ = hm3301_->init();
|
||||
if (error_code_ != NO_ERROR) {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
@@ -35,7 +38,7 @@ void HM3301Component::dump_config() {
|
||||
float HM3301Component::get_setup_priority() const { return setup_priority::DATA; }
|
||||
|
||||
void HM3301Component::update() {
|
||||
if (this->read(data_buffer_, 29) != i2c::ERROR_OK) {
|
||||
if (!this->read_sensor_value_(data_buffer_)) {
|
||||
ESP_LOGW(TAG, "Read result failed");
|
||||
this->status_set_warning();
|
||||
return;
|
||||
@@ -84,6 +87,8 @@ void HM3301Component::update() {
|
||||
this->status_clear_warning();
|
||||
}
|
||||
|
||||
bool HM3301Component::read_sensor_value_(uint8_t *data) { return !hm3301_->read_sensor_value(data, 29); }
|
||||
|
||||
bool HM3301Component::validate_checksum_(const uint8_t *data) {
|
||||
uint8_t sum = 0;
|
||||
for (int i = 0; i < 28; i++) {
|
||||
@@ -99,3 +104,5 @@ uint16_t HM3301Component::get_sensor_value_(const uint8_t *data, uint8_t i) {
|
||||
|
||||
} // namespace hm3301
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_ARDUINO
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_ARDUINO
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
#include "aqi_calculator_factory.h"
|
||||
|
||||
#include <Seeed_HM330X.h>
|
||||
|
||||
namespace esphome {
|
||||
namespace hm3301 {
|
||||
|
||||
static const uint8_t SELECT_COMM_CMD = 0X88;
|
||||
|
||||
class HM3301Component : public PollingComponent, public i2c::I2CDevice {
|
||||
public:
|
||||
HM3301Component() = default;
|
||||
@@ -27,12 +29,9 @@ class HM3301Component : public PollingComponent, public i2c::I2CDevice {
|
||||
void update() override;
|
||||
|
||||
protected:
|
||||
enum {
|
||||
NO_ERROR = 0,
|
||||
ERROR_PARAM = -1,
|
||||
ERROR_COMM = -2,
|
||||
ERROR_OTHERS = -128,
|
||||
} error_code_{NO_ERROR};
|
||||
std::unique_ptr<HM330X> hm3301_;
|
||||
|
||||
HM330XErrorCode error_code_{NO_ERROR};
|
||||
|
||||
uint8_t data_buffer_[30];
|
||||
|
||||
@@ -44,9 +43,12 @@ class HM3301Component : public PollingComponent, public i2c::I2CDevice {
|
||||
AQICalculatorType aqi_calc_type_;
|
||||
AQICalculatorFactory aqi_calculator_factory_ = AQICalculatorFactory();
|
||||
|
||||
bool read_sensor_value_(uint8_t *);
|
||||
bool validate_checksum_(const uint8_t *);
|
||||
uint16_t get_sensor_value_(const uint8_t *, uint8_t);
|
||||
};
|
||||
|
||||
} // namespace hm3301
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_ARDUINO
|
||||
|
||||
@@ -84,6 +84,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
.extend(cv.polling_component_schema("60s"))
|
||||
.extend(i2c.i2c_device_schema(0x40)),
|
||||
_validate,
|
||||
cv.only_with_arduino,
|
||||
)
|
||||
|
||||
|
||||
@@ -108,3 +109,6 @@ async def to_code(config):
|
||||
sens = await sensor.new_sensor(config[CONF_AQI])
|
||||
cg.add(var.set_aqi_sensor(sens))
|
||||
cg.add(var.set_aqi_calculation_type(config[CONF_AQI][CONF_CALCULATION_TYPE]))
|
||||
|
||||
# https://platformio.org/lib/show/6306/Grove%20-%20Laser%20PM2.5%20Sensor%20HM3301
|
||||
cg.add_library("seeed-studio/Grove - Laser PM2.5 Sensor HM3301", "1.0.3")
|
||||
|
||||
@@ -114,8 +114,8 @@ CONFIG_SCHEMA = (
|
||||
|
||||
|
||||
def auto_data_rate(config):
|
||||
interval_msec = config[CONF_UPDATE_INTERVAL].total_milliseconds
|
||||
interval_hz = 1000.0 / interval_msec
|
||||
interval_sec = config[CONF_UPDATE_INTERVAL].seconds
|
||||
interval_hz = 1.0 / interval_sec
|
||||
for datarate in sorted(HMC5883LDatarates.keys()):
|
||||
if float(datarate) >= interval_hz:
|
||||
return HMC5883LDatarates[datarate]
|
||||
|
||||
1
esphome/components/improv/__init__.py
Normal file
1
esphome/components/improv/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
CODEOWNERS = ["@jesserockz"]
|
||||
99
esphome/components/improv/improv.cpp
Normal file
99
esphome/components/improv/improv.cpp
Normal file
@@ -0,0 +1,99 @@
|
||||
#include "improv.h"
|
||||
|
||||
namespace improv {
|
||||
|
||||
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, bool check_checksum) {
|
||||
ImprovCommand improv_command;
|
||||
Command command = (Command) data[0];
|
||||
uint8_t data_length = data[1];
|
||||
|
||||
if (data_length != length - 2 - check_checksum) {
|
||||
improv_command.command = UNKNOWN;
|
||||
return improv_command;
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
if ((uint8_t) calculated_checksum != checksum) {
|
||||
improv_command.command = BAD_CHECKSUM;
|
||||
return improv_command;
|
||||
}
|
||||
}
|
||||
|
||||
if (command == WIFI_SETTINGS) {
|
||||
uint8_t ssid_length = data[2];
|
||||
uint8_t ssid_start = 3;
|
||||
size_t ssid_end = ssid_start + ssid_length;
|
||||
|
||||
uint8_t pass_length = data[ssid_end];
|
||||
size_t pass_start = ssid_end + 1;
|
||||
size_t pass_end = pass_start + pass_length;
|
||||
|
||||
std::string ssid(data + ssid_start, data + ssid_end);
|
||||
std::string password(data + pass_start, data + pass_end);
|
||||
return {.command = command, .ssid = ssid, .password = password};
|
||||
}
|
||||
|
||||
improv_command.command = command;
|
||||
return improv_command;
|
||||
}
|
||||
|
||||
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);
|
||||
for (auto str : datum) {
|
||||
uint8_t len = str.length();
|
||||
length += len;
|
||||
out.push_back(len);
|
||||
out.insert(out.end(), str.begin(), str.end());
|
||||
}
|
||||
out.insert(out.begin() + 1, length);
|
||||
|
||||
if (add_checksum) {
|
||||
uint32_t calculated_checksum = 0;
|
||||
|
||||
for (uint8_t byte : out) {
|
||||
calculated_checksum += byte;
|
||||
}
|
||||
out.push_back(calculated_checksum);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
#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);
|
||||
for (auto str : datum) {
|
||||
uint8_t len = str.length();
|
||||
length += len;
|
||||
out.push_back(len);
|
||||
out.insert(out.end(), str.begin(), str.end());
|
||||
}
|
||||
out.insert(out.begin() + 1, length);
|
||||
|
||||
if (add_checksum) {
|
||||
uint32_t calculated_checksum = 0;
|
||||
|
||||
for (uint8_t byte : out) {
|
||||
calculated_checksum += byte;
|
||||
}
|
||||
out.push_back(calculated_checksum);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
#endif // ARDUINO
|
||||
|
||||
} // namespace improv
|
||||
63
esphome/components/improv/improv.h
Normal file
63
esphome/components/improv/improv.h
Normal file
@@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef ARDUINO
|
||||
#include "WString.h"
|
||||
#endif // ARDUINO
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace improv {
|
||||
|
||||
static const char *const SERVICE_UUID = "00467768-6228-2272-4663-277478268000";
|
||||
static const char *const STATUS_UUID = "00467768-6228-2272-4663-277478268001";
|
||||
static const char *const ERROR_UUID = "00467768-6228-2272-4663-277478268002";
|
||||
static const char *const RPC_COMMAND_UUID = "00467768-6228-2272-4663-277478268003";
|
||||
static const char *const RPC_RESULT_UUID = "00467768-6228-2272-4663-277478268004";
|
||||
static const char *const CAPABILITIES_UUID = "00467768-6228-2272-4663-277478268005";
|
||||
|
||||
enum Error : uint8_t {
|
||||
ERROR_NONE = 0x00,
|
||||
ERROR_INVALID_RPC = 0x01,
|
||||
ERROR_UNKNOWN_RPC = 0x02,
|
||||
ERROR_UNABLE_TO_CONNECT = 0x03,
|
||||
ERROR_NOT_AUTHORIZED = 0x04,
|
||||
ERROR_UNKNOWN = 0xFF,
|
||||
};
|
||||
|
||||
enum State : uint8_t {
|
||||
STATE_STOPPED = 0x00,
|
||||
STATE_AWAITING_AUTHORIZATION = 0x01,
|
||||
STATE_AUTHORIZED = 0x02,
|
||||
STATE_PROVISIONING = 0x03,
|
||||
STATE_PROVISIONED = 0x04,
|
||||
};
|
||||
|
||||
enum Command : uint8_t {
|
||||
UNKNOWN = 0x00,
|
||||
WIFI_SETTINGS = 0x01,
|
||||
IDENTIFY = 0x02,
|
||||
GET_CURRENT_STATE = 0x02,
|
||||
GET_DEVICE_INFO = 0x03,
|
||||
BAD_CHECKSUM = 0xFF,
|
||||
};
|
||||
|
||||
static const uint8_t CAPABILITY_IDENTIFY = 0x01;
|
||||
|
||||
struct ImprovCommand {
|
||||
Command command;
|
||||
std::string ssid;
|
||||
std::string password;
|
||||
};
|
||||
|
||||
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,
|
||||
bool add_checksum = true);
|
||||
#ifdef ARDUINO
|
||||
std::vector<uint8_t> build_rpc_response(Command command, const std::vector<String> &datum, bool add_checksum = true);
|
||||
#endif // ARDUINO
|
||||
|
||||
} // namespace improv
|
||||
@@ -5,6 +5,7 @@ import esphome.final_validate as fv
|
||||
|
||||
CODEOWNERS = ["@esphome/core"]
|
||||
DEPENDENCIES = ["logger", "wifi"]
|
||||
AUTO_LOAD = ["improv"]
|
||||
|
||||
improv_serial_ns = cg.esphome_ns.namespace("improv_serial")
|
||||
|
||||
@@ -30,4 +31,3 @@ FINAL_VALIDATE_SCHEMA = validate_logger_baud_rate
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
cg.add_library("esphome/Improv", "1.0.0")
|
||||
|
||||
@@ -111,7 +111,7 @@ std::vector<uint8_t> ImprovSerialComponent::build_version_info_() {
|
||||
bool ImprovSerialComponent::parse_improv_serial_byte_(uint8_t byte) {
|
||||
size_t at = this->rx_buffer_.size();
|
||||
this->rx_buffer_.push_back(byte);
|
||||
ESP_LOGV(TAG, "Improv Serial byte: 0x%02X", byte);
|
||||
ESP_LOGD(TAG, "Improv Serial byte: 0x%02X", byte);
|
||||
const uint8_t *raw = &this->rx_buffer_[0];
|
||||
if (at == 0)
|
||||
return byte == 'I';
|
||||
@@ -176,7 +176,7 @@ bool ImprovSerialComponent::parse_improv_payload_(improv::ImprovCommand &command
|
||||
wifi::global_wifi_component->set_sta(sta);
|
||||
wifi::global_wifi_component->start_scanning();
|
||||
this->set_state_(improv::STATE_PROVISIONING);
|
||||
ESP_LOGI(TAG, "Received Improv wifi settings ssid=%s, password=" LOG_SECRET("%s"), command.ssid.c_str(),
|
||||
ESP_LOGD(TAG, "Received Improv wifi settings ssid=%s, password=" LOG_SECRET("%s"), command.ssid.c_str(),
|
||||
command.password.c_str());
|
||||
|
||||
auto f = std::bind(&ImprovSerialComponent::on_wifi_connect_timeout_, this);
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/improv/improv.h"
|
||||
#include "esphome/components/wifi/wifi_component.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
#include <improv.h>
|
||||
|
||||
#ifdef USE_ARDUINO
|
||||
#include <HardwareSerial.h>
|
||||
#endif
|
||||
|
||||
@@ -15,37 +15,17 @@ namespace ledc {
|
||||
|
||||
static const char *const TAG = "ledc.output";
|
||||
|
||||
#ifdef USE_ESP_IDF
|
||||
static const int MAX_RES_BITS = LEDC_TIMER_BIT_MAX - 1;
|
||||
#if SOC_LEDC_SUPPORT_HS_MODE
|
||||
// Only ESP32 has LEDC_HIGH_SPEED_MODE
|
||||
inline ledc_mode_t get_speed_mode(uint8_t channel) { return channel < 8 ? LEDC_HIGH_SPEED_MODE : LEDC_LOW_SPEED_MODE; }
|
||||
#else
|
||||
// S2, C3, S3 only support LEDC_LOW_SPEED_MODE
|
||||
// See
|
||||
// https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/api-reference/peripherals/ledc.html#functionality-overview
|
||||
inline ledc_mode_t get_speed_mode(uint8_t) { return LEDC_LOW_SPEED_MODE; }
|
||||
#endif
|
||||
#else
|
||||
static const int MAX_RES_BITS = 20;
|
||||
#endif
|
||||
|
||||
float ledc_max_frequency_for_bit_depth(uint8_t bit_depth) { return 80e6f / float(1 << bit_depth); }
|
||||
|
||||
float ledc_min_frequency_for_bit_depth(uint8_t bit_depth, bool low_frequency) {
|
||||
const float max_div_num = ((1 << MAX_RES_BITS) - 1) / (low_frequency ? 32.0f : 256.0f);
|
||||
float ledc_min_frequency_for_bit_depth(uint8_t bit_depth) {
|
||||
const float max_div_num = ((1 << 20) - 1) / 256.0f;
|
||||
return 80e6f / (max_div_num * float(1 << bit_depth));
|
||||
}
|
||||
|
||||
optional<uint8_t> ledc_bit_depth_for_frequency(float frequency) {
|
||||
ESP_LOGD(TAG, "Calculating resolution bit-depth for frequency %f", frequency);
|
||||
for (int i = MAX_RES_BITS; i >= 1; i--) {
|
||||
const float min_frequency = ledc_min_frequency_for_bit_depth(i, (frequency < 100));
|
||||
for (int i = 20; i >= 1; i--) {
|
||||
const float min_frequency = ledc_min_frequency_for_bit_depth(i);
|
||||
const float max_frequency = ledc_max_frequency_for_bit_depth(i);
|
||||
if (min_frequency <= frequency && frequency <= max_frequency) {
|
||||
ESP_LOGD(TAG, "Resolution calculated as %d", i);
|
||||
if (min_frequency <= frequency && frequency <= max_frequency)
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
@@ -68,7 +48,7 @@ void LEDCOutput::write_state(float state) {
|
||||
ledcWrite(this->channel_, duty);
|
||||
#endif
|
||||
#ifdef USE_ESP_IDF
|
||||
auto speed_mode = get_speed_mode(channel_);
|
||||
auto speed_mode = channel_ < 8 ? LEDC_HIGH_SPEED_MODE : LEDC_LOW_SPEED_MODE;
|
||||
auto chan_num = static_cast<ledc_channel_t>(channel_ % 8);
|
||||
ledc_set_duty(speed_mode, chan_num, duty);
|
||||
ledc_update_duty(speed_mode, chan_num);
|
||||
@@ -83,15 +63,11 @@ void LEDCOutput::setup() {
|
||||
ledcAttachPin(this->pin_->get_pin(), this->channel_);
|
||||
#endif
|
||||
#ifdef USE_ESP_IDF
|
||||
auto speed_mode = get_speed_mode(channel_);
|
||||
auto speed_mode = channel_ < 8 ? LEDC_HIGH_SPEED_MODE : LEDC_LOW_SPEED_MODE;
|
||||
auto timer_num = static_cast<ledc_timer_t>((channel_ % 8) / 2);
|
||||
auto chan_num = static_cast<ledc_channel_t>(channel_ % 8);
|
||||
|
||||
bit_depth_ = *ledc_bit_depth_for_frequency(frequency_);
|
||||
if (bit_depth_ < 1) {
|
||||
ESP_LOGW(TAG, "Frequency %f can't be achieved with any bit depth", frequency_);
|
||||
this->status_set_warning();
|
||||
}
|
||||
|
||||
ledc_timer_config_t timer_conf{};
|
||||
timer_conf.speed_mode = speed_mode;
|
||||
@@ -138,7 +114,7 @@ void LEDCOutput::update_frequency(float frequency) {
|
||||
ESP_LOGW(TAG, "LEDC output hasn't been initialized yet!");
|
||||
return;
|
||||
}
|
||||
auto speed_mode = get_speed_mode(channel_);
|
||||
auto speed_mode = channel_ < 8 ? LEDC_HIGH_SPEED_MODE : LEDC_LOW_SPEED_MODE;
|
||||
auto timer_num = static_cast<ledc_timer_t>((channel_ % 8) / 2);
|
||||
|
||||
ledc_timer_config_t timer_conf{};
|
||||
|
||||
@@ -14,7 +14,6 @@ from esphome.const import (
|
||||
CONF_DISCOVERY,
|
||||
CONF_DISCOVERY_PREFIX,
|
||||
CONF_DISCOVERY_RETAIN,
|
||||
CONF_DISCOVERY_UNIQUE_ID_GENERATOR,
|
||||
CONF_ID,
|
||||
CONF_KEEPALIVE,
|
||||
CONF_LEVEL,
|
||||
@@ -96,12 +95,6 @@ MQTTTextSensor = mqtt_ns.class_("MQTTTextSensor", MQTTComponent)
|
||||
MQTTNumberComponent = mqtt_ns.class_("MQTTNumberComponent", MQTTComponent)
|
||||
MQTTSelectComponent = mqtt_ns.class_("MQTTSelectComponent", MQTTComponent)
|
||||
|
||||
MQTTDiscoveryUniqueIdGenerator = mqtt_ns.enum("MQTTDiscoveryUniqueIdGenerator")
|
||||
MQTT_DISCOVERY_UNIQUE_ID_GENERATOR_OPTIONS = {
|
||||
"legacy": MQTTDiscoveryUniqueIdGenerator.MQTT_LEGACY_UNIQUE_ID_GENERATOR,
|
||||
"mac": MQTTDiscoveryUniqueIdGenerator.MQTT_MAC_ADDRESS_UNIQUE_ID_GENERATOR,
|
||||
}
|
||||
|
||||
|
||||
def validate_config(value):
|
||||
# Populate default fields
|
||||
@@ -160,9 +153,6 @@ CONFIG_SCHEMA = cv.All(
|
||||
cv.Optional(
|
||||
CONF_DISCOVERY_PREFIX, default="homeassistant"
|
||||
): cv.publish_topic,
|
||||
cv.Optional(CONF_DISCOVERY_UNIQUE_ID_GENERATOR, default="legacy"): cv.enum(
|
||||
MQTT_DISCOVERY_UNIQUE_ID_GENERATOR_OPTIONS
|
||||
),
|
||||
cv.Optional(CONF_USE_ABBREVIATIONS, default=True): cv.boolean,
|
||||
cv.Optional(CONF_BIRTH_MESSAGE): MQTT_MESSAGE_SCHEMA,
|
||||
cv.Optional(CONF_WILL_MESSAGE): MQTT_MESSAGE_SCHEMA,
|
||||
@@ -241,22 +231,13 @@ async def to_code(config):
|
||||
discovery = config[CONF_DISCOVERY]
|
||||
discovery_retain = config[CONF_DISCOVERY_RETAIN]
|
||||
discovery_prefix = config[CONF_DISCOVERY_PREFIX]
|
||||
discovery_unique_id_generator = config[CONF_DISCOVERY_UNIQUE_ID_GENERATOR]
|
||||
|
||||
if not discovery:
|
||||
cg.add(var.disable_discovery())
|
||||
elif discovery == "CLEAN":
|
||||
cg.add(
|
||||
var.set_discovery_info(
|
||||
discovery_prefix, discovery_unique_id_generator, discovery_retain, True
|
||||
)
|
||||
)
|
||||
cg.add(var.set_discovery_info(discovery_prefix, discovery_retain, True))
|
||||
elif CONF_DISCOVERY_RETAIN in config or CONF_DISCOVERY_PREFIX in config:
|
||||
cg.add(
|
||||
var.set_discovery_info(
|
||||
discovery_prefix, discovery_unique_id_generator, discovery_retain
|
||||
)
|
||||
)
|
||||
cg.add(var.set_discovery_info(discovery_prefix, discovery_retain))
|
||||
|
||||
cg.add(var.set_topic_prefix(config[CONF_TOPIC_PREFIX]))
|
||||
|
||||
|
||||
@@ -535,10 +535,8 @@ void MQTTClientComponent::set_birth_message(MQTTMessage &&message) {
|
||||
|
||||
void MQTTClientComponent::set_shutdown_message(MQTTMessage &&message) { this->shutdown_message_ = std::move(message); }
|
||||
|
||||
void MQTTClientComponent::set_discovery_info(std::string &&prefix, MQTTDiscoveryUniqueIdGenerator unique_id_generator,
|
||||
bool retain, bool clean) {
|
||||
void MQTTClientComponent::set_discovery_info(std::string &&prefix, bool retain, bool clean) {
|
||||
this->discovery_info_.prefix = std::move(prefix);
|
||||
this->discovery_info_.unique_id_generator = unique_id_generator;
|
||||
this->discovery_info_.retain = retain;
|
||||
this->discovery_info_.clean = clean;
|
||||
}
|
||||
|
||||
@@ -55,12 +55,6 @@ struct Availability {
|
||||
std::string payload_not_available;
|
||||
};
|
||||
|
||||
/// available discovery unique_id generators
|
||||
enum MQTTDiscoveryUniqueIdGenerator {
|
||||
MQTT_LEGACY_UNIQUE_ID_GENERATOR = 0,
|
||||
MQTT_MAC_ADDRESS_UNIQUE_ID_GENERATOR,
|
||||
};
|
||||
|
||||
/** Internal struct for MQTT Home Assistant discovery
|
||||
*
|
||||
* See <a href="https://www.home-assistant.io/docs/mqtt/discovery/">MQTT Discovery</a>.
|
||||
@@ -69,7 +63,6 @@ struct MQTTDiscoveryInfo {
|
||||
std::string prefix; ///< The Home Assistant discovery prefix. Empty means disabled.
|
||||
bool retain; ///< Whether to retain discovery messages.
|
||||
bool clean;
|
||||
MQTTDiscoveryUniqueIdGenerator unique_id_generator;
|
||||
};
|
||||
|
||||
enum MQTTClientState {
|
||||
@@ -105,11 +98,9 @@ class MQTTClientComponent : public Component {
|
||||
*
|
||||
* See <a href="https://www.home-assistant.io/docs/mqtt/discovery/">MQTT Discovery</a>.
|
||||
* @param prefix The Home Assistant discovery prefix.
|
||||
* @param unique_id_generator Controls how UniqueId is generated.
|
||||
* @param retain Whether to retain discovery messages.
|
||||
*/
|
||||
void set_discovery_info(std::string &&prefix, MQTTDiscoveryUniqueIdGenerator unique_id_generator, bool retain,
|
||||
bool clean = false);
|
||||
void set_discovery_info(std::string &&prefix, bool retain, bool clean = false);
|
||||
/// Get Home Assistant discovery info.
|
||||
const MQTTDiscoveryInfo &get_discovery_info() const;
|
||||
/// Globally disable Home Assistant discovery.
|
||||
|
||||
@@ -114,17 +114,9 @@ bool MQTTComponent::send_discovery_() {
|
||||
if (!unique_id.empty()) {
|
||||
root[MQTT_UNIQUE_ID] = unique_id;
|
||||
} else {
|
||||
const MQTTDiscoveryInfo &discovery_info = global_mqtt_client->get_discovery_info();
|
||||
if (discovery_info.unique_id_generator == MQTT_MAC_ADDRESS_UNIQUE_ID_GENERATOR) {
|
||||
char friendly_name_hash[9];
|
||||
sprintf(friendly_name_hash, "%08x", fnv1_hash(this->friendly_name()));
|
||||
friendly_name_hash[8] = 0; // ensure the hash-string ends with null
|
||||
root[MQTT_UNIQUE_ID] = get_mac_address() + "-" + this->component_type() + "-" + friendly_name_hash;
|
||||
} else {
|
||||
// default to almost-unique ID. It's a hack but the only way to get that
|
||||
// gorgeous device registry view.
|
||||
root[MQTT_UNIQUE_ID] = "ESP" + this->component_type() + this->get_default_object_id_();
|
||||
}
|
||||
// default to almost-unique ID. It's a hack but the only way to get that
|
||||
// gorgeous device registry view.
|
||||
root[MQTT_UNIQUE_ID] = "ESP" + this->component_type() + this->get_default_object_id_();
|
||||
}
|
||||
|
||||
JsonObject &device_info = root.createNestedObject(MQTT_DEVICE);
|
||||
|
||||
@@ -96,7 +96,6 @@ optional<bool> PMSX003Component::check_byte_() {
|
||||
length_matches = payload_length == 28 || payload_length == 20;
|
||||
break;
|
||||
case PMSX003_TYPE_5003T:
|
||||
case PMSX003_TYPE_5003S:
|
||||
length_matches = payload_length == 28;
|
||||
break;
|
||||
case PMSX003_TYPE_5003ST:
|
||||
@@ -134,25 +133,20 @@ optional<bool> PMSX003Component::check_byte_() {
|
||||
void PMSX003Component::parse_data_() {
|
||||
switch (this->type_) {
|
||||
case PMSX003_TYPE_5003ST: {
|
||||
uint16_t formaldehyde = this->get_16_bit_uint_(28);
|
||||
float temperature = this->get_16_bit_uint_(30) / 10.0f;
|
||||
float humidity = this->get_16_bit_uint_(32) / 10.0f;
|
||||
|
||||
ESP_LOGD(TAG, "Got Temperature: %.1f°C, Humidity: %.1f%%", temperature, humidity);
|
||||
ESP_LOGD(TAG, "Got Temperature: %.1f°C, Humidity: %.1f%% Formaldehyde: %u µg/m^3", temperature, humidity,
|
||||
formaldehyde);
|
||||
|
||||
if (this->temperature_sensor_ != nullptr)
|
||||
this->temperature_sensor_->publish_state(temperature);
|
||||
if (this->humidity_sensor_ != nullptr)
|
||||
this->humidity_sensor_->publish_state(humidity);
|
||||
// The rest of the PMS5003ST matches the PMS5003S, continue on
|
||||
}
|
||||
case PMSX003_TYPE_5003S: {
|
||||
uint16_t formaldehyde = this->get_16_bit_uint_(28);
|
||||
|
||||
ESP_LOGD(TAG, "Got Formaldehyde: %u µg/m^3", formaldehyde);
|
||||
|
||||
if (this->formaldehyde_sensor_ != nullptr)
|
||||
this->formaldehyde_sensor_->publish_state(formaldehyde);
|
||||
// The rest of the PMS5003S matches the PMS5003, continue on
|
||||
// The rest of the PMS5003ST matches the PMS5003, continue on
|
||||
}
|
||||
case PMSX003_TYPE_X003: {
|
||||
uint16_t pm_1_0_std_concentration = this->get_16_bit_uint_(4);
|
||||
|
||||
@@ -11,7 +11,6 @@ enum PMSX003Type {
|
||||
PMSX003_TYPE_X003 = 0,
|
||||
PMSX003_TYPE_5003T,
|
||||
PMSX003_TYPE_5003ST,
|
||||
PMSX003_TYPE_5003S,
|
||||
};
|
||||
|
||||
class PMSX003Component : public uart::UARTDevice, public Component {
|
||||
|
||||
@@ -42,23 +42,21 @@ PMSX003Sensor = pmsx003_ns.class_("PMSX003Sensor", sensor.Sensor)
|
||||
TYPE_PMSX003 = "PMSX003"
|
||||
TYPE_PMS5003T = "PMS5003T"
|
||||
TYPE_PMS5003ST = "PMS5003ST"
|
||||
TYPE_PMS5003S = "PMS5003S"
|
||||
|
||||
PMSX003Type = pmsx003_ns.enum("PMSX003Type")
|
||||
PMSX003_TYPES = {
|
||||
TYPE_PMSX003: PMSX003Type.PMSX003_TYPE_X003,
|
||||
TYPE_PMS5003T: PMSX003Type.PMSX003_TYPE_5003T,
|
||||
TYPE_PMS5003ST: PMSX003Type.PMSX003_TYPE_5003ST,
|
||||
TYPE_PMS5003S: PMSX003Type.PMSX003_TYPE_5003S,
|
||||
}
|
||||
|
||||
SENSORS_TO_TYPE = {
|
||||
CONF_PM_1_0: [TYPE_PMSX003, TYPE_PMS5003ST, TYPE_PMS5003S],
|
||||
CONF_PM_2_5: [TYPE_PMSX003, TYPE_PMS5003T, TYPE_PMS5003ST, TYPE_PMS5003S],
|
||||
CONF_PM_10_0: [TYPE_PMSX003, TYPE_PMS5003ST, TYPE_PMS5003S],
|
||||
CONF_PM_1_0: [TYPE_PMSX003, TYPE_PMS5003ST],
|
||||
CONF_PM_2_5: [TYPE_PMSX003, TYPE_PMS5003T, TYPE_PMS5003ST],
|
||||
CONF_PM_10_0: [TYPE_PMSX003, TYPE_PMS5003ST],
|
||||
CONF_TEMPERATURE: [TYPE_PMS5003T, TYPE_PMS5003ST],
|
||||
CONF_HUMIDITY: [TYPE_PMS5003T, TYPE_PMS5003ST],
|
||||
CONF_FORMALDEHYDE: [TYPE_PMS5003ST, TYPE_PMS5003S],
|
||||
CONF_FORMALDEHYDE: [TYPE_PMS5003ST],
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -17,14 +17,14 @@ void NECProtocol::encode(RemoteTransmitData *dst, const NECData &data) {
|
||||
dst->set_carrier_frequency(38000);
|
||||
|
||||
dst->item(HEADER_HIGH_US, HEADER_LOW_US);
|
||||
for (uint16_t mask = 1; mask; mask <<= 1) {
|
||||
for (uint32_t mask = 1UL << 15; mask; mask >>= 1) {
|
||||
if (data.address & mask)
|
||||
dst->item(BIT_HIGH_US, BIT_ONE_LOW_US);
|
||||
else
|
||||
dst->item(BIT_HIGH_US, BIT_ZERO_LOW_US);
|
||||
}
|
||||
|
||||
for (uint16_t mask = 1; mask; mask <<= 1) {
|
||||
for (uint32_t mask = 1UL << 15; mask; mask >>= 1) {
|
||||
if (data.command & mask)
|
||||
dst->item(BIT_HIGH_US, BIT_ONE_LOW_US);
|
||||
else
|
||||
@@ -41,7 +41,7 @@ optional<NECData> NECProtocol::decode(RemoteReceiveData src) {
|
||||
if (!src.expect_item(HEADER_HIGH_US, HEADER_LOW_US))
|
||||
return {};
|
||||
|
||||
for (uint16_t mask = 1; mask; mask <<= 1) {
|
||||
for (uint32_t mask = 1UL << 15; mask != 0; mask >>= 1) {
|
||||
if (src.expect_item(BIT_HIGH_US, BIT_ONE_LOW_US)) {
|
||||
data.address |= mask;
|
||||
} else if (src.expect_item(BIT_HIGH_US, BIT_ZERO_LOW_US)) {
|
||||
@@ -51,7 +51,7 @@ optional<NECData> NECProtocol::decode(RemoteReceiveData src) {
|
||||
}
|
||||
}
|
||||
|
||||
for (uint16_t mask = 1; mask; mask <<= 1) {
|
||||
for (uint32_t mask = 1UL << 15; mask != 0; mask >>= 1) {
|
||||
if (src.expect_item(BIT_HIGH_US, BIT_ONE_LOW_US)) {
|
||||
data.command |= mask;
|
||||
} else if (src.expect_item(BIT_HIGH_US, BIT_ZERO_LOW_US)) {
|
||||
|
||||
@@ -52,7 +52,7 @@ bool RFBridgeComponent::parse_bridge_byte_(uint8_t byte) {
|
||||
if (action == RF_CODE_LEARN_OK)
|
||||
ESP_LOGD(TAG, "Learning success");
|
||||
|
||||
ESP_LOGI(TAG, "Received RFBridge Code: sync=0x%04X low=0x%04X high=0x%04X code=0x%06X", data.sync, data.low,
|
||||
ESP_LOGD(TAG, "Received RFBridge Code: sync=0x%04X low=0x%04X high=0x%04X code=0x%06X", data.sync, data.low,
|
||||
data.high, data.code);
|
||||
this->data_callback_.call(data);
|
||||
break;
|
||||
@@ -73,7 +73,7 @@ bool RFBridgeComponent::parse_bridge_byte_(uint8_t byte) {
|
||||
data.code += next_byte;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Received RFBridge Advanced Code: length=0x%02X protocol=0x%02X code=0x%s", data.length,
|
||||
ESP_LOGD(TAG, "Received RFBridge Advanced Code: length=0x%02X protocol=0x%02X code=0x%s", data.length,
|
||||
data.protocol, data.code.c_str());
|
||||
this->advanced_data_callback_.call(data);
|
||||
break;
|
||||
@@ -97,7 +97,7 @@ bool RFBridgeComponent::parse_bridge_byte_(uint8_t byte) {
|
||||
str += " ";
|
||||
}
|
||||
}
|
||||
ESP_LOGI(TAG, "Received RFBridge Bucket: %s", str.c_str());
|
||||
ESP_LOGD(TAG, "Received RFBridge Bucket: %s", str.c_str());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -186,7 +186,7 @@ void RFBridgeComponent::dump_config() {
|
||||
}
|
||||
|
||||
void RFBridgeComponent::start_advanced_sniffing() {
|
||||
ESP_LOGI(TAG, "Advanced Sniffing on");
|
||||
ESP_LOGD(TAG, "Advanced Sniffing on");
|
||||
this->write(RF_CODE_START);
|
||||
this->write(RF_CODE_SNIFFING_ON);
|
||||
this->write(RF_CODE_STOP);
|
||||
@@ -194,7 +194,7 @@ void RFBridgeComponent::start_advanced_sniffing() {
|
||||
}
|
||||
|
||||
void RFBridgeComponent::stop_advanced_sniffing() {
|
||||
ESP_LOGI(TAG, "Advanced Sniffing off");
|
||||
ESP_LOGD(TAG, "Advanced Sniffing off");
|
||||
this->write(RF_CODE_START);
|
||||
this->write(RF_CODE_SNIFFING_OFF);
|
||||
this->write(RF_CODE_STOP);
|
||||
@@ -202,7 +202,7 @@ void RFBridgeComponent::stop_advanced_sniffing() {
|
||||
}
|
||||
|
||||
void RFBridgeComponent::start_bucket_sniffing() {
|
||||
ESP_LOGI(TAG, "Raw Bucket Sniffing on");
|
||||
ESP_LOGD(TAG, "Raw Bucket Sniffing on");
|
||||
this->write(RF_CODE_START);
|
||||
this->write(RF_CODE_RFIN_BUCKET);
|
||||
this->write(RF_CODE_STOP);
|
||||
|
||||
@@ -11,7 +11,6 @@ static const uint8_t SDP3X_SOFT_RESET[2] = {0x00, 0x06};
|
||||
static const uint8_t SDP3X_READ_ID1[2] = {0x36, 0x7C};
|
||||
static const uint8_t SDP3X_READ_ID2[2] = {0xE1, 0x02};
|
||||
static const uint8_t SDP3X_START_DP_AVG[2] = {0x36, 0x15};
|
||||
static const uint8_t SDP3X_START_MASS_FLOW_AVG[2] = {0x36, 0x03};
|
||||
static const uint8_t SDP3X_STOP_MEAS[2] = {0x3F, 0xF9};
|
||||
|
||||
void SDP3XComponent::update() { this->read_pressure_(); }
|
||||
@@ -27,69 +26,46 @@ void SDP3XComponent::setup() {
|
||||
ESP_LOGW(TAG, "Soft Reset SDP3X failed!"); // This sometimes fails for no good reason
|
||||
}
|
||||
|
||||
this->set_timeout(20, [this] {
|
||||
if (this->write(SDP3X_READ_ID1, 2) != i2c::ERROR_OK) {
|
||||
ESP_LOGE(TAG, "Read ID1 SDP3X failed!");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
if (this->write(SDP3X_READ_ID2, 2) != i2c::ERROR_OK) {
|
||||
ESP_LOGE(TAG, "Read ID2 SDP3X failed!");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
delayMicroseconds(20000);
|
||||
|
||||
uint8_t data[18];
|
||||
if (this->read(data, 18) != i2c::ERROR_OK) {
|
||||
ESP_LOGE(TAG, "Read ID SDP3X failed!");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
if (!(check_crc_(&data[0], 2, data[2]) && check_crc_(&data[3], 2, data[5]))) {
|
||||
ESP_LOGE(TAG, "CRC ID SDP3X failed!");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
if (this->write(SDP3X_READ_ID1, 2) != i2c::ERROR_OK) {
|
||||
ESP_LOGE(TAG, "Read ID1 SDP3X failed!");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
if (this->write(SDP3X_READ_ID2, 2) != i2c::ERROR_OK) {
|
||||
ESP_LOGE(TAG, "Read ID2 SDP3X failed!");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
// SDP8xx
|
||||
// ref:
|
||||
// https://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/8_Differential_Pressure/Datasheets/Sensirion_Differential_Pressure_Datasheet_SDP8xx_Digital.pdf
|
||||
if (data[2] == 0x02) {
|
||||
switch (data[3]) {
|
||||
case 0x01: // SDP800-500Pa
|
||||
ESP_LOGCONFIG(TAG, "Sensor is SDP800-500Pa");
|
||||
break;
|
||||
case 0x0A: // SDP810-500Pa
|
||||
ESP_LOGCONFIG(TAG, "Sensor is SDP810-500Pa");
|
||||
break;
|
||||
case 0x04: // SDP801-500Pa
|
||||
ESP_LOGCONFIG(TAG, "Sensor is SDP801-500Pa");
|
||||
break;
|
||||
case 0x0D: // SDP811-500Pa
|
||||
ESP_LOGCONFIG(TAG, "Sensor is SDP811-500Pa");
|
||||
break;
|
||||
case 0x02: // SDP800-125Pa
|
||||
ESP_LOGCONFIG(TAG, "Sensor is SDP800-125Pa");
|
||||
break;
|
||||
case 0x0B: // SDP810-125Pa
|
||||
ESP_LOGCONFIG(TAG, "Sensor is SDP810-125Pa");
|
||||
break;
|
||||
}
|
||||
} else if (data[2] == 0x01) {
|
||||
if (data[3] == 0x01) {
|
||||
ESP_LOGCONFIG(TAG, "Sensor is SDP31-500Pa");
|
||||
} else if (data[3] == 0x02) {
|
||||
ESP_LOGCONFIG(TAG, "Sensor is SDP32-125Pa");
|
||||
}
|
||||
}
|
||||
uint8_t data[18];
|
||||
if (this->read(data, 18) != i2c::ERROR_OK) {
|
||||
ESP_LOGE(TAG, "Read ID SDP3X failed!");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->write(measurement_mode_ == DP_AVG ? SDP3X_START_DP_AVG : SDP3X_START_MASS_FLOW_AVG, 2) != i2c::ERROR_OK) {
|
||||
ESP_LOGE(TAG, "Start Measurements SDP3X failed!");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
ESP_LOGCONFIG(TAG, "SDP3X started!");
|
||||
});
|
||||
if (!(check_crc_(&data[0], 2, data[2]) && check_crc_(&data[3], 2, data[5]))) {
|
||||
ESP_LOGE(TAG, "CRC ID SDP3X failed!");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
if (data[3] == 0x01) {
|
||||
ESP_LOGCONFIG(TAG, "SDP3X is SDP31");
|
||||
pressure_scale_factor_ = 60.0f * 100.0f; // Scale factors converted to hPa per count
|
||||
} else if (data[3] == 0x02) {
|
||||
ESP_LOGCONFIG(TAG, "SDP3X is SDP32");
|
||||
pressure_scale_factor_ = 240.0f * 100.0f;
|
||||
}
|
||||
|
||||
if (this->write(SDP3X_START_DP_AVG, 2) != i2c::ERROR_OK) {
|
||||
ESP_LOGE(TAG, "Start Measurements SDP3X failed!");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
ESP_LOGCONFIG(TAG, "SDP3X started!");
|
||||
}
|
||||
void SDP3XComponent::dump_config() {
|
||||
LOG_SENSOR(" ", "SDP3X", this);
|
||||
@@ -115,12 +91,8 @@ void SDP3XComponent::read_pressure_() {
|
||||
}
|
||||
|
||||
int16_t pressure_raw = encode_uint16(data[0], data[1]);
|
||||
int16_t temperature_raw = encode_uint16(data[3], data[4]);
|
||||
int16_t scale_factor_raw = encode_uint16(data[6], data[7]);
|
||||
// scale factor is in Pa - convert to hPa
|
||||
float pressure = pressure_raw / (scale_factor_raw * 100.0f);
|
||||
ESP_LOGV(TAG, "Got raw pressure=%d, raw scale factor =%d, raw temperature=%d ", pressure_raw, scale_factor_raw,
|
||||
temperature_raw);
|
||||
float pressure = pressure_raw / pressure_scale_factor_;
|
||||
ESP_LOGV(TAG, "Got raw pressure=%d, scale factor =%.3f ", pressure_raw, pressure_scale_factor_);
|
||||
ESP_LOGD(TAG, "Got Pressure=%.3f hPa", pressure);
|
||||
|
||||
this->publish_state(pressure);
|
||||
|
||||
@@ -7,8 +7,6 @@
|
||||
namespace esphome {
|
||||
namespace sdp3x {
|
||||
|
||||
enum MeasurementMode { MASS_FLOW_AVG, DP_AVG };
|
||||
|
||||
class SDP3XComponent : public PollingComponent, public i2c::I2CDevice, public sensor::Sensor {
|
||||
public:
|
||||
/// Schedule temperature+pressure readings.
|
||||
@@ -18,14 +16,14 @@ class SDP3XComponent : public PollingComponent, public i2c::I2CDevice, public se
|
||||
void dump_config() override;
|
||||
|
||||
float get_setup_priority() const override;
|
||||
void set_measurement_mode(MeasurementMode mode) { measurement_mode_ = mode; }
|
||||
|
||||
protected:
|
||||
/// Internal method to read the pressure from the component after it has been scheduled.
|
||||
void read_pressure_();
|
||||
|
||||
bool check_crc_(const uint8_t data[], uint8_t size, uint8_t checksum);
|
||||
MeasurementMode measurement_mode_;
|
||||
|
||||
float pressure_scale_factor_ = 0.0f; // hPa per count
|
||||
};
|
||||
|
||||
} // namespace sdp3x
|
||||
|
||||
@@ -14,14 +14,6 @@ CODEOWNERS = ["@Azimath"]
|
||||
sdp3x_ns = cg.esphome_ns.namespace("sdp3x")
|
||||
SDP3XComponent = sdp3x_ns.class_("SDP3XComponent", cg.PollingComponent, i2c.I2CDevice)
|
||||
|
||||
|
||||
MeasurementMode = sdp3x_ns.enum("MeasurementMode")
|
||||
MEASUREMENT_MODE = {
|
||||
"mass_flow": MeasurementMode.MASS_FLOW_AVG,
|
||||
"differential_pressure": MeasurementMode.DP_AVG,
|
||||
}
|
||||
CONF_MEASUREMENT_MODE = "measurement_mode"
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_HECTOPASCAL,
|
||||
@@ -32,9 +24,6 @@ CONFIG_SCHEMA = (
|
||||
.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(SDP3XComponent),
|
||||
cv.Optional(
|
||||
CONF_MEASUREMENT_MODE, default="differential_pressure"
|
||||
): cv.enum(MEASUREMENT_MODE),
|
||||
}
|
||||
)
|
||||
.extend(cv.polling_component_schema("60s"))
|
||||
@@ -47,4 +36,3 @@ async def to_code(config):
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
await sensor.register_sensor(var, config)
|
||||
cg.add(var.set_measurement_mode(config[CONF_MEASUREMENT_MODE]))
|
||||
|
||||
@@ -71,7 +71,7 @@ void SNTPComponent::loop() {
|
||||
if (!time.is_valid())
|
||||
return;
|
||||
|
||||
ESP_LOGD(TAG, "Synchronized time: %04d-%02d-%02d %02d:%02d:%02d", time.year, time.month, time.day_of_month, time.hour,
|
||||
ESP_LOGD(TAG, "Synchronized time: %d-%d-%d %d:%02d:%02d", time.year, time.month, time.day_of_month, time.hour,
|
||||
time.minute, time.second);
|
||||
this->time_sync_callback_.call();
|
||||
this->has_time_ = true;
|
||||
|
||||
@@ -22,7 +22,7 @@ i2c::ErrorCode TCA9548AChannel::writev(uint8_t address, i2c::WriteBuffer *buffer
|
||||
void TCA9548AComponent::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up TCA9548A...");
|
||||
uint8_t status = 0;
|
||||
if (this->read(&status, 1) != i2c::ERROR_OK) {
|
||||
if (this->read_register(0x00, &status, 1) != i2c::ERROR_OK) {
|
||||
ESP_LOGI(TAG, "TCA9548A failed");
|
||||
this->mark_failed();
|
||||
return;
|
||||
|
||||
@@ -24,7 +24,6 @@ class TCA9548AComponent : public Component, public i2c::I2CDevice {
|
||||
public:
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::IO; }
|
||||
void update();
|
||||
|
||||
i2c::ErrorCode switch_to_channel(uint8_t channel);
|
||||
|
||||
@@ -35,7 +35,7 @@ void RealTimeClock::synchronize_epoch_(uint32_t epoch) {
|
||||
}
|
||||
|
||||
auto time = this->now();
|
||||
ESP_LOGD(TAG, "Synchronized time: %04d-%02d-%02d %02d:%02d:%02d", time.year, time.month, time.day_of_month, time.hour,
|
||||
ESP_LOGD(TAG, "Synchronized time: %d-%d-%d %d:%d:%d", time.year, time.month, time.day_of_month, time.hour,
|
||||
time.minute, time.second);
|
||||
|
||||
this->time_sync_callback_.call();
|
||||
|
||||
@@ -3,7 +3,6 @@ from typing import Optional
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
import esphome.final_validate as fv
|
||||
from esphome.yaml_util import make_data_base
|
||||
from esphome import pins, automation
|
||||
from esphome.const import (
|
||||
CONF_BAUD_RATE,
|
||||
@@ -25,7 +24,6 @@ from esphome.const import (
|
||||
CONF_DELIMITER,
|
||||
CONF_DUMMY_RECEIVER,
|
||||
CONF_DUMMY_RECEIVER_ID,
|
||||
CONF_LAMBDA,
|
||||
)
|
||||
from esphome.core import CORE
|
||||
|
||||
@@ -96,47 +94,22 @@ UART_DIRECTIONS = {
|
||||
"BOTH": UARTDirection.UART_DIRECTION_BOTH,
|
||||
}
|
||||
|
||||
# The reason for having CONF_BYTES at 150 by default:
|
||||
#
|
||||
# The log message buffer size is 512 bytes by default. About 35 bytes are
|
||||
# used for the log prefix. That leaves us with 477 bytes for logging data.
|
||||
# The default log output is hex, which uses 3 characters per represented
|
||||
# byte (2 hex chars + 1 separator). That means that 477 / 3 = 159 bytes
|
||||
# can be represented in a single log line. Using 150, because people love
|
||||
# round numbers.
|
||||
AFTER_DEFAULTS = {CONF_BYTES: 150, CONF_TIMEOUT: "100ms"}
|
||||
|
||||
# By default, log in hex format when no specific sequence is provided.
|
||||
DEFAULT_DEBUG_OUTPUT = "UARTDebug::log_hex(direction, bytes, ':');"
|
||||
DEFAULT_SEQUENCE = [{CONF_LAMBDA: make_data_base(DEFAULT_DEBUG_OUTPUT)}]
|
||||
|
||||
|
||||
def maybe_empty_debug(value):
|
||||
if value is None:
|
||||
value = {}
|
||||
return DEBUG_SCHEMA(value)
|
||||
|
||||
|
||||
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_AFTER): cv.Schema(
|
||||
{
|
||||
cv.Optional(CONF_BYTES, default=256): cv.validate_bytes,
|
||||
cv.Optional(
|
||||
CONF_BYTES, default=AFTER_DEFAULTS[CONF_BYTES]
|
||||
): cv.validate_bytes,
|
||||
cv.Optional(
|
||||
CONF_TIMEOUT, default=AFTER_DEFAULTS[CONF_TIMEOUT]
|
||||
CONF_TIMEOUT, default="100ms"
|
||||
): cv.positive_time_period_milliseconds,
|
||||
cv.Optional(CONF_DELIMITER): cv.templatable(validate_raw_data),
|
||||
}
|
||||
),
|
||||
cv.Optional(
|
||||
CONF_SEQUENCE, default=DEFAULT_SEQUENCE
|
||||
): automation.validate_automation(),
|
||||
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),
|
||||
}
|
||||
@@ -158,7 +131,7 @@ 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): maybe_empty_debug,
|
||||
cv.Optional(CONF_DEBUG): DEBUG_SCHEMA,
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA),
|
||||
cv.has_at_least_one_key(CONF_TX_PIN, CONF_RX_PIN),
|
||||
|
||||
@@ -1183,7 +1183,7 @@ static const uint8_t PART_UPDATE_LUT_TTGO_DKE[LUT_SIZE_TTGO_DKE_PART] = {
|
||||
0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0, 0x40, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0xF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0xF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
|
||||
@@ -57,46 +57,38 @@ void IRAM_ATTR ZaSensorStore::interrupt(ZaSensorStore *arg) {
|
||||
void IRAM_ATTR ZaSensorStore::set_data_(ZaMessage *message) {
|
||||
switch (message->type) {
|
||||
case HUMIDITY:
|
||||
this->humidity = message->value;
|
||||
this->humidity = (message->value > 10000) ? NAN : (message->value / 100.0f);
|
||||
break;
|
||||
|
||||
case TEMPERATURE:
|
||||
this->temperature = message->value;
|
||||
this->temperature = (message->value > 5970) ? NAN : (message->value / 16.0f - 273.15f);
|
||||
break;
|
||||
|
||||
case CO2:
|
||||
this->co2 = message->value;
|
||||
this->co2 = (message->value > 10000) ? NAN : message->value;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool ZyAuraSensor::publish_state_(ZaDataType data_type, sensor::Sensor *sensor, uint16_t *data_value) {
|
||||
// Sensor wasn't added to configuration
|
||||
bool ZyAuraSensor::publish_state_(sensor::Sensor *sensor, float *value) {
|
||||
// Sensor doesn't added to configuration
|
||||
if (sensor == nullptr) {
|
||||
return true;
|
||||
}
|
||||
|
||||
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->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;
|
||||
}
|
||||
|
||||
*data_value = -1;
|
||||
*value = NAN;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -112,9 +104,9 @@ void ZyAuraSensor::dump_config() {
|
||||
}
|
||||
|
||||
void ZyAuraSensor::update() {
|
||||
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);
|
||||
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);
|
||||
|
||||
if (co2_result && temperature_result && humidity_result) {
|
||||
this->status_clear_warning();
|
||||
|
||||
@@ -42,9 +42,9 @@ class ZaDataProcessor {
|
||||
|
||||
class ZaSensorStore {
|
||||
public:
|
||||
uint16_t co2 = -1;
|
||||
uint16_t temperature = -1;
|
||||
uint16_t humidity = -1;
|
||||
float co2 = NAN;
|
||||
float temperature = NAN;
|
||||
float humidity = NAN;
|
||||
|
||||
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_(ZaDataType data_type, sensor::Sensor *sensor, uint16_t *data_value);
|
||||
bool publish_state_(sensor::Sensor *sensor, float *value);
|
||||
};
|
||||
|
||||
} // namespace zyaura
|
||||
|
||||
@@ -296,11 +296,9 @@ def icon(value):
|
||||
value = string_strict(value)
|
||||
if not value:
|
||||
return value
|
||||
if re.match("^[\\w\\-]+:[\\w\\-]+$", value):
|
||||
if value.startswith("mdi:"):
|
||||
return value
|
||||
raise Invalid(
|
||||
'Icons must match the format "[icon pack]:[icon]", e.g. "mdi:home-assistant"'
|
||||
)
|
||||
raise Invalid('Icons should start with prefix "mdi:"')
|
||||
|
||||
|
||||
def boolean(value):
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""Constants used by esphome."""
|
||||
|
||||
__version__ = "2021.12.0-dev"
|
||||
__version__ = "2021.11.0b9"
|
||||
|
||||
ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_"
|
||||
|
||||
@@ -8,10 +8,31 @@ PLATFORM_ESP32 = "esp32"
|
||||
PLATFORM_ESP8266 = "esp8266"
|
||||
|
||||
TARGET_PLATFORMS = [PLATFORM_ESP32, PLATFORM_ESP8266]
|
||||
TARGET_FRAMEWORKS = ["arduino", "esp-idf"]
|
||||
|
||||
# See also https://github.com/platformio/platform-espressif8266/releases
|
||||
ARDUINO_VERSION_ESP8266 = {
|
||||
"dev": "https://github.com/platformio/platform-espressif8266.git",
|
||||
"3.0.1": "platformio/espressif8266@3.1.0",
|
||||
"3.0.0": "platformio/espressif8266@3.0.0",
|
||||
"2.7.4": "platformio/espressif8266@2.6.2",
|
||||
"2.7.3": "platformio/espressif8266@2.6.1",
|
||||
"2.7.2": "platformio/espressif8266@2.6.0",
|
||||
"2.7.1": "platformio/espressif8266@2.5.3",
|
||||
"2.7.0": "platformio/espressif8266@2.5.0",
|
||||
"2.6.3": "platformio/espressif8266@2.4.0",
|
||||
"2.6.2": "platformio/espressif8266@2.3.1",
|
||||
"2.6.1": "platformio/espressif8266@2.3.0",
|
||||
"2.5.2": "platformio/espressif8266@2.2.3",
|
||||
"2.5.1": "platformio/espressif8266@2.1.1",
|
||||
"2.5.0": "platformio/espressif8266@2.0.4",
|
||||
"2.4.2": "platformio/espressif8266@1.8.0",
|
||||
"2.4.1": "platformio/espressif8266@1.7.3",
|
||||
"2.4.0": "platformio/espressif8266@1.6.0",
|
||||
"2.3.0": "platformio/espressif8266@1.5.0",
|
||||
}
|
||||
SOURCE_FILE_EXTENSIONS = {".cpp", ".hpp", ".h", ".c", ".tcc", ".ino"}
|
||||
HEADER_FILE_EXTENSIONS = {".h", ".hpp", ".tcc"}
|
||||
SECRETS_FILES = {"secrets.yaml", "secrets.yml"}
|
||||
|
||||
|
||||
CONF_ABOVE = "above"
|
||||
@@ -168,7 +189,6 @@ CONF_DISABLED_BY_DEFAULT = "disabled_by_default"
|
||||
CONF_DISCOVERY = "discovery"
|
||||
CONF_DISCOVERY_PREFIX = "discovery_prefix"
|
||||
CONF_DISCOVERY_RETAIN = "discovery_retain"
|
||||
CONF_DISCOVERY_UNIQUE_ID_GENERATOR = "discovery_unique_id_generator"
|
||||
CONF_DISTANCE = "distance"
|
||||
CONF_DITHER = "dither"
|
||||
CONF_DIV_RATIO = "div_ratio"
|
||||
@@ -863,7 +883,6 @@ 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"
|
||||
# device classes of both binary_sensor and sensor component
|
||||
|
||||
@@ -55,15 +55,6 @@ bool Component::cancel_interval(const std::string &name) { // NOLINT
|
||||
return App.scheduler.cancel_interval(this, name);
|
||||
}
|
||||
|
||||
void Component::set_retry(const std::string &name, uint32_t initial_wait_time, uint8_t max_attempts,
|
||||
std::function<RetryResult()> &&f, float backoff_increase_factor) { // NOLINT
|
||||
App.scheduler.set_retry(this, name, initial_wait_time, max_attempts, std::move(f), backoff_increase_factor);
|
||||
}
|
||||
|
||||
bool Component::cancel_retry(const std::string &name) { // NOLINT
|
||||
return App.scheduler.cancel_retry(this, name);
|
||||
}
|
||||
|
||||
void Component::set_timeout(const std::string &name, uint32_t timeout, std::function<void()> &&f) { // NOLINT
|
||||
return App.scheduler.set_timeout(this, name, timeout, std::move(f));
|
||||
}
|
||||
@@ -129,10 +120,6 @@ void Component::set_timeout(uint32_t timeout, std::function<void()> &&f) { // N
|
||||
void Component::set_interval(uint32_t interval, std::function<void()> &&f) { // NOLINT
|
||||
App.scheduler.set_interval(this, "", interval, std::move(f));
|
||||
}
|
||||
void Component::set_retry(uint32_t initial_wait_time, uint8_t max_attempts, std::function<RetryResult()> &&f,
|
||||
float backoff_increase_factor) { // NOLINT
|
||||
App.scheduler.set_retry(this, "", initial_wait_time, max_attempts, std::move(f), backoff_increase_factor);
|
||||
}
|
||||
bool Component::is_failed() { return (this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_FAILED; }
|
||||
bool Component::can_proceed() { return true; }
|
||||
bool Component::status_has_warning() { return this->component_state_ & STATUS_LED_WARNING; }
|
||||
|
||||
@@ -61,8 +61,6 @@ extern const uint32_t STATUS_LED_OK;
|
||||
extern const uint32_t STATUS_LED_WARNING;
|
||||
extern const uint32_t STATUS_LED_ERROR;
|
||||
|
||||
enum RetryResult { DONE, RETRY };
|
||||
|
||||
class Component {
|
||||
public:
|
||||
/** Where the component's initialization should happen.
|
||||
@@ -182,35 +180,7 @@ class Component {
|
||||
*/
|
||||
bool cancel_interval(const std::string &name); // NOLINT
|
||||
|
||||
/** Set an retry function with a unique name. Empty name means no cancelling possible.
|
||||
*
|
||||
* This will call f. If f returns RetryResult::RETRY f is called again after initial_wait_time ms.
|
||||
* f should return RetryResult::DONE if no repeat is required. The initial wait time will be increased
|
||||
* by backoff_increase_factor for each iteration. Default is doubling the time between iterations
|
||||
* Can be cancelled via cancel_retry().
|
||||
*
|
||||
* IMPORTANT: Do not rely on this having correct timing. This is only called from
|
||||
* loop() and therefore can be significantly delayed.
|
||||
*
|
||||
* @param name The identifier for this retry function.
|
||||
* @param initial_wait_time The time in ms before f is called again
|
||||
* @param max_attempts The maximum number of retries
|
||||
* @param f The function (or lambda) that should be called
|
||||
* @param backoff_increase_factor time between retries is increased by this factor on every retry
|
||||
* @see cancel_retry()
|
||||
*/
|
||||
void set_retry(const std::string &name, uint32_t initial_wait_time, uint8_t max_attempts, // NOLINT
|
||||
std::function<RetryResult()> &&f, float backoff_increase_factor = 1.0f); // NOLINT
|
||||
|
||||
void set_retry(uint32_t initial_wait_time, uint8_t max_attempts, std::function<RetryResult()> &&f, // NOLINT
|
||||
float backoff_increase_factor = 1.0f); // NOLINT
|
||||
|
||||
/** Cancel a retry function.
|
||||
*
|
||||
* @param name The identifier for this retry function.
|
||||
* @return Whether a retry function was deleted.
|
||||
*/
|
||||
bool cancel_retry(const std::string &name); // NOLINT
|
||||
void set_timeout(uint32_t timeout, std::function<void()> &&f); // NOLINT
|
||||
|
||||
/** Set a timeout function with a unique name.
|
||||
*
|
||||
@@ -228,8 +198,6 @@ class Component {
|
||||
*/
|
||||
void set_timeout(const std::string &name, uint32_t timeout, std::function<void()> &&f); // NOLINT
|
||||
|
||||
void set_timeout(uint32_t timeout, std::function<void()> &&f); // NOLINT
|
||||
|
||||
/** Cancel a timeout function.
|
||||
*
|
||||
* @param name The identifier for this timeout function.
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#define USE_CLIMATE
|
||||
#define USE_COVER
|
||||
#define USE_DEEP_SLEEP
|
||||
#define USE_ESP8266_PREFERENCES_FLASH
|
||||
#define USE_FAN
|
||||
#define USE_GRAPH
|
||||
#define USE_HOMEASSISTANT_TIME
|
||||
@@ -29,6 +30,7 @@
|
||||
#define USE_OTA_PASSWORD
|
||||
#define USE_OTA_STATE_CALLBACK
|
||||
#define USE_POWER_SUPPLY
|
||||
#define USE_PROMETHEUS
|
||||
#define USE_SELECT
|
||||
#define USE_SENSOR
|
||||
#define USE_STATUS_LED
|
||||
@@ -36,18 +38,18 @@
|
||||
#define USE_TEXT_SENSOR
|
||||
#define USE_TIME
|
||||
#define USE_UART_DEBUGGER
|
||||
#define USE_WEBSERVER
|
||||
#define USE_WIFI
|
||||
|
||||
#define WEBSERVER_PORT 80 // NOLINT
|
||||
|
||||
// Arduino-specific feature flags
|
||||
#ifdef USE_ARDUINO
|
||||
#define USE_CAPTIVE_PORTAL
|
||||
#define USE_JSON
|
||||
#define USE_NEXTION_TFT_UPLOAD
|
||||
#define USE_MQTT
|
||||
#define USE_PROMETHEUS
|
||||
#define USE_WEBSERVER
|
||||
#define USE_WIFI_WPA2_EAP
|
||||
#define WEBSERVER_PORT 80 // NOLINT
|
||||
#endif
|
||||
|
||||
// ESP32-specific feature flags
|
||||
@@ -66,7 +68,6 @@
|
||||
// ESP8266-specific feature flags
|
||||
#ifdef USE_ESP8266
|
||||
#define USE_ADC_SENSOR_VCC
|
||||
#define USE_ESP8266_PREFERENCES_FLASH
|
||||
#define USE_HTTP_REQUEST_ESP8266_HTTPS
|
||||
#define USE_SOCKET_IMPL_LWIP_TCP
|
||||
#endif
|
||||
|
||||
@@ -6,10 +6,11 @@
|
||||
#include <cstring>
|
||||
|
||||
#if defined(USE_ESP8266)
|
||||
#include <osapi.h>
|
||||
#include <user_interface.h>
|
||||
// for xt_rsil()/xt_wsr_ps()
|
||||
#ifdef USE_WIFI
|
||||
#include <ESP8266WiFi.h>
|
||||
#endif
|
||||
#include <Arduino.h>
|
||||
#include <osapi.h>
|
||||
#elif defined(USE_ESP32_FRAMEWORK_ARDUINO)
|
||||
#include <Esp.h>
|
||||
#elif defined(USE_ESP_IDF)
|
||||
@@ -30,8 +31,8 @@ namespace esphome {
|
||||
static const char *const TAG = "helpers";
|
||||
|
||||
void get_mac_address_raw(uint8_t *mac) {
|
||||
#if defined(USE_ESP32)
|
||||
#if defined(USE_ESP32_IGNORE_EFUSE_MAC_CRC)
|
||||
#ifdef USE_ESP32
|
||||
#ifdef USE_ESP32_IGNORE_EFUSE_MAC_CRC
|
||||
// On some devices, the MAC address that is burnt into EFuse does not
|
||||
// match the CRC that goes along with it. For those devices, this
|
||||
// work-around reads and uses the MAC address as-is from EFuse,
|
||||
@@ -40,21 +41,30 @@ void get_mac_address_raw(uint8_t *mac) {
|
||||
#else
|
||||
esp_efuse_mac_get_default(mac);
|
||||
#endif
|
||||
#elif defined(USE_ESP8266)
|
||||
wifi_get_macaddr(STATION_IF, mac);
|
||||
#endif
|
||||
#if (defined USE_ESP8266 && defined USE_WIFI)
|
||||
WiFi.macAddress(mac);
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string get_mac_address() {
|
||||
char tmp[20];
|
||||
uint8_t mac[6];
|
||||
get_mac_address_raw(mac);
|
||||
return str_snprintf("%02x%02x%02x%02x%02x%02x", 12, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
#ifdef USE_WIFI
|
||||
sprintf(tmp, "%02x%02x%02x%02x%02x%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
#else
|
||||
return "";
|
||||
#endif
|
||||
return std::string(tmp);
|
||||
}
|
||||
|
||||
std::string get_mac_address_pretty() {
|
||||
char tmp[20];
|
||||
uint8_t mac[6];
|
||||
get_mac_address_raw(mac);
|
||||
return str_snprintf("%02X:%02X:%02X:%02X:%02X:%02X", 17, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
sprintf(tmp, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
return std::string(tmp);
|
||||
}
|
||||
|
||||
#ifdef USE_ESP32
|
||||
@@ -325,20 +335,6 @@ bool str_startswith(const std::string &full, const std::string &start) { return
|
||||
bool str_endswith(const std::string &full, const std::string &ending) {
|
||||
return full.rfind(ending) == (full.size() - ending.size());
|
||||
}
|
||||
std::string str_snprintf(const char *fmt, size_t length, ...) {
|
||||
std::string str;
|
||||
va_list args;
|
||||
|
||||
str.resize(length);
|
||||
va_start(args, length);
|
||||
size_t out_length = vsnprintf(&str[0], length + 1, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
if (out_length < length)
|
||||
str.resize(out_length);
|
||||
|
||||
return str;
|
||||
}
|
||||
std::string str_sprintf(const char *fmt, ...) {
|
||||
std::string str;
|
||||
va_list args;
|
||||
|
||||
@@ -25,13 +25,14 @@
|
||||
|
||||
namespace esphome {
|
||||
|
||||
/// Get the device MAC address as raw bytes, written into the provided byte array (6 bytes).
|
||||
/// Read the raw MAC address into the provided byte array (6 bytes).
|
||||
void get_mac_address_raw(uint8_t *mac);
|
||||
|
||||
/// Get the device MAC address as a string, in lowercase hex notation.
|
||||
/// Get the MAC address as a string, using lower case hex notation.
|
||||
/// This can be used as way to identify this ESP.
|
||||
std::string get_mac_address();
|
||||
|
||||
/// Get the device MAC address as a string, in colon-separated uppercase hex notation.
|
||||
/// Get the MAC address as a string, using colon-separated upper case hex notation.
|
||||
std::string get_mac_address_pretty();
|
||||
|
||||
#ifdef USE_ESP32
|
||||
@@ -57,10 +58,7 @@ bool str_equals_case_insensitive(const std::string &a, const std::string &b);
|
||||
bool str_startswith(const std::string &full, const std::string &start);
|
||||
bool str_endswith(const std::string &full, const std::string &ending);
|
||||
|
||||
/// snprintf-like function returning std::string with a given maximum length.
|
||||
std::string __attribute__((format(printf, 1, 3))) str_snprintf(const char *fmt, size_t length, ...);
|
||||
|
||||
/// sprintf-like function returning std::string.
|
||||
/// sprintf-like function returning std::string instead of writing to char array.
|
||||
std::string __attribute__((format(printf, 1, 2))) str_sprintf(const char *fmt, ...);
|
||||
|
||||
class HighFrequencyLoopRequester {
|
||||
|
||||
@@ -32,7 +32,7 @@ void HOT Scheduler::set_timeout(Component *component, const std::string &name, u
|
||||
item->timeout = timeout;
|
||||
item->last_execution = now;
|
||||
item->last_execution_major = this->millis_major_;
|
||||
item->void_callback = std::move(func);
|
||||
item->f = std::move(func);
|
||||
item->remove = false;
|
||||
this->push_(std::move(item));
|
||||
}
|
||||
@@ -65,47 +65,13 @@ void HOT Scheduler::set_interval(Component *component, const std::string &name,
|
||||
item->last_execution_major = this->millis_major_;
|
||||
if (item->last_execution > now)
|
||||
item->last_execution_major--;
|
||||
item->void_callback = std::move(func);
|
||||
item->f = std::move(func);
|
||||
item->remove = false;
|
||||
this->push_(std::move(item));
|
||||
}
|
||||
bool HOT Scheduler::cancel_interval(Component *component, const std::string &name) {
|
||||
return this->cancel_item_(component, name, SchedulerItem::INTERVAL);
|
||||
}
|
||||
|
||||
void HOT Scheduler::set_retry(Component *component, const std::string &name, uint32_t initial_wait_time,
|
||||
uint8_t max_attempts, std::function<RetryResult()> &&func,
|
||||
float backoff_increase_factor) {
|
||||
const uint32_t now = this->millis_();
|
||||
|
||||
if (!name.empty())
|
||||
this->cancel_retry(component, name);
|
||||
|
||||
if (initial_wait_time == SCHEDULER_DONT_RUN)
|
||||
return;
|
||||
|
||||
ESP_LOGVV(TAG, "set_retry(name='%s', initial_wait_time=%u,max_attempts=%u, backoff_factor=%0.1f)", name.c_str(),
|
||||
initial_wait_time, max_attempts, backoff_increase_factor);
|
||||
|
||||
auto item = make_unique<SchedulerItem>();
|
||||
item->component = component;
|
||||
item->name = name;
|
||||
item->type = SchedulerItem::RETRY;
|
||||
item->interval = initial_wait_time;
|
||||
item->retry_countdown = max_attempts;
|
||||
item->backoff_multiplier = backoff_increase_factor;
|
||||
item->last_execution = now - initial_wait_time;
|
||||
item->last_execution_major = this->millis_major_;
|
||||
if (item->last_execution > now)
|
||||
item->last_execution_major--;
|
||||
item->retry_callback = std::move(func);
|
||||
item->remove = false;
|
||||
this->push_(std::move(item));
|
||||
}
|
||||
bool HOT Scheduler::cancel_retry(Component *component, const std::string &name) {
|
||||
return this->cancel_item_(component, name, SchedulerItem::RETRY);
|
||||
}
|
||||
|
||||
optional<uint32_t> HOT Scheduler::next_schedule_in() {
|
||||
if (this->empty_())
|
||||
return {};
|
||||
@@ -129,9 +95,10 @@ void IRAM_ATTR HOT Scheduler::call() {
|
||||
ESP_LOGVV(TAG, "Items: count=%u, now=%u", this->items_.size(), now);
|
||||
while (!this->empty_()) {
|
||||
auto item = std::move(this->items_[0]);
|
||||
ESP_LOGVV(TAG, " %s '%s' interval=%u last_execution=%u (%u) next=%u (%u)", item->get_type_str(),
|
||||
item->name.c_str(), item->interval, item->last_execution, item->last_execution_major,
|
||||
item->next_execution(), item->next_execution_major());
|
||||
const char *type = item->type == SchedulerItem::INTERVAL ? "interval" : "timeout";
|
||||
ESP_LOGVV(TAG, " %s '%s' interval=%u last_execution=%u (%u) next=%u (%u)", type, item->name.c_str(),
|
||||
item->interval, item->last_execution, item->last_execution_major, item->next_execution(),
|
||||
item->next_execution_major());
|
||||
|
||||
this->pop_raw_();
|
||||
old_items.push_back(std::move(item));
|
||||
@@ -162,7 +129,6 @@ void IRAM_ATTR HOT Scheduler::call() {
|
||||
}
|
||||
|
||||
while (!this->empty_()) {
|
||||
RetryResult retry_result = RETRY;
|
||||
// use scoping to indicate visibility of `item` variable
|
||||
{
|
||||
// Don't copy-by value yet
|
||||
@@ -181,19 +147,17 @@ void IRAM_ATTR HOT Scheduler::call() {
|
||||
}
|
||||
|
||||
#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
|
||||
ESP_LOGVV(TAG, "Running %s '%s' with interval=%u last_execution=%u (now=%u)", item->get_type_str(),
|
||||
item->name.c_str(), item->interval, item->last_execution, now);
|
||||
const char *type = item->type == SchedulerItem::INTERVAL ? "interval" : "timeout";
|
||||
ESP_LOGVV(TAG, "Running %s '%s' with interval=%u last_execution=%u (now=%u)", type, item->name.c_str(),
|
||||
item->interval, item->last_execution, now);
|
||||
#endif
|
||||
|
||||
// Warning: During callback(), a lot of stuff can happen, including:
|
||||
// Warning: During f(), a lot of stuff can happen, including:
|
||||
// - timeouts/intervals get added, potentially invalidating vector pointers
|
||||
// - timeouts/intervals get cancelled
|
||||
{
|
||||
WarnIfComponentBlockingGuard guard{item->component};
|
||||
if (item->type == SchedulerItem::RETRY)
|
||||
retry_result = item->retry_callback();
|
||||
else
|
||||
item->void_callback();
|
||||
item->f();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,16 +175,13 @@ void IRAM_ATTR HOT Scheduler::call() {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (item->type == SchedulerItem::INTERVAL ||
|
||||
(item->type == SchedulerItem::RETRY && (--item->retry_countdown > 0 && retry_result != RetryResult::DONE))) {
|
||||
if (item->type == SchedulerItem::INTERVAL) {
|
||||
if (item->interval != 0) {
|
||||
const uint32_t before = item->last_execution;
|
||||
const uint32_t amount = (now - item->last_execution) / item->interval;
|
||||
item->last_execution += amount * item->interval;
|
||||
if (item->last_execution < before)
|
||||
item->last_execution_major++;
|
||||
if (item->type == SchedulerItem::RETRY)
|
||||
item->interval *= item->backoff_multiplier;
|
||||
}
|
||||
this->push_(std::move(item));
|
||||
}
|
||||
|
||||
@@ -15,10 +15,6 @@ class Scheduler {
|
||||
void set_interval(Component *component, const std::string &name, uint32_t interval, std::function<void()> &&func);
|
||||
bool cancel_interval(Component *component, const std::string &name);
|
||||
|
||||
void set_retry(Component *component, const std::string &name, uint32_t initial_wait_time, uint8_t max_attempts,
|
||||
std::function<RetryResult()> &&func, float backoff_increase_factor = 1.0f);
|
||||
bool cancel_retry(Component *component, const std::string &name);
|
||||
|
||||
optional<uint32_t> next_schedule_in();
|
||||
|
||||
void call();
|
||||
@@ -29,20 +25,13 @@ class Scheduler {
|
||||
struct SchedulerItem {
|
||||
Component *component;
|
||||
std::string name;
|
||||
enum Type { TIMEOUT, INTERVAL, RETRY } type;
|
||||
enum Type { TIMEOUT, INTERVAL } type;
|
||||
union {
|
||||
uint32_t interval;
|
||||
uint32_t timeout;
|
||||
};
|
||||
uint32_t last_execution;
|
||||
// Ideally this should be a union or std::variant
|
||||
// but unions don't work with object like std::function
|
||||
// union CallBack_{
|
||||
std::function<void()> void_callback;
|
||||
std::function<RetryResult()> retry_callback;
|
||||
// };
|
||||
uint8_t retry_countdown{3};
|
||||
float backoff_multiplier{1.0f};
|
||||
std::function<void()> f;
|
||||
bool remove;
|
||||
uint8_t last_execution_major;
|
||||
|
||||
@@ -56,18 +45,6 @@ class Scheduler {
|
||||
}
|
||||
|
||||
static bool cmp(const std::unique_ptr<SchedulerItem> &a, const std::unique_ptr<SchedulerItem> &b);
|
||||
const char *get_type_str() {
|
||||
switch (this->type) {
|
||||
case SchedulerItem::INTERVAL:
|
||||
return "interval";
|
||||
case SchedulerItem::RETRY:
|
||||
return "retry";
|
||||
case SchedulerItem::TIMEOUT:
|
||||
return "timeout";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
uint32_t millis_();
|
||||
|
||||
@@ -970,17 +970,16 @@ def start_web_server(args):
|
||||
server.add_socket(socket)
|
||||
else:
|
||||
_LOGGER.info(
|
||||
"Starting dashboard web server on http://%s:%s and configuration dir %s...",
|
||||
args.address,
|
||||
"Starting dashboard web server on http://0.0.0.0:%s and configuration dir %s...",
|
||||
args.port,
|
||||
settings.config_dir,
|
||||
)
|
||||
app.listen(args.port, args.address)
|
||||
app.listen(args.port)
|
||||
|
||||
if args.open_ui:
|
||||
import webbrowser
|
||||
|
||||
webbrowser.open(f"http://{args.address}:{args.port}")
|
||||
webbrowser.open(f"localhost:{args.port}")
|
||||
|
||||
if settings.status_use_ping:
|
||||
status_thread = PingStatusThread()
|
||||
|
||||
@@ -28,7 +28,6 @@ build_flags =
|
||||
lib_deps =
|
||||
esphome/noise-c@0.1.4 ; api
|
||||
makuna/NeoPixelBus@2.6.9 ; neopixelbus
|
||||
esphome/Improv@1.0.0 ; improv_serial / esp32_improv
|
||||
build_flags =
|
||||
-DESPHOME_LOG_LEVEL=ESPHOME_LOG_LEVEL_VERY_VERBOSE
|
||||
src_filter =
|
||||
@@ -46,12 +45,11 @@ lib_deps =
|
||||
fastled/FastLED@3.3.2 ; fastled_base
|
||||
mikalhart/TinyGPSPlus@1.0.2 ; gps
|
||||
freekode/TM1651@1.0.1 ; tm1651
|
||||
seeed-studio/Grove - Laser PM2.5 Sensor HM3301@1.0.3 ; hm3301
|
||||
glmnet/Dsmr@0.5 ; dsmr
|
||||
rweather/Crypto@0.2.0 ; dsmr
|
||||
dudanov/MideaUART@1.1.8 ; midea
|
||||
; PIO isn't update releases correctly, see:
|
||||
; https://github.com/ToniA/arduino-heatpumpir/commit/0948c619d86407a4e50e8db2f3c193e0576c86fd
|
||||
https://github.com/ToniA/arduino-heatpumpir.git#1.0.18 ; heatpumpir
|
||||
tonia/HeatpumpIR@1.0.15 ; heatpumpir
|
||||
build_flags =
|
||||
${common.build_flags}
|
||||
-DUSE_ARDUINO
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
pylint==2.11.1
|
||||
flake8==4.0.1
|
||||
black==21.11b1
|
||||
black==21.10b0
|
||||
pexpect==4.8.0
|
||||
pre-commit
|
||||
|
||||
|
||||
@@ -98,7 +98,6 @@ mqtt:
|
||||
discovery: True
|
||||
discovery_retain: False
|
||||
discovery_prefix: discovery
|
||||
discovery_unique_id_generator: legacy
|
||||
topic_prefix: helloworld
|
||||
log_topic:
|
||||
topic: helloworld/hi
|
||||
@@ -1708,8 +1707,6 @@ climate:
|
||||
min_temperature: 18
|
||||
max_temperature: 30
|
||||
- platform: midea
|
||||
on_state:
|
||||
logger.log: "State changed!"
|
||||
id: midea_unit
|
||||
uart_id: uart0
|
||||
name: Midea Climate
|
||||
|
||||
@@ -39,12 +39,6 @@ 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
|
||||
|
||||
@@ -254,8 +254,6 @@ uart:
|
||||
tx_pin: GPIO4
|
||||
rx_pin: GPIO5
|
||||
baud_rate: 38400
|
||||
# Specifically added for testing debug with no options at all.
|
||||
debug:
|
||||
|
||||
modbus:
|
||||
uart_id: uart1
|
||||
@@ -1310,9 +1308,6 @@ fingerprint_grow:
|
||||
dsmr:
|
||||
decryption_key: 00112233445566778899aabbccddeeff
|
||||
uart_id: uart6
|
||||
max_telegram_length: 1000
|
||||
request_pin: D5
|
||||
request_interval: 20s
|
||||
|
||||
daly_bms:
|
||||
update_interval: 20s
|
||||
|
||||
@@ -66,7 +66,7 @@ def test_string_string__invalid(value):
|
||||
config_validation.string_strict(value)
|
||||
|
||||
|
||||
@given(builds(lambda v: "mdi:" + v, text(alphabet=string.ascii_letters + string.digits + "-_", min_size=1, max_size=20)))
|
||||
@given(builds(lambda v: "mdi:" + v, text()))
|
||||
@example("")
|
||||
def test_icon__valid(value):
|
||||
actual = config_validation.icon(value)
|
||||
@@ -75,7 +75,7 @@ def test_icon__valid(value):
|
||||
|
||||
|
||||
def test_icon__invalid():
|
||||
with pytest.raises(Invalid, match="Icons must match the format "):
|
||||
with pytest.raises(Invalid, match="Icons should start with prefix"):
|
||||
config_validation.icon("foo")
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user