mirror of
https://github.com/esphome/esphome.git
synced 2024-12-22 13:34:54 +01:00
commit
020da89b6a
10 changed files with 104 additions and 40 deletions
|
@ -14,6 +14,7 @@ FROM base-${BASEIMGTYPE} AS base
|
||||||
|
|
||||||
ARG TARGETARCH
|
ARG TARGETARCH
|
||||||
ARG TARGETVARIANT
|
ARG TARGETVARIANT
|
||||||
|
ARG PIP_EXTRA_INDEX_URL
|
||||||
|
|
||||||
# Note that --break-system-packages is used below because
|
# Note that --break-system-packages is used below because
|
||||||
# https://peps.python.org/pep-0668/ added a safety check that prevents
|
# https://peps.python.org/pep-0668/ added a safety check that prevents
|
||||||
|
@ -46,7 +47,7 @@ RUN \
|
||||||
libssl-dev=3.0.11-1~deb12u2 \
|
libssl-dev=3.0.11-1~deb12u2 \
|
||||||
libffi-dev=3.4.4-1 \
|
libffi-dev=3.4.4-1 \
|
||||||
cargo=0.66.0+ds1-1 \
|
cargo=0.66.0+ds1-1 \
|
||||||
pkg-config=1.8.1-1; \
|
pkg-config=1.8.1-1 \
|
||||||
gcc-arm-linux-gnueabihf=4:12.2.0-3; \
|
gcc-arm-linux-gnueabihf=4:12.2.0-3; \
|
||||||
fi; \
|
fi; \
|
||||||
rm -rf \
|
rm -rf \
|
||||||
|
@ -58,8 +59,8 @@ ENV \
|
||||||
# Fix click python3 lang warning https://click.palletsprojects.com/en/7.x/python3/
|
# Fix click python3 lang warning https://click.palletsprojects.com/en/7.x/python3/
|
||||||
LANG=C.UTF-8 LC_ALL=C.UTF-8 \
|
LANG=C.UTF-8 LC_ALL=C.UTF-8 \
|
||||||
# Store globally installed pio libs in /piolibs
|
# Store globally installed pio libs in /piolibs
|
||||||
PLATFORMIO_GLOBALLIB_DIR=/piolibs
|
PLATFORMIO_GLOBALLIB_DIR=/piolibs \
|
||||||
|
PIP_EXTRA_INDEX_URL=${PIP_EXTRA_INDEX_URL}
|
||||||
|
|
||||||
# Support legacy binaries on Debian multiarch system. There is no "correct" way
|
# Support legacy binaries on Debian multiarch system. There is no "correct" way
|
||||||
# to do this, other than using properly built toolchains...
|
# to do this, other than using properly built toolchains...
|
||||||
|
|
|
@ -143,15 +143,25 @@ def main():
|
||||||
imgs = [f"{params.build_to}:{tag}" for tag in tags_to_push]
|
imgs = [f"{params.build_to}:{tag}" for tag in tags_to_push]
|
||||||
imgs += [f"ghcr.io/{params.build_to}:{tag}" for tag in tags_to_push]
|
imgs += [f"ghcr.io/{params.build_to}:{tag}" for tag in tags_to_push]
|
||||||
|
|
||||||
|
build_args = [
|
||||||
|
"--build-arg",
|
||||||
|
f"BASEIMGTYPE={params.baseimgtype}",
|
||||||
|
"--build-arg",
|
||||||
|
f"BUILD_VERSION={args.tag}",
|
||||||
|
]
|
||||||
|
|
||||||
|
if args.arch == ARCH_ARMV7:
|
||||||
|
build_args += [
|
||||||
|
"--build-arg",
|
||||||
|
"PIP_EXTRA_INDEX_URL=https://www.piwheels.org/simple",
|
||||||
|
]
|
||||||
|
|
||||||
# 3. build
|
# 3. build
|
||||||
cmd = [
|
cmd = [
|
||||||
"docker",
|
"docker",
|
||||||
"buildx",
|
"buildx",
|
||||||
"build",
|
"build",
|
||||||
"--build-arg",
|
*build_args,
|
||||||
f"BASEIMGTYPE={params.baseimgtype}",
|
|
||||||
"--build-arg",
|
|
||||||
f"BUILD_VERSION={args.tag}",
|
|
||||||
"--cache-from",
|
"--cache-from",
|
||||||
f"type=registry,ref={cache_img}",
|
f"type=registry,ref={cache_img}",
|
||||||
"--file",
|
"--file",
|
||||||
|
|
|
@ -20,16 +20,21 @@ static const esp_bt_uuid_t NOTIFY_DESC_UUID = {
|
||||||
void BLEClientBase::setup() {
|
void BLEClientBase::setup() {
|
||||||
static uint8_t connection_index = 0;
|
static uint8_t connection_index = 0;
|
||||||
this->connection_index_ = connection_index++;
|
this->connection_index_ = connection_index++;
|
||||||
|
|
||||||
auto ret = esp_ble_gattc_app_register(this->app_id);
|
|
||||||
if (ret) {
|
|
||||||
ESP_LOGE(TAG, "gattc app register failed. app_id=%d code=%d", this->app_id, ret);
|
|
||||||
this->mark_failed();
|
|
||||||
}
|
|
||||||
this->set_state(espbt::ClientState::IDLE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BLEClientBase::loop() {
|
void BLEClientBase::loop() {
|
||||||
|
if (!esp32_ble::global_ble->is_active()) {
|
||||||
|
this->set_state(espbt::ClientState::INIT);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this->state_ == espbt::ClientState::INIT) {
|
||||||
|
auto ret = esp_ble_gattc_app_register(this->app_id);
|
||||||
|
if (ret) {
|
||||||
|
ESP_LOGE(TAG, "gattc app register failed. app_id=%d code=%d", this->app_id, ret);
|
||||||
|
this->mark_failed();
|
||||||
|
}
|
||||||
|
this->set_state(espbt::ClientState::IDLE);
|
||||||
|
}
|
||||||
// READY_TO_CONNECT means we have discovered the device
|
// READY_TO_CONNECT means we have discovered the device
|
||||||
// and the scanner has been stopped by the tracker.
|
// and the scanner has been stopped by the tracker.
|
||||||
if (this->state_ == espbt::ClientState::READY_TO_CONNECT) {
|
if (this->state_ == espbt::ClientState::READY_TO_CONNECT) {
|
||||||
|
|
|
@ -93,19 +93,19 @@ CONFIG_SCHEMA = cv.All(
|
||||||
cv.Inclusive(
|
cv.Inclusive(
|
||||||
CONF_BIT0_HIGH,
|
CONF_BIT0_HIGH,
|
||||||
"custom",
|
"custom",
|
||||||
): cv.positive_time_period_microseconds,
|
): cv.positive_time_period_nanoseconds,
|
||||||
cv.Inclusive(
|
cv.Inclusive(
|
||||||
CONF_BIT0_LOW,
|
CONF_BIT0_LOW,
|
||||||
"custom",
|
"custom",
|
||||||
): cv.positive_time_period_microseconds,
|
): cv.positive_time_period_nanoseconds,
|
||||||
cv.Inclusive(
|
cv.Inclusive(
|
||||||
CONF_BIT1_HIGH,
|
CONF_BIT1_HIGH,
|
||||||
"custom",
|
"custom",
|
||||||
): cv.positive_time_period_microseconds,
|
): cv.positive_time_period_nanoseconds,
|
||||||
cv.Inclusive(
|
cv.Inclusive(
|
||||||
CONF_BIT1_LOW,
|
CONF_BIT1_LOW,
|
||||||
"custom",
|
"custom",
|
||||||
): cv.positive_time_period_microseconds,
|
): cv.positive_time_period_nanoseconds,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
cv.has_exactly_one_key(CONF_CHIPSET, CONF_BIT0_HIGH),
|
cv.has_exactly_one_key(CONF_CHIPSET, CONF_BIT0_HIGH),
|
||||||
|
|
|
@ -66,6 +66,7 @@ from esphome.core import (
|
||||||
TimePeriod,
|
TimePeriod,
|
||||||
TimePeriodMicroseconds,
|
TimePeriodMicroseconds,
|
||||||
TimePeriodMilliseconds,
|
TimePeriodMilliseconds,
|
||||||
|
TimePeriodNanoseconds,
|
||||||
TimePeriodSeconds,
|
TimePeriodSeconds,
|
||||||
TimePeriodMinutes,
|
TimePeriodMinutes,
|
||||||
)
|
)
|
||||||
|
@ -718,6 +719,8 @@ def time_period_str_unit(value):
|
||||||
raise Invalid("Expected string for time period with unit.")
|
raise Invalid("Expected string for time period with unit.")
|
||||||
|
|
||||||
unit_to_kwarg = {
|
unit_to_kwarg = {
|
||||||
|
"ns": "nanoseconds",
|
||||||
|
"nanoseconds": "nanoseconds",
|
||||||
"us": "microseconds",
|
"us": "microseconds",
|
||||||
"microseconds": "microseconds",
|
"microseconds": "microseconds",
|
||||||
"ms": "milliseconds",
|
"ms": "milliseconds",
|
||||||
|
@ -739,7 +742,10 @@ def time_period_str_unit(value):
|
||||||
raise Invalid(f"Expected time period with unit, got {value}")
|
raise Invalid(f"Expected time period with unit, got {value}")
|
||||||
kwarg = unit_to_kwarg[one_of(*unit_to_kwarg)(match.group(2))]
|
kwarg = unit_to_kwarg[one_of(*unit_to_kwarg)(match.group(2))]
|
||||||
|
|
||||||
return TimePeriod(**{kwarg: float(match.group(1))})
|
try:
|
||||||
|
return TimePeriod(**{kwarg: float(match.group(1))})
|
||||||
|
except ValueError as e:
|
||||||
|
raise Invalid(e) from e
|
||||||
|
|
||||||
|
|
||||||
def time_period_in_milliseconds_(value):
|
def time_period_in_milliseconds_(value):
|
||||||
|
@ -749,10 +755,18 @@ def time_period_in_milliseconds_(value):
|
||||||
|
|
||||||
|
|
||||||
def time_period_in_microseconds_(value):
|
def time_period_in_microseconds_(value):
|
||||||
|
if value.nanoseconds is not None and value.nanoseconds != 0:
|
||||||
|
raise Invalid("Maximum precision is microseconds")
|
||||||
return TimePeriodMicroseconds(**value.as_dict())
|
return TimePeriodMicroseconds(**value.as_dict())
|
||||||
|
|
||||||
|
|
||||||
|
def time_period_in_nanoseconds_(value):
|
||||||
|
return TimePeriodNanoseconds(**value.as_dict())
|
||||||
|
|
||||||
|
|
||||||
def time_period_in_seconds_(value):
|
def time_period_in_seconds_(value):
|
||||||
|
if value.nanoseconds is not None and value.nanoseconds != 0:
|
||||||
|
raise Invalid("Maximum precision is seconds")
|
||||||
if value.microseconds is not None and value.microseconds != 0:
|
if value.microseconds is not None and value.microseconds != 0:
|
||||||
raise Invalid("Maximum precision is seconds")
|
raise Invalid("Maximum precision is seconds")
|
||||||
if value.milliseconds is not None and value.milliseconds != 0:
|
if value.milliseconds is not None and value.milliseconds != 0:
|
||||||
|
@ -761,6 +775,8 @@ def time_period_in_seconds_(value):
|
||||||
|
|
||||||
|
|
||||||
def time_period_in_minutes_(value):
|
def time_period_in_minutes_(value):
|
||||||
|
if value.nanoseconds is not None and value.nanoseconds != 0:
|
||||||
|
raise Invalid("Maximum precision is minutes")
|
||||||
if value.microseconds is not None and value.microseconds != 0:
|
if value.microseconds is not None and value.microseconds != 0:
|
||||||
raise Invalid("Maximum precision is minutes")
|
raise Invalid("Maximum precision is minutes")
|
||||||
if value.milliseconds is not None and value.milliseconds != 0:
|
if value.milliseconds is not None and value.milliseconds != 0:
|
||||||
|
@ -787,6 +803,9 @@ time_period_microseconds = All(time_period, time_period_in_microseconds_)
|
||||||
positive_time_period_microseconds = All(
|
positive_time_period_microseconds = All(
|
||||||
positive_time_period, time_period_in_microseconds_
|
positive_time_period, time_period_in_microseconds_
|
||||||
)
|
)
|
||||||
|
positive_time_period_nanoseconds = All(
|
||||||
|
positive_time_period, time_period_in_nanoseconds_
|
||||||
|
)
|
||||||
positive_not_null_time_period = All(
|
positive_not_null_time_period = All(
|
||||||
time_period, Range(min=TimePeriod(), min_included=False)
|
time_period, Range(min=TimePeriod(), min_included=False)
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
"""Constants used by esphome."""
|
"""Constants used by esphome."""
|
||||||
|
|
||||||
__version__ = "2023.11.0b1"
|
__version__ = "2023.11.0b2"
|
||||||
|
|
||||||
ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_"
|
ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_"
|
||||||
VALID_SUBSTITUTIONS_CHARACTERS = (
|
VALID_SUBSTITUTIONS_CHARACTERS = (
|
||||||
|
|
|
@ -87,6 +87,7 @@ def is_approximately_integer(value):
|
||||||
class TimePeriod:
|
class TimePeriod:
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
nanoseconds=None,
|
||||||
microseconds=None,
|
microseconds=None,
|
||||||
milliseconds=None,
|
milliseconds=None,
|
||||||
seconds=None,
|
seconds=None,
|
||||||
|
@ -136,13 +137,23 @@ class TimePeriod:
|
||||||
|
|
||||||
if microseconds is not None:
|
if microseconds is not None:
|
||||||
if not is_approximately_integer(microseconds):
|
if not is_approximately_integer(microseconds):
|
||||||
raise ValueError("Maximum precision is microseconds")
|
frac_microseconds, microseconds = math.modf(microseconds)
|
||||||
|
nanoseconds = (nanoseconds or 0) + frac_microseconds * 1000
|
||||||
self.microseconds = int(round(microseconds))
|
self.microseconds = int(round(microseconds))
|
||||||
else:
|
else:
|
||||||
self.microseconds = None
|
self.microseconds = None
|
||||||
|
|
||||||
|
if nanoseconds is not None:
|
||||||
|
if not is_approximately_integer(nanoseconds):
|
||||||
|
raise ValueError("Maximum precision is nanoseconds")
|
||||||
|
self.nanoseconds = int(round(nanoseconds))
|
||||||
|
else:
|
||||||
|
self.nanoseconds = None
|
||||||
|
|
||||||
def as_dict(self):
|
def as_dict(self):
|
||||||
out = OrderedDict()
|
out = OrderedDict()
|
||||||
|
if self.nanoseconds is not None:
|
||||||
|
out["nanoseconds"] = self.nanoseconds
|
||||||
if self.microseconds is not None:
|
if self.microseconds is not None:
|
||||||
out["microseconds"] = self.microseconds
|
out["microseconds"] = self.microseconds
|
||||||
if self.milliseconds is not None:
|
if self.milliseconds is not None:
|
||||||
|
@ -158,6 +169,8 @@ class TimePeriod:
|
||||||
return out
|
return out
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
if self.nanoseconds is not None:
|
||||||
|
return f"{self.total_nanoseconds}ns"
|
||||||
if self.microseconds is not None:
|
if self.microseconds is not None:
|
||||||
return f"{self.total_microseconds}us"
|
return f"{self.total_microseconds}us"
|
||||||
if self.milliseconds is not None:
|
if self.milliseconds is not None:
|
||||||
|
@ -173,7 +186,11 @@ class TimePeriod:
|
||||||
return "0s"
|
return "0s"
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"TimePeriod<{self.total_microseconds}>"
|
return f"TimePeriod<{self.total_nanoseconds}ns>"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def total_nanoseconds(self):
|
||||||
|
return self.total_microseconds * 1000 + (self.nanoseconds or 0)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def total_microseconds(self):
|
def total_microseconds(self):
|
||||||
|
@ -201,35 +218,39 @@ class TimePeriod:
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
if isinstance(other, TimePeriod):
|
if isinstance(other, TimePeriod):
|
||||||
return self.total_microseconds == other.total_microseconds
|
return self.total_nanoseconds == other.total_nanoseconds
|
||||||
return NotImplemented
|
return NotImplemented
|
||||||
|
|
||||||
def __ne__(self, other):
|
def __ne__(self, other):
|
||||||
if isinstance(other, TimePeriod):
|
if isinstance(other, TimePeriod):
|
||||||
return self.total_microseconds != other.total_microseconds
|
return self.total_nanoseconds != other.total_nanoseconds
|
||||||
return NotImplemented
|
return NotImplemented
|
||||||
|
|
||||||
def __lt__(self, other):
|
def __lt__(self, other):
|
||||||
if isinstance(other, TimePeriod):
|
if isinstance(other, TimePeriod):
|
||||||
return self.total_microseconds < other.total_microseconds
|
return self.total_nanoseconds < other.total_nanoseconds
|
||||||
return NotImplemented
|
return NotImplemented
|
||||||
|
|
||||||
def __gt__(self, other):
|
def __gt__(self, other):
|
||||||
if isinstance(other, TimePeriod):
|
if isinstance(other, TimePeriod):
|
||||||
return self.total_microseconds > other.total_microseconds
|
return self.total_nanoseconds > other.total_nanoseconds
|
||||||
return NotImplemented
|
return NotImplemented
|
||||||
|
|
||||||
def __le__(self, other):
|
def __le__(self, other):
|
||||||
if isinstance(other, TimePeriod):
|
if isinstance(other, TimePeriod):
|
||||||
return self.total_microseconds <= other.total_microseconds
|
return self.total_nanoseconds <= other.total_nanoseconds
|
||||||
return NotImplemented
|
return NotImplemented
|
||||||
|
|
||||||
def __ge__(self, other):
|
def __ge__(self, other):
|
||||||
if isinstance(other, TimePeriod):
|
if isinstance(other, TimePeriod):
|
||||||
return self.total_microseconds >= other.total_microseconds
|
return self.total_nanoseconds >= other.total_nanoseconds
|
||||||
return NotImplemented
|
return NotImplemented
|
||||||
|
|
||||||
|
|
||||||
|
class TimePeriodNanoseconds(TimePeriod):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class TimePeriodMicroseconds(TimePeriod):
|
class TimePeriodMicroseconds(TimePeriod):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ from esphome.core import (
|
||||||
TimePeriodMicroseconds,
|
TimePeriodMicroseconds,
|
||||||
TimePeriodMilliseconds,
|
TimePeriodMilliseconds,
|
||||||
TimePeriodMinutes,
|
TimePeriodMinutes,
|
||||||
|
TimePeriodNanoseconds,
|
||||||
TimePeriodSeconds,
|
TimePeriodSeconds,
|
||||||
)
|
)
|
||||||
from esphome.helpers import cpp_string_escape, indent_all_but_first_and_last
|
from esphome.helpers import cpp_string_escape, indent_all_but_first_and_last
|
||||||
|
@ -351,6 +352,8 @@ def safe_exp(obj: SafeExpType) -> Expression:
|
||||||
return IntLiteral(obj)
|
return IntLiteral(obj)
|
||||||
if isinstance(obj, float):
|
if isinstance(obj, float):
|
||||||
return FloatLiteral(obj)
|
return FloatLiteral(obj)
|
||||||
|
if isinstance(obj, TimePeriodNanoseconds):
|
||||||
|
return IntLiteral(int(obj.total_nanoseconds))
|
||||||
if isinstance(obj, TimePeriodMicroseconds):
|
if isinstance(obj, TimePeriodMicroseconds):
|
||||||
return IntLiteral(int(obj.total_microseconds))
|
return IntLiteral(int(obj.total_microseconds))
|
||||||
if isinstance(obj, TimePeriodMilliseconds):
|
if isinstance(obj, TimePeriodMilliseconds):
|
||||||
|
|
|
@ -4,6 +4,7 @@ import base64
|
||||||
import binascii
|
import binascii
|
||||||
import codecs
|
import codecs
|
||||||
import collections
|
import collections
|
||||||
|
import datetime
|
||||||
import functools
|
import functools
|
||||||
import gzip
|
import gzip
|
||||||
import hashlib
|
import hashlib
|
||||||
|
@ -1406,15 +1407,17 @@ def make_app(debug=get_bool_env(ENV_DEV)):
|
||||||
)
|
)
|
||||||
|
|
||||||
class StaticFileHandler(tornado.web.StaticFileHandler):
|
class StaticFileHandler(tornado.web.StaticFileHandler):
|
||||||
def set_extra_headers(self, path):
|
def get_cache_time(
|
||||||
if "favicon.ico" in path:
|
self, path: str, modified: datetime.datetime | None, mime_type: str
|
||||||
self.set_header("Cache-Control", "max-age=84600, public")
|
) -> int:
|
||||||
else:
|
"""Override to customize cache control behavior."""
|
||||||
if debug:
|
if debug:
|
||||||
self.set_header(
|
return 0
|
||||||
"Cache-Control",
|
# Assets that are hashed have ?hash= in the URL, all javascript
|
||||||
"no-store, no-cache, must-revalidate, max-age=0",
|
# filenames hashed so we can cache them for a long time
|
||||||
)
|
if "hash" in self.request.arguments or "/javascript" in mime_type:
|
||||||
|
return self.CACHE_MAX_AGE
|
||||||
|
return super().get_cache_time(path, modified, mime_type)
|
||||||
|
|
||||||
app_settings = {
|
app_settings = {
|
||||||
"debug": debug,
|
"debug": debug,
|
||||||
|
|
|
@ -116,14 +116,16 @@ class TestTimePeriod:
|
||||||
|
|
||||||
assert actual == expected
|
assert actual == expected
|
||||||
|
|
||||||
def test_init__microseconds_with_fraction(self):
|
def test_init__nanoseconds_with_fraction(self):
|
||||||
with pytest.raises(ValueError, match="Maximum precision is microseconds"):
|
with pytest.raises(ValueError, match="Maximum precision is nanoseconds"):
|
||||||
core.TimePeriod(microseconds=1.1)
|
core.TimePeriod(nanoseconds=1.1)
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"kwargs, expected",
|
"kwargs, expected",
|
||||||
(
|
(
|
||||||
({}, "0s"),
|
({}, "0s"),
|
||||||
|
({"nanoseconds": 1}, "1ns"),
|
||||||
|
({"nanoseconds": 1.0001}, "1ns"),
|
||||||
({"microseconds": 1}, "1us"),
|
({"microseconds": 1}, "1us"),
|
||||||
({"microseconds": 1.0001}, "1us"),
|
({"microseconds": 1.0001}, "1us"),
|
||||||
({"milliseconds": 2}, "2ms"),
|
({"milliseconds": 2}, "2ms"),
|
||||||
|
|
Loading…
Reference in a new issue