mirror of
https://github.com/esphome/esphome.git
synced 2025-08-03 17:07:54 +00:00
Merge branch 'integration' into memory_api
This commit is contained in:
commit
f27ef9210a
@ -1 +1 @@
|
|||||||
a3cdfc378d28b53b416a1d5bf0ab9077ee18867f0d39436ea8013cf5a4ead87a
|
a3cdfc378d28b53b416a1d5bf0ab9077ee18867f0d39436ea8013cf5a4ead87a
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
[run]
|
[run]
|
||||||
omit =
|
omit =
|
||||||
esphome/components/*
|
esphome/components/*
|
||||||
tests/integration/*
|
tests/integration/*
|
||||||
|
1
.github/workflows/ci-clang-tidy-hash.yml
vendored
1
.github/workflows/ci-clang-tidy-hash.yml
vendored
@ -73,4 +73,3 @@ jobs:
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
26
.github/workflows/ci.yml
vendored
26
.github/workflows/ci.yml
vendored
@ -84,29 +84,6 @@ jobs:
|
|||||||
run: script/ci-suggest-changes
|
run: script/ci-suggest-changes
|
||||||
if: always()
|
if: always()
|
||||||
|
|
||||||
pyupgrade:
|
|
||||||
name: Check pyupgrade
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
needs:
|
|
||||||
- common
|
|
||||||
- determine-jobs
|
|
||||||
if: needs.determine-jobs.outputs.python-linters == 'true'
|
|
||||||
steps:
|
|
||||||
- name: Check out code from GitHub
|
|
||||||
uses: actions/checkout@v4.2.2
|
|
||||||
- name: Restore Python
|
|
||||||
uses: ./.github/actions/restore-python
|
|
||||||
with:
|
|
||||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
|
||||||
cache-key: ${{ needs.common.outputs.cache-key }}
|
|
||||||
- name: Run pyupgrade
|
|
||||||
run: |
|
|
||||||
. venv/bin/activate
|
|
||||||
pyupgrade ${{ env.PYUPGRADE_TARGET }} `find esphome -name "*.py" -type f`
|
|
||||||
- name: Suggested changes
|
|
||||||
run: script/ci-suggest-changes
|
|
||||||
if: always()
|
|
||||||
|
|
||||||
ci-custom:
|
ci-custom:
|
||||||
name: Run script/ci-custom
|
name: Run script/ci-custom
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
@ -512,7 +489,7 @@ jobs:
|
|||||||
cache-key: ${{ needs.common.outputs.cache-key }}
|
cache-key: ${{ needs.common.outputs.cache-key }}
|
||||||
- uses: pre-commit/action@v3.0.1
|
- uses: pre-commit/action@v3.0.1
|
||||||
env:
|
env:
|
||||||
SKIP: pylint,clang-tidy-hash,yamllint
|
SKIP: pylint,clang-tidy-hash
|
||||||
- uses: pre-commit-ci/lite-action@v1.1.0
|
- uses: pre-commit-ci/lite-action@v1.1.0
|
||||||
if: always()
|
if: always()
|
||||||
|
|
||||||
@ -525,7 +502,6 @@ jobs:
|
|||||||
- pylint
|
- pylint
|
||||||
- pytest
|
- pytest
|
||||||
- integration-tests
|
- integration-tests
|
||||||
- pyupgrade
|
|
||||||
- clang-tidy-deps
|
- clang-tidy-deps
|
||||||
- clang-tidy
|
- clang-tidy
|
||||||
- determine-jobs
|
- determine-jobs
|
||||||
|
25
.github/workflows/yaml-lint.yml
vendored
25
.github/workflows/yaml-lint.yml
vendored
@ -1,25 +0,0 @@
|
|||||||
---
|
|
||||||
name: YAML lint
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [dev, beta, release]
|
|
||||||
paths:
|
|
||||||
- "**.yaml"
|
|
||||||
- "**.yml"
|
|
||||||
pull_request:
|
|
||||||
paths:
|
|
||||||
- "**.yaml"
|
|
||||||
- "**.yml"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
yamllint:
|
|
||||||
name: yamllint
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Check out code from GitHub
|
|
||||||
uses: actions/checkout@v4.2.2
|
|
||||||
- name: Run yamllint
|
|
||||||
uses: frenck/action-yamllint@v1.5.0
|
|
||||||
with:
|
|
||||||
strict: true
|
|
@ -6,7 +6,7 @@ ci:
|
|||||||
autoupdate_commit_msg: 'pre-commit: autoupdate'
|
autoupdate_commit_msg: 'pre-commit: autoupdate'
|
||||||
autoupdate_schedule: off # Disabled until ruff versions are synced between deps and pre-commit
|
autoupdate_schedule: off # Disabled until ruff versions are synced between deps and pre-commit
|
||||||
# Skip hooks that have issues in pre-commit CI environment
|
# Skip hooks that have issues in pre-commit CI environment
|
||||||
skip: [pylint, clang-tidy-hash, yamllint]
|
skip: [pylint, clang-tidy-hash]
|
||||||
|
|
||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||||
@ -27,13 +27,15 @@ repos:
|
|||||||
- pydocstyle==5.1.1
|
- pydocstyle==5.1.1
|
||||||
files: ^(esphome|tests)/.+\.py$
|
files: ^(esphome|tests)/.+\.py$
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
rev: v3.4.0
|
rev: v5.0.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: no-commit-to-branch
|
- id: no-commit-to-branch
|
||||||
args:
|
args:
|
||||||
- --branch=dev
|
- --branch=dev
|
||||||
- --branch=release
|
- --branch=release
|
||||||
- --branch=beta
|
- --branch=beta
|
||||||
|
- id: end-of-file-fixer
|
||||||
|
- id: trailing-whitespace
|
||||||
- repo: https://github.com/asottile/pyupgrade
|
- repo: https://github.com/asottile/pyupgrade
|
||||||
rev: v3.20.0
|
rev: v3.20.0
|
||||||
hooks:
|
hooks:
|
||||||
@ -43,6 +45,7 @@ repos:
|
|||||||
rev: v1.37.1
|
rev: v1.37.1
|
||||||
hooks:
|
hooks:
|
||||||
- id: yamllint
|
- id: yamllint
|
||||||
|
exclude: ^(\.clang-format|\.clang-tidy)$
|
||||||
- repo: https://github.com/pre-commit/mirrors-clang-format
|
- repo: https://github.com/pre-commit/mirrors-clang-format
|
||||||
rev: v13.0.1
|
rev: v13.0.1
|
||||||
hooks:
|
hooks:
|
||||||
|
@ -31,7 +31,7 @@ CONFIG_SCHEMA = cv.All(
|
|||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
if CORE.is_esp32 or CORE.is_libretiny:
|
if CORE.is_esp32 or CORE.is_libretiny:
|
||||||
# https://github.com/ESP32Async/AsyncTCP
|
# https://github.com/ESP32Async/AsyncTCP
|
||||||
cg.add_library("ESP32Async/AsyncTCP", "3.4.4")
|
cg.add_library("ESP32Async/AsyncTCP", "3.4.5")
|
||||||
elif CORE.is_esp8266:
|
elif CORE.is_esp8266:
|
||||||
# https://github.com/ESP32Async/ESPAsyncTCP
|
# https://github.com/ESP32Async/ESPAsyncTCP
|
||||||
cg.add_library("ESP32Async/ESPAsyncTCP", "2.0.0")
|
cg.add_library("ESP32Async/ESPAsyncTCP", "2.0.0")
|
||||||
|
@ -85,13 +85,13 @@ async def to_code(config):
|
|||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
|
|
||||||
cg.add(var.set_active(config[CONF_ACTIVE]))
|
cg.add(var.set_active(config[CONF_ACTIVE]))
|
||||||
await esp32_ble_tracker.register_ble_device(var, config)
|
await esp32_ble_tracker.register_raw_ble_device(var, config)
|
||||||
|
|
||||||
for connection_conf in config.get(CONF_CONNECTIONS, []):
|
for connection_conf in config.get(CONF_CONNECTIONS, []):
|
||||||
connection_var = cg.new_Pvariable(connection_conf[CONF_ID])
|
connection_var = cg.new_Pvariable(connection_conf[CONF_ID])
|
||||||
await cg.register_component(connection_var, connection_conf)
|
await cg.register_component(connection_var, connection_conf)
|
||||||
cg.add(var.register_connection(connection_var))
|
cg.add(var.register_connection(connection_var))
|
||||||
await esp32_ble_tracker.register_client(connection_var, connection_conf)
|
await esp32_ble_tracker.register_raw_client(connection_var, connection_conf)
|
||||||
|
|
||||||
if config.get(CONF_CACHE_SERVICES):
|
if config.get(CONF_CACHE_SERVICES):
|
||||||
add_idf_sdkconfig_option("CONFIG_BT_GATTC_CACHE_NVS_FLASH", True)
|
add_idf_sdkconfig_option("CONFIG_BT_GATTC_CACHE_NVS_FLASH", True)
|
||||||
|
@ -42,15 +42,13 @@ void BluetoothProxy::send_bluetooth_scanner_state_(esp32_ble_tracker::ScannerSta
|
|||||||
this->api_connection_->send_message(resp);
|
this->api_connection_->send_message(resp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef USE_ESP32_BLE_DEVICE
|
||||||
bool BluetoothProxy::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
|
bool BluetoothProxy::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
|
||||||
if (!api::global_api_server->is_connected() || this->api_connection_ == nullptr || this->raw_advertisements_)
|
// This method should never be called since bluetooth_proxy always uses raw advertisements
|
||||||
return false;
|
// but we need to provide an implementation to satisfy the virtual method requirement
|
||||||
|
return false;
|
||||||
ESP_LOGV(TAG, "Proxying packet from %s - %s. RSSI: %d dB", device.get_name().c_str(), device.address_str().c_str(),
|
|
||||||
device.get_rssi());
|
|
||||||
this->send_api_packet_(device);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Batch size for BLE advertisements to maximize WiFi efficiency
|
// Batch size for BLE advertisements to maximize WiFi efficiency
|
||||||
// Each advertisement is up to 80 bytes when packaged (including protocol overhead)
|
// Each advertisement is up to 80 bytes when packaged (including protocol overhead)
|
||||||
@ -69,7 +67,7 @@ std::vector<api::BluetoothLERawAdvertisement> batch_buffer;
|
|||||||
static std::vector<api::BluetoothLERawAdvertisement> &get_batch_buffer() { return batch_buffer; }
|
static std::vector<api::BluetoothLERawAdvertisement> &get_batch_buffer() { return batch_buffer; }
|
||||||
|
|
||||||
bool BluetoothProxy::parse_devices(const esp32_ble::BLEScanResult *scan_results, size_t count) {
|
bool BluetoothProxy::parse_devices(const esp32_ble::BLEScanResult *scan_results, size_t count) {
|
||||||
if (!api::global_api_server->is_connected() || this->api_connection_ == nullptr || !this->raw_advertisements_)
|
if (!api::global_api_server->is_connected() || this->api_connection_ == nullptr)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Get the batch buffer reference
|
// Get the batch buffer reference
|
||||||
@ -116,6 +114,7 @@ void BluetoothProxy::flush_pending_advertisements() {
|
|||||||
this->api_connection_->send_message(resp);
|
this->api_connection_->send_message(resp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef USE_ESP32_BLE_DEVICE
|
||||||
void BluetoothProxy::send_api_packet_(const esp32_ble_tracker::ESPBTDevice &device) {
|
void BluetoothProxy::send_api_packet_(const esp32_ble_tracker::ESPBTDevice &device) {
|
||||||
api::BluetoothLEAdvertisementResponse resp;
|
api::BluetoothLEAdvertisementResponse resp;
|
||||||
resp.address = device.address_uint64();
|
resp.address = device.address_uint64();
|
||||||
@ -153,14 +152,14 @@ void BluetoothProxy::send_api_packet_(const esp32_ble_tracker::ESPBTDevice &devi
|
|||||||
|
|
||||||
this->api_connection_->send_message(resp);
|
this->api_connection_->send_message(resp);
|
||||||
}
|
}
|
||||||
|
#endif // USE_ESP32_BLE_DEVICE
|
||||||
|
|
||||||
void BluetoothProxy::dump_config() {
|
void BluetoothProxy::dump_config() {
|
||||||
ESP_LOGCONFIG(TAG, "Bluetooth Proxy:");
|
ESP_LOGCONFIG(TAG, "Bluetooth Proxy:");
|
||||||
ESP_LOGCONFIG(TAG,
|
ESP_LOGCONFIG(TAG,
|
||||||
" Active: %s\n"
|
" Active: %s\n"
|
||||||
" Connections: %d\n"
|
" Connections: %d",
|
||||||
" Raw advertisements: %s",
|
YESNO(this->active_), this->connections_.size());
|
||||||
YESNO(this->active_), this->connections_.size(), YESNO(this->raw_advertisements_));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int BluetoothProxy::get_bluetooth_connections_free() {
|
int BluetoothProxy::get_bluetooth_connections_free() {
|
||||||
@ -188,15 +187,13 @@ void BluetoothProxy::loop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Flush any pending BLE advertisements that have been accumulated but not yet sent
|
// Flush any pending BLE advertisements that have been accumulated but not yet sent
|
||||||
if (this->raw_advertisements_) {
|
static uint32_t last_flush_time = 0;
|
||||||
static uint32_t last_flush_time = 0;
|
uint32_t now = App.get_loop_component_start_time();
|
||||||
uint32_t now = App.get_loop_component_start_time();
|
|
||||||
|
|
||||||
// Flush accumulated advertisements every 100ms
|
// Flush accumulated advertisements every 100ms
|
||||||
if (now - last_flush_time >= 100) {
|
if (now - last_flush_time >= 100) {
|
||||||
this->flush_pending_advertisements();
|
this->flush_pending_advertisements();
|
||||||
last_flush_time = now;
|
last_flush_time = now;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
for (auto *connection : this->connections_) {
|
for (auto *connection : this->connections_) {
|
||||||
if (connection->send_service_ == connection->service_count_) {
|
if (connection->send_service_ == connection->service_count_) {
|
||||||
@ -318,9 +315,7 @@ void BluetoothProxy::loop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
esp32_ble_tracker::AdvertisementParserType BluetoothProxy::get_advertisement_parser_type() {
|
esp32_ble_tracker::AdvertisementParserType BluetoothProxy::get_advertisement_parser_type() {
|
||||||
if (this->raw_advertisements_)
|
return esp32_ble_tracker::AdvertisementParserType::RAW_ADVERTISEMENTS;
|
||||||
return esp32_ble_tracker::AdvertisementParserType::RAW_ADVERTISEMENTS;
|
|
||||||
return esp32_ble_tracker::AdvertisementParserType::PARSED_ADVERTISEMENTS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BluetoothConnection *BluetoothProxy::get_connection_(uint64_t address, bool reserve) {
|
BluetoothConnection *BluetoothProxy::get_connection_(uint64_t address, bool reserve) {
|
||||||
@ -565,7 +560,6 @@ void BluetoothProxy::subscribe_api_connection(api::APIConnection *api_connection
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this->api_connection_ = api_connection;
|
this->api_connection_ = api_connection;
|
||||||
this->raw_advertisements_ = flags & BluetoothProxySubscriptionFlag::SUBSCRIPTION_RAW_ADVERTISEMENTS;
|
|
||||||
this->parent_->recalculate_advertisement_parser_types();
|
this->parent_->recalculate_advertisement_parser_types();
|
||||||
|
|
||||||
this->send_bluetooth_scanner_state_(this->parent_->get_scanner_state());
|
this->send_bluetooth_scanner_state_(this->parent_->get_scanner_state());
|
||||||
@ -577,7 +571,6 @@ void BluetoothProxy::unsubscribe_api_connection(api::APIConnection *api_connecti
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this->api_connection_ = nullptr;
|
this->api_connection_ = nullptr;
|
||||||
this->raw_advertisements_ = false;
|
|
||||||
this->parent_->recalculate_advertisement_parser_types();
|
this->parent_->recalculate_advertisement_parser_types();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,7 +51,9 @@ enum BluetoothProxySubscriptionFlag : uint32_t {
|
|||||||
class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Component {
|
class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Component {
|
||||||
public:
|
public:
|
||||||
BluetoothProxy();
|
BluetoothProxy();
|
||||||
|
#ifdef USE_ESP32_BLE_DEVICE
|
||||||
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override;
|
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override;
|
||||||
|
#endif
|
||||||
bool parse_devices(const esp32_ble::BLEScanResult *scan_results, size_t count) override;
|
bool parse_devices(const esp32_ble::BLEScanResult *scan_results, size_t count) override;
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
void setup() override;
|
void setup() override;
|
||||||
@ -129,7 +131,9 @@ class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Com
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
#ifdef USE_ESP32_BLE_DEVICE
|
||||||
void send_api_packet_(const esp32_ble_tracker::ESPBTDevice &device);
|
void send_api_packet_(const esp32_ble_tracker::ESPBTDevice &device);
|
||||||
|
#endif
|
||||||
void send_bluetooth_scanner_state_(esp32_ble_tracker::ScannerState state);
|
void send_bluetooth_scanner_state_(esp32_ble_tracker::ScannerState state);
|
||||||
|
|
||||||
BluetoothConnection *get_connection_(uint64_t address, bool reserve);
|
BluetoothConnection *get_connection_(uint64_t address, bool reserve);
|
||||||
@ -143,8 +147,7 @@ class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Com
|
|||||||
|
|
||||||
// Group 3: 1-byte types grouped together
|
// Group 3: 1-byte types grouped together
|
||||||
bool active_;
|
bool active_;
|
||||||
bool raw_advertisements_{false};
|
// 1 byte used, 3 bytes padding
|
||||||
// 2 bytes used, 2 bytes padding
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern BluetoothProxy *global_bluetooth_proxy; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
extern BluetoothProxy *global_bluetooth_proxy; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||||
|
@ -105,6 +105,7 @@ void BLEClientBase::dump_config() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef USE_ESP32_BLE_DEVICE
|
||||||
bool BLEClientBase::parse_device(const espbt::ESPBTDevice &device) {
|
bool BLEClientBase::parse_device(const espbt::ESPBTDevice &device) {
|
||||||
if (!this->auto_connect_)
|
if (!this->auto_connect_)
|
||||||
return false;
|
return false;
|
||||||
@ -122,6 +123,7 @@ bool BLEClientBase::parse_device(const espbt::ESPBTDevice &device) {
|
|||||||
this->remote_addr_type_ = device.get_address_type();
|
this->remote_addr_type_ = device.get_address_type();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void BLEClientBase::connect() {
|
void BLEClientBase::connect() {
|
||||||
ESP_LOGI(TAG, "[%d] [%s] 0x%02x Attempting BLE connection", this->connection_index_, this->address_str_.c_str(),
|
ESP_LOGI(TAG, "[%d] [%s] 0x%02x Attempting BLE connection", this->connection_index_, this->address_str_.c_str(),
|
||||||
|
@ -31,7 +31,9 @@ class BLEClientBase : public espbt::ESPBTClient, public Component {
|
|||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
|
|
||||||
void run_later(std::function<void()> &&f); // NOLINT
|
void run_later(std::function<void()> &&f); // NOLINT
|
||||||
|
#ifdef USE_ESP32_BLE_DEVICE
|
||||||
bool parse_device(const espbt::ESPBTDevice &device) override;
|
bool parse_device(const espbt::ESPBTDevice &device) override;
|
||||||
|
#endif
|
||||||
void on_scan_end() override {}
|
void on_scan_end() override {}
|
||||||
bool gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
bool gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||||
esp_ble_gattc_cb_param_t *param) override;
|
esp_ble_gattc_cb_param_t *param) override;
|
||||||
|
@ -31,6 +31,8 @@ from esphome.const import (
|
|||||||
CONF_TRIGGER_ID,
|
CONF_TRIGGER_ID,
|
||||||
)
|
)
|
||||||
from esphome.core import CORE
|
from esphome.core import CORE
|
||||||
|
from esphome.enum import StrEnum
|
||||||
|
from esphome.types import ConfigType
|
||||||
|
|
||||||
AUTO_LOAD = ["esp32_ble"]
|
AUTO_LOAD = ["esp32_ble"]
|
||||||
DEPENDENCIES = ["esp32"]
|
DEPENDENCIES = ["esp32"]
|
||||||
@ -50,6 +52,25 @@ IDF_MAX_CONNECTIONS = 9
|
|||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
# Enum for BLE features
|
||||||
|
class BLEFeatures(StrEnum):
|
||||||
|
ESP_BT_DEVICE = "ESP_BT_DEVICE"
|
||||||
|
|
||||||
|
|
||||||
|
# Set to track which features are needed by components
|
||||||
|
_required_features: set[BLEFeatures] = set()
|
||||||
|
|
||||||
|
|
||||||
|
def register_ble_features(features: set[BLEFeatures]) -> None:
|
||||||
|
"""Register BLE features that a component needs.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
features: Set of BLEFeatures enum members
|
||||||
|
"""
|
||||||
|
_required_features.update(features)
|
||||||
|
|
||||||
|
|
||||||
esp32_ble_tracker_ns = cg.esphome_ns.namespace("esp32_ble_tracker")
|
esp32_ble_tracker_ns = cg.esphome_ns.namespace("esp32_ble_tracker")
|
||||||
ESP32BLETracker = esp32_ble_tracker_ns.class_(
|
ESP32BLETracker = esp32_ble_tracker_ns.class_(
|
||||||
"ESP32BLETracker",
|
"ESP32BLETracker",
|
||||||
@ -277,6 +298,15 @@ async def to_code(config):
|
|||||||
cg.add(var.set_scan_window(int(params[CONF_WINDOW].total_milliseconds / 0.625)))
|
cg.add(var.set_scan_window(int(params[CONF_WINDOW].total_milliseconds / 0.625)))
|
||||||
cg.add(var.set_scan_active(params[CONF_ACTIVE]))
|
cg.add(var.set_scan_active(params[CONF_ACTIVE]))
|
||||||
cg.add(var.set_scan_continuous(params[CONF_CONTINUOUS]))
|
cg.add(var.set_scan_continuous(params[CONF_CONTINUOUS]))
|
||||||
|
|
||||||
|
# Register ESP_BT_DEVICE feature if any of the automation triggers are used
|
||||||
|
if (
|
||||||
|
config.get(CONF_ON_BLE_ADVERTISE)
|
||||||
|
or config.get(CONF_ON_BLE_SERVICE_DATA_ADVERTISE)
|
||||||
|
or config.get(CONF_ON_BLE_MANUFACTURER_DATA_ADVERTISE)
|
||||||
|
):
|
||||||
|
register_ble_features({BLEFeatures.ESP_BT_DEVICE})
|
||||||
|
|
||||||
for conf in config.get(CONF_ON_BLE_ADVERTISE, []):
|
for conf in config.get(CONF_ON_BLE_ADVERTISE, []):
|
||||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||||
if CONF_MAC_ADDRESS in conf:
|
if CONF_MAC_ADDRESS in conf:
|
||||||
@ -334,6 +364,11 @@ async def to_code(config):
|
|||||||
|
|
||||||
cg.add_define("USE_OTA_STATE_CALLBACK") # To be notified when an OTA update starts
|
cg.add_define("USE_OTA_STATE_CALLBACK") # To be notified when an OTA update starts
|
||||||
cg.add_define("USE_ESP32_BLE_CLIENT")
|
cg.add_define("USE_ESP32_BLE_CLIENT")
|
||||||
|
|
||||||
|
# Add feature-specific defines based on what's needed
|
||||||
|
if BLEFeatures.ESP_BT_DEVICE in _required_features:
|
||||||
|
cg.add_define("USE_ESP32_BLE_DEVICE")
|
||||||
|
|
||||||
if config.get(CONF_SOFTWARE_COEXISTENCE):
|
if config.get(CONF_SOFTWARE_COEXISTENCE):
|
||||||
cg.add_define("USE_ESP32_BLE_SOFTWARE_COEXISTENCE")
|
cg.add_define("USE_ESP32_BLE_SOFTWARE_COEXISTENCE")
|
||||||
|
|
||||||
@ -382,13 +417,43 @@ async def esp32_ble_tracker_stop_scan_action_to_code(
|
|||||||
return var
|
return var
|
||||||
|
|
||||||
|
|
||||||
async def register_ble_device(var, config):
|
async def register_ble_device(
|
||||||
|
var: cg.SafeExpType, config: ConfigType
|
||||||
|
) -> cg.SafeExpType:
|
||||||
|
register_ble_features({BLEFeatures.ESP_BT_DEVICE})
|
||||||
paren = await cg.get_variable(config[CONF_ESP32_BLE_ID])
|
paren = await cg.get_variable(config[CONF_ESP32_BLE_ID])
|
||||||
cg.add(paren.register_listener(var))
|
cg.add(paren.register_listener(var))
|
||||||
return var
|
return var
|
||||||
|
|
||||||
|
|
||||||
async def register_client(var, config):
|
async def register_client(var: cg.SafeExpType, config: ConfigType) -> cg.SafeExpType:
|
||||||
|
register_ble_features({BLEFeatures.ESP_BT_DEVICE})
|
||||||
|
paren = await cg.get_variable(config[CONF_ESP32_BLE_ID])
|
||||||
|
cg.add(paren.register_client(var))
|
||||||
|
return var
|
||||||
|
|
||||||
|
|
||||||
|
async def register_raw_ble_device(
|
||||||
|
var: cg.SafeExpType, config: ConfigType
|
||||||
|
) -> cg.SafeExpType:
|
||||||
|
"""Register a BLE device listener that only needs raw advertisement data.
|
||||||
|
|
||||||
|
This does NOT register the ESP_BT_DEVICE feature, meaning ESPBTDevice
|
||||||
|
will not be compiled in if this is the only registration method used.
|
||||||
|
"""
|
||||||
|
paren = await cg.get_variable(config[CONF_ESP32_BLE_ID])
|
||||||
|
cg.add(paren.register_listener(var))
|
||||||
|
return var
|
||||||
|
|
||||||
|
|
||||||
|
async def register_raw_client(
|
||||||
|
var: cg.SafeExpType, config: ConfigType
|
||||||
|
) -> cg.SafeExpType:
|
||||||
|
"""Register a BLE client that only needs raw advertisement data.
|
||||||
|
|
||||||
|
This does NOT register the ESP_BT_DEVICE feature, meaning ESPBTDevice
|
||||||
|
will not be compiled in if this is the only registration method used.
|
||||||
|
"""
|
||||||
paren = await cg.get_variable(config[CONF_ESP32_BLE_ID])
|
paren = await cg.get_variable(config[CONF_ESP32_BLE_ID])
|
||||||
cg.add(paren.register_client(var))
|
cg.add(paren.register_client(var))
|
||||||
return var
|
return var
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace esp32_ble_tracker {
|
namespace esp32_ble_tracker {
|
||||||
|
#ifdef USE_ESP32_BLE_DEVICE
|
||||||
class ESPBTAdvertiseTrigger : public Trigger<const ESPBTDevice &>, public ESPBTDeviceListener {
|
class ESPBTAdvertiseTrigger : public Trigger<const ESPBTDevice &>, public ESPBTDeviceListener {
|
||||||
public:
|
public:
|
||||||
explicit ESPBTAdvertiseTrigger(ESP32BLETracker *parent) { parent->register_listener(this); }
|
explicit ESPBTAdvertiseTrigger(ESP32BLETracker *parent) { parent->register_listener(this); }
|
||||||
@ -87,6 +88,7 @@ class BLEEndOfScanTrigger : public Trigger<>, public ESPBTDeviceListener {
|
|||||||
bool parse_device(const ESPBTDevice &device) override { return false; }
|
bool parse_device(const ESPBTDevice &device) override { return false; }
|
||||||
void on_scan_end() override { this->trigger(); }
|
void on_scan_end() override { this->trigger(); }
|
||||||
};
|
};
|
||||||
|
#endif // USE_ESP32_BLE_DEVICE
|
||||||
|
|
||||||
template<typename... Ts> class ESP32BLEStartScanAction : public Action<Ts...> {
|
template<typename... Ts> class ESP32BLEStartScanAction : public Action<Ts...> {
|
||||||
public:
|
public:
|
||||||
|
@ -141,6 +141,7 @@ void ESP32BLETracker::loop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this->parse_advertisements_) {
|
if (this->parse_advertisements_) {
|
||||||
|
#ifdef USE_ESP32_BLE_DEVICE
|
||||||
ESPBTDevice device;
|
ESPBTDevice device;
|
||||||
device.parse_scan_rst(scan_result);
|
device.parse_scan_rst(scan_result);
|
||||||
|
|
||||||
@ -162,6 +163,7 @@ void ESP32BLETracker::loop() {
|
|||||||
if (!found && !this->scan_continuous_) {
|
if (!found && !this->scan_continuous_) {
|
||||||
this->print_bt_device_info(device);
|
this->print_bt_device_info(device);
|
||||||
}
|
}
|
||||||
|
#endif // USE_ESP32_BLE_DEVICE
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move to next entry in ring buffer
|
// Move to next entry in ring buffer
|
||||||
@ -511,6 +513,7 @@ void ESP32BLETracker::set_scanner_state_(ScannerState state) {
|
|||||||
this->scanner_state_callbacks_.call(state);
|
this->scanner_state_callbacks_.call(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef USE_ESP32_BLE_DEVICE
|
||||||
ESPBLEiBeacon::ESPBLEiBeacon(const uint8_t *data) { memcpy(&this->beacon_data_, data, sizeof(beacon_data_)); }
|
ESPBLEiBeacon::ESPBLEiBeacon(const uint8_t *data) { memcpy(&this->beacon_data_, data, sizeof(beacon_data_)); }
|
||||||
optional<ESPBLEiBeacon> ESPBLEiBeacon::from_manufacturer_data(const ServiceData &data) {
|
optional<ESPBLEiBeacon> ESPBLEiBeacon::from_manufacturer_data(const ServiceData &data) {
|
||||||
if (!data.uuid.contains(0x4C, 0x00))
|
if (!data.uuid.contains(0x4C, 0x00))
|
||||||
@ -751,13 +754,16 @@ void ESPBTDevice::parse_adv_(const uint8_t *payload, uint8_t len) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ESPBTDevice::address_str() const {
|
std::string ESPBTDevice::address_str() const {
|
||||||
char mac[24];
|
char mac[24];
|
||||||
snprintf(mac, sizeof(mac), "%02X:%02X:%02X:%02X:%02X:%02X", this->address_[0], this->address_[1], this->address_[2],
|
snprintf(mac, sizeof(mac), "%02X:%02X:%02X:%02X:%02X:%02X", this->address_[0], this->address_[1], this->address_[2],
|
||||||
this->address_[3], this->address_[4], this->address_[5]);
|
this->address_[3], this->address_[4], this->address_[5]);
|
||||||
return mac;
|
return mac;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t ESPBTDevice::address_uint64() const { return esp32_ble::ble_addr_to_uint64(this->address_); }
|
uint64_t ESPBTDevice::address_uint64() const { return esp32_ble::ble_addr_to_uint64(this->address_); }
|
||||||
|
#endif // USE_ESP32_BLE_DEVICE
|
||||||
|
|
||||||
void ESP32BLETracker::dump_config() {
|
void ESP32BLETracker::dump_config() {
|
||||||
ESP_LOGCONFIG(TAG, "BLE Tracker:");
|
ESP_LOGCONFIG(TAG, "BLE Tracker:");
|
||||||
@ -796,6 +802,7 @@ void ESP32BLETracker::dump_config() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef USE_ESP32_BLE_DEVICE
|
||||||
void ESP32BLETracker::print_bt_device_info(const ESPBTDevice &device) {
|
void ESP32BLETracker::print_bt_device_info(const ESPBTDevice &device) {
|
||||||
const uint64_t address = device.address_uint64();
|
const uint64_t address = device.address_uint64();
|
||||||
for (auto &disc : this->already_discovered_) {
|
for (auto &disc : this->already_discovered_) {
|
||||||
@ -866,8 +873,9 @@ bool ESPBTDevice::resolve_irk(const uint8_t *irk) const {
|
|||||||
return ecb_ciphertext[15] == (addr64 & 0xff) && ecb_ciphertext[14] == ((addr64 >> 8) & 0xff) &&
|
return ecb_ciphertext[15] == (addr64 & 0xff) && ecb_ciphertext[14] == ((addr64 >> 8) & 0xff) &&
|
||||||
ecb_ciphertext[13] == ((addr64 >> 16) & 0xff);
|
ecb_ciphertext[13] == ((addr64 >> 16) & 0xff);
|
||||||
}
|
}
|
||||||
|
#endif // USE_ESP32_BLE_DEVICE
|
||||||
|
|
||||||
} // namespace esp32_ble_tracker
|
} // namespace esp32_ble_tracker
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
||||||
#endif
|
#endif // USE_ESP32
|
||||||
|
@ -39,6 +39,7 @@ struct ServiceData {
|
|||||||
adv_data_t data;
|
adv_data_t data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef USE_ESP32_BLE_DEVICE
|
||||||
class ESPBLEiBeacon {
|
class ESPBLEiBeacon {
|
||||||
public:
|
public:
|
||||||
ESPBLEiBeacon() { memset(&this->beacon_data_, 0, sizeof(this->beacon_data_)); }
|
ESPBLEiBeacon() { memset(&this->beacon_data_, 0, sizeof(this->beacon_data_)); }
|
||||||
@ -116,13 +117,16 @@ class ESPBTDevice {
|
|||||||
std::vector<ServiceData> service_datas_{};
|
std::vector<ServiceData> service_datas_{};
|
||||||
const BLEScanResult *scan_result_{nullptr};
|
const BLEScanResult *scan_result_{nullptr};
|
||||||
};
|
};
|
||||||
|
#endif // USE_ESP32_BLE_DEVICE
|
||||||
|
|
||||||
class ESP32BLETracker;
|
class ESP32BLETracker;
|
||||||
|
|
||||||
class ESPBTDeviceListener {
|
class ESPBTDeviceListener {
|
||||||
public:
|
public:
|
||||||
virtual void on_scan_end() {}
|
virtual void on_scan_end() {}
|
||||||
|
#ifdef USE_ESP32_BLE_DEVICE
|
||||||
virtual bool parse_device(const ESPBTDevice &device) = 0;
|
virtual bool parse_device(const ESPBTDevice &device) = 0;
|
||||||
|
#endif
|
||||||
virtual bool parse_devices(const BLEScanResult *scan_results, size_t count) { return false; };
|
virtual bool parse_devices(const BLEScanResult *scan_results, size_t count) { return false; };
|
||||||
virtual AdvertisementParserType get_advertisement_parser_type() {
|
virtual AdvertisementParserType get_advertisement_parser_type() {
|
||||||
return AdvertisementParserType::PARSED_ADVERTISEMENTS;
|
return AdvertisementParserType::PARSED_ADVERTISEMENTS;
|
||||||
@ -237,7 +241,9 @@ class ESP32BLETracker : public Component,
|
|||||||
void register_client(ESPBTClient *client);
|
void register_client(ESPBTClient *client);
|
||||||
void recalculate_advertisement_parser_types();
|
void recalculate_advertisement_parser_types();
|
||||||
|
|
||||||
|
#ifdef USE_ESP32_BLE_DEVICE
|
||||||
void print_bt_device_info(const ESPBTDevice &device);
|
void print_bt_device_info(const ESPBTDevice &device);
|
||||||
|
#endif
|
||||||
|
|
||||||
void start_scan();
|
void start_scan();
|
||||||
void stop_scan();
|
void stop_scan();
|
||||||
|
@ -83,7 +83,7 @@ void HttpRequestUpdate::update_task(void *params) {
|
|||||||
container.reset(); // Release ownership of the container's shared_ptr
|
container.reset(); // Release ownership of the container's shared_ptr
|
||||||
|
|
||||||
valid = json::parse_json(response, [this_update](JsonObject root) -> bool {
|
valid = json::parse_json(response, [this_update](JsonObject root) -> bool {
|
||||||
if (!root.containsKey("name") || !root.containsKey("version") || !root.containsKey("builds")) {
|
if (!root["name"].is<const char *>() || !root["version"].is<const char *>() || !root["builds"].is<JsonArray>()) {
|
||||||
ESP_LOGE(TAG, "Manifest does not contain required fields");
|
ESP_LOGE(TAG, "Manifest does not contain required fields");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -91,26 +91,26 @@ void HttpRequestUpdate::update_task(void *params) {
|
|||||||
this_update->update_info_.latest_version = root["version"].as<std::string>();
|
this_update->update_info_.latest_version = root["version"].as<std::string>();
|
||||||
|
|
||||||
for (auto build : root["builds"].as<JsonArray>()) {
|
for (auto build : root["builds"].as<JsonArray>()) {
|
||||||
if (!build.containsKey("chipFamily")) {
|
if (!build["chipFamily"].is<const char *>()) {
|
||||||
ESP_LOGE(TAG, "Manifest does not contain required fields");
|
ESP_LOGE(TAG, "Manifest does not contain required fields");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (build["chipFamily"] == ESPHOME_VARIANT) {
|
if (build["chipFamily"] == ESPHOME_VARIANT) {
|
||||||
if (!build.containsKey("ota")) {
|
if (!build["ota"].is<JsonObject>()) {
|
||||||
ESP_LOGE(TAG, "Manifest does not contain required fields");
|
ESP_LOGE(TAG, "Manifest does not contain required fields");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
auto ota = build["ota"];
|
JsonObject ota = build["ota"].as<JsonObject>();
|
||||||
if (!ota.containsKey("path") || !ota.containsKey("md5")) {
|
if (!ota["path"].is<const char *>() || !ota["md5"].is<const char *>()) {
|
||||||
ESP_LOGE(TAG, "Manifest does not contain required fields");
|
ESP_LOGE(TAG, "Manifest does not contain required fields");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
this_update->update_info_.firmware_url = ota["path"].as<std::string>();
|
this_update->update_info_.firmware_url = ota["path"].as<std::string>();
|
||||||
this_update->update_info_.md5 = ota["md5"].as<std::string>();
|
this_update->update_info_.md5 = ota["md5"].as<std::string>();
|
||||||
|
|
||||||
if (ota.containsKey("summary"))
|
if (ota["summary"].is<const char *>())
|
||||||
this_update->update_info_.summary = ota["summary"].as<std::string>();
|
this_update->update_info_.summary = ota["summary"].as<std::string>();
|
||||||
if (ota.containsKey("release_url"))
|
if (ota["release_url"].is<const char *>())
|
||||||
this_update->update_info_.release_url = ota["release_url"].as<std::string>();
|
this_update->update_info_.release_url = ota["release_url"].as<std::string>();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -12,6 +12,6 @@ CONFIG_SCHEMA = cv.All(
|
|||||||
|
|
||||||
@coroutine_with_priority(1.0)
|
@coroutine_with_priority(1.0)
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
cg.add_library("bblanchon/ArduinoJson", "6.18.5")
|
cg.add_library("bblanchon/ArduinoJson", "7.4.2")
|
||||||
cg.add_define("USE_JSON")
|
cg.add_define("USE_JSON")
|
||||||
cg.add_global(json_ns.using)
|
cg.add_global(json_ns.using)
|
||||||
|
@ -1,83 +1,76 @@
|
|||||||
#include "json_util.h"
|
#include "json_util.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
// ArduinoJson::Allocator is included via ArduinoJson.h in json_util.h
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace json {
|
namespace json {
|
||||||
|
|
||||||
static const char *const TAG = "json";
|
static const char *const TAG = "json";
|
||||||
|
|
||||||
static std::vector<char> global_json_build_buffer; // NOLINT
|
// Build an allocator for the JSON Library using the RAMAllocator class
|
||||||
static const auto ALLOCATOR = RAMAllocator<uint8_t>(RAMAllocator<uint8_t>::ALLOC_INTERNAL);
|
struct SpiRamAllocator : ArduinoJson::Allocator {
|
||||||
|
void *allocate(size_t size) override { return this->allocator_.allocate(size); }
|
||||||
|
|
||||||
|
void deallocate(void *pointer) override {
|
||||||
|
// ArduinoJson's Allocator interface doesn't provide the size parameter in deallocate.
|
||||||
|
// RAMAllocator::deallocate() requires the size, which we don't have access to here.
|
||||||
|
// RAMAllocator::deallocate implementation just calls free() regardless of whether
|
||||||
|
// the memory was allocated with heap_caps_malloc or malloc.
|
||||||
|
// This is safe because ESP-IDF's heap implementation internally tracks the memory region
|
||||||
|
// and routes free() to the appropriate heap.
|
||||||
|
free(pointer); // NOLINT(cppcoreguidelines-owning-memory,cppcoreguidelines-no-malloc)
|
||||||
|
}
|
||||||
|
|
||||||
|
void *reallocate(void *ptr, size_t new_size) override {
|
||||||
|
return this->allocator_.reallocate(static_cast<uint8_t *>(ptr), new_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
RAMAllocator<uint8_t> allocator_{RAMAllocator<uint8_t>(RAMAllocator<uint8_t>::NONE)};
|
||||||
|
};
|
||||||
|
|
||||||
std::string build_json(const json_build_t &f) {
|
std::string build_json(const json_build_t &f) {
|
||||||
// Here we are allocating up to 5kb of memory,
|
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||||
// with the heap size minus 2kb to be safe if less than 5kb
|
auto doc_allocator = SpiRamAllocator();
|
||||||
// as we can not have a true dynamic sized document.
|
JsonDocument json_document(&doc_allocator);
|
||||||
// The excess memory is freed below with `shrinkToFit()`
|
if (json_document.overflowed()) {
|
||||||
auto free_heap = ALLOCATOR.get_max_free_block_size();
|
ESP_LOGE(TAG, "Could not allocate memory for JSON document!");
|
||||||
size_t request_size = std::min(free_heap, (size_t) 512);
|
return "{}";
|
||||||
while (true) {
|
|
||||||
ESP_LOGV(TAG, "Attempting to allocate %zu bytes for JSON serialization", request_size);
|
|
||||||
DynamicJsonDocument json_document(request_size);
|
|
||||||
if (json_document.capacity() == 0) {
|
|
||||||
ESP_LOGE(TAG, "Could not allocate memory for document! Requested %zu bytes, largest free heap block: %zu bytes",
|
|
||||||
request_size, free_heap);
|
|
||||||
return "{}";
|
|
||||||
}
|
|
||||||
JsonObject root = json_document.to<JsonObject>();
|
|
||||||
f(root);
|
|
||||||
if (json_document.overflowed()) {
|
|
||||||
if (request_size == free_heap) {
|
|
||||||
ESP_LOGE(TAG, "Could not allocate memory for document! Overflowed largest free heap block: %zu bytes",
|
|
||||||
free_heap);
|
|
||||||
return "{}";
|
|
||||||
}
|
|
||||||
request_size = std::min(request_size * 2, free_heap);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
json_document.shrinkToFit();
|
|
||||||
ESP_LOGV(TAG, "Size after shrink %zu bytes", json_document.capacity());
|
|
||||||
std::string output;
|
|
||||||
serializeJson(json_document, output);
|
|
||||||
return output;
|
|
||||||
}
|
}
|
||||||
|
JsonObject root = json_document.to<JsonObject>();
|
||||||
|
f(root);
|
||||||
|
if (json_document.overflowed()) {
|
||||||
|
ESP_LOGE(TAG, "Could not allocate memory for JSON document!");
|
||||||
|
return "{}";
|
||||||
|
}
|
||||||
|
std::string output;
|
||||||
|
serializeJson(json_document, output);
|
||||||
|
return output;
|
||||||
|
// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
|
||||||
}
|
}
|
||||||
|
|
||||||
bool parse_json(const std::string &data, const json_parse_t &f) {
|
bool parse_json(const std::string &data, const json_parse_t &f) {
|
||||||
// Here we are allocating 1.5 times the data size,
|
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||||
// with the heap size minus 2kb to be safe if less than that
|
auto doc_allocator = SpiRamAllocator();
|
||||||
// as we can not have a true dynamic sized document.
|
JsonDocument json_document(&doc_allocator);
|
||||||
// The excess memory is freed below with `shrinkToFit()`
|
if (json_document.overflowed()) {
|
||||||
auto free_heap = ALLOCATOR.get_max_free_block_size();
|
ESP_LOGE(TAG, "Could not allocate memory for JSON document!");
|
||||||
size_t request_size = std::min(free_heap, (size_t) (data.size() * 1.5));
|
return false;
|
||||||
while (true) {
|
}
|
||||||
DynamicJsonDocument json_document(request_size);
|
DeserializationError err = deserializeJson(json_document, data);
|
||||||
if (json_document.capacity() == 0) {
|
|
||||||
ESP_LOGE(TAG, "Could not allocate memory for document! Requested %zu bytes, free heap: %zu", request_size,
|
|
||||||
free_heap);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
DeserializationError err = deserializeJson(json_document, data);
|
|
||||||
json_document.shrinkToFit();
|
|
||||||
|
|
||||||
JsonObject root = json_document.as<JsonObject>();
|
JsonObject root = json_document.as<JsonObject>();
|
||||||
|
|
||||||
if (err == DeserializationError::Ok) {
|
if (err == DeserializationError::Ok) {
|
||||||
return f(root);
|
return f(root);
|
||||||
} else if (err == DeserializationError::NoMemory) {
|
} else if (err == DeserializationError::NoMemory) {
|
||||||
if (request_size * 2 >= free_heap) {
|
ESP_LOGE(TAG, "Can not allocate more memory for deserialization. Consider making source string smaller");
|
||||||
ESP_LOGE(TAG, "Can not allocate more memory for deserialization. Consider making source string smaller");
|
return false;
|
||||||
return false;
|
}
|
||||||
}
|
ESP_LOGE(TAG, "Parse error: %s", err.c_str());
|
||||||
ESP_LOGV(TAG, "Increasing memory allocation.");
|
|
||||||
request_size *= 2;
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
ESP_LOGE(TAG, "Parse error: %s", err.c_str());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return false;
|
return false;
|
||||||
|
// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace json
|
} // namespace json
|
||||||
|
@ -9,6 +9,7 @@ namespace light {
|
|||||||
// See https://www.home-assistant.io/integrations/light.mqtt/#json-schema for documentation on the schema
|
// See https://www.home-assistant.io/integrations/light.mqtt/#json-schema for documentation on the schema
|
||||||
|
|
||||||
void LightJSONSchema::dump_json(LightState &state, JsonObject root) {
|
void LightJSONSchema::dump_json(LightState &state, JsonObject root) {
|
||||||
|
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||||
if (state.supports_effects())
|
if (state.supports_effects())
|
||||||
root["effect"] = state.get_effect_name();
|
root["effect"] = state.get_effect_name();
|
||||||
|
|
||||||
@ -52,7 +53,7 @@ void LightJSONSchema::dump_json(LightState &state, JsonObject root) {
|
|||||||
if (values.get_color_mode() & ColorCapability::BRIGHTNESS)
|
if (values.get_color_mode() & ColorCapability::BRIGHTNESS)
|
||||||
root["brightness"] = uint8_t(values.get_brightness() * 255);
|
root["brightness"] = uint8_t(values.get_brightness() * 255);
|
||||||
|
|
||||||
JsonObject color = root.createNestedObject("color");
|
JsonObject color = root["color"].to<JsonObject>();
|
||||||
if (values.get_color_mode() & ColorCapability::RGB) {
|
if (values.get_color_mode() & ColorCapability::RGB) {
|
||||||
color["r"] = uint8_t(values.get_color_brightness() * values.get_red() * 255);
|
color["r"] = uint8_t(values.get_color_brightness() * values.get_red() * 255);
|
||||||
color["g"] = uint8_t(values.get_color_brightness() * values.get_green() * 255);
|
color["g"] = uint8_t(values.get_color_brightness() * values.get_green() * 255);
|
||||||
@ -73,7 +74,7 @@ void LightJSONSchema::dump_json(LightState &state, JsonObject root) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void LightJSONSchema::parse_color_json(LightState &state, LightCall &call, JsonObject root) {
|
void LightJSONSchema::parse_color_json(LightState &state, LightCall &call, JsonObject root) {
|
||||||
if (root.containsKey("state")) {
|
if (root["state"].is<const char *>()) {
|
||||||
auto val = parse_on_off(root["state"]);
|
auto val = parse_on_off(root["state"]);
|
||||||
switch (val) {
|
switch (val) {
|
||||||
case PARSE_ON:
|
case PARSE_ON:
|
||||||
@ -90,40 +91,40 @@ void LightJSONSchema::parse_color_json(LightState &state, LightCall &call, JsonO
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (root.containsKey("brightness")) {
|
if (root["brightness"].is<uint8_t>()) {
|
||||||
call.set_brightness(float(root["brightness"]) / 255.0f);
|
call.set_brightness(float(root["brightness"]) / 255.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (root.containsKey("color")) {
|
if (root["color"].is<JsonObject>()) {
|
||||||
JsonObject color = root["color"];
|
JsonObject color = root["color"];
|
||||||
// HA also encodes brightness information in the r, g, b values, so extract that and set it as color brightness.
|
// HA also encodes brightness information in the r, g, b values, so extract that and set it as color brightness.
|
||||||
float max_rgb = 0.0f;
|
float max_rgb = 0.0f;
|
||||||
if (color.containsKey("r")) {
|
if (color["r"].is<uint8_t>()) {
|
||||||
float r = float(color["r"]) / 255.0f;
|
float r = float(color["r"]) / 255.0f;
|
||||||
max_rgb = fmaxf(max_rgb, r);
|
max_rgb = fmaxf(max_rgb, r);
|
||||||
call.set_red(r);
|
call.set_red(r);
|
||||||
}
|
}
|
||||||
if (color.containsKey("g")) {
|
if (color["g"].is<uint8_t>()) {
|
||||||
float g = float(color["g"]) / 255.0f;
|
float g = float(color["g"]) / 255.0f;
|
||||||
max_rgb = fmaxf(max_rgb, g);
|
max_rgb = fmaxf(max_rgb, g);
|
||||||
call.set_green(g);
|
call.set_green(g);
|
||||||
}
|
}
|
||||||
if (color.containsKey("b")) {
|
if (color["b"].is<uint8_t>()) {
|
||||||
float b = float(color["b"]) / 255.0f;
|
float b = float(color["b"]) / 255.0f;
|
||||||
max_rgb = fmaxf(max_rgb, b);
|
max_rgb = fmaxf(max_rgb, b);
|
||||||
call.set_blue(b);
|
call.set_blue(b);
|
||||||
}
|
}
|
||||||
if (color.containsKey("r") || color.containsKey("g") || color.containsKey("b")) {
|
if (color["r"].is<uint8_t>() || color["g"].is<uint8_t>() || color["b"].is<uint8_t>()) {
|
||||||
call.set_color_brightness(max_rgb);
|
call.set_color_brightness(max_rgb);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (color.containsKey("c")) {
|
if (color["c"].is<uint8_t>()) {
|
||||||
call.set_cold_white(float(color["c"]) / 255.0f);
|
call.set_cold_white(float(color["c"]) / 255.0f);
|
||||||
}
|
}
|
||||||
if (color.containsKey("w")) {
|
if (color["w"].is<uint8_t>()) {
|
||||||
// the HA scheme is ambiguous here, the same key is used for white channel in RGBW and warm
|
// the HA scheme is ambiguous here, the same key is used for white channel in RGBW and warm
|
||||||
// white channel in RGBWW.
|
// white channel in RGBWW.
|
||||||
if (color.containsKey("c")) {
|
if (color["c"].is<uint8_t>()) {
|
||||||
call.set_warm_white(float(color["w"]) / 255.0f);
|
call.set_warm_white(float(color["w"]) / 255.0f);
|
||||||
} else {
|
} else {
|
||||||
call.set_white(float(color["w"]) / 255.0f);
|
call.set_white(float(color["w"]) / 255.0f);
|
||||||
@ -131,11 +132,11 @@ void LightJSONSchema::parse_color_json(LightState &state, LightCall &call, JsonO
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (root.containsKey("white_value")) { // legacy API
|
if (root["white_value"].is<uint8_t>()) { // legacy API
|
||||||
call.set_white(float(root["white_value"]) / 255.0f);
|
call.set_white(float(root["white_value"]) / 255.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (root.containsKey("color_temp")) {
|
if (root["color_temp"].is<uint16_t>()) {
|
||||||
call.set_color_temperature(float(root["color_temp"]));
|
call.set_color_temperature(float(root["color_temp"]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -143,17 +144,17 @@ void LightJSONSchema::parse_color_json(LightState &state, LightCall &call, JsonO
|
|||||||
void LightJSONSchema::parse_json(LightState &state, LightCall &call, JsonObject root) {
|
void LightJSONSchema::parse_json(LightState &state, LightCall &call, JsonObject root) {
|
||||||
LightJSONSchema::parse_color_json(state, call, root);
|
LightJSONSchema::parse_color_json(state, call, root);
|
||||||
|
|
||||||
if (root.containsKey("flash")) {
|
if (root["flash"].is<uint32_t>()) {
|
||||||
auto length = uint32_t(float(root["flash"]) * 1000);
|
auto length = uint32_t(float(root["flash"]) * 1000);
|
||||||
call.set_flash_length(length);
|
call.set_flash_length(length);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (root.containsKey("transition")) {
|
if (root["transition"].is<uint16_t>()) {
|
||||||
auto length = uint32_t(float(root["transition"]) * 1000);
|
auto length = uint32_t(float(root["transition"]) * 1000);
|
||||||
call.set_transition_length(length);
|
call.set_transition_length(length);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (root.containsKey("effect")) {
|
if (root["effect"].is<const char *>()) {
|
||||||
const char *effect = root["effect"];
|
const char *effect = root["effect"];
|
||||||
call.set_effect(effect);
|
call.set_effect(effect);
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,8 @@ void MQTTAlarmControlPanelComponent::dump_config() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MQTTAlarmControlPanelComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
|
void MQTTAlarmControlPanelComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
|
||||||
JsonArray supported_features = root.createNestedArray(MQTT_SUPPORTED_FEATURES);
|
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||||
|
JsonArray supported_features = root[MQTT_SUPPORTED_FEATURES].to<JsonArray>();
|
||||||
const uint32_t acp_supported_features = this->alarm_control_panel_->get_supported_features();
|
const uint32_t acp_supported_features = this->alarm_control_panel_->get_supported_features();
|
||||||
if (acp_supported_features & ACP_FEAT_ARM_AWAY) {
|
if (acp_supported_features & ACP_FEAT_ARM_AWAY) {
|
||||||
supported_features.add("arm_away");
|
supported_features.add("arm_away");
|
||||||
|
@ -30,6 +30,7 @@ MQTTBinarySensorComponent::MQTTBinarySensorComponent(binary_sensor::BinarySensor
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MQTTBinarySensorComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
|
void MQTTBinarySensorComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
|
||||||
|
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||||
if (!this->binary_sensor_->get_device_class().empty())
|
if (!this->binary_sensor_->get_device_class().empty())
|
||||||
root[MQTT_DEVICE_CLASS] = this->binary_sensor_->get_device_class();
|
root[MQTT_DEVICE_CLASS] = this->binary_sensor_->get_device_class();
|
||||||
if (this->binary_sensor_->is_status_binary_sensor())
|
if (this->binary_sensor_->is_status_binary_sensor())
|
||||||
|
@ -31,9 +31,12 @@ void MQTTButtonComponent::dump_config() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MQTTButtonComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
|
void MQTTButtonComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
|
||||||
|
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||||
config.state_topic = false;
|
config.state_topic = false;
|
||||||
if (!this->button_->get_device_class().empty())
|
if (!this->button_->get_device_class().empty()) {
|
||||||
root[MQTT_DEVICE_CLASS] = this->button_->get_device_class();
|
root[MQTT_DEVICE_CLASS] = this->button_->get_device_class();
|
||||||
|
}
|
||||||
|
// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string MQTTButtonComponent::component_type() const { return "button"; }
|
std::string MQTTButtonComponent::component_type() const { return "button"; }
|
||||||
|
@ -92,6 +92,7 @@ void MQTTClientComponent::send_device_info_() {
|
|||||||
std::string topic = "esphome/discover/";
|
std::string topic = "esphome/discover/";
|
||||||
topic.append(App.get_name());
|
topic.append(App.get_name());
|
||||||
|
|
||||||
|
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||||
this->publish_json(
|
this->publish_json(
|
||||||
topic,
|
topic,
|
||||||
[](JsonObject root) {
|
[](JsonObject root) {
|
||||||
@ -147,6 +148,7 @@ void MQTTClientComponent::send_device_info_() {
|
|||||||
#endif
|
#endif
|
||||||
},
|
},
|
||||||
2, this->discovery_info_.retain);
|
2, this->discovery_info_.retain);
|
||||||
|
// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
|
||||||
}
|
}
|
||||||
|
|
||||||
void MQTTClientComponent::dump_config() {
|
void MQTTClientComponent::dump_config() {
|
||||||
|
@ -14,6 +14,7 @@ static const char *const TAG = "mqtt.climate";
|
|||||||
using namespace esphome::climate;
|
using namespace esphome::climate;
|
||||||
|
|
||||||
void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
|
void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
|
||||||
|
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||||
auto traits = this->device_->get_traits();
|
auto traits = this->device_->get_traits();
|
||||||
// current_temperature_topic
|
// current_temperature_topic
|
||||||
if (traits.get_supports_current_temperature()) {
|
if (traits.get_supports_current_temperature()) {
|
||||||
@ -28,7 +29,7 @@ void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCo
|
|||||||
// mode_state_topic
|
// mode_state_topic
|
||||||
root[MQTT_MODE_STATE_TOPIC] = this->get_mode_state_topic();
|
root[MQTT_MODE_STATE_TOPIC] = this->get_mode_state_topic();
|
||||||
// modes
|
// modes
|
||||||
JsonArray modes = root.createNestedArray(MQTT_MODES);
|
JsonArray modes = root[MQTT_MODES].to<JsonArray>();
|
||||||
// sort array for nice UI in HA
|
// sort array for nice UI in HA
|
||||||
if (traits.supports_mode(CLIMATE_MODE_AUTO))
|
if (traits.supports_mode(CLIMATE_MODE_AUTO))
|
||||||
modes.add("auto");
|
modes.add("auto");
|
||||||
@ -89,7 +90,7 @@ void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCo
|
|||||||
// preset_mode_state_topic
|
// preset_mode_state_topic
|
||||||
root[MQTT_PRESET_MODE_STATE_TOPIC] = this->get_preset_state_topic();
|
root[MQTT_PRESET_MODE_STATE_TOPIC] = this->get_preset_state_topic();
|
||||||
// presets
|
// presets
|
||||||
JsonArray presets = root.createNestedArray("preset_modes");
|
JsonArray presets = root["preset_modes"].to<JsonArray>();
|
||||||
if (traits.supports_preset(CLIMATE_PRESET_HOME))
|
if (traits.supports_preset(CLIMATE_PRESET_HOME))
|
||||||
presets.add("home");
|
presets.add("home");
|
||||||
if (traits.supports_preset(CLIMATE_PRESET_AWAY))
|
if (traits.supports_preset(CLIMATE_PRESET_AWAY))
|
||||||
@ -119,7 +120,7 @@ void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCo
|
|||||||
// fan_mode_state_topic
|
// fan_mode_state_topic
|
||||||
root[MQTT_FAN_MODE_STATE_TOPIC] = this->get_fan_mode_state_topic();
|
root[MQTT_FAN_MODE_STATE_TOPIC] = this->get_fan_mode_state_topic();
|
||||||
// fan_modes
|
// fan_modes
|
||||||
JsonArray fan_modes = root.createNestedArray("fan_modes");
|
JsonArray fan_modes = root["fan_modes"].to<JsonArray>();
|
||||||
if (traits.supports_fan_mode(CLIMATE_FAN_ON))
|
if (traits.supports_fan_mode(CLIMATE_FAN_ON))
|
||||||
fan_modes.add("on");
|
fan_modes.add("on");
|
||||||
if (traits.supports_fan_mode(CLIMATE_FAN_OFF))
|
if (traits.supports_fan_mode(CLIMATE_FAN_OFF))
|
||||||
@ -150,7 +151,7 @@ void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCo
|
|||||||
// swing_mode_state_topic
|
// swing_mode_state_topic
|
||||||
root[MQTT_SWING_MODE_STATE_TOPIC] = this->get_swing_mode_state_topic();
|
root[MQTT_SWING_MODE_STATE_TOPIC] = this->get_swing_mode_state_topic();
|
||||||
// swing_modes
|
// swing_modes
|
||||||
JsonArray swing_modes = root.createNestedArray("swing_modes");
|
JsonArray swing_modes = root["swing_modes"].to<JsonArray>();
|
||||||
if (traits.supports_swing_mode(CLIMATE_SWING_OFF))
|
if (traits.supports_swing_mode(CLIMATE_SWING_OFF))
|
||||||
swing_modes.add("off");
|
swing_modes.add("off");
|
||||||
if (traits.supports_swing_mode(CLIMATE_SWING_BOTH))
|
if (traits.supports_swing_mode(CLIMATE_SWING_BOTH))
|
||||||
@ -163,6 +164,7 @@ void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCo
|
|||||||
|
|
||||||
config.state_topic = false;
|
config.state_topic = false;
|
||||||
config.command_topic = false;
|
config.command_topic = false;
|
||||||
|
// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
|
||||||
}
|
}
|
||||||
void MQTTClimateComponent::setup() {
|
void MQTTClimateComponent::setup() {
|
||||||
auto traits = this->device_->get_traits();
|
auto traits = this->device_->get_traits();
|
||||||
|
@ -70,6 +70,7 @@ bool MQTTComponent::send_discovery_() {
|
|||||||
|
|
||||||
ESP_LOGV(TAG, "'%s': Sending discovery", this->friendly_name().c_str());
|
ESP_LOGV(TAG, "'%s': Sending discovery", this->friendly_name().c_str());
|
||||||
|
|
||||||
|
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||||
return global_mqtt_client->publish_json(
|
return global_mqtt_client->publish_json(
|
||||||
this->get_discovery_topic_(discovery_info),
|
this->get_discovery_topic_(discovery_info),
|
||||||
[this](JsonObject root) {
|
[this](JsonObject root) {
|
||||||
@ -155,7 +156,7 @@ bool MQTTComponent::send_discovery_() {
|
|||||||
}
|
}
|
||||||
std::string node_area = App.get_area();
|
std::string node_area = App.get_area();
|
||||||
|
|
||||||
JsonObject device_info = root.createNestedObject(MQTT_DEVICE);
|
JsonObject device_info = root[MQTT_DEVICE].to<JsonObject>();
|
||||||
const auto mac = get_mac_address();
|
const auto mac = get_mac_address();
|
||||||
device_info[MQTT_DEVICE_IDENTIFIERS] = mac;
|
device_info[MQTT_DEVICE_IDENTIFIERS] = mac;
|
||||||
device_info[MQTT_DEVICE_NAME] = node_friendly_name;
|
device_info[MQTT_DEVICE_NAME] = node_friendly_name;
|
||||||
@ -192,6 +193,7 @@ bool MQTTComponent::send_discovery_() {
|
|||||||
device_info[MQTT_DEVICE_CONNECTIONS][0][1] = mac;
|
device_info[MQTT_DEVICE_CONNECTIONS][0][1] = mac;
|
||||||
},
|
},
|
||||||
this->qos_, discovery_info.retain);
|
this->qos_, discovery_info.retain);
|
||||||
|
// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t MQTTComponent::get_qos() const { return this->qos_; }
|
uint8_t MQTTComponent::get_qos() const { return this->qos_; }
|
||||||
|
@ -67,6 +67,7 @@ void MQTTCoverComponent::dump_config() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
void MQTTCoverComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
|
void MQTTCoverComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
|
||||||
|
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||||
if (!this->cover_->get_device_class().empty())
|
if (!this->cover_->get_device_class().empty())
|
||||||
root[MQTT_DEVICE_CLASS] = this->cover_->get_device_class();
|
root[MQTT_DEVICE_CLASS] = this->cover_->get_device_class();
|
||||||
|
|
||||||
|
@ -20,13 +20,13 @@ MQTTDateComponent::MQTTDateComponent(DateEntity *date) : date_(date) {}
|
|||||||
void MQTTDateComponent::setup() {
|
void MQTTDateComponent::setup() {
|
||||||
this->subscribe_json(this->get_command_topic_(), [this](const std::string &topic, JsonObject root) {
|
this->subscribe_json(this->get_command_topic_(), [this](const std::string &topic, JsonObject root) {
|
||||||
auto call = this->date_->make_call();
|
auto call = this->date_->make_call();
|
||||||
if (root.containsKey("year")) {
|
if (root["year"].is<uint16_t>()) {
|
||||||
call.set_year(root["year"]);
|
call.set_year(root["year"]);
|
||||||
}
|
}
|
||||||
if (root.containsKey("month")) {
|
if (root["month"].is<uint8_t>()) {
|
||||||
call.set_month(root["month"]);
|
call.set_month(root["month"]);
|
||||||
}
|
}
|
||||||
if (root.containsKey("day")) {
|
if (root["day"].is<uint8_t>()) {
|
||||||
call.set_day(root["day"]);
|
call.set_day(root["day"]);
|
||||||
}
|
}
|
||||||
call.perform();
|
call.perform();
|
||||||
@ -55,6 +55,7 @@ bool MQTTDateComponent::send_initial_state() {
|
|||||||
}
|
}
|
||||||
bool MQTTDateComponent::publish_state(uint16_t year, uint8_t month, uint8_t day) {
|
bool MQTTDateComponent::publish_state(uint16_t year, uint8_t month, uint8_t day) {
|
||||||
return this->publish_json(this->get_state_topic_(), [year, month, day](JsonObject root) {
|
return this->publish_json(this->get_state_topic_(), [year, month, day](JsonObject root) {
|
||||||
|
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||||
root["year"] = year;
|
root["year"] = year;
|
||||||
root["month"] = month;
|
root["month"] = month;
|
||||||
root["day"] = day;
|
root["day"] = day;
|
||||||
|
@ -20,22 +20,22 @@ MQTTDateTimeComponent::MQTTDateTimeComponent(DateTimeEntity *datetime) : datetim
|
|||||||
void MQTTDateTimeComponent::setup() {
|
void MQTTDateTimeComponent::setup() {
|
||||||
this->subscribe_json(this->get_command_topic_(), [this](const std::string &topic, JsonObject root) {
|
this->subscribe_json(this->get_command_topic_(), [this](const std::string &topic, JsonObject root) {
|
||||||
auto call = this->datetime_->make_call();
|
auto call = this->datetime_->make_call();
|
||||||
if (root.containsKey("year")) {
|
if (root["year"].is<uint16_t>()) {
|
||||||
call.set_year(root["year"]);
|
call.set_year(root["year"]);
|
||||||
}
|
}
|
||||||
if (root.containsKey("month")) {
|
if (root["month"].is<uint8_t>()) {
|
||||||
call.set_month(root["month"]);
|
call.set_month(root["month"]);
|
||||||
}
|
}
|
||||||
if (root.containsKey("day")) {
|
if (root["day"].is<uint8_t>()) {
|
||||||
call.set_day(root["day"]);
|
call.set_day(root["day"]);
|
||||||
}
|
}
|
||||||
if (root.containsKey("hour")) {
|
if (root["hour"].is<uint8_t>()) {
|
||||||
call.set_hour(root["hour"]);
|
call.set_hour(root["hour"]);
|
||||||
}
|
}
|
||||||
if (root.containsKey("minute")) {
|
if (root["minute"].is<uint8_t>()) {
|
||||||
call.set_minute(root["minute"]);
|
call.set_minute(root["minute"]);
|
||||||
}
|
}
|
||||||
if (root.containsKey("second")) {
|
if (root["second"].is<uint8_t>()) {
|
||||||
call.set_second(root["second"]);
|
call.set_second(root["second"]);
|
||||||
}
|
}
|
||||||
call.perform();
|
call.perform();
|
||||||
@ -68,6 +68,7 @@ bool MQTTDateTimeComponent::send_initial_state() {
|
|||||||
bool MQTTDateTimeComponent::publish_state(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute,
|
bool MQTTDateTimeComponent::publish_state(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute,
|
||||||
uint8_t second) {
|
uint8_t second) {
|
||||||
return this->publish_json(this->get_state_topic_(), [year, month, day, hour, minute, second](JsonObject root) {
|
return this->publish_json(this->get_state_topic_(), [year, month, day, hour, minute, second](JsonObject root) {
|
||||||
|
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||||
root["year"] = year;
|
root["year"] = year;
|
||||||
root["month"] = month;
|
root["month"] = month;
|
||||||
root["day"] = day;
|
root["day"] = day;
|
||||||
|
@ -16,7 +16,8 @@ using namespace esphome::event;
|
|||||||
MQTTEventComponent::MQTTEventComponent(event::Event *event) : event_(event) {}
|
MQTTEventComponent::MQTTEventComponent(event::Event *event) : event_(event) {}
|
||||||
|
|
||||||
void MQTTEventComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
|
void MQTTEventComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
|
||||||
JsonArray event_types = root.createNestedArray(MQTT_EVENT_TYPES);
|
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||||
|
JsonArray event_types = root[MQTT_EVENT_TYPES].to<JsonArray>();
|
||||||
for (const auto &event_type : this->event_->get_event_types())
|
for (const auto &event_type : this->event_->get_event_types())
|
||||||
event_types.add(event_type);
|
event_types.add(event_type);
|
||||||
|
|
||||||
@ -40,8 +41,10 @@ void MQTTEventComponent::dump_config() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool MQTTEventComponent::publish_event_(const std::string &event_type) {
|
bool MQTTEventComponent::publish_event_(const std::string &event_type) {
|
||||||
return this->publish_json(this->get_state_topic_(),
|
return this->publish_json(this->get_state_topic_(), [event_type](JsonObject root) {
|
||||||
[event_type](JsonObject root) { root[MQTT_EVENT_TYPE] = event_type; });
|
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||||
|
root[MQTT_EVENT_TYPE] = event_type;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string MQTTEventComponent::component_type() const { return "event"; }
|
std::string MQTTEventComponent::component_type() const { return "event"; }
|
||||||
|
@ -143,6 +143,7 @@ void MQTTFanComponent::dump_config() {
|
|||||||
bool MQTTFanComponent::send_initial_state() { return this->publish_state(); }
|
bool MQTTFanComponent::send_initial_state() { return this->publish_state(); }
|
||||||
|
|
||||||
void MQTTFanComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
|
void MQTTFanComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
|
||||||
|
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||||
if (this->state_->get_traits().supports_direction()) {
|
if (this->state_->get_traits().supports_direction()) {
|
||||||
root[MQTT_DIRECTION_COMMAND_TOPIC] = this->get_direction_command_topic();
|
root[MQTT_DIRECTION_COMMAND_TOPIC] = this->get_direction_command_topic();
|
||||||
root[MQTT_DIRECTION_STATE_TOPIC] = this->get_direction_state_topic();
|
root[MQTT_DIRECTION_STATE_TOPIC] = this->get_direction_state_topic();
|
||||||
|
@ -32,17 +32,21 @@ void MQTTJSONLightComponent::setup() {
|
|||||||
MQTTJSONLightComponent::MQTTJSONLightComponent(LightState *state) : state_(state) {}
|
MQTTJSONLightComponent::MQTTJSONLightComponent(LightState *state) : state_(state) {}
|
||||||
|
|
||||||
bool MQTTJSONLightComponent::publish_state_() {
|
bool MQTTJSONLightComponent::publish_state_() {
|
||||||
return this->publish_json(this->get_state_topic_(),
|
return this->publish_json(this->get_state_topic_(), [this](JsonObject root) {
|
||||||
[this](JsonObject root) { LightJSONSchema::dump_json(*this->state_, root); });
|
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||||
|
LightJSONSchema::dump_json(*this->state_, root);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
LightState *MQTTJSONLightComponent::get_state() const { return this->state_; }
|
LightState *MQTTJSONLightComponent::get_state() const { return this->state_; }
|
||||||
|
|
||||||
void MQTTJSONLightComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
|
void MQTTJSONLightComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
|
||||||
|
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||||
root["schema"] = "json";
|
root["schema"] = "json";
|
||||||
auto traits = this->state_->get_traits();
|
auto traits = this->state_->get_traits();
|
||||||
|
|
||||||
root[MQTT_COLOR_MODE] = true;
|
root[MQTT_COLOR_MODE] = true;
|
||||||
JsonArray color_modes = root.createNestedArray("supported_color_modes");
|
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||||
|
JsonArray color_modes = root["supported_color_modes"].to<JsonArray>();
|
||||||
if (traits.supports_color_mode(ColorMode::ON_OFF))
|
if (traits.supports_color_mode(ColorMode::ON_OFF))
|
||||||
color_modes.add("onoff");
|
color_modes.add("onoff");
|
||||||
if (traits.supports_color_mode(ColorMode::BRIGHTNESS))
|
if (traits.supports_color_mode(ColorMode::BRIGHTNESS))
|
||||||
@ -67,7 +71,7 @@ void MQTTJSONLightComponent::send_discovery(JsonObject root, mqtt::SendDiscovery
|
|||||||
|
|
||||||
if (this->state_->supports_effects()) {
|
if (this->state_->supports_effects()) {
|
||||||
root["effect"] = true;
|
root["effect"] = true;
|
||||||
JsonArray effect_list = root.createNestedArray(MQTT_EFFECT_LIST);
|
JsonArray effect_list = root[MQTT_EFFECT_LIST].to<JsonArray>();
|
||||||
for (auto *effect : this->state_->get_effects())
|
for (auto *effect : this->state_->get_effects())
|
||||||
effect_list.add(effect->get_name());
|
effect_list.add(effect->get_name());
|
||||||
effect_list.add("None");
|
effect_list.add("None");
|
||||||
|
@ -38,8 +38,10 @@ void MQTTLockComponent::dump_config() {
|
|||||||
std::string MQTTLockComponent::component_type() const { return "lock"; }
|
std::string MQTTLockComponent::component_type() const { return "lock"; }
|
||||||
const EntityBase *MQTTLockComponent::get_entity() const { return this->lock_; }
|
const EntityBase *MQTTLockComponent::get_entity() const { return this->lock_; }
|
||||||
void MQTTLockComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
|
void MQTTLockComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
|
||||||
if (this->lock_->traits.get_assumed_state())
|
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||||
|
if (this->lock_->traits.get_assumed_state()) {
|
||||||
root[MQTT_OPTIMISTIC] = true;
|
root[MQTT_OPTIMISTIC] = true;
|
||||||
|
}
|
||||||
if (this->lock_->traits.get_supports_open())
|
if (this->lock_->traits.get_supports_open())
|
||||||
root[MQTT_PAYLOAD_OPEN] = "OPEN";
|
root[MQTT_PAYLOAD_OPEN] = "OPEN";
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,7 @@ const EntityBase *MQTTNumberComponent::get_entity() const { return this->number_
|
|||||||
void MQTTNumberComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
|
void MQTTNumberComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
|
||||||
const auto &traits = number_->traits;
|
const auto &traits = number_->traits;
|
||||||
// https://www.home-assistant.io/integrations/number.mqtt/
|
// https://www.home-assistant.io/integrations/number.mqtt/
|
||||||
|
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||||
root[MQTT_MIN] = traits.get_min_value();
|
root[MQTT_MIN] = traits.get_min_value();
|
||||||
root[MQTT_MAX] = traits.get_max_value();
|
root[MQTT_MAX] = traits.get_max_value();
|
||||||
root[MQTT_STEP] = traits.get_step();
|
root[MQTT_STEP] = traits.get_step();
|
||||||
|
@ -35,7 +35,8 @@ const EntityBase *MQTTSelectComponent::get_entity() const { return this->select_
|
|||||||
void MQTTSelectComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
|
void MQTTSelectComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
|
||||||
const auto &traits = select_->traits;
|
const auto &traits = select_->traits;
|
||||||
// https://www.home-assistant.io/integrations/select.mqtt/
|
// https://www.home-assistant.io/integrations/select.mqtt/
|
||||||
JsonArray options = root.createNestedArray(MQTT_OPTIONS);
|
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||||
|
JsonArray options = root[MQTT_OPTIONS].to<JsonArray>();
|
||||||
for (const auto &option : traits.get_options())
|
for (const auto &option : traits.get_options())
|
||||||
options.add(option);
|
options.add(option);
|
||||||
|
|
||||||
|
@ -44,8 +44,10 @@ void MQTTSensorComponent::set_expire_after(uint32_t expire_after) { this->expire
|
|||||||
void MQTTSensorComponent::disable_expire_after() { this->expire_after_ = 0; }
|
void MQTTSensorComponent::disable_expire_after() { this->expire_after_ = 0; }
|
||||||
|
|
||||||
void MQTTSensorComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
|
void MQTTSensorComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
|
||||||
if (!this->sensor_->get_device_class().empty())
|
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||||
|
if (!this->sensor_->get_device_class().empty()) {
|
||||||
root[MQTT_DEVICE_CLASS] = this->sensor_->get_device_class();
|
root[MQTT_DEVICE_CLASS] = this->sensor_->get_device_class();
|
||||||
|
}
|
||||||
|
|
||||||
if (!this->sensor_->get_unit_of_measurement().empty())
|
if (!this->sensor_->get_unit_of_measurement().empty())
|
||||||
root[MQTT_UNIT_OF_MEASUREMENT] = this->sensor_->get_unit_of_measurement();
|
root[MQTT_UNIT_OF_MEASUREMENT] = this->sensor_->get_unit_of_measurement();
|
||||||
|
@ -45,8 +45,10 @@ void MQTTSwitchComponent::dump_config() {
|
|||||||
std::string MQTTSwitchComponent::component_type() const { return "switch"; }
|
std::string MQTTSwitchComponent::component_type() const { return "switch"; }
|
||||||
const EntityBase *MQTTSwitchComponent::get_entity() const { return this->switch_; }
|
const EntityBase *MQTTSwitchComponent::get_entity() const { return this->switch_; }
|
||||||
void MQTTSwitchComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
|
void MQTTSwitchComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
|
||||||
if (this->switch_->assumed_state())
|
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||||
|
if (this->switch_->assumed_state()) {
|
||||||
root[MQTT_OPTIMISTIC] = true;
|
root[MQTT_OPTIMISTIC] = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
bool MQTTSwitchComponent::send_initial_state() { return this->publish_state(this->switch_->state); }
|
bool MQTTSwitchComponent::send_initial_state() { return this->publish_state(this->switch_->state); }
|
||||||
|
|
||||||
|
@ -34,6 +34,7 @@ std::string MQTTTextComponent::component_type() const { return "text"; }
|
|||||||
const EntityBase *MQTTTextComponent::get_entity() const { return this->text_; }
|
const EntityBase *MQTTTextComponent::get_entity() const { return this->text_; }
|
||||||
|
|
||||||
void MQTTTextComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
|
void MQTTTextComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
|
||||||
|
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||||
switch (this->text_->traits.get_mode()) {
|
switch (this->text_->traits.get_mode()) {
|
||||||
case TEXT_MODE_TEXT:
|
case TEXT_MODE_TEXT:
|
||||||
root[MQTT_MODE] = "text";
|
root[MQTT_MODE] = "text";
|
||||||
|
@ -15,8 +15,10 @@ using namespace esphome::text_sensor;
|
|||||||
|
|
||||||
MQTTTextSensor::MQTTTextSensor(TextSensor *sensor) : sensor_(sensor) {}
|
MQTTTextSensor::MQTTTextSensor(TextSensor *sensor) : sensor_(sensor) {}
|
||||||
void MQTTTextSensor::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
|
void MQTTTextSensor::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
|
||||||
if (!this->sensor_->get_device_class().empty())
|
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||||
|
if (!this->sensor_->get_device_class().empty()) {
|
||||||
root[MQTT_DEVICE_CLASS] = this->sensor_->get_device_class();
|
root[MQTT_DEVICE_CLASS] = this->sensor_->get_device_class();
|
||||||
|
}
|
||||||
config.command_topic = false;
|
config.command_topic = false;
|
||||||
}
|
}
|
||||||
void MQTTTextSensor::setup() {
|
void MQTTTextSensor::setup() {
|
||||||
|
@ -20,13 +20,13 @@ MQTTTimeComponent::MQTTTimeComponent(TimeEntity *time) : time_(time) {}
|
|||||||
void MQTTTimeComponent::setup() {
|
void MQTTTimeComponent::setup() {
|
||||||
this->subscribe_json(this->get_command_topic_(), [this](const std::string &topic, JsonObject root) {
|
this->subscribe_json(this->get_command_topic_(), [this](const std::string &topic, JsonObject root) {
|
||||||
auto call = this->time_->make_call();
|
auto call = this->time_->make_call();
|
||||||
if (root.containsKey("hour")) {
|
if (root["hour"].is<uint8_t>()) {
|
||||||
call.set_hour(root["hour"]);
|
call.set_hour(root["hour"]);
|
||||||
}
|
}
|
||||||
if (root.containsKey("minute")) {
|
if (root["minute"].is<uint8_t>()) {
|
||||||
call.set_minute(root["minute"]);
|
call.set_minute(root["minute"]);
|
||||||
}
|
}
|
||||||
if (root.containsKey("second")) {
|
if (root["second"].is<uint8_t>()) {
|
||||||
call.set_second(root["second"]);
|
call.set_second(root["second"]);
|
||||||
}
|
}
|
||||||
call.perform();
|
call.perform();
|
||||||
@ -55,6 +55,7 @@ bool MQTTTimeComponent::send_initial_state() {
|
|||||||
}
|
}
|
||||||
bool MQTTTimeComponent::publish_state(uint8_t hour, uint8_t minute, uint8_t second) {
|
bool MQTTTimeComponent::publish_state(uint8_t hour, uint8_t minute, uint8_t second) {
|
||||||
return this->publish_json(this->get_state_topic_(), [hour, minute, second](JsonObject root) {
|
return this->publish_json(this->get_state_topic_(), [hour, minute, second](JsonObject root) {
|
||||||
|
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||||
root["hour"] = hour;
|
root["hour"] = hour;
|
||||||
root["minute"] = minute;
|
root["minute"] = minute;
|
||||||
root["second"] = second;
|
root["second"] = second;
|
||||||
|
@ -41,6 +41,7 @@ bool MQTTUpdateComponent::publish_state() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MQTTUpdateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
|
void MQTTUpdateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
|
||||||
|
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||||
root["schema"] = "json";
|
root["schema"] = "json";
|
||||||
root[MQTT_PAYLOAD_INSTALL] = "INSTALL";
|
root[MQTT_PAYLOAD_INSTALL] = "INSTALL";
|
||||||
}
|
}
|
||||||
|
@ -49,8 +49,10 @@ void MQTTValveComponent::dump_config() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
void MQTTValveComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
|
void MQTTValveComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
|
||||||
if (!this->valve_->get_device_class().empty())
|
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||||
|
if (!this->valve_->get_device_class().empty()) {
|
||||||
root[MQTT_DEVICE_CLASS] = this->valve_->get_device_class();
|
root[MQTT_DEVICE_CLASS] = this->valve_->get_device_class();
|
||||||
|
}
|
||||||
|
|
||||||
auto traits = this->valve_->get_traits();
|
auto traits = this->valve_->get_traits();
|
||||||
if (traits.get_is_assumed_state()) {
|
if (traits.get_is_assumed_state()) {
|
||||||
|
@ -792,7 +792,7 @@ std::string WebServer::light_json(light::LightState *obj, JsonDetail start_confi
|
|||||||
|
|
||||||
light::LightJSONSchema::dump_json(*obj, root);
|
light::LightJSONSchema::dump_json(*obj, root);
|
||||||
if (start_config == DETAIL_ALL) {
|
if (start_config == DETAIL_ALL) {
|
||||||
JsonArray opt = root.createNestedArray("effects");
|
JsonArray opt = root["effects"].to<JsonArray>();
|
||||||
opt.add("None");
|
opt.add("None");
|
||||||
for (auto const &option : obj->get_effects()) {
|
for (auto const &option : obj->get_effects()) {
|
||||||
opt.add(option->get_name());
|
opt.add(option->get_name());
|
||||||
@ -1238,7 +1238,7 @@ std::string WebServer::select_json(select::Select *obj, const std::string &value
|
|||||||
return json::build_json([this, obj, value, start_config](JsonObject root) {
|
return json::build_json([this, obj, value, start_config](JsonObject root) {
|
||||||
set_json_icon_state_value(root, obj, "select-" + obj->get_object_id(), value, value, start_config);
|
set_json_icon_state_value(root, obj, "select-" + obj->get_object_id(), value, value, start_config);
|
||||||
if (start_config == DETAIL_ALL) {
|
if (start_config == DETAIL_ALL) {
|
||||||
JsonArray opt = root.createNestedArray("option");
|
JsonArray opt = root["option"].to<JsonArray>();
|
||||||
for (auto &option : obj->traits.get_options()) {
|
for (auto &option : obj->traits.get_options()) {
|
||||||
opt.add(option);
|
opt.add(option);
|
||||||
}
|
}
|
||||||
@ -1322,6 +1322,7 @@ std::string WebServer::climate_all_json_generator(WebServer *web_server, void *s
|
|||||||
return web_server->climate_json((climate::Climate *) (source), DETAIL_ALL);
|
return web_server->climate_json((climate::Climate *) (source), DETAIL_ALL);
|
||||||
}
|
}
|
||||||
std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_config) {
|
std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_config) {
|
||||||
|
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||||
return json::build_json([this, obj, start_config](JsonObject root) {
|
return json::build_json([this, obj, start_config](JsonObject root) {
|
||||||
set_json_id(root, obj, "climate-" + obj->get_object_id(), start_config);
|
set_json_id(root, obj, "climate-" + obj->get_object_id(), start_config);
|
||||||
const auto traits = obj->get_traits();
|
const auto traits = obj->get_traits();
|
||||||
@ -1330,32 +1331,32 @@ std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_conf
|
|||||||
char buf[16];
|
char buf[16];
|
||||||
|
|
||||||
if (start_config == DETAIL_ALL) {
|
if (start_config == DETAIL_ALL) {
|
||||||
JsonArray opt = root.createNestedArray("modes");
|
JsonArray opt = root["modes"].to<JsonArray>();
|
||||||
for (climate::ClimateMode m : traits.get_supported_modes())
|
for (climate::ClimateMode m : traits.get_supported_modes())
|
||||||
opt.add(PSTR_LOCAL(climate::climate_mode_to_string(m)));
|
opt.add(PSTR_LOCAL(climate::climate_mode_to_string(m)));
|
||||||
if (!traits.get_supported_custom_fan_modes().empty()) {
|
if (!traits.get_supported_custom_fan_modes().empty()) {
|
||||||
JsonArray opt = root.createNestedArray("fan_modes");
|
JsonArray opt = root["fan_modes"].to<JsonArray>();
|
||||||
for (climate::ClimateFanMode m : traits.get_supported_fan_modes())
|
for (climate::ClimateFanMode m : traits.get_supported_fan_modes())
|
||||||
opt.add(PSTR_LOCAL(climate::climate_fan_mode_to_string(m)));
|
opt.add(PSTR_LOCAL(climate::climate_fan_mode_to_string(m)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!traits.get_supported_custom_fan_modes().empty()) {
|
if (!traits.get_supported_custom_fan_modes().empty()) {
|
||||||
JsonArray opt = root.createNestedArray("custom_fan_modes");
|
JsonArray opt = root["custom_fan_modes"].to<JsonArray>();
|
||||||
for (auto const &custom_fan_mode : traits.get_supported_custom_fan_modes())
|
for (auto const &custom_fan_mode : traits.get_supported_custom_fan_modes())
|
||||||
opt.add(custom_fan_mode);
|
opt.add(custom_fan_mode);
|
||||||
}
|
}
|
||||||
if (traits.get_supports_swing_modes()) {
|
if (traits.get_supports_swing_modes()) {
|
||||||
JsonArray opt = root.createNestedArray("swing_modes");
|
JsonArray opt = root["swing_modes"].to<JsonArray>();
|
||||||
for (auto swing_mode : traits.get_supported_swing_modes())
|
for (auto swing_mode : traits.get_supported_swing_modes())
|
||||||
opt.add(PSTR_LOCAL(climate::climate_swing_mode_to_string(swing_mode)));
|
opt.add(PSTR_LOCAL(climate::climate_swing_mode_to_string(swing_mode)));
|
||||||
}
|
}
|
||||||
if (traits.get_supports_presets() && obj->preset.has_value()) {
|
if (traits.get_supports_presets() && obj->preset.has_value()) {
|
||||||
JsonArray opt = root.createNestedArray("presets");
|
JsonArray opt = root["presets"].to<JsonArray>();
|
||||||
for (climate::ClimatePreset m : traits.get_supported_presets())
|
for (climate::ClimatePreset m : traits.get_supported_presets())
|
||||||
opt.add(PSTR_LOCAL(climate::climate_preset_to_string(m)));
|
opt.add(PSTR_LOCAL(climate::climate_preset_to_string(m)));
|
||||||
}
|
}
|
||||||
if (!traits.get_supported_custom_presets().empty() && obj->custom_preset.has_value()) {
|
if (!traits.get_supported_custom_presets().empty() && obj->custom_preset.has_value()) {
|
||||||
JsonArray opt = root.createNestedArray("custom_presets");
|
JsonArray opt = root["custom_presets"].to<JsonArray>();
|
||||||
for (auto const &custom_preset : traits.get_supported_custom_presets())
|
for (auto const &custom_preset : traits.get_supported_custom_presets())
|
||||||
opt.add(custom_preset);
|
opt.add(custom_preset);
|
||||||
}
|
}
|
||||||
@ -1407,6 +1408,7 @@ std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_conf
|
|||||||
root["state"] = root["target_temperature"];
|
root["state"] = root["target_temperature"];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -1635,7 +1637,7 @@ std::string WebServer::event_json(event::Event *obj, const std::string &event_ty
|
|||||||
root["event_type"] = event_type;
|
root["event_type"] = event_type;
|
||||||
}
|
}
|
||||||
if (start_config == DETAIL_ALL) {
|
if (start_config == DETAIL_ALL) {
|
||||||
JsonArray event_types = root.createNestedArray("event_types");
|
JsonArray event_types = root["event_types"].to<JsonArray>();
|
||||||
for (auto const &event_type : obj->get_event_types()) {
|
for (auto const &event_type : obj->get_event_types()) {
|
||||||
event_types.add(event_type);
|
event_types.add(event_type);
|
||||||
}
|
}
|
||||||
@ -1682,6 +1684,7 @@ std::string WebServer::update_all_json_generator(WebServer *web_server, void *so
|
|||||||
return web_server->update_json((update::UpdateEntity *) (source), DETAIL_STATE);
|
return web_server->update_json((update::UpdateEntity *) (source), DETAIL_STATE);
|
||||||
}
|
}
|
||||||
std::string WebServer::update_json(update::UpdateEntity *obj, JsonDetail start_config) {
|
std::string WebServer::update_json(update::UpdateEntity *obj, JsonDetail start_config) {
|
||||||
|
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||||
return json::build_json([this, obj, start_config](JsonObject root) {
|
return json::build_json([this, obj, start_config](JsonObject root) {
|
||||||
set_json_id(root, obj, "update-" + obj->get_object_id(), start_config);
|
set_json_id(root, obj, "update-" + obj->get_object_id(), start_config);
|
||||||
root["value"] = obj->update_info.latest_version;
|
root["value"] = obj->update_info.latest_version;
|
||||||
@ -1707,6 +1710,7 @@ std::string WebServer::update_json(update::UpdateEntity *obj, JsonDetail start_c
|
|||||||
this->add_sorting_info_(root, obj);
|
this->add_sorting_info_(root, obj);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -1887,7 +1891,7 @@ void WebServer::handleRequest(AsyncWebServerRequest *request) {
|
|||||||
void (WebServer::*handler)(AsyncWebServerRequest *, const UrlMatch &);
|
void (WebServer::*handler)(AsyncWebServerRequest *, const UrlMatch &);
|
||||||
};
|
};
|
||||||
|
|
||||||
static const ComponentRoute routes[] = {
|
static const ComponentRoute ROUTES[] = {
|
||||||
#ifdef USE_SENSOR
|
#ifdef USE_SENSOR
|
||||||
{"sensor", &WebServer::handle_sensor_request},
|
{"sensor", &WebServer::handle_sensor_request},
|
||||||
#endif
|
#endif
|
||||||
@ -1948,7 +1952,7 @@ void WebServer::handleRequest(AsyncWebServerRequest *request) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Check each route
|
// Check each route
|
||||||
for (const auto &route : routes) {
|
for (const auto &route : ROUTES) {
|
||||||
if (match.domain_equals(route.domain)) {
|
if (match.domain_equals(route.domain)) {
|
||||||
(this->*route.handler)(request, match);
|
(this->*route.handler)(request, match);
|
||||||
return;
|
return;
|
||||||
|
@ -40,7 +40,4 @@ async def to_code(config):
|
|||||||
if CORE.is_esp8266:
|
if CORE.is_esp8266:
|
||||||
cg.add_library("ESP8266WiFi", None)
|
cg.add_library("ESP8266WiFi", None)
|
||||||
# https://github.com/ESP32Async/ESPAsyncWebServer/blob/main/library.json
|
# https://github.com/ESP32Async/ESPAsyncWebServer/blob/main/library.json
|
||||||
# Use fork with libretiny compatibility fix
|
cg.add_library("ESP32Async/ESPAsyncWebServer", "3.7.10")
|
||||||
cg.add_library(
|
|
||||||
"https://github.com/bdraco/ESPAsyncWebServer.git#libretiny_Fix", None
|
|
||||||
)
|
|
||||||
|
@ -145,6 +145,7 @@
|
|||||||
#define USE_CAPTIVE_PORTAL
|
#define USE_CAPTIVE_PORTAL
|
||||||
#define USE_ESP32_BLE
|
#define USE_ESP32_BLE
|
||||||
#define USE_ESP32_BLE_CLIENT
|
#define USE_ESP32_BLE_CLIENT
|
||||||
|
#define USE_ESP32_BLE_DEVICE
|
||||||
#define USE_ESP32_BLE_SERVER
|
#define USE_ESP32_BLE_SERVER
|
||||||
#define USE_I2C
|
#define USE_I2C
|
||||||
#define USE_IMPROV
|
#define USE_IMPROV
|
||||||
|
@ -79,6 +79,8 @@ def run_platformio_cli(*args, **kwargs) -> str | int:
|
|||||||
os.environ.setdefault(
|
os.environ.setdefault(
|
||||||
"PLATFORMIO_LIBDEPS_DIR", os.path.abspath(CORE.relative_piolibdeps_path())
|
"PLATFORMIO_LIBDEPS_DIR", os.path.abspath(CORE.relative_piolibdeps_path())
|
||||||
)
|
)
|
||||||
|
# Suppress Python syntax warnings from third-party scripts during compilation
|
||||||
|
os.environ.setdefault("PYTHONWARNINGS", "ignore::SyntaxWarning")
|
||||||
cmd = ["platformio"] + list(args)
|
cmd = ["platformio"] + list(args)
|
||||||
|
|
||||||
if not CORE.verbose:
|
if not CORE.verbose:
|
||||||
|
@ -162,6 +162,9 @@ def get_ini_content():
|
|||||||
# Sort to avoid changing build unflags order
|
# Sort to avoid changing build unflags order
|
||||||
CORE.add_platformio_option("build_unflags", sorted(CORE.build_unflags))
|
CORE.add_platformio_option("build_unflags", sorted(CORE.build_unflags))
|
||||||
|
|
||||||
|
# Add extra script for C++ flags
|
||||||
|
CORE.add_platformio_option("extra_scripts", ["pre:cxx_flags.py"])
|
||||||
|
|
||||||
content = "[platformio]\n"
|
content = "[platformio]\n"
|
||||||
content += f"description = ESPHome {__version__}\n"
|
content += f"description = ESPHome {__version__}\n"
|
||||||
|
|
||||||
@ -222,6 +225,9 @@ def write_platformio_project():
|
|||||||
write_gitignore()
|
write_gitignore()
|
||||||
write_platformio_ini(content)
|
write_platformio_ini(content)
|
||||||
|
|
||||||
|
# Write extra script for C++ specific flags
|
||||||
|
write_cxx_flags_script()
|
||||||
|
|
||||||
|
|
||||||
DEFINES_H_FORMAT = ESPHOME_H_FORMAT = """\
|
DEFINES_H_FORMAT = ESPHOME_H_FORMAT = """\
|
||||||
#pragma once
|
#pragma once
|
||||||
@ -394,3 +400,16 @@ def write_gitignore():
|
|||||||
if not os.path.isfile(path):
|
if not os.path.isfile(path):
|
||||||
with open(file=path, mode="w", encoding="utf-8") as f:
|
with open(file=path, mode="w", encoding="utf-8") as f:
|
||||||
f.write(GITIGNORE_CONTENT)
|
f.write(GITIGNORE_CONTENT)
|
||||||
|
|
||||||
|
|
||||||
|
CXX_FLAGS_SCRIPT = """# Auto-generated ESPHome script for C++ specific compiler flags
|
||||||
|
Import("env")
|
||||||
|
|
||||||
|
# Add C++ specific warning flags
|
||||||
|
env.Append(CXXFLAGS=["-Wno-volatile"])
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def write_cxx_flags_script() -> None:
|
||||||
|
path = CORE.relative_build_path("cxx_flags.py")
|
||||||
|
write_file_if_changed(path, CXX_FLAGS_SCRIPT)
|
||||||
|
@ -35,7 +35,7 @@ build_flags =
|
|||||||
lib_deps =
|
lib_deps =
|
||||||
esphome/noise-c@0.1.10 ; api
|
esphome/noise-c@0.1.10 ; api
|
||||||
improv/Improv@1.2.4 ; improv_serial / esp32_improv
|
improv/Improv@1.2.4 ; improv_serial / esp32_improv
|
||||||
bblanchon/ArduinoJson@6.18.5 ; json
|
bblanchon/ArduinoJson@7.4.2 ; json
|
||||||
wjtje/qr-code-generator-library@1.7.0 ; qr_code
|
wjtje/qr-code-generator-library@1.7.0 ; qr_code
|
||||||
functionpointer/arduino-MLX90393@1.0.2 ; mlx90393
|
functionpointer/arduino-MLX90393@1.0.2 ; mlx90393
|
||||||
pavlodn/HaierProtocol@0.9.31 ; haier
|
pavlodn/HaierProtocol@0.9.31 ; haier
|
||||||
@ -235,7 +235,7 @@ build_flags =
|
|||||||
-DUSE_ZEPHYR
|
-DUSE_ZEPHYR
|
||||||
-DUSE_NRF52
|
-DUSE_NRF52
|
||||||
lib_deps =
|
lib_deps =
|
||||||
bblanchon/ArduinoJson@7.0.0 ; json
|
bblanchon/ArduinoJson@7.4.2 ; json
|
||||||
wjtje/qr-code-generator-library@1.7.0 ; qr_code
|
wjtje/qr-code-generator-library@1.7.0 ; qr_code
|
||||||
pavlodn/HaierProtocol@0.9.31 ; haier
|
pavlodn/HaierProtocol@0.9.31 ; haier
|
||||||
functionpointer/arduino-MLX90393@1.0.2 ; mlx90393
|
functionpointer/arduino-MLX90393@1.0.2 ; mlx90393
|
||||||
|
@ -126,7 +126,8 @@ def write_file_content(path: Path, content: str) -> None:
|
|||||||
def write_hash(hash_value: str) -> None:
|
def write_hash(hash_value: str) -> None:
|
||||||
"""Write hash to file"""
|
"""Write hash to file"""
|
||||||
hash_file = Path(__file__).parent.parent / ".clang-tidy.hash"
|
hash_file = Path(__file__).parent.parent / ".clang-tidy.hash"
|
||||||
write_file_content(hash_file, hash_value)
|
# Strip any trailing newlines to ensure consistent formatting
|
||||||
|
write_file_content(hash_file, hash_value.strip() + "\n")
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
|
@ -72,7 +72,7 @@ for f in ./tests/components/$target_component/*.*.yaml; do
|
|||||||
|
|
||||||
if [ "$target_platform" = "all" ] || [ $file_name_parts = 2 ]; then
|
if [ "$target_platform" = "all" ] || [ $file_name_parts = 2 ]; then
|
||||||
# Test has *not* defined a specific target platform. Need to run tests for all possible target platforms.
|
# Test has *not* defined a specific target platform. Need to run tests for all possible target platforms.
|
||||||
|
|
||||||
for target_platform_file in ./tests/test_build_components/build_components_base.*.yaml; do
|
for target_platform_file in ./tests/test_build_components/build_components_base.*.yaml; do
|
||||||
IFS='/' read -r -a folder_name <<< "$target_platform_file"
|
IFS='/' read -r -a folder_name <<< "$target_platform_file"
|
||||||
IFS='.' read -r -a file_name <<< "${folder_name[3]}"
|
IFS='.' read -r -a file_name <<< "${folder_name[3]}"
|
||||||
@ -83,7 +83,7 @@ for f in ./tests/components/$target_component/*.*.yaml; do
|
|||||||
|
|
||||||
else
|
else
|
||||||
# Test has defined a specific target platform.
|
# Test has defined a specific target platform.
|
||||||
|
|
||||||
# Validate we have a base test yaml for selected platform.
|
# Validate we have a base test yaml for selected platform.
|
||||||
# The target_platform is sourced from the following location.
|
# The target_platform is sourced from the following location.
|
||||||
# 1. `./tests/test_build_components/build_components_base.[target_platform].yaml`
|
# 1. `./tests/test_build_components/build_components_base.[target_platform].yaml`
|
||||||
|
1
tests/components/animation/.gitattributes
vendored
1
tests/components/animation/.gitattributes
vendored
@ -1,4 +1,3 @@
|
|||||||
*.apng -text
|
*.apng -text
|
||||||
*.webp -text
|
*.webp -text
|
||||||
*.gif -text
|
*.gif -text
|
||||||
|
|
||||||
|
@ -15,4 +15,3 @@ display:
|
|||||||
|
|
||||||
packages:
|
packages:
|
||||||
animation: !include common.yaml
|
animation: !include common.yaml
|
||||||
|
|
||||||
|
@ -21,4 +21,3 @@ binary_sensor:
|
|||||||
- platform: ble_presence
|
- platform: ble_presence
|
||||||
irk: 1234567890abcdef1234567890abcdef
|
irk: 1234567890abcdef1234567890abcdef
|
||||||
name: "ESP32 BLE Tracker with Identity Resolving Key"
|
name: "ESP32 BLE Tracker with Identity Resolving Key"
|
||||||
|
|
||||||
|
@ -17,4 +17,3 @@ color:
|
|||||||
hex: 008000
|
hex: 008000
|
||||||
- id: cps_blue
|
- id: cps_blue
|
||||||
hex: 000080
|
hex: 000080
|
||||||
|
|
||||||
|
@ -41,4 +41,3 @@ display:
|
|||||||
- delay 120ms
|
- delay 120ms
|
||||||
- [0x29]
|
- [0x29]
|
||||||
- delay 20ms
|
- delay 20ms
|
||||||
|
|
||||||
|
@ -42,4 +42,3 @@ binary_sensor:
|
|||||||
x_max: 480
|
x_max: 480
|
||||||
y_min: 320
|
y_min: 320
|
||||||
y_max: 360
|
y_max: 360
|
||||||
|
|
||||||
|
@ -15,4 +15,3 @@ sensor:
|
|||||||
name: "Loop Time"
|
name: "Loop Time"
|
||||||
cpu_frequency:
|
cpu_frequency:
|
||||||
name: "CPU Frequency"
|
name: "CPU Frequency"
|
||||||
|
|
||||||
|
@ -14,4 +14,3 @@ sensor:
|
|||||||
name: "ENS160 Total Volatile Organic Compounds"
|
name: "ENS160 Total Volatile Organic Compounds"
|
||||||
aqi:
|
aqi:
|
||||||
name: "ENS160 Air Quality Index"
|
name: "ENS160 Air Quality Index"
|
||||||
|
|
||||||
|
@ -9,4 +9,3 @@ esp32:
|
|||||||
wifi:
|
wifi:
|
||||||
ssid: MySSID
|
ssid: MySSID
|
||||||
password: password1
|
password: password1
|
||||||
|
|
||||||
|
@ -1,2 +1 @@
|
|||||||
<<: !include common.yaml
|
<<: !include common.yaml
|
||||||
|
|
||||||
|
@ -1,2 +1 @@
|
|||||||
<<: !include common.yaml
|
<<: !include common.yaml
|
||||||
|
|
||||||
|
1
tests/components/font/.gitattributes
vendored
1
tests/components/font/.gitattributes
vendored
@ -1,2 +1 @@
|
|||||||
*.pcf -text
|
*.pcf -text
|
||||||
|
|
||||||
|
1
tests/components/lvgl/.gitattributes
vendored
1
tests/components/lvgl/.gitattributes
vendored
@ -1,2 +1 @@
|
|||||||
*.ttf -text
|
*.ttf -text
|
||||||
|
|
||||||
|
@ -56,4 +56,3 @@ lvgl:
|
|||||||
packages:
|
packages:
|
||||||
lvgl: !include lvgl-package.yaml
|
lvgl: !include lvgl-package.yaml
|
||||||
xvgl: !include common.yaml
|
xvgl: !include common.yaml
|
||||||
|
|
||||||
|
@ -3,4 +3,3 @@ substitutions:
|
|||||||
scl_pin: GPIO22
|
scl_pin: GPIO22
|
||||||
|
|
||||||
<<: !include common.yaml
|
<<: !include common.yaml
|
||||||
|
|
||||||
|
@ -35,4 +35,3 @@ display:
|
|||||||
allow_other_uses: true
|
allow_other_uses: true
|
||||||
- number: ${enable_pin}
|
- number: ${enable_pin}
|
||||||
bus_mode: single
|
bus_mode: single
|
||||||
|
|
||||||
|
@ -4,4 +4,3 @@ substitutions:
|
|||||||
|
|
||||||
packages:
|
packages:
|
||||||
base: !include common.yaml
|
base: !include common.yaml
|
||||||
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
<<: !include common-esp32.yaml
|
<<: !include common-esp32.yaml
|
||||||
|
|
||||||
http_request:
|
http_request:
|
||||||
|
|
||||||
|
@ -11,4 +11,3 @@ openthread:
|
|||||||
pskc: 0xc23a76e98f1a6483639b1ac1271e2e27
|
pskc: 0xc23a76e98f1a6483639b1ac1271e2e27
|
||||||
mesh_local_prefix: fd53:145f:ed22:ad81::/64
|
mesh_local_prefix: fd53:145f:ed22:ad81::/64
|
||||||
force_dataset: true
|
force_dataset: true
|
||||||
|
|
||||||
|
@ -9,4 +9,3 @@ packages:
|
|||||||
file: common.yaml
|
file: common.yaml
|
||||||
ref: dev
|
ref: dev
|
||||||
refresh: 1d
|
refresh: 1d
|
||||||
|
|
||||||
|
@ -11,4 +11,3 @@ packages:
|
|||||||
file: common.yaml
|
file: common.yaml
|
||||||
ref: dev
|
ref: dev
|
||||||
refresh: 1d
|
refresh: 1d
|
||||||
|
|
||||||
|
@ -41,4 +41,3 @@ display:
|
|||||||
- delay 120ms
|
- delay 120ms
|
||||||
- [0x29]
|
- [0x29]
|
||||||
- delay 20ms
|
- delay 20ms
|
||||||
|
|
||||||
|
@ -35,4 +35,3 @@ spi:
|
|||||||
interface: any
|
interface: any
|
||||||
clk_pin: 8
|
clk_pin: 8
|
||||||
mosi_pin: 9
|
mosi_pin: 9
|
||||||
|
|
||||||
|
@ -35,4 +35,3 @@ spi:
|
|||||||
interface: any
|
interface: any
|
||||||
clk_pin: 8
|
clk_pin: 8
|
||||||
mosi_pin: 9
|
mosi_pin: 9
|
||||||
|
|
||||||
|
@ -17,4 +17,3 @@ udp:
|
|||||||
id: my_udp
|
id: my_udp
|
||||||
data: !lambda |-
|
data: !lambda |-
|
||||||
return std::vector<uint8_t>{1,3,4,5,6};
|
return std::vector<uint8_t>{1,3,4,5,6};
|
||||||
|
|
||||||
|
@ -27,4 +27,3 @@ logger:
|
|||||||
logs:
|
logs:
|
||||||
web_server: VERBOSE
|
web_server: VERBOSE
|
||||||
web_server_idf: VERBOSE
|
web_server_idf: VERBOSE
|
||||||
|
|
||||||
|
@ -3,4 +3,3 @@ substitutions:
|
|||||||
sda_pin: GPIO21
|
sda_pin: GPIO21
|
||||||
|
|
||||||
<<: !include common.yaml
|
<<: !include common.yaml
|
||||||
|
|
||||||
|
@ -54,4 +54,3 @@ sensor:
|
|||||||
device_id: smart_switch_device
|
device_id: smart_switch_device
|
||||||
lambda: return 4.0;
|
lambda: return 4.0;
|
||||||
update_interval: 0.1s
|
update_interval: 0.1s
|
||||||
|
|
||||||
|
@ -82,4 +82,3 @@ output:
|
|||||||
write_action:
|
write_action:
|
||||||
- lambda: |-
|
- lambda: |-
|
||||||
ESP_LOGD("test", "Light output: %d", state);
|
ESP_LOGD("test", "Light output: %d", state);
|
||||||
|
|
||||||
|
@ -134,4 +134,3 @@ switch:
|
|||||||
name: "Test Switch"
|
name: "Test Switch"
|
||||||
id: test_switch
|
id: test_switch
|
||||||
optimistic: true
|
optimistic: true
|
||||||
|
|
||||||
|
@ -158,7 +158,7 @@ def test_write_hash() -> None:
|
|||||||
mock_write.assert_called_once()
|
mock_write.assert_called_once()
|
||||||
args = mock_write.call_args[0]
|
args = mock_write.call_args[0]
|
||||||
assert str(args[0]).endswith(".clang-tidy.hash")
|
assert str(args[0]).endswith(".clang-tidy.hash")
|
||||||
assert args[1] == hash_value
|
assert args[1] == hash_value.strip() + "\n"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
|
@ -1 +1 @@
|
|||||||
*.received.yaml
|
*.received.yaml
|
||||||
|
Loading…
x
Reference in New Issue
Block a user