Merge pull request #6440 from esphome/bump-2024.3.1

2024.3.1
This commit is contained in:
Jesse Hills 2024-03-27 14:14:45 +13:00 committed by GitHub
commit 3290ab7f42
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 80 additions and 59 deletions

View file

@ -42,19 +42,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;

View file

@ -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()

View file

@ -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:

View file

@ -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):

View file

@ -1,6 +1,6 @@
"""Constants used by esphome.""" """Constants used by esphome."""
__version__ = "2024.3.0" __version__ = "2024.3.1"
ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_"
VALID_SUBSTITUTIONS_CHARACTERS = ( VALID_SUBSTITUTIONS_CHARACTERS = (

View file

@ -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))

View file

@ -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);

View file

@ -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:

View file

@ -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