mirror of
https://github.com/esphome/esphome.git
synced 2024-11-25 12:15:33 +01:00
Merge branch 'dev' into optolink
This commit is contained in:
commit
27ce6f4a0a
@ -40,19 +40,18 @@ void AHT10Component::setup() {
|
|||||||
}
|
}
|
||||||
delay(AHT10_SOFTRESET_DELAY);
|
delay(AHT10_SOFTRESET_DELAY);
|
||||||
|
|
||||||
const uint8_t *init_cmd;
|
i2c::ErrorCode error_code = i2c::ERROR_INVALID_ARGUMENT;
|
||||||
switch (this->variant_) {
|
switch (this->variant_) {
|
||||||
case AHT10Variant::AHT20:
|
case AHT10Variant::AHT20:
|
||||||
init_cmd = AHT20_INITIALIZE_CMD;
|
|
||||||
ESP_LOGCONFIG(TAG, "Setting up AHT20");
|
ESP_LOGCONFIG(TAG, "Setting up AHT20");
|
||||||
|
error_code = this->write(AHT20_INITIALIZE_CMD, sizeof(AHT20_INITIALIZE_CMD));
|
||||||
break;
|
break;
|
||||||
case AHT10Variant::AHT10:
|
case AHT10Variant::AHT10:
|
||||||
default:
|
|
||||||
init_cmd = AHT10_INITIALIZE_CMD;
|
|
||||||
ESP_LOGCONFIG(TAG, "Setting up AHT10");
|
ESP_LOGCONFIG(TAG, "Setting up AHT10");
|
||||||
|
error_code = this->write(AHT10_INITIALIZE_CMD, sizeof(AHT10_INITIALIZE_CMD));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
if (error_code != i2c::ERROR_OK) {
|
||||||
if (this->write(init_cmd, sizeof(init_cmd)) != i2c::ERROR_OK) {
|
|
||||||
ESP_LOGE(TAG, "Communication with AHT10 failed!");
|
ESP_LOGE(TAG, "Communication with AHT10 failed!");
|
||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
return;
|
return;
|
||||||
|
@ -1,36 +1,87 @@
|
|||||||
#ifdef USE_HOST
|
#ifdef USE_HOST
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
#include "preferences.h"
|
#include "preferences.h"
|
||||||
#include <cstring>
|
#include "esphome/core/application.h"
|
||||||
#include "esphome/core/preferences.h"
|
|
||||||
#include "esphome/core/helpers.h"
|
|
||||||
#include "esphome/core/log.h"
|
|
||||||
#include "esphome/core/defines.h"
|
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace host {
|
namespace host {
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
static const char *const TAG = "host.preferences";
|
static const char *const TAG = "host.preferences";
|
||||||
|
|
||||||
class HostPreferences : public ESPPreferences {
|
void HostPreferences::setup_() {
|
||||||
public:
|
if (this->setup_complete_)
|
||||||
ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash) override { return {}; }
|
return;
|
||||||
|
this->filename_.append(getenv("HOME"));
|
||||||
|
this->filename_.append("/.esphome");
|
||||||
|
this->filename_.append("/prefs");
|
||||||
|
fs::create_directories(this->filename_);
|
||||||
|
this->filename_.append("/");
|
||||||
|
this->filename_.append(App.get_name());
|
||||||
|
this->filename_.append(".prefs");
|
||||||
|
FILE *fp = fopen(this->filename_.c_str(), "rb");
|
||||||
|
if (fp != nullptr) {
|
||||||
|
while (!feof((fp))) {
|
||||||
|
uint32_t key;
|
||||||
|
uint8_t len;
|
||||||
|
if (fread(&key, sizeof(key), 1, fp) != 1)
|
||||||
|
break;
|
||||||
|
if (fread(&len, sizeof(len), 1, fp) != 1)
|
||||||
|
break;
|
||||||
|
uint8_t data[len];
|
||||||
|
if (fread(data, sizeof(uint8_t), len, fp) != len)
|
||||||
|
break;
|
||||||
|
std::vector vec(data, data + len);
|
||||||
|
this->data[key] = vec;
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
this->setup_complete_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
ESPPreferenceObject make_preference(size_t length, uint32_t type) override { return {}; }
|
bool HostPreferences::sync() {
|
||||||
|
this->setup_();
|
||||||
|
FILE *fp = fopen(this->filename_.c_str(), "wb");
|
||||||
|
std::map<uint32_t, std::vector<uint8_t>>::iterator it;
|
||||||
|
|
||||||
bool sync() override { return true; }
|
for (it = this->data.begin(); it != this->data.end(); ++it) {
|
||||||
bool reset() override { return true; }
|
fwrite(&it->first, sizeof(uint32_t), 1, fp);
|
||||||
|
uint8_t len = it->second.size();
|
||||||
|
fwrite(&len, sizeof(len), 1, fp);
|
||||||
|
fwrite(it->second.data(), sizeof(uint8_t), it->second.size(), fp);
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HostPreferences::reset() {
|
||||||
|
host_preferences->data.clear();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESPPreferenceObject HostPreferences::make_preference(size_t length, uint32_t type, bool in_flash) {
|
||||||
|
auto backend = new HostPreferenceBackend(type);
|
||||||
|
return ESPPreferenceObject(backend);
|
||||||
};
|
};
|
||||||
|
|
||||||
void setup_preferences() {
|
void setup_preferences() {
|
||||||
auto *pref = new HostPreferences(); // NOLINT(cppcoreguidelines-owning-memory)
|
auto *pref = new HostPreferences(); // NOLINT(cppcoreguidelines-owning-memory)
|
||||||
|
host_preferences = pref;
|
||||||
global_preferences = pref;
|
global_preferences = pref;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool HostPreferenceBackend::save(const uint8_t *data, size_t len) {
|
||||||
|
return host_preferences->save(this->key_, data, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HostPreferenceBackend::load(uint8_t *data, size_t len) { return host_preferences->load(this->key_, data, len); }
|
||||||
|
|
||||||
|
HostPreferences *host_preferences;
|
||||||
} // namespace host
|
} // namespace host
|
||||||
|
|
||||||
ESPPreferences *global_preferences; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
ESPPreferences *global_preferences; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||||
|
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
||||||
#endif // USE_HOST
|
#endif // USE_HOST
|
||||||
|
@ -2,10 +2,63 @@
|
|||||||
|
|
||||||
#ifdef USE_HOST
|
#ifdef USE_HOST
|
||||||
|
|
||||||
|
#include "esphome/core/preferences.h"
|
||||||
|
#include <map>
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace host {
|
namespace host {
|
||||||
|
|
||||||
|
class HostPreferenceBackend : public ESPPreferenceBackend {
|
||||||
|
public:
|
||||||
|
explicit HostPreferenceBackend(uint32_t key) { this->key_ = key; }
|
||||||
|
|
||||||
|
bool save(const uint8_t *data, size_t len) override;
|
||||||
|
bool load(uint8_t *data, size_t len) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
uint32_t key_{};
|
||||||
|
};
|
||||||
|
|
||||||
|
class HostPreferences : public ESPPreferences {
|
||||||
|
public:
|
||||||
|
bool sync() override;
|
||||||
|
bool reset() override;
|
||||||
|
|
||||||
|
ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash) override;
|
||||||
|
ESPPreferenceObject make_preference(size_t length, uint32_t type) override {
|
||||||
|
return make_preference(length, type, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool save(uint32_t key, const uint8_t *data, size_t len) {
|
||||||
|
if (len > 255)
|
||||||
|
return false;
|
||||||
|
this->setup_();
|
||||||
|
std::vector vec(data, data + len);
|
||||||
|
this->data[key] = vec;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool load(uint32_t key, uint8_t *data, size_t len) {
|
||||||
|
if (len > 255)
|
||||||
|
return false;
|
||||||
|
this->setup_();
|
||||||
|
if (this->data.count(key) == 0)
|
||||||
|
return false;
|
||||||
|
auto vec = this->data[key];
|
||||||
|
if (vec.size() != len)
|
||||||
|
return false;
|
||||||
|
memcpy(data, vec.data(), len);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void setup_();
|
||||||
|
bool setup_complete_{};
|
||||||
|
std::string filename_{};
|
||||||
|
std::map<uint32_t, std::vector<uint8_t>> data{};
|
||||||
|
};
|
||||||
void setup_preferences();
|
void setup_preferences();
|
||||||
|
extern HostPreferences *host_preferences; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||||
|
|
||||||
} // namespace host
|
} // namespace host
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
@ -287,7 +287,7 @@ def _load_model_data(manifest_path: Path):
|
|||||||
except cv.Invalid as e:
|
except cv.Invalid as e:
|
||||||
raise EsphomeError(f"Invalid manifest file: {e}") from e
|
raise EsphomeError(f"Invalid manifest file: {e}") from e
|
||||||
|
|
||||||
model_path = urljoin(str(manifest_path), manifest[CONF_MODEL])
|
model_path = manifest_path.parent / manifest[CONF_MODEL]
|
||||||
|
|
||||||
with open(model_path, "rb") as f:
|
with open(model_path, "rb") as f:
|
||||||
model = f.read()
|
model = f.read()
|
||||||
|
@ -22,7 +22,7 @@ CONF_PEER_ALLOWED_IPS = "peer_allowed_ips"
|
|||||||
CONF_PEER_PERSISTENT_KEEPALIVE = "peer_persistent_keepalive"
|
CONF_PEER_PERSISTENT_KEEPALIVE = "peer_persistent_keepalive"
|
||||||
CONF_REQUIRE_CONNECTION_TO_PROCEED = "require_connection_to_proceed"
|
CONF_REQUIRE_CONNECTION_TO_PROCEED = "require_connection_to_proceed"
|
||||||
|
|
||||||
DEPENDENCIES = ["time", "esp32"]
|
DEPENDENCIES = ["time"]
|
||||||
CODEOWNERS = ["@lhoracek", "@droscy", "@thomas0bernard"]
|
CODEOWNERS = ["@lhoracek", "@droscy", "@thomas0bernard"]
|
||||||
|
|
||||||
# The key validation regex has been described by Jason Donenfeld himself
|
# The key validation regex has been described by Jason Donenfeld himself
|
||||||
@ -120,7 +120,7 @@ async def to_code(config):
|
|||||||
# the '+1' modifier is relative to the device's own address that will
|
# the '+1' modifier is relative to the device's own address that will
|
||||||
# be automatically added to the provided list.
|
# be automatically added to the provided list.
|
||||||
cg.add_build_flag(f"-DCONFIG_WIREGUARD_MAX_SRC_IPS={len(allowed_ips) + 1}")
|
cg.add_build_flag(f"-DCONFIG_WIREGUARD_MAX_SRC_IPS={len(allowed_ips) + 1}")
|
||||||
cg.add_library("droscy/esp_wireguard", "0.3.2")
|
cg.add_library("droscy/esp_wireguard", "0.4.0")
|
||||||
|
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
|
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
#include "wireguard.h"
|
#include "wireguard.h"
|
||||||
|
|
||||||
#ifdef USE_ESP32
|
|
||||||
|
|
||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
@ -11,26 +9,20 @@
|
|||||||
#include "esphome/core/time.h"
|
#include "esphome/core/time.h"
|
||||||
#include "esphome/components/network/util.h"
|
#include "esphome/components/network/util.h"
|
||||||
|
|
||||||
#include <esp_err.h>
|
|
||||||
|
|
||||||
#include <esp_wireguard.h>
|
#include <esp_wireguard.h>
|
||||||
|
#include <esp_wireguard_err.h>
|
||||||
// includes for resume/suspend wdt
|
|
||||||
#if defined(USE_ESP_IDF)
|
|
||||||
#include <esp_task_wdt.h>
|
|
||||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
|
||||||
#include <spi_flash_mmap.h>
|
|
||||||
#endif
|
|
||||||
#elif defined(USE_ARDUINO)
|
|
||||||
#include <esp32-hal.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace wireguard {
|
namespace wireguard {
|
||||||
|
|
||||||
static const char *const TAG = "wireguard";
|
static const char *const TAG = "wireguard";
|
||||||
|
|
||||||
static const char *const LOGMSG_PEER_STATUS = "WireGuard remote peer is %s (latest handshake %s)";
|
/*
|
||||||
|
* Cannot use `static const char*` for LOGMSG_PEER_STATUS on esp8266 platform
|
||||||
|
* because log messages in `Wireguard::update()` method fail.
|
||||||
|
*/
|
||||||
|
#define LOGMSG_PEER_STATUS "WireGuard remote peer is %s (latest handshake %s)"
|
||||||
|
|
||||||
static const char *const LOGMSG_ONLINE = "online";
|
static const char *const LOGMSG_ONLINE = "online";
|
||||||
static const char *const LOGMSG_OFFLINE = "offline";
|
static const char *const LOGMSG_OFFLINE = "offline";
|
||||||
|
|
||||||
@ -257,20 +249,13 @@ void Wireguard::start_connection_() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ESP_LOGD(TAG, "starting WireGuard connection...");
|
ESP_LOGD(TAG, "starting WireGuard connection...");
|
||||||
|
|
||||||
/*
|
|
||||||
* The function esp_wireguard_connect() contains a DNS resolution
|
|
||||||
* that could trigger the watchdog, so before it we suspend (or
|
|
||||||
* increase the time, it depends on the platform) the wdt and
|
|
||||||
* then we resume the normal timeout.
|
|
||||||
*/
|
|
||||||
suspend_wdt();
|
|
||||||
ESP_LOGV(TAG, "executing esp_wireguard_connect");
|
|
||||||
this->wg_connected_ = esp_wireguard_connect(&(this->wg_ctx_));
|
this->wg_connected_ = esp_wireguard_connect(&(this->wg_ctx_));
|
||||||
resume_wdt();
|
|
||||||
|
|
||||||
if (this->wg_connected_ == ESP_OK) {
|
if (this->wg_connected_ == ESP_OK) {
|
||||||
ESP_LOGI(TAG, "WireGuard connection started");
|
ESP_LOGI(TAG, "WireGuard connection started");
|
||||||
|
} else if (this->wg_connected_ == ESP_ERR_RETRY) {
|
||||||
|
ESP_LOGD(TAG, "WireGuard is waiting for endpoint IP address to be available");
|
||||||
|
return;
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGW(TAG, "cannot start WireGuard connection, error code %d", this->wg_connected_);
|
ESP_LOGW(TAG, "cannot start WireGuard connection, error code %d", this->wg_connected_);
|
||||||
return;
|
return;
|
||||||
@ -300,44 +285,7 @@ void Wireguard::stop_connection_() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void suspend_wdt() {
|
|
||||||
#if defined(USE_ESP_IDF)
|
|
||||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
|
||||||
ESP_LOGV(TAG, "temporarily increasing wdt timeout to 15000 ms");
|
|
||||||
esp_task_wdt_config_t wdtc;
|
|
||||||
wdtc.timeout_ms = 15000;
|
|
||||||
wdtc.idle_core_mask = 0;
|
|
||||||
wdtc.trigger_panic = false;
|
|
||||||
esp_task_wdt_reconfigure(&wdtc);
|
|
||||||
#else
|
|
||||||
ESP_LOGV(TAG, "temporarily increasing wdt timeout to 15 seconds");
|
|
||||||
esp_task_wdt_init(15, false);
|
|
||||||
#endif
|
|
||||||
#elif defined(USE_ARDUINO)
|
|
||||||
ESP_LOGV(TAG, "temporarily disabling the wdt");
|
|
||||||
disableLoopWDT();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void resume_wdt() {
|
|
||||||
#if defined(USE_ESP_IDF)
|
|
||||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
|
||||||
wdtc.timeout_ms = CONFIG_ESP_TASK_WDT_TIMEOUT_S * 1000;
|
|
||||||
esp_task_wdt_reconfigure(&wdtc);
|
|
||||||
ESP_LOGV(TAG, "wdt resumed with %" PRIu32 " ms timeout", wdtc.timeout_ms);
|
|
||||||
#else
|
|
||||||
esp_task_wdt_init(CONFIG_ESP_TASK_WDT_TIMEOUT_S, false);
|
|
||||||
ESP_LOGV(TAG, "wdt resumed with %d seconds timeout", CONFIG_ESP_TASK_WDT_TIMEOUT_S);
|
|
||||||
#endif
|
|
||||||
#elif defined(USE_ARDUINO)
|
|
||||||
enableLoopWDT();
|
|
||||||
ESP_LOGV(TAG, "wdt resumed");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string mask_key(const std::string &key) { return (key.substr(0, 5) + "[...]="); }
|
std::string mask_key(const std::string &key) { return (key.substr(0, 5) + "[...]="); }
|
||||||
|
|
||||||
} // namespace wireguard
|
} // namespace wireguard
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
||||||
#endif // USE_ESP32
|
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#ifdef USE_ESP32
|
|
||||||
|
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
@ -172,5 +170,3 @@ template<typename... Ts> class WireguardDisableAction : public Action<Ts...>, pu
|
|||||||
|
|
||||||
} // namespace wireguard
|
} // namespace wireguard
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
||||||
#endif // USE_ESP32
|
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
|
from __future__ import annotations
|
||||||
import abc
|
import abc
|
||||||
import functools
|
import functools
|
||||||
import heapq
|
import heapq
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from typing import Optional, Union
|
from typing import Union, Any
|
||||||
|
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
import contextvars
|
import contextvars
|
||||||
@ -76,7 +77,7 @@ def _path_begins_with(path: ConfigPath, other: ConfigPath) -> bool:
|
|||||||
|
|
||||||
@functools.total_ordering
|
@functools.total_ordering
|
||||||
class _ValidationStepTask:
|
class _ValidationStepTask:
|
||||||
def __init__(self, priority: float, id_number: int, step: "ConfigValidationStep"):
|
def __init__(self, priority: float, id_number: int, step: ConfigValidationStep):
|
||||||
self.priority = priority
|
self.priority = priority
|
||||||
self.id_number = id_number
|
self.id_number = id_number
|
||||||
self.step = step
|
self.step = step
|
||||||
@ -130,7 +131,7 @@ class Config(OrderedDict, fv.FinalValidateConfig):
|
|||||||
)
|
)
|
||||||
self.errors.append(error)
|
self.errors.append(error)
|
||||||
|
|
||||||
def add_validation_step(self, step: "ConfigValidationStep"):
|
def add_validation_step(self, step: ConfigValidationStep):
|
||||||
id_num = self._validation_tasks_id
|
id_num = self._validation_tasks_id
|
||||||
self._validation_tasks_id += 1
|
self._validation_tasks_id += 1
|
||||||
heapq.heappush(
|
heapq.heappush(
|
||||||
@ -172,7 +173,7 @@ class Config(OrderedDict, fv.FinalValidateConfig):
|
|||||||
conf = conf[key]
|
conf = conf[key]
|
||||||
conf[path[-1]] = value
|
conf[path[-1]] = value
|
||||||
|
|
||||||
def get_error_for_path(self, path: ConfigPath) -> Optional[vol.Invalid]:
|
def get_error_for_path(self, path: ConfigPath) -> vol.Invalid | None:
|
||||||
for err in self.errors:
|
for err in self.errors:
|
||||||
if self.get_deepest_path(err.path) == path:
|
if self.get_deepest_path(err.path) == path:
|
||||||
self.errors.remove(err)
|
self.errors.remove(err)
|
||||||
@ -181,7 +182,7 @@ class Config(OrderedDict, fv.FinalValidateConfig):
|
|||||||
|
|
||||||
def get_deepest_document_range_for_path(
|
def get_deepest_document_range_for_path(
|
||||||
self, path: ConfigPath, get_key: bool = False
|
self, path: ConfigPath, get_key: bool = False
|
||||||
) -> Optional[ESPHomeDataBase]:
|
) -> ESPHomeDataBase | None:
|
||||||
data = self
|
data = self
|
||||||
doc_range = None
|
doc_range = None
|
||||||
for index, path_item in enumerate(path):
|
for index, path_item in enumerate(path):
|
||||||
@ -733,7 +734,9 @@ class PinUseValidationCheck(ConfigValidationStep):
|
|||||||
pins.PIN_SCHEMA_REGISTRY.final_validate(result)
|
pins.PIN_SCHEMA_REGISTRY.final_validate(result)
|
||||||
|
|
||||||
|
|
||||||
def validate_config(config, command_line_substitutions) -> Config:
|
def validate_config(
|
||||||
|
config: dict[str, Any], command_line_substitutions: dict[str, Any]
|
||||||
|
) -> Config:
|
||||||
result = Config()
|
result = Config()
|
||||||
|
|
||||||
loader.clear_component_meta_finders()
|
loader.clear_component_meta_finders()
|
||||||
@ -897,24 +900,23 @@ class InvalidYAMLError(EsphomeError):
|
|||||||
self.base_exc = base_exc
|
self.base_exc = base_exc
|
||||||
|
|
||||||
|
|
||||||
def _load_config(command_line_substitutions):
|
def _load_config(command_line_substitutions: dict[str, Any]) -> Config:
|
||||||
|
"""Load the configuration file."""
|
||||||
try:
|
try:
|
||||||
config = yaml_util.load_yaml(CORE.config_path)
|
config = yaml_util.load_yaml(CORE.config_path)
|
||||||
except EsphomeError as e:
|
except EsphomeError as e:
|
||||||
raise InvalidYAMLError(e) from e
|
raise InvalidYAMLError(e) from e
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = validate_config(config, command_line_substitutions)
|
return validate_config(config, command_line_substitutions)
|
||||||
except EsphomeError:
|
except EsphomeError:
|
||||||
raise
|
raise
|
||||||
except Exception:
|
except Exception:
|
||||||
_LOGGER.error("Unexpected exception while reading configuration:")
|
_LOGGER.error("Unexpected exception while reading configuration:")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
def load_config(command_line_substitutions: dict[str, Any]) -> Config:
|
||||||
def load_config(command_line_substitutions):
|
|
||||||
try:
|
try:
|
||||||
return _load_config(command_line_substitutions)
|
return _load_config(command_line_substitutions)
|
||||||
except vol.Invalid as err:
|
except vol.Invalid as err:
|
||||||
|
@ -1,9 +1,4 @@
|
|||||||
import json
|
|
||||||
import os
|
|
||||||
|
|
||||||
from esphome.const import CONF_ID
|
from esphome.const import CONF_ID
|
||||||
from esphome.core import CORE
|
|
||||||
from esphome.helpers import read_file
|
|
||||||
|
|
||||||
|
|
||||||
class Extend:
|
class Extend:
|
||||||
@ -38,25 +33,6 @@ class Remove:
|
|||||||
return isinstance(b, Remove) and self.value == b.value
|
return isinstance(b, Remove) and self.value == b.value
|
||||||
|
|
||||||
|
|
||||||
def read_config_file(path: str) -> str:
|
|
||||||
if CORE.vscode and (
|
|
||||||
not CORE.ace or os.path.abspath(path) == os.path.abspath(CORE.config_path)
|
|
||||||
):
|
|
||||||
print(
|
|
||||||
json.dumps(
|
|
||||||
{
|
|
||||||
"type": "read_file",
|
|
||||||
"path": path,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
data = json.loads(input())
|
|
||||||
assert data["type"] == "file_response"
|
|
||||||
return data["content"]
|
|
||||||
|
|
||||||
return read_file(path)
|
|
||||||
|
|
||||||
|
|
||||||
def merge_config(full_old, full_new):
|
def merge_config(full_old, full_new):
|
||||||
def merge(old, new):
|
def merge(old, new):
|
||||||
if isinstance(new, dict):
|
if isinstance(new, dict):
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
|
#ifdef USE_DATETIME
|
||||||
#include <regex>
|
#include <regex>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
#include "time.h" // NOLINT
|
#include "time.h" // NOLINT
|
||||||
@ -64,6 +66,8 @@ std::string ESPTime::strftime(const std::string &format) {
|
|||||||
return timestr;
|
return timestr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef USE_DATETIME
|
||||||
|
|
||||||
bool ESPTime::strptime(const std::string &time_to_parse, ESPTime &esp_time) {
|
bool ESPTime::strptime(const std::string &time_to_parse, ESPTime &esp_time) {
|
||||||
// clang-format off
|
// clang-format off
|
||||||
std::regex dt_regex(R"(^
|
std::regex dt_regex(R"(^
|
||||||
@ -102,6 +106,8 @@ bool ESPTime::strptime(const std::string &time_to_parse, ESPTime &esp_time) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
void ESPTime::increment_second() {
|
void ESPTime::increment_second() {
|
||||||
this->timestamp++;
|
this->timestamp++;
|
||||||
if (!increment_time_value(this->second, 0, 60))
|
if (!increment_time_value(this->second, 0, 60))
|
||||||
|
@ -67,6 +67,8 @@ struct ESPTime {
|
|||||||
this->day_of_year < 367 && this->month > 0 && this->month < 13;
|
this->day_of_year < 367 && this->month > 0 && this->month < 13;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef USE_DATETIME
|
||||||
|
|
||||||
/** Convert a string to ESPTime struct as specified by the format argument.
|
/** Convert a string to ESPTime struct as specified by the format argument.
|
||||||
* @param time_to_parse null-terminated c string formatet like this: 2020-08-25 05:30:00.
|
* @param time_to_parse null-terminated c string formatet like this: 2020-08-25 05:30:00.
|
||||||
* @param esp_time an instance of a ESPTime struct
|
* @param esp_time an instance of a ESPTime struct
|
||||||
@ -74,6 +76,8 @@ struct ESPTime {
|
|||||||
*/
|
*/
|
||||||
static bool strptime(const std::string &time_to_parse, ESPTime &esp_time);
|
static bool strptime(const std::string &time_to_parse, ESPTime &esp_time);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
/// Convert a C tm struct instance with a C unix epoch timestamp to an ESPTime instance.
|
/// Convert a C tm struct instance with a C unix epoch timestamp to an ESPTime instance.
|
||||||
static ESPTime from_c_tm(struct tm *c_tm, time_t c_time);
|
static ESPTime from_c_tm(struct tm *c_tm, time_t c_time);
|
||||||
|
|
||||||
|
@ -1,20 +1,22 @@
|
|||||||
|
from __future__ import annotations
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
from io import StringIO
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from typing import Optional
|
from esphome.yaml_util import parse_yaml
|
||||||
|
from esphome.config import validate_config, _format_vol_invalid, Config
|
||||||
from esphome.config import load_config, _format_vol_invalid, Config
|
|
||||||
from esphome.core import CORE, DocumentRange
|
from esphome.core import CORE, DocumentRange
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
|
|
||||||
|
|
||||||
def _get_invalid_range(res: Config, invalid: cv.Invalid) -> Optional[DocumentRange]:
|
def _get_invalid_range(res: Config, invalid: cv.Invalid) -> DocumentRange | None:
|
||||||
return res.get_deepest_document_range_for_path(
|
return res.get_deepest_document_range_for_path(
|
||||||
invalid.path, invalid.error_message == "extra keys not allowed"
|
invalid.path, invalid.error_message == "extra keys not allowed"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _dump_range(range: Optional[DocumentRange]) -> Optional[dict]:
|
def _dump_range(range: DocumentRange | None) -> dict | None:
|
||||||
if range is None:
|
if range is None:
|
||||||
return None
|
return None
|
||||||
return {
|
return {
|
||||||
@ -56,6 +58,25 @@ class VSCodeResult:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _read_file_content_from_json_on_stdin() -> str:
|
||||||
|
"""Read the content of a json encoded file from stdin."""
|
||||||
|
data = json.loads(input())
|
||||||
|
assert data["type"] == "file_response"
|
||||||
|
return data["content"]
|
||||||
|
|
||||||
|
|
||||||
|
def _print_file_read_event(path: str) -> None:
|
||||||
|
"""Print a file read event."""
|
||||||
|
print(
|
||||||
|
json.dumps(
|
||||||
|
{
|
||||||
|
"type": "read_file",
|
||||||
|
"path": path,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def read_config(args):
|
def read_config(args):
|
||||||
while True:
|
while True:
|
||||||
CORE.reset()
|
CORE.reset()
|
||||||
@ -68,9 +89,17 @@ def read_config(args):
|
|||||||
CORE.config_path = os.path.join(args.configuration, f)
|
CORE.config_path = os.path.join(args.configuration, f)
|
||||||
else:
|
else:
|
||||||
CORE.config_path = data["file"]
|
CORE.config_path = data["file"]
|
||||||
|
|
||||||
|
file_name = CORE.config_path
|
||||||
|
_print_file_read_event(file_name)
|
||||||
|
raw_yaml = _read_file_content_from_json_on_stdin()
|
||||||
|
command_line_substitutions: dict[str, Any] = (
|
||||||
|
dict(args.substitution) if args.substitution else {}
|
||||||
|
)
|
||||||
vs = VSCodeResult()
|
vs = VSCodeResult()
|
||||||
try:
|
try:
|
||||||
res = load_config(dict(args.substitution) if args.substitution else {})
|
config = parse_yaml(file_name, StringIO(raw_yaml))
|
||||||
|
res = validate_config(config, command_line_substitutions)
|
||||||
except Exception as err: # pylint: disable=broad-except
|
except Exception as err: # pylint: disable=broad-except
|
||||||
vs.add_yaml_error(str(err))
|
vs.add_yaml_error(str(err))
|
||||||
else:
|
else:
|
||||||
|
@ -417,20 +417,25 @@ def load_yaml(fname: str, clear_secrets: bool = True) -> Any:
|
|||||||
return _load_yaml_internal(fname)
|
return _load_yaml_internal(fname)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_yaml(file_name: str, file_handle: TextIOWrapper) -> Any:
|
||||||
|
"""Parse a YAML file."""
|
||||||
|
try:
|
||||||
|
return _load_yaml_internal_with_type(ESPHomeLoader, file_name, file_handle)
|
||||||
|
except EsphomeError:
|
||||||
|
# Loading failed, so we now load with the Python loader which has more
|
||||||
|
# readable exceptions
|
||||||
|
# Rewind the stream so we can try again
|
||||||
|
file_handle.seek(0, 0)
|
||||||
|
return _load_yaml_internal_with_type(
|
||||||
|
ESPHomePurePythonLoader, file_name, file_handle
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _load_yaml_internal(fname: str) -> Any:
|
def _load_yaml_internal(fname: str) -> Any:
|
||||||
"""Load a YAML file."""
|
"""Load a YAML file."""
|
||||||
try:
|
try:
|
||||||
with open(fname, encoding="utf-8") as f_handle:
|
with open(fname, encoding="utf-8") as f_handle:
|
||||||
try:
|
return parse_yaml(fname, f_handle)
|
||||||
return _load_yaml_internal_with_type(ESPHomeLoader, fname, f_handle)
|
|
||||||
except EsphomeError:
|
|
||||||
# Loading failed, so we now load with the Python loader which has more
|
|
||||||
# readable exceptions
|
|
||||||
# Rewind the stream so we can try again
|
|
||||||
f_handle.seek(0, 0)
|
|
||||||
return _load_yaml_internal_with_type(
|
|
||||||
ESPHomePurePythonLoader, fname, f_handle
|
|
||||||
)
|
|
||||||
except (UnicodeDecodeError, OSError) as err:
|
except (UnicodeDecodeError, OSError) as err:
|
||||||
raise EsphomeError(f"Error reading file {fname}: {err}") from err
|
raise EsphomeError(f"Error reading file {fname}: {err}") from err
|
||||||
|
|
||||||
|
@ -95,6 +95,7 @@ lib_deps =
|
|||||||
ESP8266mDNS ; mdns (Arduino built-in)
|
ESP8266mDNS ; mdns (Arduino built-in)
|
||||||
DNSServer ; captive_portal (Arduino built-in)
|
DNSServer ; captive_portal (Arduino built-in)
|
||||||
crankyoldgit/IRremoteESP8266@~2.8.4 ; heatpumpir
|
crankyoldgit/IRremoteESP8266@~2.8.4 ; heatpumpir
|
||||||
|
droscy/esp_wireguard@0.4.0 ; wireguard
|
||||||
build_flags =
|
build_flags =
|
||||||
${common:arduino.build_flags}
|
${common:arduino.build_flags}
|
||||||
-Wno-nonnull-compare
|
-Wno-nonnull-compare
|
||||||
@ -124,7 +125,7 @@ lib_deps =
|
|||||||
DNSServer ; captive_portal (Arduino built-in)
|
DNSServer ; captive_portal (Arduino built-in)
|
||||||
esphome/ESP32-audioI2S@2.0.7 ; i2s_audio
|
esphome/ESP32-audioI2S@2.0.7 ; i2s_audio
|
||||||
crankyoldgit/IRremoteESP8266@~2.8.4 ; heatpumpir
|
crankyoldgit/IRremoteESP8266@~2.8.4 ; heatpumpir
|
||||||
droscy/esp_wireguard@0.3.2 ; wireguard
|
droscy/esp_wireguard@0.4.0 ; wireguard
|
||||||
build_flags =
|
build_flags =
|
||||||
${common:arduino.build_flags}
|
${common:arduino.build_flags}
|
||||||
-DUSE_ESP32
|
-DUSE_ESP32
|
||||||
@ -143,7 +144,7 @@ framework = espidf
|
|||||||
lib_deps =
|
lib_deps =
|
||||||
${common:idf.lib_deps}
|
${common:idf.lib_deps}
|
||||||
espressif/esp32-camera@1.0.0 ; esp32_camera
|
espressif/esp32-camera@1.0.0 ; esp32_camera
|
||||||
droscy/esp_wireguard@0.3.2 ; wireguard
|
droscy/esp_wireguard@0.4.0 ; wireguard
|
||||||
build_flags =
|
build_flags =
|
||||||
${common:idf.build_flags}
|
${common:idf.build_flags}
|
||||||
-Wno-nonnull-compare
|
-Wno-nonnull-compare
|
||||||
|
@ -13,7 +13,6 @@ classifier =
|
|||||||
Programming Language :: C++
|
Programming Language :: C++
|
||||||
Programming Language :: Python :: 3
|
Programming Language :: Python :: 3
|
||||||
Topic :: Home Automation
|
Topic :: Home Automation
|
||||||
Topic :: Home Automation
|
|
||||||
|
|
||||||
[flake8]
|
[flake8]
|
||||||
max-line-length = 120
|
max-line-length = 120
|
||||||
|
@ -1,36 +1,14 @@
|
|||||||
---
|
|
||||||
esphome:
|
|
||||||
name: test10
|
|
||||||
build_path: build/test10
|
|
||||||
|
|
||||||
esp32:
|
|
||||||
board: esp32doit-devkit-v1
|
|
||||||
framework:
|
|
||||||
type: arduino
|
|
||||||
|
|
||||||
wifi:
|
wifi:
|
||||||
ssid: "MySSID1"
|
ssid: "MySSID1"
|
||||||
password: "password1"
|
password: "password1"
|
||||||
reboot_timeout: 3min
|
|
||||||
power_save_mode: high
|
|
||||||
|
|
||||||
network:
|
network:
|
||||||
enable_ipv6: true
|
enable_ipv6: true
|
||||||
|
|
||||||
logger:
|
|
||||||
level: VERBOSE
|
|
||||||
|
|
||||||
api:
|
|
||||||
reboot_timeout: 10min
|
|
||||||
|
|
||||||
web_server:
|
|
||||||
version: 3
|
|
||||||
|
|
||||||
time:
|
time:
|
||||||
- platform: sntp
|
- platform: sntp
|
||||||
|
|
||||||
wireguard:
|
wireguard:
|
||||||
id: vpn
|
|
||||||
address: 172.16.34.100
|
address: 172.16.34.100
|
||||||
netmask: 255.255.255.0
|
netmask: 255.255.255.0
|
||||||
# NEVER use the following keys for your vpn, they are now public!
|
# NEVER use the following keys for your vpn, they are now public!
|
62
tests/components/wireguard/test.esp32.yaml
Normal file
62
tests/components/wireguard/test.esp32.yaml
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
wifi:
|
||||||
|
ssid: "MySSID1"
|
||||||
|
password: "password1"
|
||||||
|
|
||||||
|
network:
|
||||||
|
enable_ipv6: true
|
||||||
|
|
||||||
|
time:
|
||||||
|
- platform: sntp
|
||||||
|
|
||||||
|
wireguard:
|
||||||
|
address: 172.16.34.100
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
# NEVER use the following keys for your vpn, they are now public!
|
||||||
|
private_key: wPBMxtNYH3mChicrbpsRpZIasIdPq3yZuthn23FbGG8=
|
||||||
|
peer_public_key: Hs2JfikvYU03/Kv3YoAs1hrUIPPTEkpsZKSPUljE9yc=
|
||||||
|
peer_preshared_key: 20fjM5GRnSolGPC5SRj9ljgIUyQfruv0B0bvLl3Yt60=
|
||||||
|
peer_endpoint: wg.server.example
|
||||||
|
peer_persistent_keepalive: 25s
|
||||||
|
peer_allowed_ips:
|
||||||
|
- 172.16.34.0/24
|
||||||
|
- 192.168.4.0/24
|
||||||
|
|
||||||
|
binary_sensor:
|
||||||
|
- platform: wireguard
|
||||||
|
status:
|
||||||
|
name: 'WireGuard Status'
|
||||||
|
enabled:
|
||||||
|
name: 'WireGuard Enabled'
|
||||||
|
|
||||||
|
sensor:
|
||||||
|
- platform: wireguard
|
||||||
|
latest_handshake:
|
||||||
|
name: 'WireGuard Latest Handshake'
|
||||||
|
|
||||||
|
text_sensor:
|
||||||
|
- platform: wireguard
|
||||||
|
address:
|
||||||
|
name: 'WireGuard Address'
|
||||||
|
|
||||||
|
button:
|
||||||
|
- platform: template
|
||||||
|
name: 'Toggle WireGuard'
|
||||||
|
entity_category: config
|
||||||
|
on_press:
|
||||||
|
- if:
|
||||||
|
condition: wireguard.enabled
|
||||||
|
then:
|
||||||
|
- wireguard.disable:
|
||||||
|
else:
|
||||||
|
- wireguard.enable:
|
||||||
|
|
||||||
|
- platform: template
|
||||||
|
name: 'Log WireGuard status'
|
||||||
|
entity_category: config
|
||||||
|
on_press:
|
||||||
|
- if:
|
||||||
|
condition: wireguard.peer_online
|
||||||
|
then:
|
||||||
|
- logger.log: 'wireguard remote peer is online'
|
||||||
|
else:
|
||||||
|
- logger.log: 'wireguard remote peer is offline'
|
62
tests/components/wireguard/test.esp8266.yaml
Normal file
62
tests/components/wireguard/test.esp8266.yaml
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
wifi:
|
||||||
|
ssid: "MySSID1"
|
||||||
|
password: "password1"
|
||||||
|
|
||||||
|
network:
|
||||||
|
enable_ipv6: true
|
||||||
|
|
||||||
|
time:
|
||||||
|
- platform: sntp
|
||||||
|
|
||||||
|
wireguard:
|
||||||
|
address: 172.16.34.100
|
||||||
|
netmask: 255.255.255.0
|
||||||
|
# NEVER use the following keys for your vpn, they are now public!
|
||||||
|
private_key: wPBMxtNYH3mChicrbpsRpZIasIdPq3yZuthn23FbGG8=
|
||||||
|
peer_public_key: Hs2JfikvYU03/Kv3YoAs1hrUIPPTEkpsZKSPUljE9yc=
|
||||||
|
peer_preshared_key: 20fjM5GRnSolGPC5SRj9ljgIUyQfruv0B0bvLl3Yt60=
|
||||||
|
peer_endpoint: wg.server.example
|
||||||
|
peer_persistent_keepalive: 25s
|
||||||
|
peer_allowed_ips:
|
||||||
|
- 172.16.34.0/24
|
||||||
|
- 192.168.4.0/24
|
||||||
|
|
||||||
|
binary_sensor:
|
||||||
|
- platform: wireguard
|
||||||
|
status:
|
||||||
|
name: 'WireGuard Status'
|
||||||
|
enabled:
|
||||||
|
name: 'WireGuard Enabled'
|
||||||
|
|
||||||
|
sensor:
|
||||||
|
- platform: wireguard
|
||||||
|
latest_handshake:
|
||||||
|
name: 'WireGuard Latest Handshake'
|
||||||
|
|
||||||
|
text_sensor:
|
||||||
|
- platform: wireguard
|
||||||
|
address:
|
||||||
|
name: 'WireGuard Address'
|
||||||
|
|
||||||
|
button:
|
||||||
|
- platform: template
|
||||||
|
name: 'Toggle WireGuard'
|
||||||
|
entity_category: config
|
||||||
|
on_press:
|
||||||
|
- if:
|
||||||
|
condition: wireguard.enabled
|
||||||
|
then:
|
||||||
|
- wireguard.disable:
|
||||||
|
else:
|
||||||
|
- wireguard.enable:
|
||||||
|
|
||||||
|
- platform: template
|
||||||
|
name: 'Log WireGuard status'
|
||||||
|
entity_category: config
|
||||||
|
on_press:
|
||||||
|
- if:
|
||||||
|
condition: wireguard.peer_online
|
||||||
|
then:
|
||||||
|
- logger.log: 'wireguard remote peer is online'
|
||||||
|
else:
|
||||||
|
- logger.log: 'wireguard remote peer is offline'
|
Loading…
Reference in New Issue
Block a user